How to send EOF to a process in Java? - java

I want to run groff in a Java program. The input comes from a string. In real command line, we will terminate the input by ^D in Linux/Mac. So how to send this terminator in Java program?
String usage +=
".Dd \\[year]\n"+
".Dt test 1\n"+
".Os\n"+
".Sh test\n"+
"^D\n"; // <--- EOF here?
Process groff = Runtime.getRuntime().exec("groff -mandoc -T ascii -");
groff.getOutputStream().write(usage.getBytes());
byte[] buffer = new byte[1024];
groff.getInputStream().read(buffer);
String s = new String(buffer);
System.out.println(s);
Or any other idea?

^D isn't a character; it's a command interpreted by your shell telling it to close the stream to the process (thus the process receives EOF on stdin).
You need to do the same in your code; flush and close the OutputStream:
String usage =
".Dd \\[year]\n" +
".Dt test 1\n" +
".Os\n" +
".Sh test\n";
...
OutputStream out = groff.getOutputStream();
out.write(usage.getBytes());
out.close();
...

I wrote this utility method:
public static String pipe(String str, String command2) throws IOException, InterruptedException {
Process p2 = Runtime.getRuntime().exec(command2);
OutputStream out = p2.getOutputStream();
out.write(str.getBytes());
out.close();
p2.waitFor();
BufferedReader reader
= new BufferedReader(new InputStreamReader(p2.getInputStream()));
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
return sb.toString();
}

Related

URLConnection doesn't read whole page

