SummarySome times it would be nice to be able to fork off a new Java process from an existing Java process. In this wiki entry, I do just that, creating a wrapper class to the java.lang.ProcessBuilder.
The JavaProcessBuilderThis class is a convenience wrapper class and collaborates with java.lang.ProcessBuilder to create properly constructed Java processes. 1 package examples.forkingjavaprocesses;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.text.MessageFormat;
6 import java.util.ArrayList;
7 import java.util.List;
8
9 /**
10 * @author Christopher Bartling, Pintail Consulting LLC
11 * @since Oct 4, 2008
12 */
13 public class JavaProcessBuilder {
14
15 private String mainClass;
16 private int startingHeapSizeInMegabytes = 40;
17 private int maximumHeapSizeInMegabytes = 128;
18 private String workingDirectory;
19 private List<String> classpathEntries = new ArrayList<String>();
20 private List<String> mainClassArguments = new ArrayList<String>();
21 private String javaRuntime = "java";
22
23 public String getMainClass() {
24 return mainClass;
25 }
26
27 public void setMainClass(String mainClass) {
28 this.mainClass = mainClass;
29 }
30
31 public int getStartingHeapSizeInMegabytes() {
32 return startingHeapSizeInMegabytes;
33 }
34
35 public void setStartingHeapSizeInMegabytes(int startingHeapSizeInMegabytes) {
36 this.startingHeapSizeInMegabytes = startingHeapSizeInMegabytes;
37 }
38
39 public int getMaximumHeapSizeInMegabytes() {
40 return maximumHeapSizeInMegabytes;
41 }
42
43 public void setMaximumHeapSizeInMegabytes(int maximumHeapSizeInMegabytes) {
44 this.maximumHeapSizeInMegabytes = maximumHeapSizeInMegabytes;
45 }
46
47 private String getClasspath() {
48 StringBuilder builder = new StringBuilder();
49 int count = 0;
50 final int totalSize = classpathEntries.size();
51 for (String classpathEntry : classpathEntries) {
52 builder.append(classpathEntry);
53 count++;
54 if (count < totalSize) {
55 builder.append(System.getProperty("path.separator"));
56 }
57 }
58 return builder.toString();
59 }
60
61 public void setWorkingDirectory(String workingDirectory) {
62 this.workingDirectory = workingDirectory;
63 }
64
65 public void addClasspathEntry(String classpathEntry) {
66 this.classpathEntries.add(classpathEntry);
67 }
68
69 public void addArgument(String argument) {
70 this.mainClassArguments.add(argument);
71 }
72
73 public void setJavaRuntime(String javaRuntime) {
74 this.javaRuntime = javaRuntime;
75 }
76
77 public Process startProcess() throws IOException {
78 List<String> argumentsList = new ArrayList<String>();
79 argumentsList.add(this.javaRuntime);
80 argumentsList.add(MessageFormat.format("-Xms{0}M", String.valueOf(this.startingHeapSizeInMegabytes)));
81 argumentsList.add(MessageFormat.format("-Xmx{0}M", String.valueOf(this.maximumHeapSizeInMegabytes)));
82 argumentsList.add("-classpath");
83 argumentsList.add(getClasspath());
84 argumentsList.add(this.mainClass);
85 for (String arg : mainClassArguments) {
86 argumentsList.add(arg);
87 }
88
89 ProcessBuilder processBuilder = new ProcessBuilder(argumentsList.toArray(new String[argumentsList.size()]));
90 processBuilder.redirectErrorStream(true);
91 processBuilder.directory(new File(this.workingDirectory));
92 return processBuilder.start();
93 }
94 }
Something to forkThe HelloWorldServer is a simple socket-based server which our test code will fork off in a new process and then call it via socket-based messages. 1 package examples.forkingjavaprocesses;
2
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStreamReader;
6 import java.io.PrintWriter;
7 import java.net.ServerSocket;
8 import java.net.Socket;
9
10 /**
11 * @author Christopher Bartling, Pintail Consulting LLC
12 * @since Oct 4, 2008 5:48:59 PM
13 */
14 public class HelloWorldServer {
15
16 private ServerSocket server;
17 private Socket client;
18 private BufferedReader in;
19 private PrintWriter out;
20 private String line;
21 private int listenerPort;
22
23
24 public HelloWorldServer(int listenerPort) {
25 this.listenerPort = listenerPort;
26 }
27
28 public void startListening() {
29 try {
30 server = new ServerSocket(this.listenerPort);
31 client = server.accept();
32 in = new BufferedReader(new InputStreamReader(client.getInputStream()));
33 out = new PrintWriter(client.getOutputStream(), true);
34 } catch (IOException e) {
35 e.printStackTrace();
36 System.exit(-1);
37 }
38
39 while (true) {
40 try {
41 line = in.readLine();
42 if (line.equalsIgnoreCase("hello")) {
43 System.out.println("Hello from the server!");
44 out.println("ack");
45 out.flush();
46 } else if (line.equalsIgnoreCase("stop")) {
47 try {
48 out.println("ack");
49 out.flush();
50 System.out.println("Client triggered this server to shutdown.");
51 in.close();
52 out.close();
53 server.close();
54 System.exit(0);
55 } catch (IOException e) {
56 System.out.println("Could not close.");
57 System.exit(-1);
58 }
59 }
60 } catch (IOException e) {
61 System.out.println("Read failed");
62 System.exit(-1);
63 }
64 }
65 }
66
67 public static void main(String[] args) {
68 final HelloWorldServer helloWorld = new HelloWorldServer(Integer.parseInt(args[0]));
69 helloWorld.startListening();
70 }
71 }
The test case 1 package examples.forkingjavaprocesses;
2
3 import org.junit.Test;
4
5 import java.io.BufferedReader;
6 import java.io.InputStreamReader;
7 import java.io.PrintWriter;
8 import java.net.Socket;
9
10 /**
11 * @author Christopher Bartling, Pintail Consulting LLC
12 * @since Oct 4, 2008
13 */
14 public class JavaProcessBuilderTests {
15
16 private static final int LISTENER_PORT = 4444;
17 private static final String CLASSPATH = "./out/production/ForkingJavaProcesses";
18 private static final String WORKING_DIRECTORY = ".";
19
20 @Test
21 public void startJavaProcess() throws Exception {
22 final JavaProcessBuilder javaProcessBuilder = new JavaProcessBuilder();
23 if (System.getProperty("os.name").toLowerCase().equals("mac os x")) {
24 javaProcessBuilder.setJavaRuntime("/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/bin/java");
25 }
26 javaProcessBuilder.setWorkingDirectory(WORKING_DIRECTORY);
27 javaProcessBuilder.addClasspathEntry(CLASSPATH);
28 javaProcessBuilder.setMainClass("examples.forkingjavaprocesses.HelloWorldServer");
29 javaProcessBuilder.addArgument(String.valueOf(LISTENER_PORT));
30 final Process process = javaProcessBuilder.startProcess();
31 BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
32
33 Thread t = new Thread(new Runnable() {
34 public void run() {
35 try {
36 Thread.sleep(1000);
37 Socket socket = new Socket("localhost", LISTENER_PORT);
38 PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
39 BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
40 System.out.println("CLIENT: Sending 'hello' command.");
41 out.println("hello");
42 System.out.println("CLIENT: Received '" + in.readLine() + "' from the server.");
43 Thread.sleep(2000);
44 System.out.println("CLIENT: Sending 'stop' command.");
45 out.println("stop");
46 System.out.println("CLIENT: Received '" + in.readLine() + "' from the server.");
47 } catch (Exception e) {
48 e.printStackTrace();
49 }
50 }
51 });
52 t.start();
53
54 String line;
55 while ((line = br.readLine()) != null) {
56 System.out.println("SERVER: " + line);
57 }
58 }
59 }
|