Using unix OS, I am trying to run .sh file using ProcessBuilder in java Multi threading (.sh file is common, but arguments different for each thread). the code follows.
public class CLMScriptExe2 implements Runnable {
final String scriptFileName = "/apps/orangd1/temp/CLM/CLM_PCF_Jenkins.sh";
private String AppName;
public CLMScriptExe2(String appName) {
AppName = appName;
}
#Override
public void run() {
try {
ProcessBuilder processBuilder = new ProcessBuilder(scriptFileName, AppName);
Process process = processBuilder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
//String line;
System.out.println(line);
}
try {
process.waitFor();
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
System.out.println("ending executeScript--Testing");
}
public static void main(String[] args) {
CLMScriptExe2 c1 = new CLMScriptExe2("appOne");
Thread t1 = new Thread(c1);
t1.start();
CLMScriptExe2 c2 = new CLMScriptExe2("appTwo");
Thread t2 = new Thread(c2);
t2.start();
CLMScriptExe2 c3 = new CLMScriptExe2("appThree");
Thread t3 = new Thread(c3);
t2.start();
}
}
the Java program is terminating after processBuilder.start().
Edited:
after this processBuilder.start(), No logs, No errors, No Exception, nothing, it just terminated for all threads.
Is there any solution?
A bit late but maybe this will help someone else.
You are starting 3 threads but you aren't closing any ie you are missing:
t1.join();
etc
I suspect you are getting a ConcurrentModificationException error.
Related
I have a GUI that starts a new thread (getFilesThread) when the user clicks a button. This thread invokes the start() method of an instance of my ClearCaseProcess class below (my organization won't let me use the ClearCase Java API), and when the process(es) ends, it updates the GUI.
private static class ClearCaseProcess {
private ArrayList<String> stdout = new ArrayList<>();
private ArrayList<String> stderr = new ArrayList<>();
private ProcessBuilder pb = null;
public ClearCaseProcess(ArrayList<String> commands, String dir) throws IOException {
pb = new ProcessBuilder(commands);
pb.directory(new File(dir));
}
public void start() throws IOException {
long minStart = System.nanoTime();
Process process = pb.start();
Thread sout = new Thread() {
#Override
public void run() {
BufferedReader out = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String outLine = "";
try {
while ((outLine = out.readLine()) != null) {
stdout.add(outLine);
System.out.println(outLine);
}
} catch (IOException ex) {
System.err.println(ex.getMessage());
}
}
};
sout.start();
Thread serr = new Thread() {
#Override
public void run() {
BufferedReader err = new BufferedReader(
new InputStreamReader(process.getErrorStream()));
String errLine = "";
try {
while ((errLine = err.readLine()) != null) {
stderr.add(errLine);
System.err.println(errLine);
}
} catch (IOException ex) {
System.err.println(ex.getMessage());
}
}
};
serr.start();
try {
process.waitFor();
long execTime = System.nanoTime() - minStart;
System.out.println("Process '" + description + "' took " + execTime);
} catch (InterruptedException ex) {
System.err.println(ex.getMessage());
}
}
}
getFiles() needs to gather data from four different ClearCaseProcesses. I plan to run these concurrently in four threads. Each of these threads has two associated auxiliary threads to consume stdout and stderr, as shown above. I'm assuming that this will be faster than running ClearCase four times sequentially. For now, however, I'm testing just one ClearCase call.
My issue is that the time elapsed between calling Process.start() and the return of Process.waitFor() is much longer (about 5 minutes) than the time elapsed when I run the same ClearCase command on the terminal (about 1.5 minutes). I suspect that my loops reading stdout and stderr are the culprits, because even my print statements are slow to produce output in NetBeans' console. How can I speed this up?
For my specific case, running (not debugging) inside the IDE was causing the overhead.
When ran from the console (java -jar myExecutable.jar), the times were very close.
Noobish mistake.
This question already has answers here:
run interactive command line application from java
(2 answers)
Closed 6 years ago.
Basically, I have a process which runs when I press a button on my java application.
And this process executes a command to the terminal of the OS.
But sometimes this command needs to have an interaction with the user.
And I would like to know if this was possible to have the interaction from the process to the user when needed?
My code:
File marsSimulator = new File("resources/mars_simulator/Mars4_5.jar");
if(marsSimulator.exists() && temp.exists()){
String res="";
try {
Process p = Runtime.getRuntime().exec(new String[]{"java","-jar",marsSimulator.getAbsolutePath(),tempAssembly.getAbsolutePath()});
p.waitFor();
InputStream is = p.getInputStream();
byte b[] = new byte[is.available()];
is.read(b, 0, b.length); // probably try b.length-1 or -2 to remove "new-line(s)"
res = new String(b);
} catch (Exception ex) {
ex.printStackTrace();
}
}
Also, I forgot to say that the application is made with SWING and that the output of the process is shown onto a TextArea... Should I change anything ?
Notice that the process blocks when there is an interaction with the user. If there isn't, the process doesn't block !
What do I need to do in this case (which I don't know how to do it ) ?
When the process needs the interaction. I need to know when the process wants some interaction.
I need to get the output generated of the process interactively (line by line).
P.S.: For people who wanna understand the process line, I am using the Mars Simulator (http://courses.missouristate.edu/KenVollmar/MARS/) and I am sending the jar application into a process with a mips assembly code associated.
This next pieces of code is working with my project
Hope it will help for the next adventurers!
And thank you to Nicolas Filotto for helping me.
My class ObservableStream:
class ObservableStream extends Observable {
private final Queue<String> lines = new ConcurrentLinkedQueue<>();
public void addLine(String line) {
lines.add(line);
setChanged();
notifyObservers();
}
public String nextLine() {
return lines.poll();
}
public String getLine(){return lines.peek();}
}
And the other part of the code:
Process p = Runtime.getRuntime().exec(new String[]{"java","-jar",marsSimulator.getAbsolutePath(),tempAssembly.getAbsolutePath()});
//This code does the interaction from the process with the GUI ! Implied, input interaction+output interaction from the process
ObservableStream out = new ObservableStream();
// Observer that simply sends to my external process line by line what we put in
// the variable output
PrintWriter writer = new PrintWriter(p.getOutputStream(), true);
out.addObserver(
(o, arg) -> {
ObservableStream stream = (ObservableStream) o;
String line;
while ((line = stream.nextLine()) != null) {
writer.println(line);
}
}
);
ObservableStream input = new ObservableStream();
input.addObserver(
(o, arg) -> {
ObservableStream stream = (ObservableStream) o;
String line;
while ((line = stream.nextLine()) != null) {
outputTextArea.appendText(line+"\n");
}
}
);
// The thread that reads the standard output stream of the external process
// and put the lines into my variable input
new Thread(
() -> {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream()))
) {
String line;
while ((line = reader.readLine()) != null) {
input.addLine(line);
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
).start();
new Thread(
()->{
while(p.isAlive()){
String res = input.getLine();
if(res!=null && res.equals("Enter integer value:")) {
boolean integerIsRequested=true;
Thread t=null;
while(integerIsRequested){
if(t==null) {
t = new Thread(new Runnable() {
public void run() {
String test1 = JOptionPane.showInputDialog("Enter Integer value:");
while(!test1.matches("^\\d+$")){
test1 = JOptionPane.showInputDialog("Error: Not a valid Integer.\nEnter a correct Integer value:");
}
Integer i = Integer.valueOf(test1);
if (i != null) {
out.addLine(test1);
}
}
});
t.start();
}
if(!t.isAlive()){
integerIsRequested=false;
}
}
}
}
outputTextArea.appendText("Program executed\n");
}
).start();
By the way, this post is unique Jarrod ;)
To implement such use case I would personally use:
An Observable object to notify my UI when a new line has been provided by the external process
An Observable object to which I add new lines provided by my UI
An Observer of #1 that will refresh the data of my UI
An Observer of #2 that will send the lines provided by my UI to my external process
A Thread that will check if a new line has been provided by my external process and if so it will provide those lines to #1
So as I don't have your full env, I will show you how it will work with mock objects:
First my fake external application that only does an Echo of what he receives:
public class Echo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (true) {
String line = scanner.nextLine();
System.out.printf("echo > %s%n", line);
}
}
}
If this class receives foo, it will print into the standard output stream echo > foo
Then my Observable class
public class ObservableStream extends Observable {
private final Queue<String> lines = new ConcurrentLinkedQueue<>();
public void addLine(String line) {
lines.add(line);
setChanged();
notifyObservers();
}
public String nextLine() {
return lines.poll();
}
}
NB: The class ObservableStream (as it is implemented so far) is meant to have only one Observer no more which is enough according to your needs. Indeed is only used to decouple your UI from how the data is retrieved or published
Then finally the main code:
Process p = Runtime.getRuntime().exec(
new String[]{"java", "-cp", "/my/path/to/my/classes", "Echo"}
);
// The Observable object allowing to get the input lines from my external process
ObservableStream input = new ObservableStream();
// A mock observer that simply prints the lines provided by the external process
// but in your case you will update your text area instead
input.addObserver(
(o, arg) -> {
ObservableStream stream = (ObservableStream) o;
String line;
while ((line = stream.nextLine()) != null) {
System.out.printf("Line Received from the external process: %s%n", line);
}
}
);
// The thread that reads the standard output stream of the external process
// and put the lines into my variable input
new Thread(
() -> {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream()))
) {
String line;
while ((line = reader.readLine()) != null) {
input.addLine(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
).start();
// The Observable object allowing to send the input lines to my external process
ObservableStream output = new ObservableStream();
// Observer that simply sends to my external process line by line what we put in
// the variable output
PrintWriter writer = new PrintWriter(p.getOutputStream(), true);
output.addObserver(
(o, arg) -> {
ObservableStream stream = (ObservableStream) o;
String line;
while ((line = stream.nextLine()) != null) {
writer.println(line);
}
}
);
// A simple scanner used to send new messages to my external process
Scanner scanner = new Scanner(System.in);
while (true) {
output.addLine(scanner.nextLine());
}
If this code receives foo, it will print into the standard output stream Line Received from the external process: echo > foo
I hope it answers your question... subProcessStuff "emulates" that sub process. It can be anything - but this way we have all in place. It requires 2 params passed into console. String and Integer. Gobbler got Callback which is an interface, with anonymous implementation - and there are checks for params. To answer if subprocess waits we simply track what is says - just like if a user would operate with it.
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Scanner;
class Test1 {
public static void main(String[] args) {
for (String arg : args)
System.out.println("arg: " + arg);
for (String arg : args)
if (arg.equals("-test")) {
subProcessStuff();
return;
}
mainProcess();
}
public static void subProcessStuff() {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try {
System.out.println("Enter String");
String s = br.readLine();
System.out.println("Enered String: " + s);
System.out.println("Enter Integer:");
int i = Integer.parseInt(br.readLine());
System.out.println("Entered Integer: " + i);
} catch (IOException e) {
System.err.println("io error - " + e.getMessage());
} catch (NumberFormatException nfe) {
System.err.println("Invalid Format!");
}
}
private static PrintStream out;
public static void mainProcess() {
String[] commands = { "ls", "-alt" };
ProcessBuilder builder = new ProcessBuilder("java", "Test1", "-test");
// builder.inheritIO(); // I avoid this. It was messing me up.
try {
Process proc = builder.start();
InputStream errStream = proc.getErrorStream();
InputStream inStream = proc.getInputStream();
OutputStream outStream = proc.getOutputStream();
new Thread(new StreamGobbler("err", out, errStream)).start();
out = new PrintStream(new BufferedOutputStream(outStream));
Callback cb = new Callback() {
#Override
public void onNextLine(String line) {
if (line.equals("Enter String")) {
out.println("aaaaa");
out.flush();
}
if (line.equals("Enter Integer:")) {
out.println("123");
out.flush();
}
}
};
new Thread(new StreamGobbler("in", out, inStream, cb)).start();
int errorCode = proc.waitFor();
System.out.println("error code: " + errorCode);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (out != null) {
out.close();
}
}
}
}
interface Callback {
void onNextLine(String line);
}
class StreamGobbler implements Runnable {
private PrintStream out;
private Scanner inScanner;
private String name;
private Callback cb;
public StreamGobbler(String name, PrintStream out, InputStream inStream) {
this.name = name;
this.out = out;
inScanner = new Scanner(new BufferedInputStream(inStream));
}
public StreamGobbler(String name, PrintStream out, InputStream inStream, Callback cb) {
this.name = name;
this.out = out;
inScanner = new Scanner(new BufferedInputStream(inStream));
this.cb = cb;
}
#Override
public void run() {
while (inScanner.hasNextLine()) {
String line = inScanner.nextLine();
if (cb != null)
cb.onNextLine(line);
System.out.printf("%s: %s%n", name, line);
}
}
}
I don't think you can check the state of the process from the Java. However you can do it by using some Linux command. (Of course if you're using Linux)
If your Java process has access to the /proc directory then you can read the status file for the process.
For example for a process with process id 12280
/proc/12280/status
Here's the relevant output of the status file
Name: java
State: S (sleeping)
Tgid: 12280
Pid: 12280
PPid: 12279
...
Second line gives the state of the process. You'll need to run a thread to continuously poll this file to read the status.
Line by Line The Code i use to interract with a different jar which is a speechRecognizer.I think you want to achieve something like this.
Example:
The jar i am interracting(speechRecognizer) is executing different commands and run some other Threads.Every time it has to interract with the main jar it prints something that i need.For example (user said:How are you),so you can have a same logic and when external jar need interraction with user it prints something and you read it into the main app.So:
// About Process
private Process process;
private BufferedReader bufferedReader;
private boolean stopped = true;
Thread processChecker;
//Running it in a Thread so the app don't lags
new Thread(() -> {
try {
stopped = false;
//Starting the external jar..
ProcessBuilder builder = new ProcessBuilder("java", "-jar", System.getProperty("user.home")
+ File.separator + "Desktop" + File.separator + "speechRecognizer.jar", "BITCH_PLEASE");
//Redirecting the ErrorStream
builder.redirectErrorStream(true);
process = builder.start();
bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
//Check continusly if the process is still alive
//i case of crash i should do something..
processChecker = new Thread(() -> {
while (process.isAlive()) {
try {
Thread.sleep(1200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
stopSpeechReader(false);
});
processChecker.start();
// Continuesly Read Output of external process
while (!stopped) {
while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) {
System.out.println(line);
checkSpeechResult(line);
}
}
// Interrupt the mf Thread if is Alive
if (processChecker.isAlive())
processChecker.interrupt();
System.out.println("SpeechReader Stopped! Process is alive:" + process.isAlive() + " >Exit Value:"
+ process.exitValue());
} catch (Exception e) {
e.printStackTrace();
}
}).start();
Here is my code:
private static model connectRemoteSession(String accountName,String password) throws IOException{
StringBuilder output = new StringBuilder();
StringBuilder error = new StringBuilder();
Runtime runtime = Runtime.getRuntime();
String com = // some command
proc = runtime.exec(com);
Worker worker = new Worker(proc);
reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
stderr = new BufferedReader(new InputStreamReader(
proc.getErrorStream()));
String outLine;
String errLine;
worker.start();
try {
worker.join(300000);
if (worker.exit != null){
//read the outout and error stream and take actions accordingly
}
else{
proc.destroy();
while ((outLine = reader.readLine()) != null)
{
CloudBackup.logger.info("online exchange output line ="+outLine);
output.append(outLine);
}
while((errLine = stderr.readLine()) != null){
CloudBackup.logger.info("online exchange error line ="+errLine);
error.append(errLine);
}
throw new TimeoutException();
}
} catch(InterruptedException ex) {
worker.interrupt();
Thread.currentThread().interrupt();
throw ex;
} finally {
proc.destroy();
}
}catch(Exception e){
CloudBackup.logger.severe(e.getMessage());
}finally{
reader.close();
proc.getOutputStream().close();
stderr.close();
}
return model;
}
class Worker extends Thread {
private final Process process;
Integer exit;
Worker(Process process) {
this.process = process;
}
public void run() {
try {
exit = process.waitFor();
} catch (InterruptedException ignore) {
return;
}
}
This issue I am facing is one one machine the code is working fine but on another machine the worker.exit is always null, although I put the logs in the worker.exit == null and saw that the process is getting over but somehow process.waitFor is not capturing it.
I know that process.waitFor() is available in Java 8 so I checked the version on both the machined and they have same version Java 8. Also there is no other thread running.
Try this, it can be a caching issue.
volatile Integer exit;
I am launching a service (process) with the code below. My issue is as follows:
I need to read the output of the process to make sure it gets started
if it gets started, I return and everything is fine
if it does not get started for whatever reason, the while will block forever as the process just hangs without outputting anything
Any ideas how I could exit the method gracefully if I don't get the expected string?
ps: I could do it with a Future and a timeout on get but thought there might be a better way.
public boolean startService() {
try {
ProcessBuilder pb = new ProcessBuilder("service.exe");
pb.directory(new File("C:/serviceFolder/"));
pb.redirectErrorStream(true);
Process p = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
if (line.toLowerCase().contains("started")) {
return true;
}
}
return false; //I never get there when it fails
} catch (IOException e) {
throw new RuntimeException("Could not start the service.exe process", e);
}
}
If you can modify the service code, it's better to change it not to hang if it can't start - it should exit and log an error message. This way your Java code will work as-is.
If you can't, there is no other way beside setting a timeout, because your Java code has no means to know what's going on.
Of course, if you can modify the service, an alternative is to watch for output other then the process's standard output/error, like a PID file, an error log message, or whatever. If the subprocess already creates a PID file, for example, you can schedule a check on this file instead of the standard input, but really it's the same concept, just applied differently to use nicer/simpler code
Something like this should work. Essentially, start the service in a separate thread and create a Timer that interrupts it after a certain period. Note that the timer task is a Daemon so it should not hold up your process if it needs to quit.
Obviously this will not work if reader.readLine() consumes and discards interrupts.
private static class ServiceRunner implements Runnable {
// Am I running?
volatile boolean running = true;
// My thread.
volatile Thread thread = Thread.currentThread();
#Override
public void run() {
// Start a timer.
Timer timer = new Timer("Wait for ServiceRunner to finish.", true);
// Fire it after 2 seconds.
timer.schedule(new StopTask(), 2000);
try {
// Start the service.
startService();
} finally {
// No longer running.
running = false;
}
}
class StopTask extends TimerTask {
#Override
public void run() {
if (running) {
// Interrupt the service runner.
thread.interrupt();
}
}
}
public boolean startService() {
try {
ProcessBuilder pb = new ProcessBuilder("service.exe");
pb.directory(new File("C:/serviceFolder/"));
pb.redirectErrorStream(true);
Process p = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
if (line.toLowerCase().contains("started")) {
return true;
}
}
return false; //I never get there when it fails
} catch (IOException e) {
throw new RuntimeException("Could not start the service.exe process", e);
}
}
}
I have not tested this code but it should work.
You will need to make adjustments to retain whether the service started or not.
It seems that the Future#get approach is preferred. For future reference, I have modified the code in the following way:
public boolean startService() {
Callable<Boolean> start = new Callable<Boolean>() {
#Override
public Boolean call() throws Exception {
ProcessBuilder pb = new ProcessBuilder("service.exe");
pb.directory(new File("C:/serviceFolder/"));
pb.redirectErrorStream(true);
Process p = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
if (line.toLowerCase().contains("started")) {
return true;
}
}
return false;
}
};
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Boolean> future = executor.submit(start);
try {
return future.get(1, TimeUnit.SECONDS);
} catch (InterruptedException ignore) {
Thread.currentThread().interrupt();
return false;
} catch (ExecutionException | TimeoutException e) {
logger.error("Could not start service", e);
return false;
} finally {
executor.shutdownNow();
}
}
this class is response to execute the command ,the print the result
public class ExecutorTask implements Runnable{
#Override
public void run() {
Process process = null;
try {
process = Runtime.getRuntime().exec("cmd /c dir");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line="";
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
process.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
return;
}
}
}
the second class is a executor to run the shell use a thread
public final class ShellCommandExecutor{
public void execute(String command){
ExecutorTask task = new ExecutorTask();
Thread executorThread = new Thread(task);
executorThread.start();
/*try {
Thread.sleep(1000);
executorThread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
}
the problem is why i must in the class ShellCommandExecutor add code snippet:
try {
Thread.sleep(1000);
executorThread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
then can i see the print result:
2012-08-21 00:32 <DIR> .
2012-08-21 00:32 <DIR> ..
2012-08-21 00:32 1,576 .classpath
2012-08-21 00:26 1,224 .project
2012-08-07 10:58 <DIR> .settings
2012-08-24 15:19 10,965 pom.xml
2012-08-07 10:57 <DIR> src
2012-08-21 00:32 <DIR> target
2012-08-24 10:22 0 velocity.log
why?
You started a thread with
executorThread.start();
if you do nothing else the thread that started it (your main thread) will not wait for your executorThread to finish before returning, so your application will exit before this thread has executed its task.
To wait for your executorThread to finish you should call:
executorThread.join();
later in the code. At this point you will be ensured that it has finished its task.
Currently it works because you wait for 1 second in your main thread, during this second your other thread performs its action. But if your executorThread needed more than one second to perform it it will not work, so you should not sleep() in this case.
See Thread.join javadoc.
First of all why are you using String as a parameter in execute() method when you are not using it...
I tried your program with slight modification and it worked without sleep() and interrupt()
Try the code below.....
public final class ShellCommandExecutor{
public void execute(){
ExecutorTask task = new ExecutorTask();
Thread executorThread = new Thread(task);
executorThread.start();
/*try {
Thread.sleep(1000);
executorThread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
public static void main(String[] args){
new ShellCommandExecutor().execute();
}
}
class ExecutorTask implements Runnable{
#Override
public void run() {
Process process = null;
try {
process = Runtime.getRuntime().exec("cmd /c dir");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line="";
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
process.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
return;
}
}
}