ProcessBuilder or Runtime.getRuntime().exec won't complete jar execution - java

I have a jar which converts one XML to other XML format using XSLT in Java. The jar copies the output to some folder. It is working absolutely fine when am running it on command prompt but running it via Runtime.getRuntime().exec or ProcessBuilder, doesn't complete the process. Just 25 files are converted and it freezes. When i shutdown the process i can see all the files being loaded in the output folder which were not being loaded into the same folder.
Any suggestions?
My Code
private boolean runLoaderScript() throws IOException, InterruptedException {
String args[] = { "java", "-jar", "C:\\Users\\gursahibsahni\\Desktop\\jar\\epnlm_new-1.0.0-jar-with-dependencies_WSJ_stringdate.jar", "-c", "-f", "-d", "7", "C:\\Users\\gursahibsahni\\Desktop\\ConsynInput\\wsjInput\\input" };
ProcessBuilder builder = new ProcessBuilder(args);
Process qq = (builder).start();
qq.waitFor();
return true;
}
private boolean runValidator() throws IOException, InterruptedException {
Process validatorProcess = Runtime.getRuntime().exec("java -jar C:\\Users\\gursahibsahni\\Desktop\\jar\\wsj_jar_20140423.jar -efv -d 7 C:\\Users\\gursahibsahni\\Desktop\\ConsynInput\\wsjInput\\output");
return (validatorProcess.waitFor()) == 0 ? true : false;
}
Additionally, when am trying to import the jar in my project and call the main function to convert the XML, it is not converting the XML properly.
Meaning, the constants are coming up very nicely but the functions which are being called into class files to get data are not being called up during the import.
YES! Running the jar on command line is a success! It works flawlessly. But when imported it is not converting properly. Why such behavior ? Its very strange. Please help.

you have to consume the StdOut (and maybe StdErr) of your process ... otherwise
the process will hang when the buffer is filled up !
if you don't want to code that yourself you might have a look at Apache CommonsExec ... it helps with executing and handling external Processes https://commons.apache.org/proper/commons-exec/tutorial.html
Among other things it captures the subprocesses output using an org.apache.commons.exec.ExecuteStreamHandler.

Related

System command hanging when launched from within Java app but not from command line

I have a Linux (Ubuntu 14.04.5 LTS) system command (an executable JAR file) that runs successfully if launched manually from the command line but hangs indefinitely when launched from within a Java application (v1.8.0_191).
I didn't write the executable JAR (it's called Tabula, an open-source PDF parser, but I don't think what the system command does is actually relevant to my problem). The executable JAR file takes the path to a PDF file as one of it's arguments, and it outputs JSON data to standard out. The average completion time of the command is usually just a few seconds.
For the majority of the PDF files that I process, there are no problems, but for a small bunch of PDFs the application that I've written hangs. I've discovered that the problem is the executable JAR command is not returning, so my application is waiting indefinitely. If I run ps -elf | grep java I can see the command is still running. Occasionally, the process will consume a very small amount of memory and CPU, but I have no idea what it's doing.
However, if I run that exact same executable JAR command myself on the command line, then it completes and returns the expected output in a few seconds. On the face it you would think that because the issue is not random and is confined to specific PDF input files, then it must be the files that the executable JAR does not like. If that was true, then why does the command always complete successfully if I run it manually from the command line.
I've extracted the relevant code into something smaller and more testable:
public static void main(String[] args) throws Exception {
if (args.length != 1) {
throw new Exception("Expected: folio ID as argument");
}
String folioID = args[0];
String[] command = new String[] {
"java", "-jar", Config.tabulaJARFile,
"--stream",
"--format", "JSON",
"--pages", "all",
String.format("%s/%s.pdf", Config.TABULA_INPUT_DIR, folioID)
};
System.out.println("Executing ==> " + String.join(" ", command));
Process process = Runtime.getRuntime().exec(command);
System.out.println("Waiting...");
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new Exception("Invalid exit code ["+exitCode+"]");
}
System.out.println("Retrieving input stream...");
InputStream output = process.getInputStream();
String jsonData = IOUtils.toString(output, "UTF-8");
System.out.println(jsonData);
}
Example output of a run would be:
Executing ==> java -jar /home/ubuntu/tabula/tabula-1.0.3-jar-with-dependencies.jar --stream --format JSON --pages all /home/ubuntu/tabula/input/CK12345.pdf
Waiting...
...and that will hang indefinitely, but if I run that exact same command myself on the command line it will complete in a few seconds. I've also tried creating the command with a ProcessBuilder but the result is the same. Why might this be happening?
It seems that if I redirect the output of the process using a ProcessBuilder e.g. processBuilder.redirectOutput(Redirect.INHERIT) then the command completes successfully, but it prints the output to the console instead of letting me capture it in the application. I've managed a workaround by redirecting the output of the command to a file instead, then reading the contents of the file.

