So I know I can use readline to get the program output line by line but if I use a while loop
String l;
while( (l=input.readLine()) != null)
rcv = rcv + l
return rcv;
But this freezes my program until the external process finishes giving output. I want to listen to the output as the external process gives it. It can take a long time for the external program to exit.
I tried using read() but it also freezes my program until the end. How can I read the output, whatever is available and then do my processing? and then go back to read output again?
You can use a separate thread to read the input stream. The idea is that the blocking operations should happen in a separate thread, so your application main thread is not blocked.
One way to do that is submitting a Callable task to an executor:
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> processOutput = executor.submit(() -> {
// your code to read the stream goes here
String l, rcv;
while( (l=input.readLine()) != null) { ... }
return rcv;
});
This returns a "future" which is a way to represent a value that may not be available now but might be at some point in the future. You can check if the value is available now, or wait for the value to be present with a timeout, etc.
Related
I am trying to search when GTA5.exe ends. It launches with "steam://rungameid" protocol, so I can't use Process#waitFor(); method.
The method below checks if (currently) the process is running
public static boolean isProcessRunning(String processName) throws IOException {
ProcessBuilder processBuilder = new ProcessBuilder("tasklist.exe");
Process process = processBuilder.start();
String tasksList = toString(process.getInputStream());
return tasksList.contains(processName);
}
But what I want is something like
waitUntilProcessEnds(String processname) {
}
It could be
while (isProcessRunning) {
Thread.sleep(1000);
}
And, as expected, my JavaFX app freezes.
If I try to run the while method in another Thread, my JavaFX App gives this error:
java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-6
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:279)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:444)
at javafx.scene.control.Dialog.show(Dialog.java:294)
at com.thunderstorm.gta5.mod_installer.app.AppController$1$1.run(AppController.java:101)
at java.lang.Thread.run(Thread.java:748)
I also tried this
Platform.runLater();
and this
Platform.setImplicitExit(false);
But nothing different...
How to fix this
This question is edited
There is an API, but it requires Java 9 or newer. Then, you can do it smoothly:
String name = "GTA5.exe";
ProcessHandle.allProcesses()
.filter(ph -> ph.info().command()
.filter(s -> s.regionMatches(true,s.length()-name.length(),name,0,name.length()))
.isPresent())
.findAny()
.ifPresentOrElse(ph -> {
System.out.println("Waiting for "+ph.pid()+" "+ph.info().command().get());
ph.onExit().thenRunAsync(() -> {
// your actual action
}, Platform::runLater);
}, () -> System.out.println("No matching process found"));
ProcessHandle.onExit() returns a CompletableFuture which allows to chain dependent actions, to be performed on the process’ completion. So you don’t need to poll the process yourself.
Further note that Platform.runLater(Runnable) matches the functional signature of the Executor interface which allows us to use thenRunAsync(…, Platform::runLater) to mandate that the dependent action (Runnable) should be executed in the JavaFX Application Thread, once the process has exited.
I think your design is ok, but you're a bit confused by the threads. First when you start your wait method, go ahead and start a new Thread.
new Thread( ()->{
waitUntilProcessEnds(processname);
Platform.runLater( ()-> callBackOnPlatformThread() );
}).start();
Then you need.
public void callBackOnPlatformThread(){
System.out.println("go ahead and modify fx components.");
}
This will start waiting for the process to end on a new thread, so your javafx UI will continue to be responsive. When it is finished, it calls back on the javafx Platform thread, so you can modify components.
This doesn't check if the thread has already been started, so if you have a button that starts the thread, you could start a bunch of them.
What are you trying to achieve? If you want to execute some code when your application ends then you can add a shutdown hook:
Runtime.getRuntime().addShutdownHook(() -> { ... });
If you want to wait until the external process ends, then you can use:
process.waitFor()
This will block your current thread until the external process exits.
Not if your program didn't create the process in the first place.
Java does not provide operating-system level facilities that are not provided in the underlying OS in the first place. In Unix-like systems, for example, "wait for process exit" can only be executed by an ancestor of the process in question. In Windows, for another example, you have to possess a handle on the process, which you would have obtained from creating the process.
You'll have to roll your own. Create a thread which periodically checks for the existence of the process you are monitoring, by whatever means is suitable to that process and your operating environment. Worst-case you'll have to fork a 'ps' command (or similar) and parse the output.
I can't advise on the JavaFX issue; not my area.
I have a script which executes a program several times, producing about 350 lines of output to both STDERR and STDOUT. Now, I need to execute the script in Java, thereby printing the output streams to their original destinations. So, basically, I execute the script from inside a Java class, maintaining the original behavior for the user.
The way I do this is inspired from suggestions like Reading streams from java Runtime.exec and, functionally, works fine.
Process p = Runtime.getRuntime().exec(cmdarray);
new Thread(new ProcessInputStreamHandler(p.getInputStream(), System.out)).start();
new Thread(new ProcessInputStreamHandler(p.getErrorStream(), System.err)).start();
return p.waitFor();
And the class ProcessInputStreamHandler:
class ProcessInputStreamHandler implements Runnable {
private BufferedReader in_reader;
private PrintStream out_stream;
public ProcessInputStreamHandler(final InputStream in_stream, final PrintStream out_stream) {
this.in_reader = new BufferedReader(new InputStreamReader(in_stream));
this.out_stream = out_stream;
}
#Override public void run() {
String line;
try {
while ((line = in_reader.readLine()) != null) {
out_stream.println(line);
}
} catch (Exception e) {throw new Error(e);}
out_stream.flush();
}
}
Now regarding my problem statement: While the execution of the script takes about 17 seconds, the "encapsulated" execution takes at least 21 seconds. Where do I lose these 4 or more seconds?
I already tried using a ProcessBuilder with redirection of STDERR to STDOUT, using POSIX vfork with libraries like https://github.com/axiak/java_posix_spawn, using a byte buffer instead of a BufferedReader... everything with no positive result at all.
Are there any suggestings? I understand that there will be some performance loss, but 4 seconds seem to be a bit much to me...
Appreciate any suggestions!
Best Regards and Thanks in Advance.
The fastest way for your task is to use Java 7 and
return new ProcessBuilder(cmdarray).inheritIO().start().waitFor();
If that doesn’t help, I think there’s nothing you can do as every other approach would add even more code to your runtime environment that has to be processed.
Don't know if it will improve performance or not, but you can try the NuProcess library which while also providing non-blocking (asynchronous) I/O will also use vfork on Linux, which does decrease process launch times (and memory overhead) quite a bit.
I am running an .exe file from my program and it is taking certain time for the same.The output from this command is used in the following statements for further processing. The output is a boolean variable. But the program is returning false immediately, but in fact the command is still in execution and is taking certain time. Because of the false value the subsequent statements is throwing an error. How do i handle this situation.
The return_var = exec(pagecmd) is the executing statement.
boolean return_var = false;
if("true".equals(getConfig("splitmode", ""))){
System.out.println("Inside splitmode if**********************");
String pagecmd = command.replace("%", page);
pagecmd = pagecmd + " -p " + page;
File f = new File(swfFilePath);
System.out.println("The swffile inside splitmode block exists is -----"+f.exists());
System.out.println("The pagecmd is -----"+pagecmd);
if(!f.exists()){
return_var = exec(pagecmd);
System.out.println("The return_var inside splitmode is----"+return_var);
if(return_var) {
strResult=doc;
}else{
strResult = "Error converting document, make sure the conversion tool is installed and that correct user permissions are applied to the SWF Path directory" +
getDocUrl();
}
In conjuction with waitFor() suggested by Andreas, you may also need to use getInputStream of Process object returned by exec() to retrieve the data written by the program you are executing.
Assumed that you are finally using Runtime.exec() inside your exec() method, you can use the waitFor() method of the Process object which is returned from Runtime.exec() to wait until the execution has finished:
...
Process p = Runtime.getRuntime().exec(pagecmd);
int result = p.waitFor();
...
The return value from waitFor() is the exit code of the sub process.
If you actually need to read output from the sub process which the sub process is writing to its stderr or stdout channel, you need to use Process.getInputStream() (Note: not getOutputStream()) and Process.getErrorStream() and read the sub processes output from these streams. Then, check the return values from the stream's read() methods to check if the sub process has terminated (or at least closed its output streams) instead of using waitFor().
Also, for these kind of problems, you should consider using the Apache commons exec library.
Alternatively, you might want to check the ProcessBuilder class.
I'm executing a batch file using java command and reading batch data in text file putting in database. For example I have to run for 430 nodes within 15 min interval using same batch file. So I divided 430 node in 12 threads, so each thread contains 40 nodes pointing same batch file . But threads running parallel are not able wait for the batch file command to complete. I can't make wait for each thread, because all task should complete within 15 min. Any suggestions?
Below is piece of code running multi-threading.
for (int i = 0; i < noOfMainThreads; i++) {
// running 12 thread for 40 node
threadArr[i] = new Thread(runnableArr[i]);
runnableArr[i] = new CodeBatchfile(nodeArr,nodeidArr);
}
for (int i = 0; i < noOfMainThreads; i++) {
threadArr[i].start;
}
class CodeBatchfile{
void run(){
for (int i=1;i<nodename.length;i++) {
// exciting batch file using 12 threads.
cmd = filepath + " " + nodenamelocal;
try {
process = Runtime.getRuntime().exec(cmd, null, bdir);
process.waitFor();
}
catch(Exception ex) {
System.out.println("Exception Running batch file" + ex.getLocalizedMessage());
}
}
}
Use ExecutorService instead. Build a pipeline where each step works like this:
Create a job object which has all the information to do the task and which contains fields for the results. Create all job objects and put them into the queue for the service to run them.
So the first step would be to create 430 jobs to run the batch program. Each job would start the batch program and wait for it to terminate. After the batch terminates, you read the output and put that into the job instance.
Create an executor which runs N jobs in parallel. You will need to tune N; if it's a CPU intensive task, N == number of cores. if it's an IO intensive job, try higher values (2-4 times the CPU cores usually work well).
Put all the jobs into the executor's queue. Wait for jobs that finish, create new jobs from them and put them into the input queue of the executor.
Keep a job counter (started, finished) so you know when to stop.
Tutorial.
I think you should use CyclicBarrier, barrier allow you to wait in specific point until all the threads reach to the barrier, so after executing the batch you should call the wait of the cyclicBarrier. a good code example can be found here:
enter link description here
Relevant Code
-- Note Instructions is merely a class with several methods which will operate on the data. A new thread is created operate on the data read.
READ THREAD:
while(true) {
System.out.println(".");
if(selector.select(500) == 0)
continue;
System.out.println("processing read");
for(SelectionKey sk : selector.keys()) {
Instructions ins = myHashTable.get(sk);
if(ins == null) {
myHashTable.put(sk, new Instructions(sk));
ins = myHashTable.get(sk);
}
ins.readChannel();
}
}
READCHANNEL
public void readChannel() {
BufferedReader reader = new BufferedReader(Channels.newReader((ReadableByteChannel) this.myKey.channel(), "UTF-8"));
Worker w = new Worker(this, reader.readLine());
(new Thread(w)).start();
}
The new thread then calls more Instructions methods.
When the ins function finishes it might write to a Writer:
Writer out = Channels.newWriter((WritableByteChannel) key.channel(), "UTF-8");
out.write(output);
out.flush();
I can confirm that my client (a flash movie), then receives and acts on the output.
Finally, w exits.
After the receipt of the first message from the client, and successful processing, however, no more messages are taken care of by the READ THREAD loop. I believe the key is registered with the selector and ready to read. I have checked by looping on all the keys to see if they are readable with isReadable & isRegistered on the channel and the result is true in all cases to date. When a second message is sent from the client, however, the only response I see in the read thread is that the '.' character is printed out not every half second, but continuously faster. I believe, then, that the data is there in the channel, but for some reason the Selector isn't selecting any key.
Can anyone help me?
I think you are missing few points here.
Firstly, you should use the selector.selectedKeys() in the for loop
as mentioned by Vijay.
One should remove the key from selectedKeys
after the key is processed. Otherwise, the key will not be
removed automatically and hence selector might spin continuously even
if there is one key with interested ops bit set. (This might be
the issue in your case).
Finally, we should perform operations on
channel if the channel is ready for it. i.e, read only if
isReadable() returns true and try to write only if isWritable() is
true. Don't forget to validate the key.
Shouldn't
for(SelectionKey sk : selector.keys())
be
for(SelectionKey sk : selector.selectedKeys())
Since you would like to process only those events that have occurred in the current select operation ?
Since you say that the select(500) returns before 5 seconds, my guess is that you have registered a channel with the selector for the WRITE operation. A channel is ready for write most of the times. Hence it is necessary to set the interest ops to WRITE only when data is available for writing.
Note that you have to remove the channel from the list of selected keys. Select() won't do that for you. Better to use iterator for this purpose:
Iterator<SelectionKey> key_interator = selector.selectedKeys().iterator();
while (key_interator.hasNext()) {
...
key_interator.remove();
}