1

I have a Java class. Its constructor calls ProcessBuilder class to run an external problem (the complete class code provided below, I think there is nothing important in it for my question, but I give it just to illustrate and make my question less abstract).

My code works well, however, when everything is done, it doesn't stop. It seems logical to me, because the external process is still running. So I find the way how to stop it. As my class just starts it, I think there is no "link" between the class instance and the running external process, so if I destroy this object, the external process will still run. My question is: how can I kill that external process?

I'm sorry for a stupid question, it has to be answered somewhere, but I can't find it.

Thank you very much for your time!

public static class OptimizerMOEA extends ExternalProblem {

    public static final String SEPARATOR = "\\s+"; //a space
    public static final float LOWER_BOUND = 0.0;
    public static final float UPPER_BOUND = 275000000*3;
    public static final String CPP_EXE_NAME = "optimizer_moea";

    public OptimizerMOEA() throws IOException{
        super(CPP_EXE_NAME);
    }

    public OptimizerMOEA(String path) throws IOException{
        super(path);
    }

    @Override
    public String getName() {
        return "OptimizerMOEA";
    }

    @Override
    public int getNumberOfVariables() {
        //!!! TODO: pass this value from C++ function to this class
       return 6890;
    }

    @Override
    public int getNumberOfObjectives() {
       return 2;
    }

    @Override
    public int getNumberOfConstraints() {
        return 0;
    }

    @Override
    public Solution newSolution() {
        Solution solution = new Solution(getNumberOfVariables(), 
        getNumberOfObjectives());


        for (int i = 0; i < getNumberOfVariables(); i++) {
            solution.setVariable(i, new RealVariable(LOWER_BOUND, UPPER_BOUND));
        } 


        return solution;
    }



}//EOF OptimizerMOEA class

EDIT:

This is the constructor from the parent class I use:

/**
 * Constructs an external problem using {@code new
 * ProcessBuilder(command).start()}.  If the command contains arguments,
 * the arguments should be passed in as separate strings, such as
 * <pre>
 *   new ExternalProblem("command", "arg1", "arg2");
 * </pre>
 * 
 * @param command a specified system command
 * @throws IOException if an I/O error occured
 */
public ExternalProblem(String... command) throws IOException {
    this(new ProcessBuilder(command).start());
}

And these are the others constructors. My Java class communicates with the external process via standard I/O. So the constructors using socket are given just for completeness.

Maybe using the constructor with a process in parameter would be a solution. In this case, I'm still interested if there is a way to solve the problem with the first constructor (just because I don't see it, find it an interesting question and am wondering in which case this constructor could be useful and why).

/**
 * Constructs an external problem that connects to a remote process via
 * sockets.  The remote process should be instantiated and already
 * listening to the designated port number prior to invoking this 
 * constructor.
 * 
 * @param host the host name of the remote system; or {@code null} to use
 *        the local host
 * @param port the port number
 * @throws UnknownHostException if the IP address of the specified host
 *         could not be determined
 * @throws IOException if an I/O error occurred
 */
public ExternalProblem(String host, int port) throws IOException, 
UnknownHostException {
    this(new Socket(host, port));
}

/**
 * Constructs an external problem that connects to a remote process via
 * sockets.  The remote process should be instantiated and already
 * listening to the designated port number prior to invoking this
 * constructor.
 * 
 * @param address the IP address of the remote system
 * @param port the port number
 * @throws IOException if an I/O error occurred
 */
public ExternalProblem(InetAddress address, int port) throws IOException {
    this(new Socket(address, port));
}

/**
 * Constructs an external problem using the specified socket.
 * 
 * @param socket the socket used to send solutions to be evaluated
 * @throws IOException if an I/O error occurred
 */
ExternalProblem(Socket socket) throws IOException {
    this(socket.getInputStream(), socket.getOutputStream());
}

/**
 * Constructs an external problem using the specified process.
 * 
 * @param process the process used to evaluate solutions
 */
ExternalProblem(Process process) {
    this(process.getInputStream(), process.getOutputStream());
    RedirectStream.redirect(process.getErrorStream(), System.err);
}

/**
 * Constructs an external problem using the specified input and output 
 * streams.
 * 
 * @param input the input stream
 * @param output the output stream
 */
ExternalProblem(InputStream input, OutputStream output) {
    super();
    reader = new BufferedReader(new InputStreamReader(input));
    writer = new BufferedWriter(new OutputStreamWriter(output));
}
kilpikonna
  • 137
  • 5
  • There is no ``ProcessBuilder`` in your code. I suspect the real work is done in the class you extend from. – f1sh Jul 04 '17 at 08:14
  • Yes, that's it, how I said (but maybe it wasn't clear) - the constructor of the parent class calls the ProcessBuilder. – kilpikonna Jul 04 '17 at 08:16
  • The stopping of the `Process` must be done in the parent class in some way. It should keep the `Process` instance after starting it and must be able to stop it using `Process.destroy()`. – kalsowerus Jul 04 '17 at 08:20
  • If the parent class is your own code please past some of its code. – Jeroen Jul 04 '17 at 08:22
  • It's not my own code, but it's a part of an open-source available framework, so I think I can past it. – kilpikonna Jul 04 '17 at 08:27
  • 2
    It's very poorly designed. The `Process` should be made available to subclasses, for input/output, waiting, exit code, and destroy. I wouldn't use this. – user207421 Jul 04 '17 at 08:38

1 Answers1

2

This:

new ProcessBuilder(command).start();

returns a Process object, and if you hold a reference to it, you can call Process.destroy() at a later time (this SO answer details the destroy() implementation)

Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
  • Thanks! If I have understood, there is no way to destroy it if I don't hold a reference ("surprisingly")... Which was actually my question, even though I thought it wasn't possible. – kilpikonna Jul 04 '17 at 13:01