| JavaInvoke.java |
1 package com.palantir.blog.processspawner;
2 /*
3 * All source code and information in this file is made
4 * available under the following licensing terms:
5 *
6 * Copyright (c) 2009, Palantir Technologies, Inc.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are
11 * met:
12 *
13 * * Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials provided
19 * with the distribution.
20 *
21 * * Neither the name of Palantir Technologies, Inc. nor the names of its
22 * contributors may be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 *
38 */
39 import java.io.File;
40 import java.util.ArrayList;
41 import java.util.HashMap;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Map.Entry;
45
46 import org.apache.log4j.LogManager;
47 import org.apache.log4j.Logger;
48
49 /**
50 * Specialized delegate for the invocation of Java processes.
51 *
52 * @see ProcessBuilder
53 * @author regs
54 *
55 */
56 public class JavaInvoke extends ProcessSpawner {
57
58 static final Logger javaInvokeLog = LogManager.getLogger(JavaInvoke.class);
59
60 /**
61 * Constructs a new JavaInvoker object. Any of the passed arguments, aside from classToInvoke, may be null.
62 * @param classToInvoke Fully-qualified classname of the class to invoke as main in the spawned VM.
63 * Does not need to be loaded in this VM (but must be on the classpath of the spawned VM).
64 * @param workingDirectory Directory this should run in.
65 * @param javaProperties Map of Java options as defined by {@link System#getProperties()}.
66 * @param args Command line arguments to be passed to the invoked main. These are not arguments to the VM
67 * but arguments to the Java class. These will be the contents of the args[] array in the main method on
68 * the invoked class.
69 * @param additionalClassPath additional classpath entries to be added to the beginning of the classpath
70 * (to allow for overriding).
71 * @param environmentToMerge Java environment entries to be merged (overwriting) with the current environment
72 * to be used as the environment for spawned processes.
73 */
74 public JavaInvoke(String classToInvoke,
75 File workingDirectory,
76 Map<String,String> javaProperties,
77 String[] args,
78 List<String> additionalClasspath,
79 Map<String,String> environmentToMerge) {
80 super(workingDirectory,
81 buildCommandLine(classToInvoke, javaProperties, new String[] {}, args),
82 buildEnvironmentToMerge(additionalClasspath, environmentToMerge));
83 }
84
85 public static final String[] buildCommandLine(String classToInvoke,
86 Map<String,String> javaProperties,
87 String[] vmargs,
88 String[] processArgs) {
89 String[] javaSysProps = new String[0]; // reasonable default
90 // construct the system properties
91 if(javaProperties != null && javaProperties.size() > 0) {
92 ArrayList<String> propList = new ArrayList<String>(javaProperties.size());
93 for(Entry<String, String> javaProp : javaProperties.entrySet()) {
94 propList.add("-D" + javaProp.getKey() + "=" + javaProp.getValue());
95 }
96 javaSysProps = propList.toArray(javaSysProps);
97 }
98
99 if(vmargs == null) {
100 vmargs = new String[]{};
101 }
102 if(processArgs == null) {
103 processArgs = new String[]{};
104 }
105
106 // construct the command line
107 final String javaPath = System.getProperty("java.home") +
108 File.separator + "bin" +
109 File.separator + "java" +
110 (File.separator.equals("\\") ? ".exe" : "");
111 final int cmdLineLength = vmargs.length +
112 processArgs.length +
113 javaSysProps.length + 2;
114 final String[] cmdarray = new String[cmdLineLength];
115
116
117 // write out the command line
118 final int javaPosition = 0;
119 final int vmargsPosition = javaPosition + 1;
120 final int javaSysPropsPosition = vmargsPosition + vmargs.length;
121 final int classPosition = javaSysPropsPosition + javaSysProps.length;
122 final int processArgsPosition = classPosition + 1;
123 cmdarray[javaPosition] = javaPath;
124 System.arraycopy(vmargs,0, cmdarray, vmargsPosition, vmargs.length);
125 System.arraycopy(javaSysProps, 0, cmdarray, javaSysPropsPosition, javaSysProps.length);
126 cmdarray[classPosition] = classToInvoke;
127 System.arraycopy(processArgs, 0, cmdarray, processArgsPosition, processArgs.length);
128
129 return cmdarray;
130 }
131
132 /**
133 * Merges with existing classpath (that this VM was spawned with), and places it into the
134 * CLASSPATH environment variable (to avoid command line escaping issues).
135 *
136 * @param additionalClasspath - classpath entries to place at the front of the classpath.
137 * @param environment - entries in the
138 * @return
139 */
140 public static final Map<String,String> buildEnvironmentToMerge(List<String> additionalClasspath,
141 Map<String,String> environment) {
142
143
144 // deal with additional classpath elements (prepend for overrides)
145 String cp = System.getProperty("java.class.path");
146 StringBuilder cpath = new StringBuilder(cp == null ? "" : cp);
147 if(additionalClasspath != null && additionalClasspath.size() > 0) {
148 for(String cpathEntry : additionalClasspath) {
149 cpath.insert(0,File.pathSeparatorChar).insert(0,cpathEntry);
150 }
151 }
152 if(environment == null) {
153 environment = new HashMap<String,String>();
154 }
155 environment.put("CLASSPATH",cpath.toString());
156
157 if(javaInvokeLog.isInfoEnabled()) {
158 javaInvokeLog.info("CLASSPATH=" + cpath.toString());
159 }
160 return environment;
161 }
162}
163