In my app I need to download some web page. I do it in a way like this
URL url = new URL(myUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(5000000);//5 seconds to download
conn.setConnectTimeout(5000000);//5 seconds to connect
conn.setRequestMethod("GET");
conn.setDoInput(true);
conn.connect();
int response = conn.getResponseCode();
is = conn.getInputStream();
String s = readIt(is, len);
System.out.println("got: " + s);
My readIt function is:
public String readIt(InputStream stream) throws IOException {
int len = 10000;
Reader reader;
reader = new InputStreamReader(stream, "UTF-8");
char[] buffer = new char[len];
reader.read(buffer);
return new String(buffer);
}
The problem is that It doesn't dowload the whole page. For example, if myUrl is "https://wikipedia.org", then the output is
How can I download the whole page?
Update
Second answer from here Read/convert an InputStream to a String solved my problem. The problem is in readIt function. You should read response from InputStream like this:
static String convertStreamToString(java.io.InputStream is) {
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
There are a number of mistakes your code:
You are reading into a character buffer with a fixed size.
You are ignoring the result of the read(char[]) method. It returns the number of characters actually read ... and you need to use that.
You are assuming that read(char[]) will read all of the data. In fact, it is only guaranteed to return at least one character ... or zero to indicate that you have reached the end of stream. When you reach from a network connection, you are liable to only get the data that has already been sent by the other end and buffered locally.
When you create the String from the char[] you are assuming that every position in the character array contains a character from your stream.
There are multiple ways to do it correctly, and this is one way:
public String readIt(InputStream stream) throws IOException {
Reader reader = new InputStreamReader(stream, "UTF-8");
char[] buffer = new char[4096];
StringBuilder builder = new StringBuilder();
int len;
while ((len = reader.read(buffer) > 0) {
builder.append(buffer, 0, len);
}
return builder.toString();
}
Another way to do it is to look for an existing 3rd-party library method with a readFully(Reader) method.
You need to read in a loop till there are no more bytes left in the InputStream.
while (-1 != (len = in.read(buffer))) { //do stuff here}
You are reading only 10000 bytes from the input stream.
Use a BufferedReader to make your life easier.
public String readIt(InputStream stream) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
StringBuilder out = new StringBuilder();
String newLine = System.getProperty("line.separator");
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append(newLine);
}
return out.toString();
}

Return BufferedReader

I ran into a problem which I don't have any idea what could be happening.
I have a class which calls a second class to send a command throught ssh and return the result.
The result is a BufferedReader, and then this result is treated in my first class.
First class:
.
.
.
String command = "ping " + ip + " -c 1";
BufferedReader result = instance.sendCommand(command);
// close only after all commands are sent
System.out.println("out of sshManager result : "+result);
System.out.println("out of sshManager line : "+result.readLine());
String line = null;
while ((line = result.readLine()) != null) {
System.out.println("out of sshManager line: "+line);
}
Second class:
public BufferedReader sendCommand(String command) throws JSchException, IOException {
//StringBuilder outputBuffer = new StringBuilder();
Channel channel = sesConnection.openChannel("exec");
((ChannelExec) channel).setCommand(command);
channel.connect();
InputStream commandOutput = channel.getInputStream();
System.out.println("This is in sshmanager SSHManager: " + commandOutput);
result = new BufferedReader(new InputStreamReader(commandOutput));
String line = null;
System.out.println(" sshmanager result : " + result());
System.out.println(" sshmanager result line : " + result.readLine());
while ((line = result.readLine()) != null) {
System.out.println("in sshmanager: " + line);
}
System.out.println("in sshmanager result : " + result);
channel.disconnect();
return result;
}
Result:
This is in sshmanager SSHManager: com.jcraft.jsch.Channel$MyPipedInputStream#40d5bd18
sshmanager result : java.io.BufferedReader#10a5ae6e
sshmanager result line : PING 192.168.11.11 (192.168.11.11) 56(84) bytes of data.
in sshmanager: From 192.168.11.77 icmp_seq=1 Destination Host Unreachable
in sshmanager:
in sshmanager: --- 192.168.11.11 ping statistics ---
in sshmanager: 1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 3006ms
in sshmanager:
in sshmanager result: java.io.BufferedReader#10a5ae6e
out of sshManager result: java.io.BufferedReader#10a5ae6e
out of sshManager line: null
The objects is well created in my second class but I don't know why, when I try to manage the object in my first class the content is null.
Do you have any idea what is worng?
The BufferedReader has already read through the file when it is returned to the first method. You may want to return a List<String> containing the contents of the file instead of the BufferedReader.
public List<String> sendCommand(String command) throws JSchException, IOException {
List<String> lines = new LinkedList<String>();
Channel channel = sesConnection.openChannel("exec");
((ChannelExec) channel).setCommand(command);
channel.connect();
InputStream commandOutput = channel.getInputStream();
result = new BufferedReader(new InputStreamReader(commandOutput));
String line = null;
while ((line = result.readLine()) != null) {
lines.add(line);
}
channel.disconnect();
return lines;
}
It's all coming from the concept of streaming.
Once you advance through the stream in the "second class", you actually consume the stream. So, at the moment when you return it to the "first class", the stream is entirely consumed. Therefore, you cannot stream anything anymore.
Please note, that even if you wrap the stream into a reader, the read operations (e.g. readLine) are forwarded to the stream.
A bufferred reader is like a string of bytes that you can read from once.
Your basic problem is that inside the function you've already extracted the text from the reader, so when you try again you just get null (because the reader is NOW empty).

Writing a simple HTTP server to accept GET requests

I'm trying to create a simple server that accepts a request, and then writes the content of a file to the browser that sent the request. The server connects and writes to the socket. However my browser says
no data received
and doesn't display anything.
public class Main {
/**
* #param args
*/
public static void main(String[] args) throws IOException{
while(true){
ServerSocket serverSock = new ServerSocket(6789);
Socket sock = serverSock.accept();
System.out.println("connected");
InputStream sis = sock.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(sis));
String request = br.readLine(); // Now you get GET index.html HTTP/1.1`
String[] requestParam = request.split(" ");
String path = requestParam[1];
System.out.println(path);
PrintWriter out = new PrintWriter(sock.getOutputStream(), true);
File file = new File(path);
BufferedReader bfr = null;
String s = "Hi";
if (!file.exists() || !file.isFile()) {
System.out.println("writing not found...");
out.write("HTTP/1.0 200 OK\r\n");
out.write(new Date() + "\r\n");
out.write("Content-Type: text/html");
out.write("Content length: " + s.length() + "\r\n");
out.write(s);
}else{
FileReader fr = new FileReader(file);
bfr = new BufferedReader(fr);
String line;
while ((line = bfr.readLine()) != null) {
out.write(line);
}
}
if(bfr != null){
bfr.close();
}
br.close();
out.close();
serverSock.close();
}
}
}
Your code works for me (data shows up in the browser), if I use
http://localhost:6789/etc/hosts
and there is a file /etc/hosts (Linux filesystem notation).
If the file does not exist, this snippet
out.write("HTTP/1.0 200 OK\r\n");
out.write(new Date() + "\r\n");
out.write("Content-Type: text/html\r\n");
out.write("\r\n");
out.write("File " + file + " not found\r\n");
out.flush();
will return data that shows up in the browser: Note that I have explicitly added a call to flush() here. Make sure that out is flushed in the other case as well.
The other possibility is to reorder your close statements.
A quote from EJP's answer on How to close a socket:
You should close the outermost output stream you have created from the socket. That will flush it.
This is especially the case if the outermost output stream is (another quote from the same source):
a buffered output stream, or a stream wrapped around one. If you don't close that, it won't be flushed.
So out.close() should be called before br.close().

BufferedReader readLine() blocks

When receiving data using readLine(), even though I put a "\n" at the end of the message
using the .flush when sending the message, the while loop that reads my message still blocks.
Only when closing the socket connection, it leaves the loop.
Here's the client code :
bos = new BufferedOutputStream(socket.
getOutputStream());
bis = new BufferedInputStream(socket.
getInputStream());
osw = new OutputStreamWriter(bos, "UTF-8");
osw.write(REG_CMD + "\n");
osw.flush();
isr = new InputStreamReader(bis, "UTF-8");
BufferedReader br = new BufferedReader(isr);
String response = "";
String line;
while((line = br.readLine()) != null){
response += line;
}
and the server's code:
BufferedInputStream is;
BufferedOutputStream os;
is = new BufferedInputStream(connection.getInputStream());
os = new BufferedOutputStream(connection.getOutputStream());
isr = new InputStreamReader(is);
String query= "";
String line;
while((line = br.readLine()) != null){
query+= line;
}
String response = executeMyQuery(query);
osw = new OutputStreamWriter(os, "UTF-8");
osw.write(returnCode + "\n");
osw.flush();
My code blocks at the server while loop.
Thanks.
The BufferedReader will keep on reading the input until it reaches the end (end of file or stream or source etc). In this case, the 'end' is the closing of the socket. So as long as the Socket connection is open, your loop will run, and the BufferedReader will just wait for more input, looping each time a '\n' is reached.
I tried a lot of solutions but the only one not blocking the execution was the following:
BufferedReader inStream = new BufferedReader(new InputStreamReader(yourInputStream));
String line;
while(inStream.ready() && (line = inStream.readLine()) != null) {
System.out.println(line);
}
The inStream.ready() returns false if the next readLine() call will block the execution.
This is because of the condition in the while-loop: while((line = br.readLine()) != null)
you read a line on every iteration and leve the loop if readLine returns null.
readLine returns only null, if eof is reached (= socked is closed) and returns a String if a '\n' is read.
if you want to exit the loop on readLine, you can omit the whole while-loop und just do:
line = br.readLine()
This happens because the InputStream is not ready to be red, so it blocks on in.readLine() .
Please try this :
boolean exitCondition= false;
while(!exitCondition){
if(in.ready()){
if((line=in.readLine())!=null){
// do whatever you like with the line
}
}
}
Of course you have to control the exitCondition .
An other option can be the use of nio package, which allows asynchronised (not blocking) reading but it depend on your need.
It'd be better avoid using readline(). This method is dangerous for network communications because some servers don't return LF/CR symbols and your code will be stuck. When you read from a file it isn't critical because you will reach end of the file anyway and stream will be closed.
public String readResponse(InputStream inStreamFromServer, int timeout) throws Exception {
BufferedReader reader = new BufferedReader(new InputStreamReader(inStreamFromServer, Charsets.UTF_8));
char[] buffer = new char[8092];
boolean timeoutNotExceeded;
StringBuilder result = new StringBuilder();
final long startTime = System.nanoTime();
while ((timeoutNotExceeded = (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) < timeout))) {
if (reader.ready()) {
int charsRead = reader.read(buffer);
if (charsRead == -1) {
break;
}
result.append(buffer, 0, charsRead);
} else {
try {
Thread.sleep(timeout / 200);
} catch (InterruptedException ex) {
LOG.error("InterruptedException ex=", ex);
}
}
}
if (!timeoutNotExceeded) throw new SocketTimeoutException("Command timeout limit was exceeded: " + timeout);
return result.toString();
}
It has a timeout and you can interrupt communication if it take a lot of time
if you want to get what's in the socket without being forced to close it simply use ObjectInputStream and ObjectOutputStream ..
Example:
ObjectInputStream ois;
ObjectOutputStream oos;
ois = new ObjectInputStream(connection.getInputStream());
String dataIn = ois.readUTF(); //or dataIn = (String)ois.readObject();
oos = new ObjectOutputStream(connection.getOutputStream());
oos.writeUtf("some message"); //or use oos.writeObject("some message");
oos.flush();
.....
readline() and read() will be blocked while socket doesn't close. So you should close socket:
Socket.shutdownInput();//after reader
Socket.shutdownOutput();//after wirite
rather than Socket.close();

