accessing files created by java runtime process - java

I'm running a windows program from within java:
String command = "cmd /C start "+fileName+".bat";
Runtime rt = Runtime.getRuntime();
Process pr = rt.exec(command, null, new File(currWD));
int exitValue = pr.waitFor();
The program completes successfully (exitValue == 0) and creates a file "fileName" in the working directory. I am trying in the same routine to find the size of this file:
xmlFileSize = (new File(fileName)).length();
Java finds the file yet it appear to be empty (xmlFileSize == 0). Once Java finishes I can see, however, that the file is non-empty.
How can I resolve this? All I want is that Java can correctly assesses the size of the file created by the windows program that Java has executed.

A zero-length file indicates that the file may not exist. From the docs:
The length, in bytes, of the file denoted by this abstract pathname, or 0L if the file does not exist.
Note that you use currWD as working directory for your bat-file. You could try to do:
new File(currWD, fileName).length()
to make sure you look for the file in the right directory.

It probably has to do with executing the bat file from a command shell. What does the bat file do? Is it launching a program?
I'm guessing that the script calls or executes another program and returns which allows the shell to die. This in turn let's the java process continue while the process from the script continues executing asynchronously.
According to the Java API for Process, that's allowable which it most definitely should be (link java.lang.Process)

I credit this answer to aioobe and John. As John suggests, the external program started by the batch file spawns a process that seems to be running for a while (50-300 millisec) after the Java sub-process running the batch file has returned. I resolved the problem by introducing a pause (as suggested by aioobe) :
int exitValue = pr.waitFor();
try {Thread.currentThread().sleep(300);} catch (InterruptedException e) {e.printStackTrace();}
After the pause Java seems to be able to see the files created by the external program. Thanks again to both contributors who helped me resolve this issue!
If anyone finds a more elegant solution, please, feel welcome to post.

Related

Running a process inside a folder on Linux in Java

So I have a JAR program that runs and reads the output of a command line Linux app. This app is located in a temp folder, which is where my JAR is.
Here's the Java code for reading the output:
Process proc;
ProcessBuilder pb = new ProcessBuilder();
pb.command("temp/myapp", "arg1");
pb.redirectErrorStream(true);
try {
proc = pb.start();
} catch (IOException ex) {
System.out.println("ERROR: Couldn't start process");
}
scan = new Scanner(proc.getInputStream());
String line = "";
while (scan.hasNext())
line += scan.nextLine() + System.lineSeparator();
scan.close();
Later I return that String I read into of course.
Now, the problem is that Scanner throws a NullPointerException, which means that the process cannot be found or cannot be run.
The moment I take the executable out of the temp and use
pb.command("./myapp", "arg1");
My program works perfectly fine.
If I open Terminal where the JAR is, temp/myapp arg1 will return exactly what it should. It's only the Java code that cannot seem to run this inside temp.
The question is, how do I point at the CLI app inside temp, if not the way I described above?
PS: The Java app works on Windows in the same setup, using pb.command("temp/myapp", "arg1") and a Win version of myapp so this is a Linux-specific issue.
I think it is not getting the process at respective path. Try by giving the absolute path of the process and then execute. Hope it will work.
I found the solution.
ProcessBuilder's directory() method, which I also use somewhere, sets not only the working directory of the Process, but also the directory where the Process will be launched from, on Linux at least, so how my code was actually parsed on Linux was temp/temp/myapp. When I set temp as the working directory, I only had to use ./myapp to launch it from temp. On Windows (my primary platform), this is not the case, I still have to use pass temp/myapp as parameter in command().

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 Process stops early even when output is redirected

I have a Java application that calls a tcsh script which in turn calls a perl script in the same directory. If I run this script from the command by typing "runPerlScript.sh", it works completely fine, and generates several output files as it should. However, if I call the script from Java, using the code below:
String[] runCmd = {"/bin/tcsh","-c","/filepath/runPerlScript.sh"};
Process run = Runtime.getRuntime().exec(runCmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(run.getInputStream()));
String line = "";
line = reader.readLine();
System.out.println("\nStarting while.");
while((line)!=null){
System.out.println("Output from script: "+line);
line=reader.readLine();
}
reader.close();
System.out.println("Finished running perl script.");
it prints out the echo statements from my shell script to my console (I'm using NetBeans), but generates only 4 output files (when normally it generates near 50). It seems as if the process is quitting to early, because after these 4 files are generated, an echo statement in my shell script that says "Finished running runPerlScript.sh" prints out to my console. I've tried several different ways to run this script, including ProcessBuilder, but none seem to generate the output files. The code I have above was in fact the only way I was able to generate ANY output, because ProcessBuilder just resulted in hangups. Does anyone know how I can continuously make the script run?
From the Runtime.exec() javadoc:
"Executes the specified string command in a separate process."
Assuming you want to wait for the process to end, you will need to wait for the process to terminate in your main java thread. The best way to do this would be by monitoring the Process returned by ProcessBuilder.start() and wait with Process.waitFor().

BAT File not executed fully using Runtime.exec()

I have a BAT file, which creates a number of csv files by reading DB tables. bcp.exe is used for this purpose, thus, for each CSV created from a table, there's a separate bcp.exe call. All these are found in the BAT file, which I invoke using Runtime.exec().
Now the issue I face is random. It can't be recreated in developer environment, but occurs once in a while in the production environment.
Sometimes after the BAT file is executed, only few of the CSV files have been created, and the rest is missing. But when you re-execute the same, you get all the CSVs.
Here's the code:
String command = "cmd /C " + batFilePath + " " + batParams;
LOGGER.info("Executing : " + command);
Runtime rt = Runtime.getRuntime();
Process process = rt.exec(command);
process.getInputStream();
is = process.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
LOGGER.info(line);
}
Would really appreciate it if anyone can enlighten me on how this might happen, since I am all at sea regarding this.
Thanks in advance,
-Raj.
Just a couple of points.
The first is that I've never understood why Java insists on getting the process's output stream with getInputStream - that's just bizarre. But that's just me ranting, there's not much you can do about that :-)
Secondly, I'm not sure why you have a "naked" process.getInputStream(); in your code. I don't think it's bad but it seems unnecessary.
Thirdly (and, to be honest, this is the only one I think may help), you need to debug the batch file itself rather than your Java code.
This can be done with the following two suggestions.
First, get the error stream and look at it. It's quite possible that cmd is delivering error information which you're just ignoring.
Secondly, change the batch file to output copious amounts of debug statements, one after each line if necessary. This will hopefully pinpoint the problem down to a specific place in the batch file.
If it only happens in production (and intermittently), that's harder, but we generally find that our customers are more than willing to accept debug-style temporary patches so we can collect the information to fix the problems they're seeing.
Output from a batch file which is simply logged is also a low-risk change. Some debug code is not so low-risk and we have to test that very thoroughly before involving the customer production systems. Some will refuse point blank, a not-unwise position to take.
It might be that you are exiting your input stream code before the batch script has completed executing.
After:
Process process = rt.exec(command);
you should probably add:
process.waitFor();
If this is the case, then you could verify it in your developer environment by deliberately slowing down your batch script and checking whether you experience the problem. Try sticking something like this:
PING 1.1.1.1 -n 1 -w 5000 > NUL
into your batch file. It will pause your script for 5 seconds.

