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