[maven-release-plugin] copy for tag EXEC_1_0_0
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/exec/tags/EXEC_1_0_0@747204 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/NOTICE.txt b/NOTICE.txt
index c1c880c..4d95380 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -1,5 +1,5 @@
Apache Commons Exec
-Copyright 2005-2008 The Apache Software Foundation
+Copyright 2005-2009 The Apache Software Foundation
This product includes software developed by
The Apache Software Foundation (http://www.apache.org/).
diff --git a/build.xml b/build.xml
index 54cf038..cfd11de 100644
--- a/build.xml
+++ b/build.xml
@@ -112,6 +112,9 @@
</copy>
<!-- make the shell script files readable/executable -->
<chmod dir="${maven.build.directory}/dist" perm="ugo+rx" includes="**/*.sh"/>
+ <!-- copy the various legal files -->
+ <copy file="${basedir}/NOTICE.txt" tofile="${maven.build.directory}/dist/NOTICE.txt"/>
+ <copy file="${basedir}/LICENSE.txt" tofile="${maven.build.directory}/dist/LICENSE.txt"/>
</target>
<!-- Create a zip containing the test environment -->
diff --git a/findbugs-exclude-filter.xml b/findbugs-exclude-filter.xml
new file mode 100644
index 0000000..39990fe
--- /dev/null
+++ b/findbugs-exclude-filter.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<!--
+ This file contains some false positive bugs detected by findbugs. Their
+ false positive nature has been analyzed individually and they have been
+ put here to instruct findbugs it must ignore them.
+-->
+<FindBugsFilter>
+
+ <!-- The following cases are intentional hard-coded paths for different operating systems -->
+ <Match>
+ <Class name="org.apache.commons.exec.environment.DefaultProcessingEnvironment" />
+ <Method name="getProcEnvCommand" params="" returns="org.apache.commons.exec.CommandLine" />
+ <Bug pattern="DMI_HARDCODED_ABSOLUTE_FILENAME" />
+ </Match>
+
+</FindBugsFilter>
diff --git a/pom.xml b/pom.xml
index 8312531..f41620a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,6 +31,11 @@
<description>A library to reliably execute external processes from within the JVM</description>
<url>http://commons.apache.org/exec/</url>
+ <issueManagement>
+ <system>jira</system>
+ <url>http://issues.apache.org/jira/browse/EXEC</url>
+ </issueManagement>
+
<dependencies>
<dependency>
<groupId>junit</groupId>
@@ -128,6 +133,16 @@
</reportSet>
</reportSets>
</plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ <version>1.2</version>
+ <configuration>
+ <threshold>Normal</threshold>
+ <effort>Default</effort>
+ <excludeFilterFile>${basedir}/findbugs-exclude-filter.xml</excludeFilterFile>
+ </configuration>
+ </plugin>
</plugins>
</reporting>
@@ -171,7 +186,7 @@
<commons.jira.pid>12310814</commons.jira.pid>
<commons.release.version>1.0.0</commons.release.version>
<!-- The RC version used in the staging repository URL. -->
- <commons.rc.version>RC3</commons.rc.version>
+ <commons.rc.version>RC4</commons.rc.version>
</properties>
-</project>
\ No newline at end of file
+</project>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 9f23bc8..2005940 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -23,7 +23,22 @@
<author email="sgoeschl@apache.org">Siegfried Goeschl</author>
</properties>
<body>
- <release version="1.0.0" date="2009-01-05" description="Sandbox release">
+ <release version="1.0.0" date="2009-02-24" description="First Public Release">
+ <action dev="sgoeschl" type="fix" due-to="Sebastien Bazley" issue="EXEC-37">
+ Removed useless synchronized statement in
+ OpenVmsProcessingEnvironment.createProcEnvironment
+ </action>
+ <action dev="sgoeschl" type="fix" issue="EXEC-33">
+ Using System.in for child process will actually hang your application -
+ see JIRA for more details. Since there is no easy fix an
+ IllegalRuntimeException is thrown when System.in is passed.
+ </action>
+ <action dev="sgoeschl" type="fix" due-to="Luc Maisonobe" issue="EXEC-35">
+ Fixing a few findbugs issues.
+ </action>
+ <action dev="sgoeschl" type="fix" due-to="Marco Ferrante" issue="EXEC-32">
+ Handle null streams consistently.
+ </action>
<action dev="sgoeschl" type="fix">
After a long discussion we decided to stick to following groupId
"org.apache.commons" instead of "commons-exec".
diff --git a/src/main/java/org/apache/commons/exec/CommandLine.java b/src/main/java/org/apache/commons/exec/CommandLine.java
index ef72f42..1adf3fb 100644
--- a/src/main/java/org/apache/commons/exec/CommandLine.java
+++ b/src/main/java/org/apache/commons/exec/CommandLine.java
@@ -34,12 +34,12 @@
/**
* The arguments of the command.
*/
- private Vector arguments = new Vector();
+ private final Vector arguments = new Vector();
/**
* The program to execute.
*/
- private String executable;
+ private final String executable;
/**
* A map of name value pairs used to expand command line arguments
@@ -54,9 +54,7 @@
/**
* Create a command line from a string.
*
- * @param line
- * the line: the first element becomes the executable, the rest
- * the arguments
+ * @param line the first element becomes the executable, the rest the arguments
* @return the parsed command line
* @throws IllegalArgumentException If line is null or all whitespace
*/
@@ -67,9 +65,7 @@
/**
* Create a command line from a string.
*
- * @param line
- * the line: the first element becomes the executable, the rest
- * the arguments
+ * @param line the first element becomes the executable, the rest the arguments
* @param substitutionMap the name/value pairs used for substitution
* @return the parsed command line
* @throws IllegalArgumentException If line is null or all whitespace
@@ -100,7 +96,7 @@
*/
public CommandLine(String executable) {
this.isFile=false;
- setExecutable(executable);
+ this.executable=getExecutable(executable);
}
/**
@@ -110,7 +106,7 @@
*/
public CommandLine(File executable) {
this.isFile=true;
- setExecutable(executable.getAbsolutePath());
+ this.executable=getExecutable(executable.getAbsolutePath());
}
/**
@@ -240,7 +236,8 @@
/**
* Set the substitutionMap to expand variables in the
- * command line
+ * command line.
+ *
* @param substitutionMap the map
*/
public void setSubstitutionMap(Map substitutionMap) {
@@ -394,18 +391,19 @@
}
/**
- * Set the executable - the argument is trimmed and '/' and '\\' are
+ * Get the executable - the argument is trimmed and '/' and '\\' are
* replaced with the platform specific file seperator char
*
* @param executable the executable
+ * @return the platform-specific executable string
*/
- private void setExecutable(final String executable) {
+ private String getExecutable(final String executable) {
if (executable == null) {
throw new IllegalArgumentException("Executable can not be null");
} else if(executable.trim().length() == 0) {
throw new IllegalArgumentException("Executable can not be empty");
} else {
- this.executable = StringUtils.fixFileSeparatorChar(executable);
+ return StringUtils.fixFileSeparatorChar(executable);
}
}
}
diff --git a/src/main/java/org/apache/commons/exec/DefaultExecutor.java b/src/main/java/org/apache/commons/exec/DefaultExecutor.java
index 7cb5201..19955bc 100644
--- a/src/main/java/org/apache/commons/exec/DefaultExecutor.java
+++ b/src/main/java/org/apache/commons/exec/DefaultExecutor.java
@@ -42,8 +42,6 @@
* CommandLine cl = new CommandLine("ls -l");
* int exitvalue = exec.execute(cl);
* </pre>
- *
- *
*/
public class DefaultExecutor implements Executor {
@@ -60,7 +58,7 @@
private int[] exitValues;
/** launches the command in a new process */
- private CommandLauncher launcher;
+ private final CommandLauncher launcher;
/** optional cleanup of started processes */
private ProcessDestroyer processDestroyer;
@@ -200,7 +198,7 @@
/** @see org.apache.commons.exec.Executor#setExitValues(int[]) */
public void setExitValues(final int[] values) {
- this.exitValues = values;
+ this.exitValues = (int[]) values.clone();
}
/** @see org.apache.commons.exec.Executor#isFailure(int) */
diff --git a/src/main/java/org/apache/commons/exec/ExecuteException.java b/src/main/java/org/apache/commons/exec/ExecuteException.java
index 1cf8aa0..fdb8b0a 100644
--- a/src/main/java/org/apache/commons/exec/ExecuteException.java
+++ b/src/main/java/org/apache/commons/exec/ExecuteException.java
@@ -33,12 +33,12 @@
/**
* The underlying cause of this exception.
*/
- private Throwable cause;
+ private final Throwable cause;
/**
* The exit value returned by the failed process
*/
- private int exitValue;
+ private final int exitValue;
/**
* Construct a new exception with the specified detail message.
@@ -49,6 +49,7 @@
*/
public ExecuteException(final String message, int exitValue) {
super(message + "(Exit value: " + exitValue + ")");
+ this.cause = null;
this.exitValue = exitValue;
}
diff --git a/src/main/java/org/apache/commons/exec/ExecuteWatchdog.java b/src/main/java/org/apache/commons/exec/ExecuteWatchdog.java
index a386ddc..8eb00a9 100644
--- a/src/main/java/org/apache/commons/exec/ExecuteWatchdog.java
+++ b/src/main/java/org/apache/commons/exec/ExecuteWatchdog.java
@@ -49,7 +49,7 @@
private boolean killedProcess;
/** Will tell us whether timeout has occurred. */
- private Watchdog watchdog;
+ private final Watchdog watchdog;
/**
* Creates a new watchdog with a given timeout.
@@ -172,7 +172,7 @@
/**
* reset the monitor flag and the process.
*/
- protected void cleanUp() {
+ protected synchronized void cleanUp() {
watch = false;
process = null;
}
diff --git a/src/main/java/org/apache/commons/exec/Executor.java b/src/main/java/org/apache/commons/exec/Executor.java
index 96aa554..37a7786 100644
--- a/src/main/java/org/apache/commons/exec/Executor.java
+++ b/src/main/java/org/apache/commons/exec/Executor.java
@@ -22,66 +22,130 @@
import java.io.IOException;
import java.util.Map;
+/**
+ * The main abstraction to start an external process.
+ *
+ * The interface allows to
+ * <ul>
+ * <li>set a current working directory for the subprocess</li>
+ * <li>provide a set of environment variables passed to the subprocess</li>
+ * <li>capture the subprocess output of stdout and stderr using an ExecuteStreamHandler</li>
+ * <li>kill long-running processes using an ExecuteWatchdog</li>
+ * <li>define a set of expected exit values</li>
+ * <li>terminate any started processes when the main process is terminating using a ProcessDestroyer</li>
+ * </ul>
+ *
+ * The following example shows the basic usage:
+ *
+ * <pre>
+ * Executor exec = new DefaultExecutor();
+ * CommandLine cl = new CommandLine("ls -l");
+ * int exitvalue = exec.execute(cl);
+ * </pre>
+ */
+
public interface Executor {
- /** Invalid exit code. * */
+ /** Invalid exit code. */
int INVALID_EXITVALUE = 0xdeadbeef;
- /*
+ /**
* Define the exit code of the process to considered
* successful.
+ *
+ * @param value the exit code representing successful execution
*/
void setExitValue(final int value);
- /*
+ /**
* Define the exit code of the process to considered
- * successful using one of the following values
+ * successful. The caller can pass one of the following values
* <ul>
* <li>an array of exit values to be considered successful</li>
* <li>an empty array for auto-detect of successful exit codes</li>
* <li>null to indicate to skip checking of exit codes</li>
* </ul>
+ *
+ * @param values a list of the exit codes
*/
void setExitValues(final int[] values);
/**
* Checks whether <code>exitValue</code> signals a failure. If no
* exit values are set than the default conventions of the OS is
- * used.
+ * used. e.g. most OS regard an exit code of '0' as successful
+ * execution and everything else as failure.
*
* @param exitValue the exit value (return code) to be checked
* @return <code>true</code> if <code>exitValue</code> signals a failure
*/
boolean isFailure(final int exitValue);
- /*
- * StreamHandlers are used for providing input,
- * retriving the output. Also used for logging.
+ /**
+ * Get the StreamHandler used for providing input and
+ * retriving the output.
+ *
+ * @return the StreamHandler
*/
ExecuteStreamHandler getStreamHandler();
+
+ /**
+ * Set the StreamHandler used for providing input and
+ * retriving the output.
+ *
+ * @param streamHandler the StreamHandler
+ */
+
void setStreamHandler(ExecuteStreamHandler streamHandler);
- /*
- * Watchdog is used to kill of processes running,
- * typically, too long time.
+ /**
+ * Get the watchdog used to kill of processes running,
+ * typically, too long time.
+ *
+ * @return the watchdog
*/
ExecuteWatchdog getWatchdog();
+
+ /**
+ * Set the watchdog used to kill of processes running,
+ * typically, too long time.
+ *
+ * @param watchDog the watchdog
+ */
void setWatchdog(ExecuteWatchdog watchDog);
/**
- * Optinal cleanup of started processes if the main process
+ * Set the handler for cleanup of started processes if the main process
* is going to terminate.
+ *
+ * @return the ProcessDestroyer
*/
ProcessDestroyer getProcessDestroyer();
+
+ /**
+ * Get the handler for cleanup of started processes if the main process
+ * is going to terminate.
+ *
+ * @param processDestroyer the ProcessDestroyer
+ */
void setProcessDestroyer(ProcessDestroyer processDestroyer);
- /*
- * Set the working directory of the created process.
+ /**
+ * Get the working directory of the created process.
+ *
+ * @return the working directory
*/
File getWorkingDirectory();
+
+ /**
+ * Set the working directory of the created process. The
+ * working directory must exist when you start the process.
+ *
+ * @param dir the working directory
+ */
void setWorkingDirectory(File dir);
- /*
+ /**
* Methods for starting synchronous execution. The child process inherits
* all environment variables of the parent process.
*
@@ -89,44 +153,44 @@
* @return process exit value
* @throws ExecuteException execution of subprocess failed
*/
- int execute(CommandLine command) throws ExecuteException, IOException;
+ int execute(CommandLine command)
+ throws ExecuteException, IOException;
- /*
- * Methods for starting synchronous execution. If
+ /**
+ * Methods for starting synchronous execution.
*
* @param command the command to execute
- * @param environment The environment for the new process. If null, the environment
- * of the current process is used.
+ * @param environment The environment for the new process. If null, the
+ * environment of the current process is used.
* @return process exit value
* @throws ExecuteException execution of subprocess failed
*/
- int execute(CommandLine command, Map environment) throws ExecuteException, IOException;
+ int execute(CommandLine command, Map environment)
+ throws ExecuteException, IOException;
- /*
- * Methods for starting asynchronous execution. Result provided to callback handler
- */
-
- /*
- * Methods for starting synchronous execution. The child process inherits
+ /**
+ * Methods for starting asynchronous execution. The child process inherits
* all environment variables of the parent process. Result provided to
* callback handler.
*
* @param command the command to execute
- * @return process exit value
+ * @param handler capture process termination and exit code
* @throws ExecuteException execution of subprocess failed
*/
- void execute(CommandLine command, ExecuteResultHandler handler) throws ExecuteException, IOException;
+ void execute(CommandLine command, ExecuteResultHandler handler)
+ throws ExecuteException, IOException;
- /*
- * Methods for starting synchronous execution. The child process inherits
+ /**
+ * Methods for starting asynchronous execution. The child process inherits
* all environment variables of the parent process. Result provided to
* callback handler.
*
* @param command the command to execute
- * @param environment The environment for the new process. If null, the environment
- * of the current process is used.
- * @return process exit value
+ * @param environment The environment for the new process. If null, the
+ * environment of the current process is used.
+ * @param handler capture process termination and exit code
* @throws ExecuteException execution of subprocess failed
*/
- void execute(CommandLine command, Map environment, ExecuteResultHandler handler) throws ExecuteException, IOException;
+ void execute(CommandLine command, Map environment, ExecuteResultHandler handler)
+ throws ExecuteException, IOException;
}
diff --git a/src/main/java/org/apache/commons/exec/LogOutputStream.java b/src/main/java/org/apache/commons/exec/LogOutputStream.java
index 559032a..bc79386 100644
--- a/src/main/java/org/apache/commons/exec/LogOutputStream.java
+++ b/src/main/java/org/apache/commons/exec/LogOutputStream.java
@@ -41,12 +41,20 @@
private static final int LF = 0x0a;
/** the internal buffer */
- private ByteArrayOutputStream buffer = new ByteArrayOutputStream(
+ private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(
INTIAL_SIZE);
private boolean skip = false;
- private int level = 999;
+ private final int level;
+
+ /**
+ * Creates a new instance of this class.
+ * Uses the default level of 999.
+ */
+ public LogOutputStream() {
+ this(999);
+ }
/**
* Creates a new instance of this class.
diff --git a/src/main/java/org/apache/commons/exec/PumpStreamHandler.java b/src/main/java/org/apache/commons/exec/PumpStreamHandler.java
index b467882..6f1f4bd 100644
--- a/src/main/java/org/apache/commons/exec/PumpStreamHandler.java
+++ b/src/main/java/org/apache/commons/exec/PumpStreamHandler.java
@@ -26,7 +26,8 @@
/**
* Copies standard output and error of subprocesses to standard output and error
- * of the parent process.
+ * of the parent process. If output or error stream are set to null, any feedback
+ * from that stream will be lost.
*/
public class PumpStreamHandler implements ExecuteStreamHandler {
@@ -36,11 +37,11 @@
private Thread inputThread;
- private OutputStream out;
+ private final OutputStream out;
- private OutputStream err;
+ private final OutputStream err;
- private InputStream input;
+ private final InputStream input;
/**
* Construct a new <CODE>PumpStreamHandler</CODE>.
@@ -54,6 +55,13 @@
*/
public PumpStreamHandler(final OutputStream out, final OutputStream err,
final InputStream input) {
+
+ // see EXEC-33
+ if(input == System.in) {
+ String msg = "Using System.in is currently not supported since it would hang your application (see EXEC-33).";
+ throw new IllegalArgumentException(msg);
+ }
+
this.out = out;
this.err = err;
this.input = input;
@@ -96,7 +104,9 @@
* the <CODE>InputStream</CODE>.
*/
public void setProcessOutputStream(final InputStream is) {
- createProcessOutputPump(is, out);
+ if (out != null) {
+ createProcessOutputPump(is, out);
+ }
}
/**
@@ -136,8 +146,12 @@
* Start the <CODE>Thread</CODE>s.
*/
public void start() {
- outputThread.start();
- errorThread.start();
+ if (outputThread != null) {
+ outputThread.start();
+ }
+ if (errorThread != null) {
+ errorThread.start();
+ }
if (inputThread != null) {
inputThread.start();
}
@@ -147,37 +161,51 @@
* Stop pumping the streams.
*/
public void stop() {
- try {
- outputThread.join();
- } catch (InterruptedException e) {
- // ignore
- }
- try {
- errorThread.join();
- } catch (InterruptedException e) {
- // ignore
- }
- if (inputThread != null) {
+ if (outputThread != null) {
try {
- inputThread.join();
+ outputThread.join();
+ outputThread = null;
} catch (InterruptedException e) {
// ignore
}
}
- try {
- err.flush();
- } catch (IOException e) {
- String msg = "Got exception while flushing the error stream";
- DebugUtils.handleException(msg ,e);
+ if (errorThread != null) {
+ try {
+ errorThread.join();
+ errorThread = null;
+ } catch (InterruptedException e) {
+ // ignore
+ }
}
- try {
- out.flush();
- } catch (IOException e) {
- String msg = "Got exception while flushing the output stream";
- DebugUtils.handleException(msg ,e);
+
+ if (inputThread != null) {
+ try {
+ inputThread.join();
+ inputThread = null;
+ } catch (InterruptedException e) {
+ // ignore
+ }
}
+
+ if (err != null && err != out) {
+ try {
+ err.flush();
+ } catch (IOException e) {
+ String msg = "Got exception while flushing the error stream";
+ DebugUtils.handleException(msg ,e);
+ }
+ }
+
+ if (out != null) {
+ try {
+ out.flush();
+ } catch (IOException e) {
+ String msg = "Got exception while flushing the output stream";
+ DebugUtils.handleException(msg ,e);
+ }
+ }
}
/**
@@ -227,6 +255,10 @@
/**
* Creates a stream pumper to copy the given input stream to the given
* output stream.
+ *
+ * @param is the input stream to copy from
+ * @param os the output stream to copy into
+ * @return the stream pumper thread
*/
protected Thread createPump(final InputStream is, final OutputStream os) {
return createPump(is, os, false);
@@ -235,6 +267,11 @@
/**
* Creates a stream pumper to copy the given input stream to the given
* output stream.
+ *
+ * @param is the input stream to copy from
+ * @param os the output stream to copy into
+ * @param closeWhenExhausted close the output stream when the input stream is exhausted
+ * @return the stream pumper thread
*/
protected Thread createPump(final InputStream is, final OutputStream os,
final boolean closeWhenExhausted) {
diff --git a/src/main/java/org/apache/commons/exec/StreamPumper.java b/src/main/java/org/apache/commons/exec/StreamPumper.java
index 1306e26..1fd4697 100644
--- a/src/main/java/org/apache/commons/exec/StreamPumper.java
+++ b/src/main/java/org/apache/commons/exec/StreamPumper.java
@@ -33,19 +33,19 @@
private static final int DEFAULT_SIZE = 1024;
/** the input stream to pump from */
- private InputStream is;
+ private final InputStream is;
/** the output stream to pmp into */
- private OutputStream os;
+ private final OutputStream os;
/** the size of the internal buffer for copying the streams */
- private int size;
+ private final int size;
/** was the end of the stream reached */
private boolean finished;
/** close the output stream when exhausted */
- private boolean closeWhenExhausted;
+ private final boolean closeWhenExhausted;
/**
* Create a new stream pumper.
diff --git a/src/main/java/org/apache/commons/exec/Watchdog.java b/src/main/java/org/apache/commons/exec/Watchdog.java
index 754d072..f9eecff 100644
--- a/src/main/java/org/apache/commons/exec/Watchdog.java
+++ b/src/main/java/org/apache/commons/exec/Watchdog.java
@@ -30,13 +30,13 @@
private Vector observers = new Vector(1);
- private long timeout = -1;
+ private final long timeout;
private boolean stopped = false;
public Watchdog(final long timeout) {
if (timeout < 1) {
- throw new IllegalArgumentException("timeout lesser than 1.");
+ throw new IllegalArgumentException("timeout must not be less than 1.");
}
this.timeout = timeout;
}
diff --git a/src/main/java/org/apache/commons/exec/environment/EnvironmentUtils.java b/src/main/java/org/apache/commons/exec/environment/EnvironmentUtils.java
index 77da29f..c102cb3 100644
--- a/src/main/java/org/apache/commons/exec/environment/EnvironmentUtils.java
+++ b/src/main/java/org/apache/commons/exec/environment/EnvironmentUtils.java
@@ -30,7 +30,7 @@
public class EnvironmentUtils
{
- private static DefaultProcessingEnvironment procEnvironment;
+ private static final DefaultProcessingEnvironment procEnvironment;
static {
if (OS.isFamilyOpenVms()) {
@@ -84,13 +84,14 @@
/**
* Add a key/value pair to the given environment.
+ * If the key matches an existing key, the previous setting is replaced.
*
* @param environment the current environment
* @param keyAndValue the key/value pair
*/
public static void addVariableToEnvironment(Map environment, String keyAndValue) {
- String[] parsedVarible = parseEnvironmentVariable(keyAndValue);
- environment.put(parsedVarible[0], parsedVarible[1]);
+ String[] parsedVariable = parseEnvironmentVariable(keyAndValue);
+ environment.put(parsedVariable[0], parsedVariable[1]);
}
/**
diff --git a/src/main/java/org/apache/commons/exec/environment/OpenVmsProcessingEnvironment.java b/src/main/java/org/apache/commons/exec/environment/OpenVmsProcessingEnvironment.java
index 5af1014..4bff50d 100644
--- a/src/main/java/org/apache/commons/exec/environment/OpenVmsProcessingEnvironment.java
+++ b/src/main/java/org/apache/commons/exec/environment/OpenVmsProcessingEnvironment.java
@@ -37,7 +37,7 @@
* @return a amp containing the environment variables
* @throws IOException the operation failed
*/
- protected synchronized Map createProcEnvironment() throws IOException {
+ protected Map createProcEnvironment() throws IOException {
if (procEnvironment == null) {
procEnvironment = new HashMap();
@@ -109,8 +109,8 @@
logicals.put(logName, logValue);
}
- for (Iterator i = logicals.keySet().iterator(); i.hasNext();) {
- String logical = (String) i.next();
+ for (Iterator i = logicals.entrySet().iterator(); i.hasNext();) {
+ String logical = (String) ((Map.Entry) i.next()).getKey();
environment.put(logical, logicals.get(logical));
}
return environment;
diff --git a/src/main/java/org/apache/commons/exec/launcher/CommandLauncherProxy.java b/src/main/java/org/apache/commons/exec/launcher/CommandLauncherProxy.java
index 4ce8c09..64f997a 100644
--- a/src/main/java/org/apache/commons/exec/launcher/CommandLauncherProxy.java
+++ b/src/main/java/org/apache/commons/exec/launcher/CommandLauncherProxy.java
@@ -33,7 +33,7 @@
myLauncher = launcher;
}
- private CommandLauncher myLauncher;
+ private final CommandLauncher myLauncher;
/**
* Launches the given command in a new process. Delegates this method to the
diff --git a/src/main/java/org/apache/commons/exec/util/MapUtils.java b/src/main/java/org/apache/commons/exec/util/MapUtils.java
index 3e1f973..5e69785 100644
--- a/src/main/java/org/apache/commons/exec/util/MapUtils.java
+++ b/src/main/java/org/apache/commons/exec/util/MapUtils.java
@@ -59,17 +59,18 @@
*/
public static Map prefix(Map source, String prefix) {
- Map result = new HashMap();
-
if(source == null) {
return null;
}
- Iterator iter = source.keySet().iterator();
+ Map result = new HashMap();
+
+ Iterator iter = source.entrySet().iterator();
while(iter.hasNext()) {
- Object key = iter.next();
- Object value = source.get(key);
+ Map.Entry entry = (Map.Entry) iter.next();
+ Object key = entry.getKey();
+ Object value = entry.getValue();
result.put(prefix + '.' + key.toString(), value);
}
diff --git a/src/test/java/org/apache/commons/exec/CommandLineTest.java b/src/test/java/org/apache/commons/exec/CommandLineTest.java
index 05ee0c7..2e3d7fc 100644
--- a/src/test/java/org/apache/commons/exec/CommandLineTest.java
+++ b/src/test/java/org/apache/commons/exec/CommandLineTest.java
@@ -239,6 +239,28 @@
assertEquals("cmd /C convert source.jpg -resize \"500x> \" target.jpg", cmdl.toString());
}
+ /**
+ * Another command line parsing puzzle from Kai Hu - as
+ * far as I understand it there is no way to express that
+ * in a one-line command string.
+ */
+ public void testParseComplexCommandLine2() {
+
+ String commandline = "./script/jrake cruise:publish_installers "
+ + "INSTALLER_VERSION=unstable_2_1 "
+ + "INSTALLER_PATH=\"/var/lib/ cruise-agent/installers\" "
+ + "INSTALLER_DOWNLOAD_SERVER=\'something\' "
+ + "WITHOUT_HELP_DOC=true";
+
+ CommandLine cmdl = CommandLine.parse(commandline);
+ String[] args = cmdl.getArguments();
+ assertEquals(args[0], "cruise:publish_installers");
+ assertEquals(args[1], "INSTALLER_VERSION=unstable_2_1");
+ // assertEquals(args[2], "INSTALLER_PATH=\"/var/lib/ cruise-agent/installers\"");
+ // assertEquals(args[3], "INSTALLER_DOWNLOAD_SERVER='something'");
+ assertEquals(args[4], "WITHOUT_HELP_DOC=true");
+ }
+
/**
* Create a command line with pre-quoted strings to test SANDBOX-192,
* e.g. "runMemorySud.cmd", "10", "30", "-XX:+UseParallelGC", "\"-XX:ParallelGCThreads=2\""
diff --git a/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java b/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java
index 8f4264f..7caed58 100644
--- a/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java
+++ b/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java
@@ -21,6 +21,8 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.Map;
@@ -35,6 +37,7 @@
private File errorTestScript = TestUtil.resolveScriptForOS(testDir + "/error");
private File foreverTestScript = TestUtil.resolveScriptForOS(testDir + "/forever");
private File nonExistingTestScript = TestUtil.resolveScriptForOS(testDir + "/grmpffffff");
+ private File redirectScript = TestUtil.resolveScriptForOS(testDir + "/redirect");
// Get suitable exit codes for the OS
private static final int SUCCESS_STATUS; // test script successful exit code
@@ -343,5 +346,90 @@
int exitValue = exec.execute(cl);
assertTrue(baos.toString().trim().indexOf("test $;`(0)[1]{2}") > 0);
assertFalse(exec.isFailure(exitValue));
- }
+ }
+
+ /**
+ * Start a process with redirected streams - stdin of the newly
+ * created process is connected to a FileInputStream whereas
+ * the "redirect" script reads all lines from stdin and prints
+ * them on stdout. Furthermore the script prints a status
+ * message on stderr.
+ */
+ public void testExecuteWithRedirectedStreams() throws Exception
+ {
+ if(OS.isFamilyUnix())
+ {
+ FileInputStream fis = new FileInputStream("./NOTICE.txt");
+ CommandLine cl = new CommandLine(redirectScript);
+ PumpStreamHandler pumpStreamHandler = new PumpStreamHandler( System.out, System.out, fis );
+ DefaultExecutor executor = new DefaultExecutor();
+ executor.setWorkingDirectory(new File("."));
+ executor.setStreamHandler( pumpStreamHandler );
+ int exitValue = executor.execute(cl);
+ fis.close();
+ assertFalse(exec.isFailure(exitValue));
+ }
+ }
+
+ /**
+ * Start a process and connect stdin, stdout and stderr. This
+ * test currenty hang. Therefore we throw an IllegalArgument
+ * Exception to notify the user (see EXEC-33).
+ */
+ public void testExecuteWithStdin() throws Exception
+ {
+ try {
+ CommandLine cl = new CommandLine(testScript);
+ PumpStreamHandler pumpStreamHandler = new PumpStreamHandler( System.out, System.err, System.in );
+ DefaultExecutor executor = new DefaultExecutor();
+ executor.setStreamHandler( pumpStreamHandler );
+ int exitValue = executor.execute(cl);
+ assertFalse(exec.isFailure(exitValue));
+ }
+ catch(IllegalArgumentException e) {
+ assertTrue( e.getMessage().indexOf("EXEC-33") >= 0);
+ }
+ }
+
+ /**
+ * Start a process and connect stdout and stderr.
+ */
+ public void testExecuteWithStdOutErr() throws Exception
+ {
+ CommandLine cl = new CommandLine(testScript);
+ PumpStreamHandler pumpStreamHandler = new PumpStreamHandler( System.out, System.err );
+ DefaultExecutor executor = new DefaultExecutor();
+ executor.setStreamHandler( pumpStreamHandler );
+ int exitValue = executor.execute(cl);
+ assertFalse(exec.isFailure(exitValue));
+ }
+
+ /**
+ * Start a process and connect it to no stream.
+ */
+ public void testExecuteWithNullOutErr() throws Exception
+ {
+ CommandLine cl = new CommandLine(testScript);
+ PumpStreamHandler pumpStreamHandler = new PumpStreamHandler( null, null );
+ DefaultExecutor executor = new DefaultExecutor();
+ executor.setStreamHandler( pumpStreamHandler );
+ int exitValue = executor.execute(cl);
+ assertFalse(exec.isFailure(exitValue));
+ }
+
+ /**
+ * Start a process and connect out and err to a file.
+ */
+ public void testExecuteWithRedirectOutErr() throws Exception
+ {
+ File outfile = File.createTempFile("EXEC", ".test");
+ outfile.deleteOnExit();
+ CommandLine cl = new CommandLine(testScript);
+ PumpStreamHandler pumpStreamHandler = new PumpStreamHandler( new FileOutputStream(outfile) );
+ DefaultExecutor executor = new DefaultExecutor();
+ executor.setStreamHandler( pumpStreamHandler );
+ int exitValue = executor.execute(cl);
+ assertFalse(exec.isFailure(exitValue));
+ assertTrue(outfile.exists());
+ }
}
diff --git a/src/test/java/org/apache/commons/exec/environment/EnvironmentUtilTest.java b/src/test/java/org/apache/commons/exec/environment/EnvironmentUtilTest.java
index 0d76884..e9a09da 100644
--- a/src/test/java/org/apache/commons/exec/environment/EnvironmentUtilTest.java
+++ b/src/test/java/org/apache/commons/exec/environment/EnvironmentUtilTest.java
@@ -31,21 +31,20 @@
public class EnvironmentUtilTest extends TestCase {
- public void testToStrings() throws IOException {
+ /**
+ * Tests the behaviour of the EnvironmentUtils.toStrings()
+ * when using a <code>null</code> environment.
+ */
+ public void testToStrings() {
+ // check for a non-existing environment when passing null
TestUtil.assertEquals(null, EnvironmentUtils.toStrings(null), false);
-
+ // check for an environment when filling in two variables
Map env = new HashMap();
-
TestUtil.assertEquals(new String[0], EnvironmentUtils.toStrings(env), false);
-
env.put("foo2", "bar2");
env.put("foo", "bar");
-
String[] envStrings = EnvironmentUtils.toStrings(env);
-
String[] expected = new String[]{"foo=bar", "foo2=bar2"};
-
-
TestUtil.assertEquals(expected, envStrings, false);
}
@@ -57,7 +56,7 @@
public void testGetProcEnvironment() throws IOException {
Map procEnvironment = EnvironmentUtils.getProcEnvironment();
// we assume that there is at least one environment variable
- // for this process
+ // for this process, i.e. $JAVA_HOME
assertTrue(procEnvironment.size() > 0);
String[] envArgs = EnvironmentUtils.toStrings(procEnvironment);
for(int i=0; i<envArgs.length; i++) {
@@ -80,11 +79,12 @@
// ensure that we have the same value for upper and lowercase keys
Map procEnvironment = EnvironmentUtils.getProcEnvironment();
- for (Iterator it = procEnvironment.keySet().iterator(); it.hasNext();) {
- String variable = (String) it.next();
- String value = (String) procEnvironment.get(variable);
- assertEquals(value, procEnvironment.get(variable.toLowerCase(Locale.ENGLISH)));
- assertEquals(value, procEnvironment.get(variable.toUpperCase(Locale.ENGLISH)));
+ for (Iterator it = procEnvironment.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry) it.next();
+ String key = (String) entry.getKey();
+ String value = (String) entry.getValue();
+ assertEquals(value, procEnvironment.get(key.toLowerCase(Locale.ENGLISH)));
+ assertEquals(value, procEnvironment.get(key.toUpperCase(Locale.ENGLISH)));
}
// add an environment variable and check access
@@ -94,4 +94,17 @@
assertEquals("bar", procEnvironment.get("foo"));
}
+ /**
+ * Accessing environment variables is case-sensitive or not depending
+ * on the operating system but the values of the environment variable
+ * are always case-sensitive. So make sure that this assumption holds
+ * on all operating systems.
+ */
+ public void testCaseInsensitiveVariableLookup() throws Exception {
+ Map procEnvironment = EnvironmentUtils.getProcEnvironment();
+ // Check that case is preserved for values
+ EnvironmentUtils.addVariableToEnvironment( procEnvironment, "foo=bAr" );
+ assertEquals("bAr", procEnvironment.get("foo"));
+ }
+
}
diff --git a/src/test/scripts/redirect.sh b/src/test/scripts/redirect.sh
new file mode 100755
index 0000000..dce5c99
--- /dev/null
+++ b/src/test/scripts/redirect.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# read from stdin and output to stdout
+
+while read myline
+do
+ echo "stdout: $myline"
+done
+
+echo 1>&2 "stderr: Finished reading from stdin"
+
+exit 0
+