Mounting and untar'ing a file in Java

I'm currently working on a web application that involves mounting a drive and extracting a tar.gz file, all in Java. Since the application runs in a linux environment, I figured I'd try using unix commands like "mount" and "tar".
Runtime runtime = Runtime.getRuntime();
Process proc;
String mountCommand = "mount -t cifs -o username=...";
String extractCommand = "tar xzf ..."
proc = runtime.exec(mountCommand);
proc.waitFor();
proc = runtime.exec(extractCommand);
proc.waitFor();
Running the mount command and extract command in the terminal works fine, but fails when FIRST run in java. The second proc.waitFor() returns exit code 2. However, running this code after the first failed attempt works fine. I have a feeling that the problem is that waitFor() isn't waiting until the mount command is fully completed. Am I missing anything important in my code?
Also, I'd rather do this all in Java, but I had a really hard time figuring out how to untar a file, so I'm taking this approach. (oh if anyone can tell me how to do this i would be very happy). Any suggestions would be muuuuuuuuuuch appreciated!
Making progress. In case anyone was wondering, here is how I am extracting a tar.gz file in Java. Put together from a few online tutorials.
public static void extract(String tgzFile, String outputDirectory)
throws Exception {
// Create the Tar input stream.
FileInputStream fin = new FileInputStream(tgzFile);
GZIPInputStream gin = new GZIPInputStream(fin);
TarInputStream tin = new TarInputStream(gin);
// Create the destination directory.
File outputDir = new File(outputDirectory);
outputDir.mkdir();
// Extract files.
TarEntry tarEntry = tin.getNextEntry();
while (tarEntry != null) {
File destPath = new File(outputDirectory + File.separator + tarEntry.getName());
if (tarEntry.isDirectory()) {
destPath.mkdirs();
} else {
// If the parent directory of a file doesn't exist, create it.
if (!destPath.getParentFile().exists())
destPath.getParentFile().mkdirs();
FileOutputStream fout = new FileOutputStream(destPath);
tin.copyEntryContents(fout);
fout.close();
// Presserve the last modified date of the tar'd files.
destPath.setLastModified(tarEntry.getModTime().getTime());
}
tarEntry = tin.getNextEntry();
}
tin.close();
}
Quick Answer
Since a dependency on external commands exists, simplify it like this:
#!/bin/bash
mount -t cifs -o username=...
tar xzf ...
Name it mount-extract.sh then call it using a single Runtime.exec() call.
Semi-integrated Answer
Use Java APIs.
http://java.sun.com/j2se/1.4.2/docs/api/java/util/zip/GZIPInputStream.html
http://www.jajakarta.org/ant/ant-1.6.1/docs/ja/manual/api/org/apache/tools/tar/TarInputStream.html
You will need Runtime.exec to execute the mount command.
Forward Looking
Since Java is a cross-platform software development tool, consider abstracting the mount command in your application to be derived dynamically based on the underlying operating system.
See: How can I mount a windows drive in Java?
See: http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html#getProperties()
Of course, Agile development would insist that this not be done until it is needed. So keep it in the back of your mind until then (as you might never run the application on anything but Unix-based systems).
Take a look at the org.apache.tools.tar package in the Ant codebase. There is a class in that package, TarInputStream, that can be used to read tar archives.
It may be related to the way you call the method.
See this answer
Basically try using
.exec( String [] command );
instead of
.exec( String command );
I'm not sure if it is even related, because you mention it runs the second time. Give it a try and let us know.
This can all be done in Java, but you have to be aware of caveats when dealing with native processes.
The waitFor() command may not be doing what you hope: if the process you started has a child process that does the actual work you need then the waitFor(), which returns when the parent process has finished, has not allowed enough time for the child process to finish.
One way to get around this is to loop over some test to see that the native processes you started have finished to your satisfaction---in this case perhaps checking if some java.io.File exists.

Categories

Resources