How to pass file as an argument to Python process invoked by Java

I am running Java program to call Python process using process builder as shown below,
processBuilder = new ProcessBuilder(
Arrays.asList(
"/usr/bin/python",
"/opt/gui/oc_db5.py",
"-c",
"/opt/gui/test.json")
);
processBuilder.directory(new File("/opt/gui"));
processBuilder.start();
Location of python program is under /opt/gui directory and there is one test.json file also needs to be passed as parameter, with "-c" option, However what i am seeing is that system is appending location of java program with path of JSON file and then pick the .JSON file causing issue for Python code.
What actually python program is getting is /opt/java//opt/gui/test.json. I tried ../../ as well but it didn't work with test.json file.
Is there a way i can specify .JSON file as an argument to python program?
This seemed to work for me. I mean, it fixed the directory problem.
try {
int exitCode = Runtime.getRuntime().exec("python /opt/gui/oc_db5.py -c /opt/gui/test.json", null, new File("/")).waitFor(); // run program and get exit code
} catch(Exception e) { // is there an error?
e.printStackTrace(); // print error
}

Java subprocess extra output to console

I've written a java-program that starts sub-processes(through the ProcessBuilder) and treats their I/O (Process.getInputStream(),Process.getErrorStream(),Process.getOutputStream()) streams in different threads.
My java-program works as a server and I don't want extra output to the console from sub-processes and
in the IDE's console it works fine, but if I try to run the program java -jar ... from the windows or linux console, the sub-process writes output to my program and also to the console.
P.S. I execute Python script files from java, for example my python script script prints a test string "Hello World" 1000 times.
My program treats it fine, but I see it's output also in the console.
Is there some kind of "console" that I need to detach from the process somehow?
UPDATE:
Here I get a ProcessBuilder for running the python(I pass the directory of the script as a parameter):
private ProcessBuilder getBuilder(File directory) {
return new ProcessBuilder("python", "-u").directory(directory);
}
Here I start a Process from the builder and return it to further treatment(
I pass the directory of the script and args to the script):
public Process execute(Path dir, String... args) {
if (!Files.exists(dir) || !Files.isDirectory(dir))
throw new IllegalArgumentException("Provided path " + dir + " is not a directory or doesn't exist");
ProcessBuilder builder = getBuilder(dir.toFile());
if (args.length > 0) {
builder.command().addAll(Arrays.asList(args));
}
try {
return builder.start();
} catch (IOException io) {
io.printStackTrace();
return null;
}
}
Here is a test dumb script:
i=0;
while True:
if i%1000000==0:
print ('Hello, world!')
i+=1
I don't know if there a sence to post my stream handlers because they look like standard gobblers that was in other topics but they read from process out/error to the queue and get input from another queue and java program treats queues and has no output to console except logger messages.
But it seems the sub-process itself writes to the console, but maybe I'm wrong.
I didn't find out how to do it programmaticaly.
The only way that helps me is to redirect the output to /dev/null when I start the jar file.
It's quite appropriate, because I also get the output by network to the client.

