Java

Recent site activity

Forking new Java processes

Summary

Some 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 JavaProcessBuilder

This 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 fork

The 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 }