Java how chain multiple PipedInput/Output streams - java

I created a CLI Java program that uses System.in and System.out and argument. It can "add" argument to System.in and write result to System.out. the Output of first rund can be fed into in put for 2nd run and so on.
In command line you can just pipe multiple calls.
I am currently writing a unit test for this and try to avoid creating copies in byte[] of input and output. My problem is: It seems forbidden to connect more than 2 PipedInput/Output Stream together?:
Here is current test:
#Test
void addDeviceToGivenFactoryJson() throws IOException {
// produce factory json
//we pipe the output directly into an input stream
PipedInputStream in = new PipedInputStream();
PipedOutputStream outPipe = new PipedOutputStream(in);
PrintStream out = new PrintStream(outPipe, true, StandardCharsets.UTF_8);
System.setOut(out);
//fills the pipe by running main in separate thread
// pipe contains factory json without device
new Thread(() -> Main.main(new String[]{})).start();
// connect to std in
PipedInputStream in2 = new PipedInputStream(outPipe); // <--- error here
System.setIn(in2);
new Thread(() -> Main.main(new String[]{MainTest.class.getResource("/device.json").getPath()})).start();
ProductionData productionData = om.readValue(in2, ProductionData.class);
Device device = productionData.getProductionData().get(0).getDevices().get(0);
assertNotNull(device);
}
Running this i get java.io.IOException: Already connected in line where i create in2 with connection to outPipe.
Is there a way to connect them or do i need to buffer it in byte[] or something similar?

Related

Error whle running python script from Java

I have been trying to invoke a Python 3.x program from Java. What I need is to get output from python and write it to a file. This is what I have done. This is creating a Json file but does not gives the output. Please help me out here.
public static void main(String[] args) throws ScriptException, IOException {
Process p = Runtime.getRuntime().exec("python <path to the file>/reg.py");
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String ret = in.readLine();
System.out.println(ret);
BufferedWriter out = new BufferedWriter(new FileWriter("<some path>/output.json"));
out.write(ret);
out.close();
}
Ok, so after our talk in the comments, your script throws an error because it cannot find a file output.txt. Now, the thing is that this is logged to an error stream not the regular one. What you should do is to open that stream and read it:
BufferedReader in = new BufferedReader(new InputStreamReader(p.getErrorStream()));
In fact, you could open both streams: errorStream and outputStream and read error stream first to check if it contains something (it means that execution failed). If it doesn't, open outputStream and read its contents normally.

What is the preferred way of piping information into another process in Java?

