Shiridish
Shiridish

Reputation: 4962

Execute command displaying console window and also get handle of the process

I am trying to run a command from Java that will start a process that runs for several minutes. I need to just trigger the command and get the process handle and continue with other operations in a loop. At regular intervals, I will have to monitor that the process is still active.

I also need the console window to display to show the output of the process for the user.

Currently, I have tried methods from both Runtime and ProcessBuilder classes to run my command but neither of them has helped me achieve my objective.

Sample code:

//Changing the directory and running Maven exec: java command on the POM file in that directory.

String cmd = "cd C:/Test & mvn exec:java"; 
String finalCmd = "cmd /c \""+ cmd +"\"";

Process process = Runtime.getRuntime().exec(finalCmd);
Thread.sleep(10);
boolean alive = process.isAlive();

The value of variable alive is True, but I don't see the process got started. When the program execution is complete, only then the process starts and I am not sure why that happens.

Also to display the console window, I found from google that I need to use the below command:

String finalCmd = "cmd /c start cmd.exe /c \"" + cmd + "\"";

However, with this, the process starts immediately but I do not get the process handle as I find the alive variable shows false.

Does someone know how this objective can be achieved? I am ok if it's not possible to do both at the same time but at least I need to get the process execution to start and get the handle to monitor the process state later in my code.

Upvotes: 5

Views: 2906

Answers (4)

Deepak
Deepak

Reputation: 4062

Couple of things that are happening incorrectly here:

  • We need to pass our command as string tokens to the exec() command
  • We need to wait for the process to exit with process.waitFor() instead of sleeping, this will block the current thread so if you don't want that you need to execute this in another thread or use an ExecutorService.
  • Advisable to check the output value of waitFor() to see if our command executed properly (value of 0) or not (any other value, typically a positive 1 in case of unsuccessful execution)
  • Optionally (to see the output) we need to redirect the standard OUT and ERR somewhere, say print it to console(), though you could put it to a file some GUI window etc.

So at a minimum the following code should work:

Process process = Runtime.getRuntime().exec(new String[] {"cmd", "/c", "cd", "C:\\dev", "&&", "dir"});
int outputVal = process.waitFor();
boolean alive = process.isAlive();
System.out.format("alive %s, outputVal: %d\n",alive, outputVal);

Further suggestions:

  • use ProcessBuilder instead of runTime.exec(), it allows more control and is the recommended way since JDK 1.5
  • read the inputStream

So the code will look some thing like this:

    List<String> cmdList = Arrays.asList("cmd", "/c", "cd", "C:\\dev", "&&", "dir");
    ProcessBuilder pb = new ProcessBuilder(cmdList);
    pb.redirectErrorStream(true); //redirect STD ERR to STD OUT
    Process process = pb.start();
    try (final BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
        String line = null;
        while ((line = br.readLine()) != null) {
              System.out.println("std-out-line: " + line);
        }
    }
    int outputVal = process.waitFor();
    System.out.format("outputVal: %d\n", outputVal);

Since waitFor() is a blocking call, you can execute this in a separate thread or using an executorService. Sample code here:

    final StringBuffer outputSb = new StringBuffer();
    ExecutorService executorService = null;
    try {
        executorService = Executors.newSingleThreadExecutor();
        final Future<Integer> future = executorService.submit(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                try (final BufferedReader br = new BufferedReader(
                        new InputStreamReader(process.getInputStream()))) {
                    String line = null;
                    while ((line = br.readLine()) != null) {
                        outputSb.append("std-out-line: ");
                        outputSb.append(line);
                        outputSb.append('\n');
                    }
                }
                int exitValue = process.waitFor();
                System.out.format("exitValue: %d\n", exitValue);

                return exitValue;
            }
        });

        while (!future.isDone()) {
            System.out.println("Waiting for command to finish doing something else..");
            Thread.sleep(1 * 1000);
        }

        int exitValue = future.get();
        System.out.println("Output: " + outputSb);

    } finally {
        executorService.shutdown();
    }

Upvotes: 3

Ezekiel Baniaga
Ezekiel Baniaga

Reputation: 953

Here's a solution that uses WMIC.

public static void main( String[] args ) throws Exception {

    // Vars
    Process process;
    String output;

    // Execution
    process = Runtime.getRuntime().exec("cmd /c wmic process call create calc.exe | findstr ProcessId");
    output = readTrimmedOutput(process.getInputStream());
    System.out.println("Output from command: " + output);

    // Basic string manipulation to get process id
    String str_proc_id = output.split(" = ")[1].replace(";","");
    System.out.println("ProcessId is: " + str_proc_id);

    // Some thread delay that you can comment/uncomment for testing if running or not
    Thread.sleep(5000);

    // Finding if process is still running
    process = Runtime.getRuntime().exec("cmd /c wmic process get processid | findstr " + str_proc_id);
    output = readTrimmedOutput(process.getInputStream());

    boolean isRunning = output.contains(str_proc_id);
    System.out.println("Is process still running? " + isRunning);

}