Splitting strings by newline trouble

I am reading in a file that is being sent though a socket and then trying to split it via newlines (\n), when I read in the file I am using a byte[] and I convert the byte array to a string so that I can split it.
public String getUserFileData()
{
try
{
byte[] mybytearray = new byte[1024];
InputStream is = clientSocket.getInputStream();
int bytesRead = is.read(mybytearray, 0, mybytearray.length);
is.close();
return new String(mybytearray);
}
catch(IOException e)
{
}
return "";
}
Here is the code used to attempting to split the String
public void readUserFile(String userData, Log logger)
{
String[] data;
String companyName;
data = userData.split("\n");
username = data[0];
password = data[1].toCharArray();
companyName = data[2];
quota = Float.parseFloat(data[3]);
company = new Company();
company.readCompanyFile("C:\\Users\\Chris\\Documents\\NetBeansProjects\\ArFile\\ArFile Clients\\" + companyName + "\\"
+ companyName + ".cmp");
cloudFiles = new CloudFiles();
cloudFiles.readCloudFiles(this, logger);
}
It causes this error
Exception in thread "AWT-EventQueue-1" java.lang.ArrayIndexOutOfBoundsException
You can use the readLine method in BufferedReader class.
Wrap the InputStream under InputStreamReader, and wrap it under BufferedReader:
InputStream is = clientSocket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
Please also check the encoding of the stream - you might need to specify the encoding in the constructor of InputStreamReader.
As stated in comments, using a BufferedReader would be best - you should be using an InputStreamReader anyway in order to convert from binary to text.
// Or use a different encoding - whatever's appropriate
BufferedReader reader = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream(), "UTF-8");
try {
String line;
// I'm assuming you want to read every incoming line
while ((line = reader.readLine()) != null) {
processLine(line);
}
} finally {
reader.close();
}
Note that it's important to state which encoding you want to use - otherwise it'll use the platform's default encoding, which will vary from machine to machine, whereas presumably the data is in one specific encoding. If you don't know which encoding that is yet, you need to find out. Until then, you simply can't reliably understand the data.
(I hope your real code doesn't have an empty catch block, by the way.)

Categories

Resources