bat file does not execute within Java

I have written some code for executing .bat file. which contains some
commands like setting java classpath,etc..And finally there is one command
which runs a Java class file.The HelloWorld class converts some xml file and generating a new xml file in some folder. When I double click .bat file, it executes fine,
but when I try to run I am not getting any output as I was getting through
double click the .bat file. How to make a batch execute and probably it would be nice
if I could see the results through Java console.
Following is MyJava code to execute the .bat file
public void run2() {
try {
String []commands = {"cmd.exe","/C","C:/MyWork/Java/classes/run.bat"} ;
Process p = Runtime.getRuntime().exec(commands);
BufferedReader in = new BufferedReader(new InputStreamReader(
p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
And below the some commands which has been set to .bat file
set CLASSPATH=%CLASSPATH%;C:/MyWork/Java
set CLASSPATH=%CLASSPATH%;C:/MyWork/Java/classes
java -cp test.jar;test2.jar test.HelloWorld
Tried with "/C" commad as well. It does not execute. Actually it does not give effect of double click the .bat file. Is there any other way that I can try with?
I can see the contents inside the .bat file through Eclipse console. But it does not give the desired output. Desired output means when I double click .bat file, it executes well. But through java call, I can see the contents only .
When using cmd.exe use /C-Parameter to pass command:
String []commands = {"cmd.exe","/C","C:/MyWork/Java/classes/run.bat"} ;
according to this, the Windows CMD needs the /c argument, to execute commands like this. try this:
String []commands = {"cmd.exe","/c","C:/MyWork/Java/classes/run.bat"} ;
Windows uses \ backslash for Windows and MS-DOS path delimiter. Forward slash / is accepted by Java in the java.io package and translated to be a path delimiter, but will not be directly acceptable to Windows or accepted by the cmd.exe shell.
You may also need to specify either the working directory for the batch file to be executed in, or possibly a full path to the cmd.exe command interpreter.
See: Runtime.exec (String[] cmdarray, String[] envp, File dir)
String[] commands = {"C:\\Windows\\System32\\cmd.exe", "/c",
"C:\\MyWork\\Java\\classes\\run.bat"};
File workDir = new File( "C:/MyWork");
Process process = Runtime.getRuntime().exec( commands, null, workDir);
To verify if the batch file is run at all, add a pause command to the batch file. That will keep the window open so you can verify if the batch file is launched at all, and debug this stage-by-stage.
You do not read the error output of your batch file, therefore, you'll never see any error messages printed from there or from CMD.EXE itself. In addition, the sub-program may stall and just wait for you to read the error stream.
Please see related discussions here: How to make a java program to print both out.println() and err.println() statements?

How to use "cd" command using Java runtime?

I've created a standalone java application in which I'm trying to change the directory using the "cd" command in Ubuntu 10.04 terminal. I've used the following code.
String[] command = new String[]{"cd",path};
Process child = Runtime.getRuntime().exec(command, null);
But the above code gives the following error
Exception in thread "main" java.io.IOException: Cannot run program "cd": java.io.IOException: error=2, No such file or directory
Can anyone please tell me how to implement it?
There is no executable called cd, because it can't be implemented in a separate process.
The problem is that each process has its own current working directory and implementing cd as a separate process would only ever change that processes current working directory.
In a Java program you can't change your current working directory and you shouldn't need to. Simply use absolute file paths.
The one case where the current working directory matters is executing an external process (using ProcessBuilder or Runtime.exec()). In those cases you can specify the working directory to use for the newly started process explicitly (ProcessBuilder.directory() and the three-argument Runtime.exec() respectively).
Note: the current working directory can be read from the system property user.dir. You might feel tempted to set that system property. Note that doing so will lead to very bad inconsistencies, because it's not meant to be writable.
See the link below (this explains how to do it):
http://alvinalexander.com/java/edu/pj/pj010016
i.e. :
String[] cmd = { "/bin/sh", "-c", "cd /var; ls -l" };
Process p = Runtime.getRuntime().exec(cmd);
Have you explored this exec command for a java Runtime, Create a file object with the path you want to "cd" to and then input it as a third parameter for the exec method.
public Process exec(String command,
String[] envp,
File dir)
throws IOException
Executes the specified string command in a separate process with the specified environment and working directory.
This is a convenience method. An invocation of the form exec(command, envp, dir) behaves in exactly the same way as the invocation exec(cmdarray, envp, dir), where cmdarray is an array of all the tokens in command.
More precisely, the command string is broken into tokens using a StringTokenizer created by the call new StringTokenizer(command) with no further modification of the character categories. The tokens produced by the tokenizer are then placed in the new string array cmdarray, in the same order.
Parameters:
command - a specified system command.
envp - array of strings, each element of which has environment variable settings in the format name=value, or null if the subprocess should inherit the environment of the current process.
dir - the working directory of the subprocess, or null if the subprocess should inherit the working directory of the current process.
Returns:
A new Process object for managing the subprocess
Throws:
SecurityException - If a security manager exists and its checkExec method doesn't allow creation of the subprocess
IOException - If an I/O error occurs
NullPointerException - If command is null, or one of the elements of envp is null
IllegalArgumentException - If command is empty
This command works just fine
Runtime.getRuntime().exec(sh -c 'cd /path/to/dir && ProgToExecute)
Using one of the process builder's method we could pass the directory where we expect the cmd to be executed. Please see the below example. Also , you can mention the timeout for the process, using wait for method.
ProcessBuilder builder = new ProcessBuilder("cmd.exe", "/c", cmd).directory(new File(path));
Process p = builder.start();
p.waitFor(timeoutSec, TimeUnit.SECONDS);
In the above code, you can pass the file object of the path[where we expect the cmd to be executed] to the directory method of ProcessBuilder
My preferred solution for this is to pass in the directory that the Runtime process will run in. I would create a little method like follows: -
public static String cmd(File dir, String command) {
System.out.println("> " + command); // better to use e.g. Slf4j
System.out.println();
try {
Process p = Runtime.getRuntime().exec(command, null, dir);
String result = IOUtils.toString(p.getInputStream(), Charset.defaultCharset());
String error = IOUtils.toString(p.getErrorStream(), Charset.defaultCharset());
if (error != null && !error.isEmpty()) { // throw exception if error stream
throw new RuntimeException(error);
}
System.out.println(result); // better to use e.g. Slf4j
return result; // return result for optional additional processing
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Note that this uses the Apache Commons IO library i.e. add to pom.xml
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.10.0</version>
</dependency>
To use the cmd method e.g.
public static void main(String[] args) throws Exception {
File dir = new File("/Users/bob/code/test-repo");
cmd(dir, "git status");
cmd(dir, "git pull");
}
This will output something like this: -
> git status
On branch main
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
> git pull
Already up to date.
Try Use:
Runtime.getRuntime.exec("cmd /c cd path");
This worked
Runtime r = Runtime.getRuntime();
r.exec("cmd /c pdftk C:\\tmp\\trashhtml_to_pdf\\b.pdf C:\\tmp\\trashhtml_to_pdf\\a.pdf cat output C:\\tmp\\trashhtml_to_pdf\\d.pdf");
The below did not work
While using array command did NOT WORK
String[] cmd = {"cmd /c pdftk C:\\tmp\\trashhtml_to_pdf\\b.pdf C:\\tmp\\trashhtml_to_pdf\\a.pdf cat output C:\\tmp\\trashhtml_to_pdf\\d.pdf"}; r.exec(cmd);
FYI am using utility to check OS if its windows above will work for other than windows remove cmd and /c
I had solved this by having the Java application execute a sh script which was in the same directory and then in the sh script had done the "cd".
It was required that I do a "cd" to a specific directory so the target application could work properly.

Categories

Resources