I need to pipe data into another process. The data is an array of strings that I concatenated into one large string. The external process accepts a text file. Currently, I am writing the string into a ByteArrayOutputStream but is there a better way to do this?
public OutputStream generateBoxFile() throws IOException {
OutputStream boxStream = new ByteArrayOutputStream();
for (String boxLine : boxLines) {
boxLine += "\n";
boxStream.write(boxLine.getBytes(Charset.forName("UTF-8")));
}
return boxStream;
}
EDIT: For further clarifications, I am launching a program called trainer which accepts a text file. So I would invoke this program like this in the shell ./trainer textfile. However, I want to do everything in memory, so I'm looking for a good way to write data into a temporary file that is not on disk and then feed this into trainer.
The simplest way to write a collection String to a file is to use a PrintWriter
public static void writeToFile(String filename, Iterable<String> strings) {
try (PrintWriter pw = new PrintWriter(filename)) {
for(String str : strings)
pw.println(str);
}
}
If you need to write UTF-8 you can change the encoding with
try (PrintWriter pw = new PrintWriter(
new OutputStreamWriter(new FileOutputStream(filename), "UTF-8")) {
You can easily pipe data to a process you've launched through its standard input stream. In the parent process, you can access the child's standard input stream through Process.getOutputStream().
This does require your child process to accept data through standard input rather than a file. Your child process currently gets its input from a file. Fortunately, you note in a comment that you own the code of the child process.

Call and return the output of a jar to another java program

I am calling jar in a java program. the inner jar returns some output. how should i read and display in following program ?
i am able to call the jar successfully but how to display the output ?
import java.io.InputStream;
public class call_xml_jar {
public static void main(String argv[]) {
try{
// Run a java app in a separate system process
Process proc = Runtime.getRuntime().exec("java -jar xml_validator.jar");
// Then retreive the process output
InputStream in = proc.getInputStream();
InputStream err = proc.getErrorStream();
System.out.println("Completed...");
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Output: Completed...
I want to print jar output as well
With the lines
InputStream in = proc.getInputStream();
InputStream err = proc.getErrorStream();
you are already on the way.
These streams give you access to the other application's standard output and standard error streams (respectively). By the way: You retrieve the other application's standard output stream by calling getInputStream(), as this is the view of your current application; you are inputting the other application's data.
Just to make it clear: The standard output and th standard error stream are accessed in an application by printing calls to System.out and System.err (respectively).
So, if you have - for example - System.out.println("Hello world") in the other application, you will retrieve the corresponding bytes (see below) in the input stream that you reference with the variable in of the above code snippet.
Normally, you are not interested in the bytes but you want to retrieve the String that you have placed into the output. So you must convert the bytes to a String. For this you normally must provide an encoding (the Java class for that is Charset). In fact, the platform's default encoding works in such cases.
The easiest way is to wrap the input stream in a buffered reader:
BufferedReader outReader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
The above mntioned platform's default encoding is used, when not specifying any character set in the InputStreamReader's constructor.
A BufferedReader knows a method readLine(), which you must use to get all the other application's output.
while(outReader.ready())
System.out.println(outReader.readLine())
One word about flushing: If you write data to the standard output stream, this stream is flushed only, when a newline is also written. This is done by calls to System.out.println(...). And this is the reason, why you must read entire lines from the reader.
Are you now able to assemble some code that reads out the other application's output? If not, you maybe should post another question.
I solved it myself.. Here is my solution...
// Then retreive the process output
InputStream in = proc.getInputStream();
InputStream err = proc.getErrorStream();
System.out.println("Completed...");
InputStreamReader is = new InputStreamReader(in);
StringBuilder sb=new StringBuilder();
BufferedReader br = new BufferedReader(is);
String read = br.readLine();
while(read != null) {
//System.out.println(read);
sb.append(read);
sb.append("\n");
read =br.readLine();
}
System.out.println(sb);

Reading InputStream from Java Interactive Process using ProcessBuilder

I am trying to run an interactive executable from Java application using ProcessBuilder; it's supposed to take input, produce output and then wait for the next input. The main problem here with Input/Output streams. I send an input and get nothing. Here is the code:
private static Process process;
private static BufferedReader result;
private static PrintWriter input;
process = new ProcessBuilder("compile-lm", lmFile.toString(), " --score yes").redirectErrorStream(true).start();
input = new PrintWriter(new OutputStreamWriter(process.getOutputStream()), true);
input.println(message);
System.out.println(message);
result = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = new String();
while ((line = result.readLine()) != null)
{
/* Some processing for the read line */
System.out.println("output:\t" + line);
}
I have tried your code it works fine there is no problem with the code. I think that the problem with the command that you are trying to execute ( it returns nothing ). try to change parameters or even change the entire command to test. and if you can execute the comand in other place ( terminal for example try it and see the output with the same parameters )
I have used a similar setup many times over but can not find a working copy right now :( My first instinct though is to move the line where you initialise the reader (result variable) to before the one where you send the command out to the process (input.println(message)).
Try closing the output stream to the process. Basically you're at the mercy of whatever buffering is happening in the output side of the child process.

While trying to run subprocess it prints only stdout then terminates

I'm using Apache Commons Exec and trying to start subprocess that would work for the entire duration of application. It should start process, accepttwo input commands and just stay in background. Now its only accepts one command (at least what stdout shows) and terminates. Can you aid me?
CommandLine cmdLine = new CommandLine("app.exe");
cmdLine.addArgument("argument");
DefaultExecutor executor = new DefaultExecutor();
OutputStream os = new ByteArrayOutputStream();
InputStream is = new ByteArrayInputStream(("command1;\ncommand2;\n").getBytes());
executor.setStreamHandler(new PumpStreamHandler(os,null,is));
DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
executor.execute(cmdLine,resultHandler);
System.out.println(os.toString());
resultHandler.waitFor();
I think these two lines are in the wrong order:
System.out.println(os.toString());
resultHandler.waitFor();
Should be like this (to allow the process to complete it's output):
resultHandler.waitFor();
System.out.println(os.toString());
EDIT
Still not 100% sure what you are after, but think I missed the "just stay in background" part of your original request. One way to achieve this would be to use a PipedInputStream & PipedOutputStream pair to talk to the process. When you are done, you can close the output stream. If you wanted access to the output from the process before it finishes, you could use a similar technique for the output with the direction reversed.
I don't have a windows machine handy, but the following works for me:
public static void main(String[] args) {
try {
CommandLine cmdLine = new CommandLine("/bin/bash");
DefaultExecutor executor = new DefaultExecutor();
OutputStream os = new ByteArrayOutputStream();
PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);
executor.setStreamHandler(new PumpStreamHandler(os, null, pis));
DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
executor.execute(cmdLine, resultHandler);
PrintWriter pw = new PrintWriter(pos);
pw.println("ls -l /usr");
pw.println("pwd");
pw.close();
resultHandler.waitFor();
System.out.println(os.toString());
} catch (Exception e) {
e.printStackTrace();
}
}

Categories

Resources