blob: a8e2160a5698f37c5b61a839a11992fa5f5a1275 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.openide.execution;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.text.Format;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Logger;
import org.openide.util.Utilities;
/** Encapsulates start information for a process. It allows the user to
* specify the process name to execute and arguments to provide. The progammer
* then uses method exec to start the process and can pass additional format that
* will be applied to arguments.
* <P>
* This allows to define arguments in format -user {USER_NAME} -do {ACTION} and then
* use MapFormat with defined values for USER_NAME and ACTION that will be substitued
* by into the arguments.
*
* @author Ian Formanek, Jaroslav Tulach
*/
public final class NbProcessDescriptor extends Object implements Serializable {
private static final long serialVersionUID = -4535211234565221486L;
/** Logger for logging execs */
private static Logger execLog;
/** The name of the executable to run */
private String processName;
/** argument format */
private String arguments;
/** info about format of the arguments */
private String info;
/** Create a new descriptor for the specified process, classpath switch, and classpath.
* @param processName the name of the executable to run
* @param arguments string for formating of arguments (may be {@link Utilities#parseParameters quoted})
*/
public NbProcessDescriptor(String processName, String arguments) {
this (processName, arguments, null);
}
/** Create a new descriptor for the specified process, classpath switch, and classpath.
* @param processName the name of the executable to run
* @param arguments string for formating of arguments (may be {@link Utilities#parseParameters quoted})
* @param info info how to format the arguments (human-readable string)
*/
public NbProcessDescriptor(String processName, String arguments, String info) {
this.processName = processName;
this.arguments = arguments;
this.info = info;
}
/** Get the name of the executable to run.
* @return the name
*/
public String getProcessName () {
return processName;
}
/** Getter the execution arguments of the process.
* @return the switch that the executable uses for passing the classpath as its command-line parameter
*/
public String getArguments () {
return arguments;
}
/** Getter for the human readable info about the arguments.
* @return the info string or null
*/
public String getInfo () {
return info;
}
/* JST: Commented out, should not be needed.
*
* Get the command string and arguments from the supplied process name.
* Normally the process name will be the actual name of the process executable,
* in which case this method will just return that name by itself.
* However, {@link org.openide.util.Utilities#parseParameters} is used
* to break apart the string into tokens, so that users may:
* <<moved to Utilities.parseParameters Javadoc>>
* @return a list of the command name itself and any arguments, unescaped
* @see Runtime#exec(String[])
*
public String[] getProcessArgs() {
if (processArguments == null) {
processArguments = parseArguments(processName);
}
return (String[]) processArguments.clone();
}
*/
/** Executes the process with arguments formatted by the provided
* format. Also the envp properties are passed to the executed process,
* and a working directory may be supplied.
*
* @param format format to be applied to arguments, process and envp supplied by user. It can be <code>null</code> if no formatting should be done.
* @param envp list of properties to be applied to the process, or <code>null</code> to leave unspecified
* @param cwd the working directory to use, or <code>null</code> if this should not be specified
* @return handle to executed process.
* @exception IOException if the start of the process fails, or if setting the working directory is not supported
*/
public Process exec (Format format, String[] envp, File cwd) throws IOException {
return exec (format, envp, false, cwd);
}
/** Executes the process with arguments, processName and envp formatted by the provided
* format. Also the envp properties are passed to the executed process,
* and a working directory may be supplied. Optionally the environment variables of the NetBeans JVM may
* be appended to (replaced when there is overlap) instead of specifying
* all of the environment variables from scratch. This requires the NetBeans core
* to translate environment variables to system properties prefixed
* by <samp>Env-</samp> in order to work correctly.
*
* @param format format to be applied to arguments, process and envp supplied by user. It can be <code>null</code> if no formatting should be done.
* @param envp list of properties to be applied to the process, or <code>null</code> to leave unspecified
* @param appendEnv if true and <code>envp</code> is not <code>null</code>, append or replace JVM's environment
* @param cwd the working directory to use, or <code>null</code> if this should not be specified
* @return handle to executed process.
* @exception IOException if the start of the process fails, or if setting the working directory is not supported
* @since 1.15
*/
public Process exec (Format format, String[] envp, boolean appendEnv, File cwd) throws IOException {
String stringArgs = format == null ? arguments : format.format (arguments);
String[] args = parseArguments (stringArgs);
String[] call = null;
envp = substituteEnv(format, envp);
// copy the call string
call = new String[args.length + 1];
call[0] = format == null ? processName : format.format(processName);
System.arraycopy (args, 0, call, 1, args.length);
logArgs(call);
ProcessBuilder pb = new ProcessBuilder(call);
if (envp != null) {
Map<String,String> e = pb.environment();
if (!appendEnv) e.clear();
for (int i = 0; i < envp.length; i++) {
String nameval = envp[i];
int idx = nameval.indexOf ('='); // NOI18N
// [PENDING] add localized annotation...
if (idx == -1) throw new IOException ("No equal sign in name=value: " + nameval); // NOI18N
e.put (nameval.substring (0, idx), nameval.substring (idx + 1));
}
}
if (cwd != null) pb.directory(cwd);
return pb.start();
}
private static void logArgs(String[] args) {
getExecLog().fine("Running: " + Arrays.asList(args)); // NOI18N
}
/** Executes the process with arguments and processNme formatted by the provided
* format. Also the envp properties are passed to the executed process.
*
* @param format format to be aplied to arguments, process and envp suplied by user. It can be <code>null</code> if no formatting should be done.
* @param envp list of properties to be applied to the process, or <code>null</code> to leave unspecified
* @return handle to executed process.
* @exception IOException if the start of the process fails
*/
public Process exec (Format format, String[] envp) throws IOException {
return exec (format, envp, null);
}
/** Executes the process with arguments and processName formatted by the provided
* format.
*
* @param format format to be aplied to arguments and process. It can be <code>null</code> if no formatting should be done.
* @return handle to executed process.
* @exception IOException if the start of the process fails
*/
public Process exec (Format format) throws IOException {
return exec (format, null);
}
/** Executes the process with arguments provided in constructor.
*
* @return handle to executed process.
* @exception IOException if the start of the process fails
*/
public Process exec () throws IOException {
return exec (null, null);
}
/* hashCode */
public int hashCode() {
return processName.hashCode() + arguments.hashCode ();
}
/* equals */
public boolean equals(Object o) {
if (! (o instanceof NbProcessDescriptor)) return false;
NbProcessDescriptor him = (NbProcessDescriptor) o;
return processName.equals(him.processName) && arguments.equals(him.arguments);
}
/** Parses given string to an array of arguments.
* @param sargs is tokenized by spaces unless a space is part of "" token
* @return tokenized string
*/
private static String[] parseArguments(String sargs) {
return Utilities.parseParameters(sargs);
}
/** Getter for the execLog */
private static Logger getExecLog() {
if (execLog == null) {
execLog = Logger.getLogger(NbProcessDescriptor.class.getName());
}
return execLog;
}
/** Iterates through envp and applies format.format() on values
* @param format for formatting, i.e. substitute {filesystems} to /home/phil/dev/classes/pack1:/home/phil/dev/classes/pack2:...
* @param envp an String array
*
* @return substitutet array
*/
private static String[] substituteEnv(Format format, String[] envp) {
if (envp == null || envp.length == 0 || format == null) {
return envp;
}
String[] ret = new String[envp.length];
StringBuffer adder = new StringBuffer();
for (int i = 0; i < envp.length; i++) {
ret[i] = envp[i];
if (ret[i] == null) {
continue;
}
int idx = ret[i].indexOf('=');
if (idx < 0) {
continue;
}
String val = ret[i].substring(idx + 1);
String key = ret[i].substring(0, idx);
adder.append(key).append('=').append(format.format(val));
ret[i] = adder.toString();
adder.setLength(0);
}
return ret;
}
}