Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Start another Process with System.console available

I have a two programs:

  • first, that uses Console object to read and write data
  • second, that should run first with some dynamically calculated arguments

Second program code looks like this:

String[] arguments = {
    "cmd", "/c",
    "java", "-cp", classPath
    lauchClass,
    // Arguments for first program
}
ProcessBuilder pb = new ProcessBuilder(arguments);
pb.environment().putAll(System.getenv());
pb.directory(workDir);
pb.inheritIO();

Process process = pb.start();
process.waitFor();

When fist programs starts from second, System.console() is null and it fails with NPE.

So, question is: is there any way to run another process with System.console() available?

like image 922
Ivan Avatar asked Feb 04 '17 15:02

Ivan


1 Answers

The answer is simple: If you run your launcher from an IDE like Eclipse or IntelliJ IDEA, probably it does not have System.console() set in the first place, thus there is nothing the subprocess can inherit from. Just try to write something to System.console() from the launcher, it will also fail with the same error. But if you start your launcher from an interactive console like Cmd.exe or Git Bash, both the launcher and the process started via ProcessBuilder can write to System.console(). Your launcher does not even need "cmd", "/c", it works with or without those parameters.

package de.scrum_master.stackoverflow;

import java.io.File;
import java.io.IOException;

public class Launcher {
  public static void main(String[] args) throws IOException, InterruptedException {
    String classPath = "out/production/SO_ExternalProcessSystemConsole";
    String launchClass = "de.scrum_master.stackoverflow.MyApp";
    File workDir = new File(".");
    System.console().printf("Hi, I am the launcher app!%n");

    String[] arguments = new String[] {
//      "cmd", "/c",
      "java", "-cp", classPath,
      launchClass
    };
    ProcessBuilder pb = new ProcessBuilder(arguments);
    pb.environment().putAll(System.getenv());
    pb.directory(workDir);
    pb.inheritIO();

    Process process = pb.start();
    process.waitFor();
  }
}
package de.scrum_master.stackoverflow;

public class MyApp {
  public static void main(String[] args) {
    System.console().printf("Hi, I am an externally started app!%n");
  }
}

Console log when started from IntelliJ IDEA:

Exception in thread "main" java.lang.NullPointerException
    at de.scrum_master.stackoverflow.Launcher.main(Launcher.java:11)
    (...)

Console log when started from cmd.exe:

Hi, I am the launcher app!
Hi, I am an externally started app!

Feel free to ask any follow-up questions.


Update: If you do not mind that the external program runs in its own interactive console instead of in the IDE console, you can use the Windows command start for that purpose and then either cmd /c (windows is closed immediately after external program has ended) or cmd /k (window stays open for you to inspect the result):

package de.scrum_master.stackoverflow;

import java.io.File;
import java.io.IOException;

public class Launcher {
  public static void main(String[] args) throws IOException, InterruptedException {
    String classPath = "out/production/SO_ExternalProcessSystemConsole";
    String launchClass = "de.scrum_master.stackoverflow.MyApp";
    String[] arguments = new String[] {
      "cmd", "/c", "start",
      "cmd", "/k", "java", "-cp", classPath, launchClass
    };
    ProcessBuilder pb = new ProcessBuilder(arguments);
    Process process = pb.start();
    process.waitFor();
  }
}

But if then you want to read/write from/to that console, you are back at square #1. You asked why you cannot inherit System.console() to a subprocess. Well, that is because it is null due to the way Eclipse and IntelliJ launch Java programs from within the IDE (see [here] for background info). But as Magnus said, you still have System.{out|in} and can use them as follows:

package de.scrum_master.stackoverflow;

import java.io.File;
import java.io.IOException;
import java.util.Scanner;

public class Launcher {
  public static void main(String[] args) throws IOException, InterruptedException {
    String classPath = "out/production/SO_ExternalProcessSystemConsole";
    String launchClass = "de.scrum_master.stackoverflow.MyApp";
    File workDir = new File(".");
    System.out.println("Hi, I am the launcher app!");

    String[] arguments = new String[] { "cmd", "/c", "java", "-cp", classPath, launchClass };
    ProcessBuilder pb = new ProcessBuilder(arguments);
    pb.environment().putAll(System.getenv());
    pb.directory(workDir);
    pb.inheritIO();
    Process process = pb.start();
    process.waitFor();

    System.out.print("What is your favourite city? ");
    Scanner scanner = new Scanner(System.in);
    String city = scanner.nextLine();
    System.out.println("I guess that " + city + " is a nice place.");
  }
}
package de.scrum_master.stackoverflow;

import java.util.Scanner;

public class MyApp {
  public static void main(String[] args) {
    System.out.println("----------------------------------------");
    System.out.println("Hi, I am an externally started app.");
    System.out.print("Please enter your name: ");
    Scanner scanner = new Scanner(System.in);
    String name = scanner.nextLine();
    System.out.println("Hello " + name + "!");
    System.out.println("----------------------------------------");
  }
}
Hi, I am the launcher app!
----------------------------------------
Hi, I am an externally started app.
Please enter your name: Alexander
Hello Alexander!
----------------------------------------
What is your favourite city? Berlin
I guess that Berlin is a nice place.
like image 101
kriegaex Avatar answered Sep 29 '22 11:09

kriegaex