private static String readTrimmedOutput(InputStream is) throws Exception {
    BufferedReader breader = new BufferedReader(new InputStreamReader(is));
    String line = breader.readLine();
    return line != null ? line.trim() : "";
}

Sample output

Output from command: ProcessId = 6480;
ProcessId is: 6480
Is process still running? true

For showing/displaying cmd console change some lines to:

// Execution
String your_command = "cmd.exe /c \"dir\"";
process = Runtime.getRuntime().exec("cmd /c wmic process call create \"" + your_command + "\" | findstr ProcessId");

References:

https://msdn.microsoft.com/en-us/library/aa394531(v=vs.85).aspx

https://www.computerhope.com/wmic.htm

Upvotes: 1

Gal Nitzan
Gal Nitzan

Reputation: 401

I believe that you need to launch you application as the process and not the CMD and then launch a child process of the CMD. It is the same as in Linux.

The CMD that you launched is alive=true but when you started java from that CMD is another process which is a child of the CMD but it will not return you the expected results.

HTH, Gal

PS. you might want to take a look at https://commons.apache.org/proper/commons-exec/ which is superior in functionality to Java in my opinion.

Upvotes: 0

parsa
parsa

Reputation: 995

since I didn't quite understand what you really need,i brought a comprehensive example of openning cmd from a java class (for instance class A) and starting a process of another java class (class B) and doing some operation from class B while class B is informing class A of whether it is processing yet or not. so the whole thing is to excecute class B from command promt that class A started and sending information from class B to A to notify it that it's still running.

in my example i took Main class as class A and myProcess class as class B. as you can see in code below the Main class is Opening cmd and is executing myProcess class then myProcess class is sending a information about the process through the socket that was created in Main class

//imports
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
//class
public class Main 
{
  //fields

  //methods


    public static void main(String[] args) throws Exception
    {

        Runtime run = Runtime.getRuntime();
        String new_dir = "C:\\Users\\Parsa\\Desktop\\New folder (2)";//imagine the directory of myProcess.class is in this folder
        startServer();
        run.exec("cmd.exe /c cd \""+new_dir+"\" & start cmd.exe /k \"java myProcess\"");

    }


    public static void startServer() 
    {
        Thread myThread = new Thread() 
        {
            @Override
            public void run() 
            {
                ServerSocket ss;// creating an open port for receiving data from network
                try {
                    ss = new ServerSocket(60010);//open port number 60010--> it can really be anything that is empty

                    Socket s = ss.accept();//Listens for a connection to be made to this socket and accepts it

                    BufferedReader in = new BufferedReader(
                            new InputStreamReader(s.getInputStream()));//get the inputstream and change it to a buffered reader in order to get a string from remote network
                    String line = null;
                    while ((line = in.readLine()) != null) //read the input
                    {
                        System.out.println(line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        };

        myThread.start();


    }

}

myProcess class:

by the way you need to compile the myProcess class manually by command prompt and excecute myProcess.class file from Main class

and the myProcess class is

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.util.Timer;
import java.util.TimerTask;

public class myProcess extends Thread
{
  //field

  //methods
    public static void main(String[] args) throws Exception
    {
        System.out.println("myProcess has started");
        startSender();

    }


    public static void startSender() 
    {
        Thread myThread = new Thread() 
        {
            @Override
            public void run() 
            {
                try 
                {
                    Socket s = new Socket("localhost", 60010);
                    BufferedWriter out = new BufferedWriter(
                            new OutputStreamWriter(s.getOutputStream()));

                    for(int i = 0 ; i<10 ; i++)
                    {
                        out.write("Process in running");
                        out.newLine();
                        out.flush();

                        Thread.sleep(200);
                    }
                    out.close();
                    //do whatever here
                    System.out.println("myProcess output");
                } 
                catch (Exception e) 
                {
                    e.printStackTrace();
                } 
            }
        };
        myThread.start();


        if(!myThread.isAlive())
        {
            System.out.println("myProcess has finished");
        }
    }
}

since i didn't exactly understand what you wanted ,this is probably not exactly what you want, but... it will definitely help you if you manipulate the code.

Upvotes: 0

Related Questions