From 13194de97c2858365330a971b6e9fb2a0969c3b5 Mon Sep 17 00:00:00 2001
From: tms08012
+ * Please note that ExecuteWatchdog is processed asynchronously, e.g. it might
+ * be still attached to a process even after the DefaultExecutor.execute
+ * has returned.
+ *
+ * @see org.apache.commons.exec.Executor
+ * @see org.apache.commons.exec.Watchdog
+ */
+public class ExecuteWatchdog implements TimeoutObserver {
+
+ /** The marker for an infinite timeout */
+ public static final long INFINITE_TIMEOUT = -1;
+
+ /** The process to execute and watch for duration. */
+ private Process process;
+
+ /** Is a user-supplied timeout in use */
+ private final boolean hasWatchdog;
+
+ /** Say whether or not the watchdog is currently monitoring a process. */
+ private boolean watch;
+
+ /** Exception that might be thrown during the process execution. */
+ private Exception caught;
+
+ /** Say whether or not the process was killed due to running overtime. */
+ private boolean killedProcess;
+
+ /** Will tell us whether timeout has occurred. */
+ private final Watchdog watchdog;
+
+ /** Indicates that the process is verified as started */
+ private volatile boolean processStarted;
+
+ /**
+ * Creates a new watchdog with a given timeout.
+ *
+ * @param timeout
+ * the timeout for the process in milliseconds. It must be
+ * greater than 0 or 'INFINITE_TIMEOUT'
+ */
+ public ExecuteWatchdog(final long timeout) {
+ this.killedProcess = false;
+ this.watch = false;
+ this.hasWatchdog = (timeout != INFINITE_TIMEOUT);
+ this.processStarted = false;
+ if(this.hasWatchdog) {
+ this.watchdog = new Watchdog(timeout);
+ this.watchdog.addTimeoutObserver(this);
+ }
+ else {
+ this.watchdog = null;
+ }
+ }
+
+ /**
+ * Watches the given process and terminates it, if it runs for too long. All
+ * information from the previous run are reset.
+ *
+ * @param process
+ * the process to monitor. It cannot be null
+ * @throws IllegalStateException
+ * if a process is still being monitored.
+ */
+ public synchronized void start(final Process process) {
+ if (process == null) {
+ throw new NullPointerException("process is null.");
+ }
+ if (this.process != null) {
+ throw new IllegalStateException("Already running.");
+ }
+ this.caught = null;
+ this.killedProcess = false;
+ this.watch = true;
+ this.process = process;
+ this.processStarted = true;
+ this.notifyAll();
+ if(this.hasWatchdog) {
+ watchdog.start();
+ }
+ }
+
+ /**
+ * Stops the watcher. It will notify all threads possibly waiting on this
+ * object.
+ */
+ public synchronized void stop() {
+ if(hasWatchdog) {
+ watchdog.stop();
+ }
+ watch = false;
+ process = null;
+ }
+
+ /**
+ * Destroys the running process manually.
+ */
+ public synchronized void destroyProcess() {
+ ensureStarted();
+ this.timeoutOccured(null);
+ this.stop();
+ }
+
+ /**
+ * Called after watchdog has finished.
+ */
+ public synchronized void timeoutOccured(final Watchdog w) {
+ try {
+ try {
+ // We must check if the process was not stopped
+ // before being here
+ if(process != null) {
+ process.exitValue();
+ }
+ } catch (IllegalThreadStateException itse) {
+ // the process is not terminated, if this is really
+ // a timeout and not a manual stop then destroy it.
+ if (watch) {
+ killedProcess = true;
+ process.destroy();
+ }
+ }
+ } catch (Exception e) {
+ caught = e;
+ DebugUtils.handleException("Getting the exit value of the process failed", e);
+ } finally {
+ cleanUp();
+ }
+ }
+
+
+ /**
+ * This method will rethrow the exception that was possibly caught during
+ * the run of the process. It will only remains valid once the process has
+ * been terminated either by 'error', timeout or manual intervention.
+ * Information will be discarded once a new process is ran.
+ *
+ * @throws Exception
+ * a wrapped exception over the one that was silently swallowed
+ * and stored during the process run.
+ */
+ public synchronized void checkException() throws Exception {
+ if (caught != null) {
+ throw caught;
+ }
+ }
+
+ /**
+ * Indicates whether or not the watchdog is still monitoring the process.
+ *
+ * @return true if the process is still running, otherwise
+ * false.
+ */
+ public synchronized boolean isWatching() {
+ ensureStarted();
+ return watch;
+ }
+
+ /**
+ * Indicates whether the last process run was killed.
+ *
+ * @return true if the process was killed
+ * false.
+ */
+ public synchronized boolean killedProcess() {
+ return killedProcess;
+ }
+
+ /**
+ * reset the monitor flag and the process.
+ */
+ protected synchronized void cleanUp() {
+ watch = false;
+ process = null;
+ }
+
+ void setProcessNotStarted(){
+ processStarted = false;
+ }
+
+ /**
+ * Ensures that the process is started, so we do not race with asynch execution.
+ * The caller of this method must be holding the lock on this
+ */
+ private void ensureStarted(){
+ while (!processStarted){
+ try {
+ this.wait();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/Executor.java b/ApacheCommonsExec/src/org/apache/commons/exec/Executor.java
new file mode 100644
index 0000000..bbc356d
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/Executor.java
@@ -0,0 +1,208 @@
+/*
+ * 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.apache.commons.exec;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * The main abstraction to start an external process.
+ *
+ * The interface allows to
+ *
+ * Note that this method relies on the conventions of the OS, it
+ * will return false results if the application you are running doesn't
+ * follow these conventions. One notable exception is the Java VM provided
+ * by HP for OpenVMS - it will return 0 if successful (like on any other
+ * platform), but this signals a failure on OpenVMS. So if you execute a new
+ * Java VM on OpenVMS, you cannot trust this method.
+ *
+ * Unlike Windows NT and friends, OS/2's cd doesn't support the /d switch to
+ * change drives and directories in one go.
+ *
+ * If the argument doesn't include spaces or quotes, return it as is. If it
+ * contains double quotes, use single quotes - else surround the argument by
+ * double quotes.
+ * The code was ported from Apache Ant and extensively
+ tested on various platforms. So there is no reason not to use it and it is very likely
+ better than any home-grown library. It is recommended to use CommandLine.addArgument() instead of CommandLine.parse(). Using
+ CommandLine.parse() the implementation tries to figure out the correct quoting using your
+ arguments and file names containing spaces. With CommandLine.addArgument() you can
+ enable/disable quoting depending on your requirements. Having said that this is the
+ recommended approach using Ant anyway. This functionality is largely depend on the operating system - on Unix it works
+ mostly and under Windows not at all (see
+ Bug 4770092). In terms of stability and cross-platform support try to start your applications directly and
+ avoid various wrapper scripts. Well - one out of 55 regression tests fails. The
+ EnvironmentUtilTest.testGetProcEnvironment() test fails because it detects no environment
+ variables for the current process but there must be one since we require JAVA_HOME to be
+ set. Not sure if this is a plain bug in java-gcj-4.2.1 or requires a work around in
+ commons-exec Assuming that you have an environment not listed on the test
+ matrix and want to make sure that everything works fine you can run easily run the
+ regression tests. Make a SVN checkout and run 'ant test-distribution' to create the test
+ distribution in './target'. On a production box downloading the ready-to-run test
+ distribution might be even more handy (
+ http://people.apache.org/~sgoeschl/download/commons-exec/). Unpack the 'zip' or
+ 'tar.gz' file and start the tests. Independent from the result we very much appreciate
+ your feedback ... :-) Please check if the shell scripts under "./src/test/script" are executable - assuming
+ that they are not executable the "testExecute*" and "testExecuteAsync*" test will
+ fail. We try very hard to keep the executable bit but they have somehow the tendency
+ to to be lost ...
+
+ We recommend you use a mirror to download our release
+ builds, but you must verify the integrity of
+ the downloaded files using signatures downloaded from our main
+ distribution directories. Recent releases (48 hours) may not yet
+ be available from the mirrors.
+
+ You are currently using [preferred]. If you
+ encounter a problem with this mirror, please select another
+ mirror. If all mirrors are failing, there are backup
+ mirrors (at the end of the mirrors list) that should be
+ available.
+
+ The KEYS
+ link links to the code signing keys used to sign the product.
+ The
+ Older releases can be obtained from the archives.
+
+ Commons Exec uses ASF JIRA for tracking issues.
+ See the Commons Exec JIRA project page.
+
+ To use JIRA you may need to create an account
+ (if you have previously created/updated Commons issues using Bugzilla an account will have been automatically
+ created and you can use the Forgot Password
+ page to get a new password).
+
+ If you would like to report a bug, or raise an enhancement request with
+ Commons Exec please do the following:
+ exception
causing the process execution to fail.
+ *
+ * @return Returns the exception.
+ * @throws IllegalStateException if the process has not exited yet
+ */
+ public ExecuteException getException() {
+
+ if(!hasResult) {
+ throw new IllegalStateException("The process has not exited yet therefore no result is available ...");
+ }
+
+ return exception;
+ }
+
+ /**
+ * Get the
exitValue
of the process.
+ *
+ * @return Returns the exitValue.
+ * @throws IllegalStateException if the process has not exited yet
+ */
+ public int getExitValue() {
+
+ if(!hasResult) {
+ throw new IllegalStateException("The process has not exited yet therefore no result is available ...");
+ }
+
+ return exitValue;
+ }
+
+ /**
+ * Has the process exited and a result is available, i.e. exitCode or exception?
+ *
+ * @return true if a result of the execution is available
+ */
+ public boolean hasResult() {
+ return hasResult;
+ }
+
+ /**
+ * Causes the current thread to wait, if necessary, until the
+ * process has terminated. This method returns immediately if
+ * the process has already terminated. If the process has
+ * not yet terminated, the calling thread will be blocked until the
+ * process exits.
+ *
+ * @exception InterruptedException if the current thread is
+ * {@linkplain Thread#interrupt() interrupted} by another
+ * thread while it is waiting, then the wait is ended and
+ * an {@link InterruptedException} is thrown.
+ */
+ public void waitFor() throws InterruptedException {
+
+ while (!hasResult()) {
+ Thread.sleep(SLEEP_TIME_MS);
+ }
+ }
+
+ /**
+ * Causes the current thread to wait, if necessary, until the
+ * process has terminated. This method returns immediately if
+ * the process has already terminated. If the process has
+ * not yet terminated, the calling thread will be blocked until the
+ * process exits.
+ *
+ * @param timeout the maximum time to wait in milliseconds
+ * @exception InterruptedException if the current thread is
+ * {@linkplain Thread#interrupt() interrupted} by another
+ * thread while it is waiting, then the wait is ended and
+ * an {@link InterruptedException} is thrown.
+ */
+ public void waitFor(long timeout) throws InterruptedException {
+
+ long until = System.currentTimeMillis() + timeout;
+
+ while (!hasResult() && (System.currentTimeMillis() < until)) {
+ Thread.sleep(SLEEP_TIME_MS);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/DefaultExecutor.java b/ApacheCommonsExec/src/org/apache/commons/exec/DefaultExecutor.java
new file mode 100644
index 0000000..02987cd
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/DefaultExecutor.java
@@ -0,0 +1,434 @@
+/*
+ * 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.apache.commons.exec;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.commons.exec.launcher.CommandLauncher;
+import org.apache.commons.exec.launcher.CommandLauncherFactory;
+
+/**
+ * The default class to start a subprocess. The implementation
+ * allows to
+ *
.
+ */
+ private void closeProcessStreams(final Process process) {
+
+ try {
+ process.getInputStream().close();
+ }
+ catch(IOException e) {
+ setExceptionCaught(e);
+ }
+
+ try {
+ process.getOutputStream().close();
+ }
+ catch(IOException e) {
+ setExceptionCaught(e);
+ }
+
+ try {
+ process.getErrorStream().close();
+ }
+ catch(IOException e) {
+ setExceptionCaught(e);
+ }
+ }
+
+ /**
+ * Execute an internal process. If the executing thread is interrupted while waiting for the
+ * child process to return the child process will be killed.
+ *
+ * @param command the command to execute
+ * @param environment the execution environment
+ * @param dir the working directory
+ * @param streams process the streams (in, out, err) of the process
+ * @return the exit code of the process
+ * @throws IOException executing the process failed
+ */
+ private int executeInternal(final CommandLine command, final Map environment,
+ final File dir, final ExecuteStreamHandler streams) throws IOException {
+
+ setExceptionCaught(null);
+
+ final Process process = this.launch(command, environment, dir);
+
+ try {
+ streams.setProcessInputStream(process.getOutputStream());
+ streams.setProcessOutputStream(process.getInputStream());
+ streams.setProcessErrorStream(process.getErrorStream());
+ } catch (IOException e) {
+ process.destroy();
+ throw e;
+ }
+
+ streams.start();
+
+ try {
+
+ // add the process to the list of those to destroy if the VM exits
+ if(this.getProcessDestroyer() != null) {
+ this.getProcessDestroyer().add(process);
+ }
+
+ // associate the watchdog with the newly created process
+ if (watchdog != null) {
+ watchdog.start(process);
+ }
+
+ int exitValue = Executor.INVALID_EXITVALUE;
+
+ try {
+ exitValue = process.waitFor();
+ } catch (InterruptedException e) {
+ process.destroy();
+ }
+ finally {
+ // see http://bugs.sun.com/view_bug.do?bug_id=6420270
+ // see https://issues.apache.org/jira/browse/EXEC-46
+ // Process.waitFor should clear interrupt status when throwing InterruptedException
+ // but we have to do that manually
+ Thread.interrupted();
+ }
+
+ if (watchdog != null) {
+ watchdog.stop();
+ }
+
+ try {
+ streams.stop();
+ }
+ catch(IOException e) {
+ setExceptionCaught(e);
+ }
+
+ closeProcessStreams(process);
+
+ if(getExceptionCaught() != null) {
+ throw getExceptionCaught();
+ }
+
+ if (watchdog != null) {
+ try {
+ watchdog.checkException();
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ if(this.isFailure(exitValue)) {
+ throw new ExecuteException("Process exited with an error: " + exitValue, exitValue);
+ }
+
+ return exitValue;
+ } finally {
+ // remove the process to the list of those to destroy if the VM exits
+ if(this.getProcessDestroyer() != null) {
+ this.getProcessDestroyer().remove(process);
+ }
+ }
+ }
+
+ /**
+ * Keep track of the first IOException being thrown.
+ *
+ * @param e the IOException
+ */
+ private void setExceptionCaught(IOException e) {
+ if(this.exceptionCaught == null) {
+ this.exceptionCaught = e;
+ }
+ }
+
+ /**
+ * Get the first IOException being thrown.
+ *
+ * @return the first IOException being caught
+ */
+ private IOException getExceptionCaught() {
+ return this.exceptionCaught;
+ }
+
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/EnvironmentUtilTest.java b/ApacheCommonsExec/src/org/apache/commons/exec/EnvironmentUtilTest.java
new file mode 100644
index 0000000..1db1da7
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/EnvironmentUtilTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.apache.commons.exec;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.exec.OS;
+import org.apache.commons.exec.TestUtil;
+import org.apache.commons.exec.environment.EnvironmentUtils;
+
+public class EnvironmentUtilTest extends TestCase {
+
+ /**
+ * Tests the behaviour of the EnvironmentUtils.toStrings()
+ * when using a
+ *
+ *
+ * The following example shows the basic usage:
+ *
+ *
+ * Executor exec = new DefaultExecutor();
+ * CommandLine cl = new CommandLine("ls -l");
+ * int exitvalue = exec.execute(cl);
+ *
+ */
+public class DefaultExecutor implements Executor {
+
+ /** taking care of output and error stream */
+ private ExecuteStreamHandler streamHandler;
+
+ /** the working directory of the process */
+ private File workingDirectory;
+
+ /** monitoring of long running processes */
+ private ExecuteWatchdog watchdog;
+
+ /** the exit values considered to be successful */
+ private int[] exitValues;
+
+ /** launches the command in a new process */
+ private final CommandLauncher launcher;
+
+ /** optional cleanup of started processes */
+ private ProcessDestroyer processDestroyer;
+
+ /** worker thread for asynchronous execution */
+ private Thread executorThread;
+
+ /** the first exception being caught to be thrown to the caller */
+ private IOException exceptionCaught;
+
+ /**
+ * Default constructor creating a default PumpStreamHandler
+ * and sets the working directory of the subprocess to the current
+ * working directory.
+ *
+ * The PumpStreamHandler
pumps the output of the subprocess
+ * into our System.out
and System.err
to avoid
+ * into our System.out
and System.err
to avoid
+ * a blocked or deadlocked subprocess (see{@link java.lang.Process Process}).
+ */
+ public DefaultExecutor() {
+ this.streamHandler = new PumpStreamHandler();
+ this.launcher = CommandLauncherFactory.createVMLauncher();
+ this.exitValues = new int[0];
+ this.workingDirectory = new File(".");
+ this.exceptionCaught = null;
+ }
+
+ /**
+ * @see org.apache.commons.exec.Executor#getStreamHandler()
+ */
+ public ExecuteStreamHandler getStreamHandler() {
+ return streamHandler;
+ }
+
+ /**
+ * @see org.apache.commons.exec.Executor#setStreamHandler(org.apache.commons.exec.ExecuteStreamHandler)
+ */
+ public void setStreamHandler(ExecuteStreamHandler streamHandler) {
+ this.streamHandler = streamHandler;
+ }
+
+ /**
+ * @see org.apache.commons.exec.Executor#getWatchdog()
+ */
+ public ExecuteWatchdog getWatchdog() {
+ return watchdog;
+ }
+
+ /**
+ * @see org.apache.commons.exec.Executor#setWatchdog(org.apache.commons.exec.ExecuteWatchdog)
+ */
+ public void setWatchdog(ExecuteWatchdog watchDog) {
+ this.watchdog = watchDog;
+ }
+
+ /**
+ * @see org.apache.commons.exec.Executor#getProcessDestroyer()
+ */
+ public ProcessDestroyer getProcessDestroyer() {
+ return this.processDestroyer;
+ }
+
+ /**
+ * @see org.apache.commons.exec.Executor#setProcessDestroyer(ProcessDestroyer)
+ */
+ public void setProcessDestroyer(ProcessDestroyer processDestroyer) {
+ this.processDestroyer = processDestroyer;
+ }
+
+ /**
+ * @see org.apache.commons.exec.Executor#getWorkingDirectory()
+ */
+ public File getWorkingDirectory() {
+ return workingDirectory;
+ }
+
+ /**
+ * @see org.apache.commons.exec.Executor#setWorkingDirectory(java.io.File)
+ */
+ public void setWorkingDirectory(File dir) {
+ this.workingDirectory = dir;
+ }
+
+ /**
+ * @see org.apache.commons.exec.Executor#execute(CommandLine)
+ */
+ public int execute(final CommandLine command) throws ExecuteException,
+ IOException {
+ return execute(command, (Map) null);
+ }
+
+ /**
+ * @see org.apache.commons.exec.Executor#execute(CommandLine, java.util.Map)
+ */
+ public int execute(final CommandLine command, Map environment)
+ throws ExecuteException, IOException {
+
+ if (workingDirectory != null && !workingDirectory.exists()) {
+ throw new IOException(workingDirectory + " doesn't exist.");
+ }
+
+ return executeInternal(command, environment, workingDirectory, streamHandler);
+
+ }
+
+ /**
+ * @see org.apache.commons.exec.Executor#execute(CommandLine,
+ * org.apache.commons.exec.ExecuteResultHandler)
+ */
+ public void execute(final CommandLine command, ExecuteResultHandler handler)
+ throws ExecuteException, IOException {
+ execute(command, null, handler);
+ }
+
+ /**
+ * @see org.apache.commons.exec.Executor#execute(CommandLine,
+ * java.util.Map, org.apache.commons.exec.ExecuteResultHandler)
+ */
+ public void execute(final CommandLine command, final Map environment,
+ final ExecuteResultHandler handler) throws ExecuteException, IOException {
+
+ if (workingDirectory != null && !workingDirectory.exists()) {
+ throw new IOException(workingDirectory + " doesn't exist.");
+ }
+
+ if (watchdog != null) {
+ watchdog.setProcessNotStarted();
+ }
+
+ Runnable runnable = new Runnable()
+ {
+ public void run()
+ {
+ int exitValue = Executor.INVALID_EXITVALUE;
+ try {
+ exitValue = executeInternal(command, environment, workingDirectory, streamHandler);
+ handler.onProcessComplete(exitValue);
+ } catch (ExecuteException e) {
+ handler.onProcessFailed(e);
+ } catch(Exception e) {
+ handler.onProcessFailed(new ExecuteException("Execution failed", exitValue, e));
+ }
+ }
+ };
+
+ this.executorThread = createThread(runnable, "Exec Default Executor");
+ getExecutorThread().start();
+ }
+
+ /** @see org.apache.commons.exec.Executor#setExitValue(int) */
+ public void setExitValue(final int value) {
+ this.setExitValues(new int[] {value});
+ }
+
+
+ /** @see org.apache.commons.exec.Executor#setExitValues(int[]) */
+ public void setExitValues(final int[] values) {
+ this.exitValues = (values == null ? null : (int[]) values.clone());
+ }
+
+ /** @see org.apache.commons.exec.Executor#isFailure(int) */
+ public boolean isFailure(final int exitValue) {
+
+ if(this.exitValues == null) {
+ return false;
+ }
+ else if(this.exitValues.length == 0) {
+ return this.launcher.isFailure(exitValue);
+ }
+ else {
+ for(int i=0; inull
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);
+ }
+
+ /**
+ * Test to access the environment variables of the current
+ * process. Please note that this test does not run on
+ * java-gjc.
+ *
+ * @throws IOException the test failed
+ */
+ public void testGetProcEnvironment() throws IOException {
+ Map procEnvironment = EnvironmentUtils.getProcEnvironment();
+ // we assume that there is at least one environment variable
+ // for this process, i.e. $JAVA_HOME
+ assertTrue("Expecting non-zero environment size", procEnvironment.size() > 0);
+ String[] envArgs = EnvironmentUtils.toStrings(procEnvironment);
+ for(int i=0; iserialVersionUID
.
+ */
+ private static final long serialVersionUID = 3256443620654331699L;
+
+ /**
+ * The underlying cause of this exception.
+ */
+ private final Throwable cause;
+
+ /**
+ * The exit value returned by the failed process
+ */
+ private final int exitValue;
+
+ /**
+ * Construct a new exception with the specified detail message.
+ *
+ * @param message
+ * The detail message
+ * @param exitValue The exit value
+ */
+ public ExecuteException(final String message, int exitValue) {
+ super(message + " (Exit value: " + exitValue + ")");
+ this.cause = null;
+ this.exitValue = exitValue;
+ }
+
+ /**
+ * Construct a new exception with the specified detail message and cause.
+ *
+ * @param message
+ * The detail message
+ * @param exitValue The exit value
+ * @param cause
+ * The underlying cause
+ */
+ public ExecuteException(final String message, int exitValue, final Throwable cause) {
+ super(message + " (Exit value: " + exitValue + ". Caused by " + cause + ")");
+ this.cause = cause; // Two-argument version requires JDK 1.4 or later
+ this.exitValue = exitValue;
+ }
+
+ /**
+ * Return the underlying cause of this exception (if any).
+ */
+ public Throwable getCause() {
+ return (this.cause);
+ }
+
+ /**
+ * Gets the exit value returned by the failed process
+ * @return The exit value
+ */
+ public int getExitValue() {
+ return exitValue;
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/ExecuteResultHandler.java b/ApacheCommonsExec/src/org/apache/commons/exec/ExecuteResultHandler.java
new file mode 100644
index 0000000..5a5cffa
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/ExecuteResultHandler.java
@@ -0,0 +1,43 @@
+/*
+ * 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.apache.commons.exec;
+
+/**
+ * The callback handlers for the result of asynchronous process execution. When a
+ * process is started asynchronously the callback provides you with the result of
+ * the executed process, i.e. the exit value or an exception.
+ *
+ * @see org.apache.commons.exec.Executor#execute(CommandLine, java.util.Map, ExecuteResultHandler)
+ */
+public interface ExecuteResultHandler {
+
+ /**
+ * The asynchronous execution completed.
+ *
+ * @param exitValue the exit value of the sub-process
+ */
+ void onProcessComplete(int exitValue);
+
+ /**
+ * The asynchronous execution failed.
+ *
+ * @param e the ExecuteException
containing the root cause
+ */
+ void onProcessFailed(ExecuteException e);
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/ExecuteStreamHandler.java b/ApacheCommonsExec/src/org/apache/commons/exec/ExecuteStreamHandler.java
new file mode 100644
index 0000000..874d6a3
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/ExecuteStreamHandler.java
@@ -0,0 +1,66 @@
+/*
+ * 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.apache.commons.exec;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Used by Execute
to handle input and output stream of
+ * subprocesses.
+ */
+public interface ExecuteStreamHandler {
+
+ /**
+ * Install a handler for the input stream of the subprocess.
+ *
+ * @param os
+ * output stream to write to the standard input stream of the
+ * subprocess
+ */
+ void setProcessInputStream(OutputStream os) throws IOException;
+
+ /**
+ * Install a handler for the error stream of the subprocess.
+ *
+ * @param is
+ * input stream to read from the error stream from the subprocess
+ */
+ void setProcessErrorStream(InputStream is) throws IOException;
+
+ /**
+ * Install a handler for the output stream of the subprocess.
+ *
+ * @param is
+ * input stream to read from the error stream from the subprocess
+ */
+ void setProcessOutputStream(InputStream is) throws IOException;
+
+ /**
+ * Start handling of the streams.
+ */
+ void start() throws IOException;
+
+ /**
+ * Stop handling of the streams - will not be restarted.
+ * Will wait for pump threads to complete.
+ */
+ void stop() throws IOException;
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/ExecuteWatchdog.java b/ApacheCommonsExec/src/org/apache/commons/exec/ExecuteWatchdog.java
new file mode 100644
index 0000000..97de809
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/ExecuteWatchdog.java
@@ -0,0 +1,233 @@
+/*
+ * 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.apache.commons.exec;
+
+import org.apache.commons.exec.util.DebugUtils;
+
+/**
+ * Destroys a process running for too long. For example:
+ *
+ *
+ * ExecuteWatchdog watchdog = new ExecuteWatchdog(30000);
+ * Executer exec = new Executer(myloghandler, watchdog);
+ * exec.setCommandLine(mycmdline);
+ * int exitvalue = exec.execute();
+ * if (Execute.isFailure(exitvalue) && watchdog.killedProcess()) {
+ * // it was killed on purpose by the watchdog
+ * }
+ *
+ *
+ * When starting an asynchronous process than 'ExecuteWatchdog' is the
+ * keeper of the process handle. In some cases it is useful not to define
+ * a timeout (and pass 'INFINITE_TIMEOUT') and to kill the process explicitly
+ * using 'destroyProcess()'.
+ *
+ *
+ *
+ * The following example shows the basic usage:
+ *
+ *
+ * Executor exec = new DefaultExecutor();
+ * CommandLine cl = new CommandLine("ls -l");
+ * int exitvalue = exec.execute(cl);
+ *
+ */
+
+public interface Executor {
+
+ /** Invalid exit code. */
+ int INVALID_EXITVALUE = 0xdeadbeef;
+
+ /**
+ * Define the exitValue
of the process to be considered
+ * successful. If a different exit value is returned by
+ * the process then {@link org.apache.commons.exec.Executor#execute(CommandLine)}
+ * will throw an {@link org.apache.commons.exec.ExecuteException}
+ *
+ * @param value the exit code representing successful execution
+ */
+ void setExitValue(final int value);
+
+ /**
+ * Define a list of exitValue
of the process to be considered
+ * successful. The caller can pass one of the following values
+ *
+ *
+ *
+ * If an undefined exit value is returned by the process then
+ * {@link org.apache.commons.exec.Executor#execute(CommandLine)} will
+ * throw an {@link org.apache.commons.exec.ExecuteException}.
+ *
+ * @param values a list of the exit codes
+ */
+ void setExitValues(final int[] values);
+
+ /**
+ * Checks whether exitValue
signals a failure. If no
+ * exit values are set than the default conventions of the OS is
+ * 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 true
if exitValue
signals a failure
+ */
+ boolean isFailure(final int exitValue);
+
+ /**
+ * Get the StreamHandler used for providing input and
+ * retrieving the output.
+ *
+ * @return the StreamHandler
+ */
+ ExecuteStreamHandler getStreamHandler();
+
+ /**
+ * Set a custom the StreamHandler used for providing
+ * input and retrieving the output. If you don't provide
+ * a proper stream handler the executed process might block
+ * when writing to stdout and/or stderr (see
+ * {@link java.lang.Process Process}).
+ *
+ * @param streamHandler the stream handler
+ */
+ void setStreamHandler(ExecuteStreamHandler streamHandler);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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.
+ *
+ * @param command the command to execute
+ * @return process exit value
+ * @throws ExecuteException execution of subprocess failed or the
+ * subprocess returned a exit value indicating a failure
+ * {@link Executor#setExitValue(int)}.
+ */
+ int execute(CommandLine command)
+ throws ExecuteException, IOException;
+
+ /**
+ * 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.
+ * @return process exit value
+ * @throws ExecuteException execution of subprocess failed or the
+ * subprocess returned a exit value indicating a failure
+ * {@link Executor#setExitValue(int)}.
+ */
+ int execute(CommandLine command, Map environment)
+ throws ExecuteException, IOException;
+
+ /**
+ * 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 handler capture process termination and exit code
+ * @throws ExecuteException execution of subprocess failed
+ */
+ void execute(CommandLine command, ExecuteResultHandler handler)
+ throws ExecuteException, IOException;
+
+ /**
+ * 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.
+ * @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;
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/InputStreamPumper.java b/ApacheCommonsExec/src/org/apache/commons/exec/InputStreamPumper.java
new file mode 100644
index 0000000..ae26fee
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/InputStreamPumper.java
@@ -0,0 +1,83 @@
+/*
+ * 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.apache.commons.exec;
+
+import org.apache.commons.exec.util.DebugUtils;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Copies all data from an System.input stream to an output stream of the executed process.
+ *
+ * @author mkleint
+ */
+public class InputStreamPumper implements Runnable {
+
+ public static final int SLEEPING_TIME = 100;
+
+ /** the input stream to pump from */
+ private final InputStream is;
+
+ /** the output stream to pmp into */
+ private final OutputStream os;
+
+ /** flag to stop the stream pumping */
+ private volatile boolean stop;
+
+
+ /**
+ * Create a new stream pumper.
+ *
+ * @param is input stream to read data from
+ * @param os output stream to write data to.
+ */
+ public InputStreamPumper(final InputStream is, final OutputStream os) {
+ this.is = is;
+ this.os = os;
+ this.stop = false;
+ }
+
+
+ /**
+ * Copies data from the input stream to the output stream. Terminates as
+ * soon as the input stream is closed or an error occurs.
+ */
+ public void run() {
+ try {
+ while (!stop) {
+ while (is.available() > 0 && !stop) {
+ os.write(is.read());
+ }
+ os.flush();
+ Thread.sleep(SLEEPING_TIME);
+ }
+ } catch (Exception e) {
+ String msg = "Got exception while reading/writing the stream";
+ DebugUtils.handleException(msg ,e);
+ } finally {
+ }
+ }
+
+
+ public void stopProcessing() {
+ stop = true;
+ }
+
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/LogOutputStream.java b/ApacheCommonsExec/src/org/apache/commons/exec/LogOutputStream.java
new file mode 100644
index 0000000..fed8378
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/LogOutputStream.java
@@ -0,0 +1,176 @@
+/*
+ * 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.apache.commons.exec;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Base class to connect a logging system to the output and/or
+ * error stream of then external process. The implementation
+ * parses the incoming data to construct a line and passes
+ * the complete line to an user-defined implementation.
+ */
+public abstract class LogOutputStream
+ extends OutputStream {
+
+ /** Initial buffer size. */
+ private static final int INTIAL_SIZE = 132;
+
+ /** Carriage return */
+ private static final int CR = 0x0d;
+
+ /** Linefeed */
+ private static final int LF = 0x0a;
+
+ /** the internal buffer */
+ private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(
+ INTIAL_SIZE);
+
+ private boolean skip = false;
+
+ 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.
+ *
+ * @param level loglevel used to log data written to this stream.
+ */
+ public LogOutputStream(final int level) {
+ this.level = level;
+ }
+
+ /**
+ * Write the data to the buffer and flush the buffer, if a line separator is
+ * detected.
+ *
+ * @param cc data to log (byte).
+ * @see java.io.OutputStream#write(int)
+ */
+ public void write(final int cc) throws IOException {
+ final byte c = (byte) cc;
+ if ((c == '\n') || (c == '\r')) {
+ if (!skip) {
+ processBuffer();
+ }
+ } else {
+ buffer.write(cc);
+ }
+ skip = (c == '\r');
+ }
+
+ /**
+ * Flush this log stream.
+ *
+ * @see java.io.OutputStream#flush()
+ */
+ public void flush() {
+ if (buffer.size() > 0) {
+ processBuffer();
+ }
+ }
+
+ /**
+ * Writes all remaining data from the buffer.
+ *
+ * @see java.io.OutputStream#close()
+ */
+ public void close() throws IOException {
+ if (buffer.size() > 0) {
+ processBuffer();
+ }
+ super.close();
+ }
+
+ /**
+ * @return the trace level of the log system
+ */
+ public int getMessageLevel() {
+ return level;
+ }
+
+ /**
+ * Write a block of characters to the output stream
+ *
+ * @param b the array containing the data
+ * @param off the offset into the array where data starts
+ * @param len the length of block
+ * @throws java.io.IOException if the data cannot be written into the stream.
+ * @see java.io.OutputStream#write(byte[], int, int)
+ */
+ public void write(final byte[] b, final int off, final int len)
+ throws IOException {
+ // find the line breaks and pass other chars through in blocks
+ int offset = off;
+ int blockStartOffset = offset;
+ int remaining = len;
+ while (remaining > 0) {
+ while (remaining > 0 && b[offset] != LF && b[offset] != CR) {
+ offset++;
+ remaining--;
+ }
+ // either end of buffer or a line separator char
+ int blockLength = offset - blockStartOffset;
+ if (blockLength > 0) {
+ buffer.write(b, blockStartOffset, blockLength);
+ }
+ while (remaining > 0 && (b[offset] == LF || b[offset] == CR)) {
+ write(b[offset]);
+ offset++;
+ remaining--;
+ }
+ blockStartOffset = offset;
+ }
+ }
+
+ /**
+ * Converts the buffer to a string and sends it to processLine
.
+ */
+ protected void processBuffer() {
+ processLine(buffer.toString());
+ buffer.reset();
+ }
+
+ /**
+ * Logs a line to the log system of the user.
+ *
+ * @param line
+ * the line to log.
+ */
+ protected void processLine(final String line) {
+ processLine(line, level);
+ }
+
+ /**
+ * Logs a line to the log system of the user.
+ *
+ * @param line the line to log.
+ * @param level the log level to use
+ */
+ protected abstract void processLine(final String line, final int level);
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/LogOutputStreamTest.java b/ApacheCommonsExec/src/org/apache/commons/exec/LogOutputStreamTest.java
new file mode 100644
index 0000000..4e3f64a
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/LogOutputStreamTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.apache.commons.exec;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.OutputStream;
+
+/**
+ * Test the LogOutputStream.
+ */
+public class LogOutputStreamTest extends TestCase
+{
+
+ private Executor exec = new DefaultExecutor();
+ private File testDir = new File("src/test/scripts");
+ private OutputStream systemOut;
+ private File environmentScript = TestUtil.resolveScriptForOS(testDir + "/environment");
+
+ static{
+ // turn on debug mode and throw an exception for each encountered problem
+ System.setProperty("org.apache.commons.exec.lenient", "false");
+ System.setProperty("org.apache.commons.exec.debug", "true");
+ }
+
+
+ protected void setUp() throws Exception {
+ this.systemOut = new SystemLogOutputStream(1);
+ this.exec.setStreamHandler(new PumpStreamHandler(systemOut, systemOut));
+ }
+
+ protected void tearDown() throws Exception {
+ this.systemOut.close();
+ }
+
+ // ======================================================================
+ // Start of regression tests
+ // ======================================================================
+
+ public void testStdout() throws Exception {
+ CommandLine cl = new CommandLine(environmentScript);
+ int exitValue = exec.execute(cl);
+ assertFalse(exec.isFailure(exitValue));
+ }
+
+ // ======================================================================
+ // Helper classes
+ // ======================================================================
+
+ private class SystemLogOutputStream extends LogOutputStream {
+
+ private SystemLogOutputStream(int level) {
+ super(level);
+ }
+
+ protected void processLine(String line, int level) {
+ System.out.println(line);
+ }
+ }
+
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/OS.java b/ApacheCommonsExec/src/org/apache/commons/exec/OS.java
new file mode 100644
index 0000000..0e83028
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/OS.java
@@ -0,0 +1,244 @@
+/*
+ * 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.apache.commons.exec;
+
+import java.util.Locale;
+
+/**
+ * Condition that tests the OS type.
+ */
+public final class OS {
+ private static final String FAMILY_OS_400 = "os/400";
+
+ private static final String FAMILY_Z_OS = "z/os";
+
+ private static final String FAMILY_WIN9X = "win9x";
+
+ private static final String FAMILY_OPENVMS = "openvms";
+
+ private static final String FAMILY_UNIX = "unix";
+
+ private static final String FAMILY_TANDEM = "tandem";
+
+ private static final String FAMILY_MAC = "mac";
+
+ private static final String FAMILY_DOS = "dos";
+
+ private static final String FAMILY_NETWARE = "netware";
+
+ private static final String FAMILY_OS_2 = "os/2";
+
+ private static final String FAMILY_WINDOWS = "windows";
+
+ private static final String OS_NAME = System.getProperty("os.name")
+ .toLowerCase(Locale.US);
+
+ private static final String OS_ARCH = System.getProperty("os.arch")
+ .toLowerCase(Locale.US);
+
+ private static final String OS_VERSION = System.getProperty("os.version")
+ .toLowerCase(Locale.US);
+
+ private static final String PATH_SEP = System.getProperty("path.separator");
+
+ /**
+ * Default constructor
+ */
+ private OS() {
+ }
+
+ /**
+ * Determines if the OS on which Ant is executing matches the given OS
+ * family. * Possible values:
+ *
+ *
+ *
+ * @param family
+ * the family to check for
+ * @return true if the OS matches
+ */
+ private static boolean isFamily(final String family) {
+ return isOs(family, null, null, null);
+ }
+
+ public static boolean isFamilyDOS() {
+ return isFamily(FAMILY_DOS);
+ }
+
+ public static boolean isFamilyMac() {
+ return isFamily(FAMILY_MAC);
+ }
+
+ public static boolean isFamilyNetware() {
+ return isFamily(FAMILY_NETWARE);
+ }
+
+ public static boolean isFamilyOS2() {
+ return isFamily(FAMILY_OS_2);
+ }
+
+ public static boolean isFamilyTandem() {
+ return isFamily(FAMILY_TANDEM);
+ }
+
+ public static boolean isFamilyUnix() {
+ return isFamily(FAMILY_UNIX);
+ }
+
+ public static boolean isFamilyWindows() {
+ return isFamily(FAMILY_WINDOWS);
+ }
+
+ public static boolean isFamilyWin9x() {
+ return isFamily(FAMILY_WIN9X);
+ }
+
+ public static boolean isFamilyZOS() {
+ return isFamily(FAMILY_Z_OS);
+ }
+
+ public static boolean isFamilyOS400() {
+ return isFamily(FAMILY_OS_400);
+ }
+
+ public static boolean isFamilyOpenVms() {
+ return isFamily(FAMILY_OPENVMS);
+ }
+
+ /**
+ * Determines if the OS on which Ant is executing matches the given OS name.
+ *
+ * @param name
+ * the OS name to check for
+ * @return true if the OS matches
+ */
+ public static boolean isName(final String name) {
+ return isOs(null, name, null, null);
+ }
+
+ /**
+ * Determines if the OS on which Ant is executing matches the given OS
+ * architecture.
+ *
+ * @param arch
+ * the OS architecture to check for
+ * @return true if the OS matches
+ */
+ public static boolean isArch(final String arch) {
+ return isOs(null, null, arch, null);
+ }
+
+ /**
+ * Determines if the OS on which Ant is executing matches the given OS
+ * version.
+ *
+ * @param version
+ * the OS version to check for
+ * @return true if the OS matches
+ */
+ public static boolean isVersion(final String version) {
+ return isOs(null, null, null, version);
+ }
+
+ /**
+ * Determines if the OS on which Ant is executing matches the given OS
+ * family, name, architecture and version
+ *
+ * @param family
+ * The OS family
+ * @param name
+ * The OS name
+ * @param arch
+ * The OS architecture
+ * @param version
+ * The OS version
+ * @return true if the OS matches
+ */
+ public static boolean isOs(final String family, final String name,
+ final String arch, final String version) {
+ boolean retValue = false;
+
+ if (family != null || name != null || arch != null || version != null) {
+
+ boolean isFamily = true;
+ boolean isName = true;
+ boolean isArch = true;
+ boolean isVersion = true;
+
+ if (family != null) {
+ if (family.equals(FAMILY_WINDOWS)) {
+ isFamily = OS_NAME.indexOf(FAMILY_WINDOWS) > -1;
+ } else if (family.equals(FAMILY_OS_2)) {
+ isFamily = OS_NAME.indexOf(FAMILY_OS_2) > -1;
+ } else if (family.equals(FAMILY_NETWARE)) {
+ isFamily = OS_NAME.indexOf(FAMILY_NETWARE) > -1;
+ } else if (family.equals(FAMILY_DOS)) {
+ isFamily = PATH_SEP.equals(";")
+ && !isFamily(FAMILY_NETWARE);
+ } else if (family.equals(FAMILY_MAC)) {
+ isFamily = OS_NAME.indexOf(FAMILY_MAC) > -1;
+ } else if (family.equals(FAMILY_TANDEM)) {
+ isFamily = OS_NAME.indexOf("nonstop_kernel") > -1;
+ } else if (family.equals(FAMILY_UNIX)) {
+ isFamily = PATH_SEP.equals(":")
+ && !isFamily(FAMILY_OPENVMS)
+ && (!isFamily(FAMILY_MAC) || OS_NAME.endsWith("x"));
+ } else if (family.equals(FAMILY_WIN9X)) {
+ isFamily = isFamily(FAMILY_WINDOWS)
+ && (OS_NAME.indexOf("95") >= 0
+ || OS_NAME.indexOf("98") >= 0
+ || OS_NAME.indexOf("me") >= 0 || OS_NAME
+ .indexOf("ce") >= 0);
+ } else if (family.equals(FAMILY_Z_OS)) {
+ isFamily = OS_NAME.indexOf(FAMILY_Z_OS) > -1
+ || OS_NAME.indexOf("os/390") > -1;
+ } else if (family.equals(FAMILY_OS_400)) {
+ isFamily = OS_NAME.indexOf(FAMILY_OS_400) > -1;
+ } else if (family.equals(FAMILY_OPENVMS)) {
+ isFamily = OS_NAME.indexOf(FAMILY_OPENVMS) > -1;
+ } else {
+ throw new IllegalArgumentException(
+ "Don\'t know how to detect os family \"" + family
+ + "\"");
+ }
+ }
+ if (name != null) {
+ isName = name.equals(OS_NAME);
+ }
+ if (arch != null) {
+ isArch = arch.equals(OS_ARCH);
+ }
+ if (version != null) {
+ isVersion = version.equals(OS_VERSION);
+ }
+ retValue = isFamily && isName && isArch && isVersion;
+ }
+ return retValue;
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/ProcessDestroyer.java b/ApacheCommonsExec/src/org/apache/commons/exec/ProcessDestroyer.java
new file mode 100644
index 0000000..d63bdb3
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/ProcessDestroyer.java
@@ -0,0 +1,60 @@
+/*
+ * 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.apache.commons.exec;
+
+/**
+ * Destroys all registered {@link java.lang.Process} after a certain event,
+ * typically when the VM exits
+ * @see org.apache.commons.exec.ShutdownHookProcessDestroyer
+ */
+public interface ProcessDestroyer {
+
+ /**
+ * Returns true
if the specified
+ * {@link java.lang.Process} was
+ * successfully added to the list of processes to be destroy.
+ *
+ * @param process
+ * the process to add
+ * @return true
if the specified
+ * {@link java.lang.Process} was
+ * successfully added
+ */
+ boolean add(Process process);
+
+ /**
+ * Returns true
if the specified
+ * {@link java.lang.Process} was
+ * successfully removed from the list of processes to be destroy.
+ *
+ * @param process
+ * the process to remove
+ * @return true
if the specified
+ * {@link java.lang.Process} was
+ * successfully removed
+ */
+ boolean remove(Process process);
+
+ /**
+ * Returns the number of registered processes.
+ *
+ * @return the number of register process
+ */
+ int size();
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/PumpStreamHandler.java b/ApacheCommonsExec/src/org/apache/commons/exec/PumpStreamHandler.java
new file mode 100644
index 0000000..6f073f7
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/PumpStreamHandler.java
@@ -0,0 +1,316 @@
+/*
+ * 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.apache.commons.exec;
+
+import org.apache.commons.exec.util.DebugUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedOutputStream;
+
+/**
+ * Copies standard output and error of sub-processes to standard output and error
+ * 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 {
+
+ private static final long STOP_TIMEOUT_ADDITION = 2000L;
+
+ private Thread outputThread;
+
+ private Thread errorThread;
+
+ private Thread inputThread;
+
+ private final OutputStream out;
+
+ private final OutputStream err;
+
+ private final InputStream input;
+
+ private InputStreamPumper inputStreamPumper;
+
+ /** the timeout in ms the implementation waits when stopping the pumper threads */
+ private long stopTimeout;
+
+ /** the last exception being caught */
+ private IOException caught = null;
+
+ /**
+ * Construct a new PumpStreamHandler
.
+ */
+ public PumpStreamHandler() {
+ this(System.out, System.err);
+ }
+
+ /**
+ * Construct a new PumpStreamHandler
.
+ *
+ * @param outAndErr the output/error OutputStream
.
+ */
+ public PumpStreamHandler(final OutputStream outAndErr) {
+ this(outAndErr, outAndErr);
+ }
+
+ /**
+ * Construct a new PumpStreamHandler
.
+ *
+ * @param out the output OutputStream
.
+ * @param err the error OutputStream
.
+ */
+ public PumpStreamHandler(final OutputStream out, final OutputStream err) {
+ this(out, err, null);
+ }
+
+ /**
+ * Construct a new PumpStreamHandler
.
+ *
+ * @param out the output OutputStream
.
+ * @param err the error OutputStream
.
+ * @param input the input InputStream
.
+ */
+ public PumpStreamHandler(final OutputStream out, final OutputStream err, final InputStream input) {
+ this.out = out;
+ this.err = err;
+ this.input = input;
+ }
+
+ /**
+ * Set maximum time to wait until output streams are exchausted
+ * when {@link #stop()} was called.
+ *
+ * @param timeout timeout in milliseconds or zero to wait forever (default)
+ */
+ public void setStopTimeout(long timeout) {
+ this.stopTimeout = timeout;
+ }
+
+ /**
+ * Set the InputStream
from which to read the standard output
+ * of the process.
+ *
+ * @param is the InputStream
.
+ */
+ public void setProcessOutputStream(final InputStream is) {
+ if (out != null) {
+ createProcessOutputPump(is, out);
+ }
+ }
+
+ /**
+ * Set the InputStream
from which to read the standard error
+ * of the process.
+ *
+ * @param is the InputStream
.
+ */
+ public void setProcessErrorStream(final InputStream is) {
+ if (err != null) {
+ createProcessErrorPump(is, err);
+ }
+ }
+
+ /**
+ * Set the OutputStream
by means of which input can be sent
+ * to the process.
+ *
+ * @param os the OutputStream
.
+ */
+ public void setProcessInputStream(final OutputStream os) {
+ if (input != null) {
+ if (input == System.in) {
+ inputThread = createSystemInPump(input, os);
+ } else {
+ inputThread = createPump(input, os, true);
+ }
+ } else {
+ try {
+ os.close();
+ } catch (IOException e) {
+ String msg = "Got exception while closing output stream";
+ DebugUtils.handleException(msg, e);
+ }
+ }
+ }
+
+ /**
+ * Start the Thread
s.
+ */
+ public void start() {
+ if (outputThread != null) {
+ outputThread.start();
+ }
+ if (errorThread != null) {
+ errorThread.start();
+ }
+ if (inputThread != null) {
+ inputThread.start();
+ }
+ }
+
+ /**
+ * Stop pumping the streams. When a timeout is specified it it is not guaranteed that the
+ * pumper threads are cleanly terminated.
+ */
+ public void stop() throws IOException {
+
+ if (inputStreamPumper != null) {
+ inputStreamPumper.stopProcessing();
+ }
+
+ stopThread(outputThread, stopTimeout);
+ stopThread(errorThread, stopTimeout);
+ stopThread(inputThread, stopTimeout);
+
+ if (err != null && err != out) {
+ try {
+ err.flush();
+ } catch (IOException e) {
+ String msg = "Got exception while flushing the error stream : " + e.getMessage();
+ 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);
+ }
+ }
+
+ if(caught != null) {
+ throw caught;
+ }
+ }
+
+ /**
+ * Get the error stream.
+ *
+ * @return OutputStream
.
+ */
+ protected OutputStream getErr() {
+ return err;
+ }
+
+ /**
+ * Get the output stream.
+ *
+ * @return OutputStream
.
+ */
+ protected OutputStream getOut() {
+ return out;
+ }
+
+ /**
+ * Create the pump to handle process output.
+ *
+ * @param is the InputStream
.
+ * @param os the OutputStream
.
+ */
+ protected void createProcessOutputPump(final InputStream is, final OutputStream os) {
+ outputThread = createPump(is, os);
+ }
+
+ /**
+ * Create the pump to handle error output.
+ *
+ * @param is the InputStream
.
+ * @param os the OutputStream
.
+ */
+ protected void createProcessErrorPump(final InputStream is, final OutputStream os) {
+ errorThread = createPump(is, os);
+ }
+
+ /**
+ * Creates a stream pumper to copy the given input stream to the given
+ * output stream. When the 'os' is an PipedOutputStream we are closing
+ * 'os' afterwards to avoid an IOException ("Write end dead").
+ *
+ * @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) {
+ boolean closeWhenExhausted = (os instanceof PipedOutputStream ? true : false);
+ return createPump(is, os, closeWhenExhausted);
+ }
+
+ /**
+ * 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) {
+ final Thread result = new Thread(new StreamPumper(is, os, closeWhenExhausted), "Exec Stream Pumper");
+ result.setDaemon(true);
+ return result;
+ }
+
+ /**
+ * Stopping a pumper thread. The implementation actually waits
+ * longer than specified in 'timeout' to detect if the timeout
+ * was indeed exceeded. If the timeout was exceeded an IOException
+ * is created to be thrown to the caller.
+ *
+ * @param thread the thread to be stopped
+ * @param timeout the time in ms to wait to join
+ */
+ protected void stopThread(Thread thread, long timeout) {
+
+ if (thread != null) {
+ try {
+ if (timeout == 0) {
+ thread.join();
+ } else {
+ long timeToWait = timeout + STOP_TIMEOUT_ADDITION;
+ long startTime = System.currentTimeMillis();
+ thread.join(timeToWait);
+ if (!(System.currentTimeMillis() < startTime + timeToWait)) {
+ String msg = "The stop timeout of " + timeout + " ms was exceeded";
+ caught = new ExecuteException(msg, Executor.INVALID_EXITVALUE);
+ }
+ }
+ } catch (InterruptedException e) {
+ thread.interrupt();
+ }
+ }
+ }
+
+ /**
+ * Creates a stream pumper to copy the given input stream to the given
+ * output stream.
+ *
+ * @param is the System.in input stream to copy from
+ * @param os the output stream to copy into
+ * @return the stream pumper thread
+ */
+ private Thread createSystemInPump(InputStream is, OutputStream os) {
+ inputStreamPumper = new InputStreamPumper(is, os);
+ final Thread result = new Thread(inputStreamPumper, "Exec Input Stream Pumper");
+ result.setDaemon(true);
+ return result;
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/ShutdownHookProcessDestroyer.java b/ApacheCommonsExec/src/org/apache/commons/exec/ShutdownHookProcessDestroyer.java
new file mode 100644
index 0000000..1e3998e
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/ShutdownHookProcessDestroyer.java
@@ -0,0 +1,194 @@
+/*
+ * 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.apache.commons.exec;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * Destroys all registered Process
es when the VM exits.
+ */
+public class ShutdownHookProcessDestroyer implements ProcessDestroyer, Runnable {
+
+ /** the list of currently running processes */
+ private final Vector processes = new Vector();
+
+ /** The thread registered at the JVM to execute the shutdown handler */
+ private ProcessDestroyerImpl destroyProcessThread = null;
+
+ /** Whether or not this ProcessDestroyer has been registered as a shutdown hook */
+ private boolean added = false;
+
+ /**
+ * Whether or not this ProcessDestroyer is currently running as shutdown hook
+ */
+ private volatile boolean running = false;
+
+ private class ProcessDestroyerImpl extends Thread {
+
+ private boolean shouldDestroy = true;
+
+ public ProcessDestroyerImpl() {
+ super("ProcessDestroyer Shutdown Hook");
+ }
+
+ public void run() {
+ if (shouldDestroy) {
+ ShutdownHookProcessDestroyer.this.run();
+ }
+ }
+
+ public void setShouldDestroy(final boolean shouldDestroy) {
+ this.shouldDestroy = shouldDestroy;
+ }
+ }
+
+ /**
+ * Constructs a ProcessDestroyer
and obtains
+ * Runtime.addShutdownHook()
and
+ * Runtime.removeShutdownHook()
through reflection. The
+ * ProcessDestroyer manages a list of processes to be destroyed when the VM
+ * exits. If a process is added when the list is empty, this
+ * ProcessDestroyer
is registered as a shutdown hook. If
+ * removing a process results in an empty list, the
+ * ProcessDestroyer
is removed as a shutdown hook.
+ */
+ public ShutdownHookProcessDestroyer() {
+ }
+
+ /**
+ * Registers this ProcessDestroyer
as a shutdown hook, uses
+ * reflection to ensure pre-JDK 1.3 compatibility.
+ */
+ private void addShutdownHook() {
+ if (!running) {
+ destroyProcessThread = new ProcessDestroyerImpl();
+ Runtime.getRuntime().addShutdownHook(destroyProcessThread);
+ added = true;
+ }
+ }
+
+ /**
+ * Removes this ProcessDestroyer
as a shutdown hook, uses
+ * reflection to ensure pre-JDK 1.3 compatibility
+ */
+ private void removeShutdownHook() {
+ if (added && !running) {
+ boolean removed = Runtime.getRuntime().removeShutdownHook(
+ destroyProcessThread);
+ if (!removed) {
+ System.err.println("Could not remove shutdown hook");
+ }
+ /*
+ * start the hook thread, a unstarted thread may not be eligible for
+ * garbage collection Cf.: http://developer.java.sun.com/developer/
+ * bugParade/bugs/4533087.html
+ */
+
+ destroyProcessThread.setShouldDestroy(false);
+ destroyProcessThread.start();
+ // this should return quickly, since it basically is a NO-OP.
+ try {
+ destroyProcessThread.join(20000);
+ } catch (InterruptedException ie) {
+ // the thread didn't die in time
+ // it should not kill any processes unexpectedly
+ }
+ destroyProcessThread = null;
+ added = false;
+ }
+ }
+
+ /**
+ * Returns whether or not the ProcessDestroyer is registered as as shutdown
+ * hook
+ *
+ * @return true if this is currently added as shutdown hook
+ */
+ public boolean isAddedAsShutdownHook() {
+ return added;
+ }
+
+ /**
+ * Returns true
if the specified Process
was
+ * successfully added to the list of processes to destroy upon VM exit.
+ *
+ * @param process
+ * the process to add
+ * @return true
if the specified Process
was
+ * successfully added
+ */
+ public boolean add(final Process process) {
+ synchronized (processes) {
+ // if this list is empty, register the shutdown hook
+ if (processes.size() == 0) {
+ addShutdownHook();
+ }
+ processes.addElement(process);
+ return processes.contains(process);
+ }
+ }
+
+ /**
+ * Returns true
if the specified Process
was
+ * successfully removed from the list of processes to destroy upon VM exit.
+ *
+ * @param process
+ * the process to remove
+ * @return true
if the specified Process
was
+ * successfully removed
+ */
+ public boolean remove(final Process process) {
+ synchronized (processes) {
+ boolean processRemoved = processes.removeElement(process);
+ if (processRemoved && processes.size() == 0) {
+ removeShutdownHook();
+ }
+ return processRemoved;
+ }
+ }
+
+ /**
+ * Returns the number of registered processes.
+ *
+ * @return the number of register process
+ */
+ public int size() {
+ return processes.size();
+ }
+
+ /**
+ * Invoked by the VM when it is exiting.
+ */
+ public void run() {
+ synchronized (processes) {
+ running = true;
+ Enumeration e = processes.elements();
+ while (e.hasMoreElements()) {
+ Process process = (Process) e.nextElement();
+ try {
+ process.destroy();
+ }
+ catch (Throwable t) {
+ System.err.println("Unable to terminate process during process shutdown");
+ }
+ }
+ }
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/StandAloneTest.java b/ApacheCommonsExec/src/org/apache/commons/exec/StandAloneTest.java
new file mode 100644
index 0000000..35f3774
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/StandAloneTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.apache.commons.exec;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+
+/**
+ * Placeholder for mailing list question - provided a minimal test case
+ * to answer the question as sel-contained regression test.
+ */
+public class StandAloneTest extends TestCase {
+
+ static{
+ System.setProperty("org.apache.commons.exec.lenient", "false");
+ System.setProperty("org.apache.commons.exec.debug", "true");
+ }
+
+ public void testMe() throws Exception {
+ if(OS.isFamilyUnix()) {
+ File testScript = TestUtil.resolveScriptForOS("./src/test/scripts/standalone");
+ Executor exec = new DefaultExecutor();
+ exec.setStreamHandler(new PumpStreamHandler());
+ CommandLine cl = new CommandLine(testScript);
+ exec.execute(cl);
+ assertTrue(new File("./target/mybackup.gz").exists());
+ }
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/StreamPumper.java b/ApacheCommonsExec/src/org/apache/commons/exec/StreamPumper.java
new file mode 100644
index 0000000..164768d
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/StreamPumper.java
@@ -0,0 +1,145 @@
+/*
+ * 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.apache.commons.exec;
+
+import org.apache.commons.exec.util.DebugUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Copies all data from an input stream to an output stream.
+ */
+public class StreamPumper implements Runnable {
+
+ /** the default size of the internal buffer for copying the streams */
+ private static final int DEFAULT_SIZE = 1024;
+
+ /** the input stream to pump from */
+ private final InputStream is;
+
+ /** the output stream to pmp into */
+ private final OutputStream os;
+
+ /** the size of the internal buffer for copying the streams */
+ private final int size;
+
+ /** was the end of the stream reached */
+ private boolean finished;
+
+ /** close the output stream when exhausted */
+ private final boolean closeWhenExhausted;
+
+ /**
+ * Create a new stream pumper.
+ *
+ * @param is input stream to read data from
+ * @param os output stream to write data to.
+ * @param closeWhenExhausted if true, the output stream will be closed when the input is exhausted.
+ */
+ public StreamPumper(final InputStream is, final OutputStream os,
+ final boolean closeWhenExhausted) {
+ this.is = is;
+ this.os = os;
+ this.size = DEFAULT_SIZE;
+ this.closeWhenExhausted = closeWhenExhausted;
+ }
+
+ /**
+ * Create a new stream pumper.
+ *
+ * @param is input stream to read data from
+ * @param os output stream to write data to.
+ * @param closeWhenExhausted if true, the output stream will be closed when the input is exhausted.
+ * @param size the size of the internal buffer for copying the streams
+ */
+ public StreamPumper(final InputStream is, final OutputStream os,
+ final boolean closeWhenExhausted, final int size) {
+ this.is = is;
+ this.os = os;
+ this.size = (size > 0 ? size : DEFAULT_SIZE);
+ this.closeWhenExhausted = closeWhenExhausted;
+ }
+
+ /**
+ * Create a new stream pumper.
+ *
+ * @param is input stream to read data from
+ * @param os output stream to write data to.
+ */
+ public StreamPumper(final InputStream is, final OutputStream os) {
+ this(is, os, false);
+ }
+
+ /**
+ * Copies data from the input stream to the output stream. Terminates as
+ * soon as the input stream is closed or an error occurs.
+ */
+ public void run() {
+ synchronized (this) {
+ // Just in case this object is reused in the future
+ finished = false;
+ }
+
+ final byte[] buf = new byte[this.size];
+
+ int length;
+ try {
+ while ((length = is.read(buf)) > 0) {
+ os.write(buf, 0, length);
+ }
+ } catch (Exception e) {
+ // nothing to do - happens quite often with watchdog
+ } finally {
+ if (closeWhenExhausted) {
+ try {
+ os.close();
+ } catch (IOException e) {
+ String msg = "Got exception while closing exhausted output stream";
+ DebugUtils.handleException(msg ,e);
+ }
+ }
+ synchronized (this) {
+ finished = true;
+ notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Tells whether the end of the stream has been reached.
+ *
+ * @return true is the stream has been exhausted.
+ */
+ public synchronized boolean isFinished() {
+ return finished;
+ }
+
+ /**
+ * This method blocks until the stream pumper finishes.
+ *
+ * @see #isFinished()
+ */
+ public synchronized void waitFor() throws InterruptedException {
+ while (!isFinished()) {
+ wait();
+ }
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/TestRunner.java b/ApacheCommonsExec/src/org/apache/commons/exec/TestRunner.java
new file mode 100644
index 0000000..0522ed6
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/TestRunner.java
@@ -0,0 +1,58 @@
+/*
+ * 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.apache.commons.exec;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+// tms import org.apache.commons.exec.environment.EnvironmentUtilTest;
+import org.apache.commons.exec.util.MapUtilTest;
+
+/**
+ * A stand-alone JUnit invocation to allow running JUnit tests without
+ * having ANT or M2 installed.
+ */
+public class TestRunner extends TestCase {
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite("TestRunner");
+ suite.addTestSuite(CommandLineTest.class);
+ //tms suite.addTestSuite(DefaultExecutorTest.class);
+ suite.addTestSuite(EnvironmentUtilTest.class);
+ suite.addTestSuite(MapUtilTest.class);
+ suite.addTestSuite(TestUtilTest.class);
+ return suite;
+ }
+
+ public static void main(String[] args) {
+
+ Test test = TestRunner.suite();
+ junit.textui.TestRunner testRunner = new junit.textui.TestRunner(System.out);
+ TestResult testResult = testRunner.doRun(test);
+
+ if(!testResult.wasSuccessful()) {
+ System.exit(1);
+ }
+
+ // not calling System.exit(0) here to ensure that the application
+ // properly terminates (e.g. not waiting for any background threads
+ // indicating serious problems
+ return;
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/TestUtil.java b/ApacheCommonsExec/src/org/apache/commons/exec/TestUtil.java
new file mode 100644
index 0000000..62d4ec6
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/TestUtil.java
@@ -0,0 +1,84 @@
+/*
+ * 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.apache.commons.exec;
+
+import java.io.File;
+import java.util.Arrays;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+public final class TestUtil {
+
+ private TestUtil() {
+ }
+
+ public static File resolveScriptForOS(String script) {
+ if (OS.isFamilyWindows()) {
+ return new File(script + ".bat");
+ } else if (OS.isFamilyUnix()) {
+ return new File(script + ".sh");
+ } else if (OS.isFamilyOpenVms()) {
+ return new File(script + ".dcl");
+ } else {
+ throw new AssertionFailedError("Test not supported for this OS");
+ }
+ }
+
+ /**
+ * Get success and fail return codes used by the test scripts
+ * @return int array[2] = {ok, success}
+ */
+ public static int[] getTestScriptCodesForOS() {
+ if (OS.isFamilyWindows()) {
+ return new int[]{0,1};
+ } else if (OS.isFamilyUnix()) {
+ return new int[]{0,1};
+ } else if (OS.isFamilyOpenVms()) {
+ return new int[]{1,2};
+ } else {
+ throw new AssertionFailedError("Test not supported for this OS");
+ }
+ }
+
+
+ public static void assertEquals(Object[] expected, Object[] actual, boolean orderSignificant) {
+
+ if(expected == null && actual == null) {
+ // all good
+ } else if (actual == null) {
+ throw new AssertionFailedError("Expected non null array");
+ } else if (expected == null) {
+ throw new AssertionFailedError("Expected null array");
+ } else {
+ if(expected.length != actual.length) {
+ throw new AssertionFailedError("Arrays not of same length");
+ }
+
+ if(!orderSignificant) {
+ Arrays.sort(expected);
+ Arrays.sort(actual);
+ }
+
+ for (int i = 0; i < actual.length; i++) {
+ TestCase.assertEquals("Array element at " + i, expected[i], actual[i]);
+ }
+ }
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/TestUtilTest.java b/ApacheCommonsExec/src/org/apache/commons/exec/TestUtilTest.java
new file mode 100644
index 0000000..10de76d
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/TestUtilTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.apache.commons.exec;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+public class TestUtilTest extends TestCase {
+
+ public void testAssertArrayEquals() {
+ String[] expected = new String[]{"aaa", "bbb", "ccc"};
+ String[] actual = new String[]{"aaa", "bbb", "ccc"};
+
+ TestUtil.assertEquals(expected, actual, true);
+ }
+
+ public void testAssertArrayNotEquals() {
+ String[] expected = new String[]{"aaa", "bbb", "ccc"};
+ String[] actual = new String[]{"aaa", "ddd", "ccc"};
+
+ try{
+ TestUtil.assertEquals(expected, actual, true);
+ fail("Must throw AssertionFailedError");
+ } catch(AssertionFailedError e) {
+ // OK
+ }
+ }
+
+ public void testAssertArrayNotOrderEquals() {
+ String[] expected = new String[]{"aaa", "ccc", "bbb"};
+ String[] actual = new String[]{"aaa", "ddd", "ccc"};
+
+ try{
+ TestUtil.assertEquals(expected, actual, true);
+ fail("Must throw AssertionFailedError");
+ } catch(AssertionFailedError e) {
+ // OK
+ }
+ }
+
+ public void testAssertArrayEqualsOrderNotSignificant() {
+ String[] expected = new String[]{"aaa", "ccc", "bbb"};
+ String[] actual = new String[]{"aaa", "bbb", "ccc"};
+
+ TestUtil.assertEquals(expected, actual, false);
+ }
+
+ public void testAssertArrayEqualsNullNull() {
+ String[] expected = null;
+ String[] actual = null;
+
+ TestUtil.assertEquals(expected, actual, false);
+ }
+
+ public void testAssertArrayEqualsActualNull() {
+ String[] expected = new String[]{"aaa", "ccc", "bbb"};
+ String[] actual = null;
+
+ try{
+ TestUtil.assertEquals(expected, actual, true);
+ fail("Must throw AssertionFailedError");
+ } catch(AssertionFailedError e) {
+ // OK
+ }
+ }
+
+ public void testAssertArrayEqualsExpectedNull() {
+ String[] expected = null;
+ String[] actual = new String[]{"aaa", "ddd", "ccc"};
+
+ try{
+ TestUtil.assertEquals(expected, actual, true);
+ fail("Must throw AssertionFailedError");
+ } catch(AssertionFailedError e) {
+ // OK
+ }
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/TimeoutObserver.java b/ApacheCommonsExec/src/org/apache/commons/exec/TimeoutObserver.java
new file mode 100644
index 0000000..dfb87cf
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/TimeoutObserver.java
@@ -0,0 +1,34 @@
+/*
+ * 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.apache.commons.exec;
+
+/**
+ * Interface for classes that want to be notified by Watchdog.
+ *
+ * @see org.apache.commons.exec.Watchdog
+ */
+public interface TimeoutObserver {
+
+ /**
+ * Called when the watchdog times out.
+ *
+ * @param w the watchdog that timed out.
+ */
+ void timeoutOccured(Watchdog w);
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/TutorialTest.java b/ApacheCommonsExec/src/org/apache/commons/exec/TutorialTest.java
new file mode 100644
index 0000000..b939e84
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/TutorialTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.apache.commons.exec;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An example based on the tutorial where the user can can safely play with
+ *
+ *
+ */
+public class TutorialTest extends TestCase {
+
+ /** the directory to pick up the test scripts */
+ private File testDir = new File("src/test/scripts");
+
+ /** simulates a PDF print job */
+ private File acroRd32Script = TestUtil.resolveScriptForOS(testDir + "/acrord32");
+
+ public void testTutorialExample() throws Exception {
+
+ long printJobTimeout = 15000;
+ boolean printInBackground = false;
+ File pdfFile = new File("/Documents and Settings/foo.pdf");
+
+ PrintResultHandler printResult;
+
+ try {
+ // printing takes around 10 seconds
+ System.out.println("[main] Preparing print job ...");
+ printResult = print(pdfFile, printJobTimeout, printInBackground);
+ System.out.println("[main] Successfully sent the print job ...");
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ fail("[main] Printing of the following document failed : " + pdfFile.getAbsolutePath());
+ throw e;
+ }
+
+ // come back to check the print result
+ System.out.println("[main] Test is exiting but waiting for the print job to finish...");
+ printResult.waitFor();
+ System.out.println("[main] The print job has finished ...");
+ }
+
+ /**
+ * Simulate printing a PDF document.
+ *
+ * @param file the file to print
+ * @param printJobTimeout the printJobTimeout (ms) before the watchdog terminates the print process
+ * @param printInBackground printing done in the background or blocking
+ * @return a print result handler (implementing a future)
+ * @throws IOException the test failed
+ */
+ public PrintResultHandler print(File file, long printJobTimeout, boolean printInBackground)
+ throws IOException {
+
+ int exitValue;
+ ExecuteWatchdog watchdog = null;
+ PrintResultHandler resultHandler;
+
+ // build up the command line to using a 'java.io.File'
+ Map map = new HashMap();
+ map.put("file", file);
+ CommandLine commandLine = new CommandLine(acroRd32Script);
+ commandLine.addArgument("/p");
+ commandLine.addArgument("/h");
+ commandLine.addArgument("${file}");
+ commandLine.setSubstitutionMap(map);
+
+ // create the executor and consider the exitValue '1' as success
+ Executor executor = new DefaultExecutor();
+ executor.setExitValue(1);
+
+ // create a watchdog if requested
+ if(printJobTimeout > 0) {
+ watchdog = new ExecuteWatchdog(printJobTimeout);
+ executor.setWatchdog(watchdog);
+ }
+
+ // pass a "ExecuteResultHandler" when doing background printing
+ if(printInBackground) {
+ System.out.println("[print] Executing non-blocking print job ...");
+ resultHandler = new PrintResultHandler(watchdog);
+ executor.execute(commandLine, resultHandler);
+ }
+ else {
+ System.out.println("[print] Executing blocking print job ...");
+ exitValue = executor.execute(commandLine);
+ resultHandler = new PrintResultHandler(exitValue);
+ }
+
+ return resultHandler;
+ }
+
+ private class PrintResultHandler extends DefaultExecuteResultHandler {
+
+ private ExecuteWatchdog watchdog;
+
+ public PrintResultHandler(ExecuteWatchdog watchdog)
+ {
+ this.watchdog = watchdog;
+ }
+
+ public PrintResultHandler(int exitValue) {
+ super.onProcessComplete(exitValue);
+ }
+
+ public void onProcessComplete(int exitValue) {
+ super.onProcessComplete(exitValue);
+ System.out.println("[resultHandler] The document was successfully printed ...");
+ }
+
+ public void onProcessFailed(ExecuteException e){
+ super.onProcessFailed(e);
+ if(watchdog != null && watchdog.killedProcess()) {
+ System.err.println("[resultHandler] The print process timed out");
+ }
+ else {
+ System.err.println("[resultHandler] The print process failed to do : " + e.getMessage());
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/Watchdog.java b/ApacheCommonsExec/src/org/apache/commons/exec/Watchdog.java
new file mode 100644
index 0000000..8442a88
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/Watchdog.java
@@ -0,0 +1,93 @@
+/*
+ * 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.apache.commons.exec;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * Generalization of ExecuteWatchdog
+ * exitValue
returned from the print script
+ * ExecuteWatchdog
+ *
+ * @see org.apache.commons.exec.ExecuteWatchdog
+ */
+public class Watchdog implements Runnable {
+
+ private Vector observers = new Vector(1);
+
+ private final long timeout;
+
+ private boolean stopped = false;
+
+ public Watchdog(final long timeout) {
+ if (timeout < 1) {
+ throw new IllegalArgumentException("timeout must not be less than 1.");
+ }
+ this.timeout = timeout;
+ }
+
+ public void addTimeoutObserver(final TimeoutObserver to) {
+ observers.addElement(to);
+ }
+
+ public void removeTimeoutObserver(final TimeoutObserver to) {
+ observers.removeElement(to);
+ }
+
+ protected final void fireTimeoutOccured() {
+ Enumeration e = observers.elements();
+ while (e.hasMoreElements()) {
+ ((TimeoutObserver) e.nextElement()).timeoutOccured(this);
+ }
+ }
+
+ public synchronized void start() {
+ stopped = false;
+ Thread t = new Thread(this, "WATCHDOG");
+ t.setDaemon(true);
+ t.start();
+ }
+
+ public synchronized void stop() {
+ stopped = true;
+ notifyAll();
+ }
+
+ public void run() {
+ final long until = System.currentTimeMillis() + timeout;
+ boolean isWaiting;
+ synchronized (this) {
+ long now = System.currentTimeMillis();
+ isWaiting = until > now;
+ while (!stopped && isWaiting) {
+ try {
+ wait(until - now);
+ } catch (InterruptedException e) {
+ }
+ now = System.currentTimeMillis();
+ isWaiting = until > now;
+ }
+ }
+
+ // notify the listeners outside of the synchronized block (see EXEC-60)
+ if (!isWaiting) {
+ fireTimeoutOccured();
+ }
+ }
+
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/environment/DefaultProcessingEnvironment.java b/ApacheCommonsExec/src/org/apache/commons/exec/environment/DefaultProcessingEnvironment.java
new file mode 100644
index 0000000..37279aa
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/environment/DefaultProcessingEnvironment.java
@@ -0,0 +1,241 @@
+/*
+ * 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.apache.commons.exec.environment;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.commons.exec.CommandLine;
+import org.apache.commons.exec.DefaultExecutor;
+import org.apache.commons.exec.Executor;
+import org.apache.commons.exec.OS;
+import org.apache.commons.exec.PumpStreamHandler;
+
+/**
+ * Helper class to determine the environment variable
+ * for the OS. Depending on the JDK the environment
+ * variables can be either retrieved directly from the
+ * JVM or requires starting a process to get them running
+ * an OS command line.
+ */
+public class DefaultProcessingEnvironment {
+
+ /** the line separator of the system */
+ private static final String LINE_SEPARATOR = System.getProperty("line.separator");
+
+ /** the environment variables of the process */
+ protected Map procEnvironment;
+
+ /**
+ * Find the list of environment variables for this process.
+ *
+ * @return a map containing the environment variables
+ * @throws IOException obtaining the environment variables failed
+ */
+ public synchronized Map getProcEnvironment() throws IOException {
+
+ if(procEnvironment == null) {
+ procEnvironment = this.createProcEnvironment();
+ }
+
+ // create a copy of the map just in case that
+ // anyone is going to modifiy it, e.g. removing
+ // or setting an evironment variable
+ Map copy = createEnvironmentMap();
+ copy.putAll(procEnvironment);
+ return copy;
+ }
+
+ /**
+ * Find the list of environment variables for this process.
+ *
+ * @return a amp containing the environment variables
+ * @throws IOException the operation failed
+ */
+ protected Map createProcEnvironment() throws IOException {
+ if (procEnvironment == null) {
+ try {
+ Method getenvs = System.class.getMethod( "getenv", (java.lang.Class[]) null );
+ Map env = (Map) getenvs.invoke( null, (java.lang.Object[]) null );
+ procEnvironment = createEnvironmentMap();
+ procEnvironment.putAll(env);
+ } catch ( NoSuchMethodException e ) {
+ // ok, just not on JDK 1.5
+ } catch ( IllegalAccessException e ) {
+ // Unexpected error obtaining environment - using JDK 1.4 method
+ } catch ( InvocationTargetException e ) {
+ // Unexpected error obtaining environment - using JDK 1.4 method
+ }
+ }
+
+ if(procEnvironment == null) {
+ procEnvironment = createEnvironmentMap();
+ BufferedReader in = runProcEnvCommand();
+
+ String var = null;
+ String line;
+ while ((line = in.readLine()) != null) {
+ if (line.indexOf('=') == -1) {
+ // Chunk part of previous env var (UNIX env vars can
+ // contain embedded new lines).
+ if (var == null) {
+ var = LINE_SEPARATOR + line;
+ } else {
+ var += LINE_SEPARATOR + line;
+ }
+ } else {
+ // New env var...append the previous one if we have it.
+ if (var != null) {
+ EnvironmentUtils.addVariableToEnvironment(procEnvironment, var);
+ }
+ var = line;
+ }
+ }
+ // Since we "look ahead" before adding, there's one last env var.
+ if (var != null) {
+ EnvironmentUtils.addVariableToEnvironment(procEnvironment, var);
+ }
+ }
+ return procEnvironment;
+ }
+
+ /**
+ * Start a process to list the environment variables.
+ *
+ * @return a reader containing the output of the process
+ * @throws IOException starting the process failed
+ */
+ protected BufferedReader runProcEnvCommand() throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ Executor exe = new DefaultExecutor();
+ exe.setStreamHandler(new PumpStreamHandler(out));
+ // ignore the exit value - Just try to use what we got
+ exe.execute(getProcEnvCommand());
+ return new BufferedReader(new StringReader(toString(out)));
+ }
+
+ /**
+ * Determine the OS specific command line to get a list of environment
+ * variables.
+ *
+ * @return the command line
+ */
+ protected CommandLine getProcEnvCommand() {
+ String executable;
+ String[] arguments = null;
+ if (OS.isFamilyOS2()) {
+ // OS/2 - use same mechanism as Windows 2000
+ executable = "cmd";
+
+ arguments = new String[] {"/c", "set"};
+ } else if (OS.isFamilyWindows()) {
+ // Determine if we're running under XP/2000/NT or 98/95
+ if (OS.isFamilyWin9x()) {
+ executable = "command.com";
+ // Windows 98/95
+ } else {
+ executable = "cmd";
+ // Windows XP/2000/NT/2003
+ }
+ arguments = new String[] {"/c", "set"};
+ } else if (OS.isFamilyZOS() || OS.isFamilyUnix()) {
+ // On most systems one could use: /bin/sh -c env
+
+ // Some systems have /bin/env, others /usr/bin/env, just try
+ if (new File("/bin/env").canRead()) {
+ executable = "/bin/env";
+ } else if (new File("/usr/bin/env").canRead()) {
+ executable = "/usr/bin/env";
+ } else {
+ // rely on PATH
+ executable = "env";
+ }
+ } else if (OS.isFamilyNetware() || OS.isFamilyOS400()) {
+ // rely on PATH
+ executable = "env";
+ } else {
+ // MAC OS 9 and previous
+ // TODO: I have no idea how to get it, someone must fix it
+ executable = null;
+ }
+ CommandLine commandLine = null;
+ if(executable != null) {
+ commandLine = new CommandLine(executable);
+ commandLine.addArguments(arguments);
+ }
+ return commandLine;
+ }
+
+ /**
+ * ByteArrayOutputStream#toString doesn't seem to work reliably on OS/390,
+ * at least not the way we use it in the execution context.
+ *
+ * @param bos
+ * the output stream that one wants to read
+ * @return the output stream as a string, read with special encodings in the
+ * case of z/os and os/400
+ */
+ private String toString(final ByteArrayOutputStream bos) {
+ if (OS.isFamilyZOS()) {
+ try {
+ return bos.toString("Cp1047");
+ } catch (java.io.UnsupportedEncodingException e) {
+ // noop default encoding used
+ }
+ } else if (OS.isFamilyOS400()) {
+ try {
+ return bos.toString("Cp500");
+ } catch (java.io.UnsupportedEncodingException e) {
+ // noop default encoding used
+ }
+ }
+ return bos.toString();
+ }
+
+ /**
+ * Creates a map that obeys the casing rules of the current platform for key
+ * lookup. E.g. on a Windows platform, the map keys will be
+ * case-insensitive.
+ *
+ * @return The map for storage of environment variables, never
+ * null
.
+ */
+ private Map createEnvironmentMap() {
+ if (OS.isFamilyWindows()) {
+ return new TreeMap(new Comparator() {
+ public int compare(Object arg0, Object arg1) {
+ String key0 = (String) arg0;
+ String key1 = (String) arg1;
+ return key0.compareToIgnoreCase(key1);
+ }
+ });
+ } else {
+ return new HashMap();
+ }
+ }
+
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/environment/EnvironmentUtils.java b/ApacheCommonsExec/src/org/apache/commons/exec/environment/EnvironmentUtils.java
new file mode 100644
index 0000000..6b88d1f
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/environment/EnvironmentUtils.java
@@ -0,0 +1,119 @@
+/*
+ * 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.apache.commons.exec.environment;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.commons.exec.OS;
+
+/**
+ * Wrapper for environment variables.
+ */
+public class EnvironmentUtils
+{
+
+ private static final DefaultProcessingEnvironment PROCESSING_ENVIRONMENT_IMPLEMENTATION;
+
+ static {
+ if (OS.isFamilyOpenVms()) {
+ PROCESSING_ENVIRONMENT_IMPLEMENTATION = new OpenVmsProcessingEnvironment();
+ } else {
+ PROCESSING_ENVIRONMENT_IMPLEMENTATION = new DefaultProcessingEnvironment();
+ }
+ }
+
+ /**
+ * Disable constructor.
+ */
+ private EnvironmentUtils() {
+
+ }
+
+ /**
+ * Get the variable list as an array.
+ *
+ * @param environment the environment to use, may be null
+ * @return array of key=value assignment strings or null
if and only if
+ * the input map was null
+ */
+ public static String[] toStrings(Map environment) {
+ if (environment == null) {
+ return null;
+ }
+ String[] result = new String[environment.size()];
+ int i = 0;
+ for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();) {
+ Map.Entry entry = (Map.Entry) iter.next();
+
+ result[i] = entry.getKey().toString() + "=" + entry.getValue().toString();
+ i++;
+ }
+ return result;
+ }
+
+ /**
+ * Find the list of environment variables for this process. The returned map preserves
+ * the casing of a variable's name on all platforms but obeys the casing rules of the
+ * current platform during lookup, e.g. key names will be case-insensitive on Windows
+ * platforms.
+ *
+ * @return a map containing the environment variables, may be empty but never null
+ * @throws IOException the operation failed
+ */
+ public static Map getProcEnvironment() throws IOException {
+ return PROCESSING_ENVIRONMENT_IMPLEMENTATION.getProcEnvironment();
+ }
+
+ /**
+ * 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[] parsedVariable = parseEnvironmentVariable(keyAndValue);
+ environment.put(parsedVariable[0], parsedVariable[1]);
+ }
+
+ /**
+ * Split a key/value pair into a String[]. It is assumed
+ * that the ky/value pair contains a '=' character.
+ *
+ * @param keyAndValue the key/value pair
+ * @return a String[] containing the key and value
+ */
+ private static String[] parseEnvironmentVariable(final String keyAndValue) {
+ int index = keyAndValue.indexOf('=');
+ if (index == -1) {
+ throw new IllegalArgumentException(
+ "Environment variable for this platform "
+ + "must contain an equals sign ('=')");
+ }
+
+ String[] result = new String[2];
+ result[0] = keyAndValue.substring(0, index);
+ result[1] = keyAndValue.substring(index + 1);
+
+ return result;
+ }
+
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/environment/OpenVmsProcessingEnvironment.java b/ApacheCommonsExec/src/org/apache/commons/exec/environment/OpenVmsProcessingEnvironment.java
new file mode 100644
index 0000000..9f2ad16
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/environment/OpenVmsProcessingEnvironment.java
@@ -0,0 +1,86 @@
+/*
+ * 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.apache.commons.exec.environment;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.exec.CommandLine;
+
+/**
+ * Helper class to determine the environment variable
+ * for VMS.
+ */
+public class OpenVmsProcessingEnvironment extends DefaultProcessingEnvironment {
+
+ /**
+ * Find the list of environment variables for this process.
+ *
+ * @return a map containing the environment variables
+ * @throws IOException the operation failed
+ */
+ protected Map createProcEnvironment() throws IOException {
+ if (procEnvironment == null) {
+ BufferedReader in = runProcEnvCommand();
+ procEnvironment = addVMSenvironmentVariables(new HashMap(), in);
+ }
+
+ return procEnvironment;
+ }
+
+ /**
+ * Determine the OS specific command line to get a list of environment
+ * variables.
+ *
+ * @return the command line
+ */
+ protected CommandLine getProcEnvCommand() {
+ CommandLine commandLine = new CommandLine("show");
+ commandLine.addArgument("symbol/global"); // the parser assumes symbols are global
+ commandLine.addArgument("*");
+ return commandLine;
+ }
+
+ /**
+ * This method is VMS specific and used by getProcEnvironment(). Parses VMS
+ * symbols from in
and adds them to environment
.
+ * in
is expected to be the output of "SHOW SYMBOL/GLOBAL *".
+ *
+ * @param environment the current environment
+ * @param in the reader from the process to determine VMS env variables
+ * @return the updated environment
+ * @throws IOException operation failed
+ */
+ private Map addVMSenvironmentVariables(final Map environment,
+ final BufferedReader in) throws IOException {
+ String line;
+ while ((line = in.readLine()) != null) {
+ final String SEP = "=="; // global symbol separator
+ int sepidx = line.indexOf(SEP);
+ if (sepidx > 0){
+ String name = line.substring(0, sepidx).trim();
+ String value = line.substring(sepidx+SEP.length()).trim();
+ value = value.substring(1,value.length()-1); // drop enclosing quotes
+ environment.put(name,value);
+ }
+ }
+ return environment;
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/launcher/CommandLauncher.java b/ApacheCommonsExec/src/org/apache/commons/exec/launcher/CommandLauncher.java
new file mode 100644
index 0000000..5d55eb9
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/launcher/CommandLauncher.java
@@ -0,0 +1,86 @@
+/*
+ * 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.apache.commons.exec.launcher;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.commons.exec.CommandLine;
+
+/**
+ * Interface to shield the caller from the various platform-dependent
+ * implementations.
+ */
+public interface CommandLauncher {
+
+ /**
+ * Launches the given command in a new process.
+ *
+ * @param cmd
+ * The command to execute
+ * @param env
+ * The environment for the new process. If null, the environment
+ * of the current process is used.
+ *
+ * @return the newly created process
+ * @throws IOException
+ * if attempting to run a command in a specific directory
+ */
+ Process exec(final CommandLine cmd, final Map env)
+ throws IOException;
+
+ /**
+ * Launches the given command in a new process, in the given working
+ * directory.
+ *
+ * @param cmd
+ * The command to execute
+ * @param env
+ * The environment for the new process. If null, the environment
+ * of the current process is used.
+ * @param workingDir
+ * The directory to start the command in. If null, the current
+ * directory is used
+ *
+ * @return the newly created process
+ * @throws IOException
+ * if trying to change directory
+ */
+ Process exec(final CommandLine cmd, final Map env,
+ final File workingDir) throws IOException;
+
+
+ /**
+ * Checks whether exitValue
signals a failure on the current
+ * system (OS specific).
+ * true
if exitValue
signals a failure
+ */
+ boolean isFailure(final int exitValue);
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/launcher/CommandLauncherFactory.java b/ApacheCommonsExec/src/org/apache/commons/exec/launcher/CommandLauncherFactory.java
new file mode 100644
index 0000000..254da23
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/launcher/CommandLauncherFactory.java
@@ -0,0 +1,48 @@
+/*
+ * 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.apache.commons.exec.launcher;
+
+import org.apache.commons.exec.OS;
+
+/**
+ * Builds a command launcher for the OS and JVM we are running under.
+ */
+public final class CommandLauncherFactory {
+
+ private CommandLauncherFactory() {
+ }
+
+ /**
+ * Factory method to create an appropriate launcher.
+ *
+ * @return the command launcher
+ */
+ public static CommandLauncher createVMLauncher() {
+ // Try using a JDK 1.3 launcher
+ CommandLauncher launcher;
+
+ if (OS.isFamilyOpenVms()) {
+ launcher = new VmsCommandLauncher();
+ } else {
+ launcher = new Java13CommandLauncher();
+ }
+
+ return launcher;
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/launcher/CommandLauncherImpl.java b/ApacheCommonsExec/src/org/apache/commons/exec/launcher/CommandLauncherImpl.java
new file mode 100644
index 0000000..f04be3a
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/launcher/CommandLauncherImpl.java
@@ -0,0 +1,50 @@
+/*
+ * 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.apache.commons.exec.launcher;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.commons.exec.CommandLine;
+import org.apache.commons.exec.environment.EnvironmentUtils;
+
+/**
+ * A command launcher for a particular JVM/OS platform. This class is a general
+ * purpose command launcher which can only launch commands in the current
+ * working directory.
+ */
+public abstract class CommandLauncherImpl implements CommandLauncher {
+
+ public Process exec(final CommandLine cmd, final Map env)
+ throws IOException {
+ String[] envVar = EnvironmentUtils.toStrings(env);
+ return Runtime.getRuntime().exec(cmd.toStrings(), envVar);
+ }
+
+ public abstract Process exec(final CommandLine cmd, final Map env,
+ final File workingDir) throws IOException;
+
+ /** @see org.apache.commons.exec.launcher.CommandLauncher#isFailure(int) */
+ public boolean isFailure(final int exitValue)
+ {
+ // non zero exit value signals failure
+ return exitValue != 0;
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/launcher/CommandLauncherProxy.java b/ApacheCommonsExec/src/org/apache/commons/exec/launcher/CommandLauncherProxy.java
new file mode 100644
index 0000000..78fb180
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/launcher/CommandLauncherProxy.java
@@ -0,0 +1,53 @@
+/*
+ * 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.apache.commons.exec.launcher;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.commons.exec.CommandLine;
+
+/**
+ * A command launcher that proxies another command launcher. Sub-classes
+ * override exec(args, env, workdir)
+ */
+public abstract class CommandLauncherProxy extends CommandLauncherImpl {
+
+ public CommandLauncherProxy(final CommandLauncher launcher) {
+ myLauncher = launcher;
+ }
+
+ private final CommandLauncher myLauncher;
+
+ /**
+ * Launches the given command in a new process. Delegates this method to the
+ * proxied launcher
+ *
+ * @param cmd
+ * the command line to execute as an array of strings
+ * @param env
+ * the environment to set as an array of strings
+ * @throws IOException
+ * forwarded from the exec method of the command launcher
+ */
+ public Process exec(final CommandLine cmd, final Map env)
+ throws IOException {
+ return myLauncher.exec(cmd, env);
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/launcher/Java13CommandLauncher.java b/ApacheCommonsExec/src/org/apache/commons/exec/launcher/Java13CommandLauncher.java
new file mode 100644
index 0000000..152466d
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/launcher/Java13CommandLauncher.java
@@ -0,0 +1,61 @@
+/*
+ * 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.apache.commons.exec.launcher;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.commons.exec.CommandLine;
+import org.apache.commons.exec.environment.EnvironmentUtils;
+
+/**
+ * A command launcher for JDK/JRE 1.3 (and higher). Uses the built-in
+ * Runtime.exec() command
+ */
+public class Java13CommandLauncher extends CommandLauncherImpl {
+
+ /**
+ * Constructor
+ */
+ public Java13CommandLauncher() {
+ }
+
+ /**
+ * Launches the given command in a new process, in the given working
+ * directory
+ *
+ * @param cmd
+ * the command line to execute as an array of strings
+ * @param env
+ * the environment to set as an array of strings
+ * @param workingDir
+ * the working directory where the command should run
+ * @throws IOException
+ * probably forwarded from Runtime#exec
+ */
+ public Process exec(final CommandLine cmd, final Map env,
+ final File workingDir) throws IOException {
+
+ String[] envVars = EnvironmentUtils.toStrings(env);
+
+ return Runtime.getRuntime().exec(cmd.toStrings(),
+ envVars, workingDir);
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/launcher/OS2CommandLauncher.java b/ApacheCommonsExec/src/org/apache/commons/exec/launcher/OS2CommandLauncher.java
new file mode 100644
index 0000000..3469ba3
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/launcher/OS2CommandLauncher.java
@@ -0,0 +1,68 @@
+/*
+ * 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.apache.commons.exec.launcher;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.commons.exec.CommandLine;
+
+/**
+ * A command launcher for OS/2 that uses 'cmd.exe' when launching commands in
+ * directories other than the current working directory.
+ * workingDir
is null or the logical
+ * JAVA$FORK_SUPPORT_CHDIR needs to be set to TRUE.
+ */
+ public Process exec(final CommandLine cmd, final Map env,
+ final File workingDir) throws IOException {
+ CommandLine vmsCmd = new CommandLine(
+ createCommandFile(cmd, env).getPath()
+ );
+
+ return super.exec(vmsCmd, env, workingDir);
+ }
+
+ /** @see org.apache.commons.exec.launcher.CommandLauncher#isFailure(int) */
+ public boolean isFailure( final int exitValue )
+ {
+ // even exit value signals failure
+ return (exitValue % 2) == 0;
+ }
+
+ /*
+ * Writes the command into a temporary DCL script and returns the
+ * corresponding File object. The script will be deleted on exit.
+ */
+ private File createCommandFile(final CommandLine cmd, final Map env)
+ throws IOException {
+ File script = File.createTempFile("EXEC", ".TMP");
+ script.deleteOnExit();
+ PrintWriter out = null;
+ try {
+ out = new PrintWriter(new FileWriter(script.getAbsolutePath(),true));
+
+ // add the environment as global symbols for the DCL script
+ if (env != null) {
+ Set entries = env.entrySet();
+
+ for (Iterator iter = entries.iterator(); iter.hasNext();) {
+ Entry entry = (Entry) iter.next();
+ out.print("$ ");
+ out.print(entry.getKey());
+ out.print(" == "); // define as global symbol
+ out.println('\"');
+ String value = (String) entry.getValue();
+ // Any embedded " values need to be doubled
+ if (value.indexOf('\"') > 0){
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < value.length(); i++) {
+ char c = value.charAt(i);
+ if (c == '\"') {
+ sb.append('\"');
+ }
+ sb.append(c);
+ }
+ value=sb.toString();
+ }
+ out.print(value);
+ out.println('\"');
+ }
+ }
+
+ final String command = cmd.getExecutable();
+ if (cmd.isFile()){// We assume it is it a script file
+ out.print("$ @");
+ // This is a bit crude, but seems to work
+ String parts[] = StringUtils.split(command,"/");
+ out.print(parts[0]); // device
+ out.print(":[");
+ out.print(parts[1]); // top level directory
+ final int lastPart = parts.length-1;
+ for(int i=2; i< lastPart; i++){
+ out.print(".");
+ out.print(parts[i]);
+ }
+ out.print("]");
+ out.print(parts[lastPart]);
+ } else {
+ out.print("$ ");
+ out.print(command);
+ }
+ String[] args = cmd.getArguments();
+ for (int i = 0; i < args.length; i++) {
+ out.println(" -");
+ out.print(args[i]);
+ }
+ out.println();
+ } finally {
+ if (out != null) {
+ out.close();
+ }
+ }
+ return script;
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/launcher/WinNTCommandLauncher.java b/ApacheCommonsExec/src/org/apache/commons/exec/launcher/WinNTCommandLauncher.java
new file mode 100644
index 0000000..6e4d189
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/launcher/WinNTCommandLauncher.java
@@ -0,0 +1,63 @@
+/*
+ * 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.apache.commons.exec.launcher;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.commons.exec.CommandLine;
+
+/**
+ * A command launcher for Windows XP/2000/NT that uses 'cmd.exe' when launching
+ * commands in directories other than the current working directory.
+ */
+public class WinNTCommandLauncher extends CommandLauncherProxy {
+ public WinNTCommandLauncher(final CommandLauncher launcher) {
+ super(launcher);
+ }
+
+ /**
+ * Launches the given command in a new process, in the given working
+ * directory.
+ *
+ * @param cmd
+ * the command line to execute as an array of strings
+ * @param env
+ * the environment to set as an array of strings
+ * @param workingDir
+ * working directory where the command should run
+ * @throws IOException
+ * forwarded from the exec method of the command launcher
+ */
+ public Process exec(final CommandLine cmd, final Map env,
+ final File workingDir) throws IOException {
+ if (workingDir == null) {
+ return exec(cmd, env);
+ }
+
+ // Use cmd.exe to change to the specified directory before running
+ // the command
+ CommandLine newCmd = new CommandLine("cmd");
+ newCmd.addArgument("/c");
+ newCmd.addArguments(cmd.toStrings());
+
+ return exec(newCmd, env);
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/util/DebugUtils.java b/ApacheCommonsExec/src/org/apache/commons/exec/util/DebugUtils.java
new file mode 100644
index 0000000..d28f748
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/util/DebugUtils.java
@@ -0,0 +1,86 @@
+/*
+ * 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.apache.commons.exec.util;
+
+/**
+ * Helper classes to provide debugging support.
+ *
+ * @author Siegfried Goeschl
+ */
+public class DebugUtils
+{
+ /**
+ * System property to determine how to handle exceptions. When
+ * set to "false" we rethrow the otherwise silently catched
+ * exceptions found in the original code. The default value
+ * is "true"
+ */
+ public static final String COMMONS_EXEC_LENIENT = "org.apache.commons.exec.lenient";
+
+ /**
+ * System property to determine how to dump an exception. When
+ * set to "true" we print any exception to stderr. The default
+ * value is "false"
+ */
+ public static final String COMMONS_EXEC_DEBUG = "org.apache.commons.exec.debug";
+
+ /**
+ * Handle an exception based on the system properties.
+ *
+ * @param msg message describing the problem
+ * @param e an exception being handled
+ */
+ public static void handleException(String msg, Exception e) {
+
+ if(isDebugEnabled()) {
+ System.err.println(msg);
+ e.printStackTrace();
+ }
+
+ if(!isLenientEnabled()) {
+ if(e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ }
+ else {
+ // can't pass root cause since the constructor is not available on JDK 1.3
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Determine if debugging is enabled based on the
+ * system property "COMMONS_EXEC_DEBUG".
+ *
+ * @return true if debug mode is enabled
+ */
+ public static boolean isDebugEnabled() {
+ return "true".equalsIgnoreCase(System.getProperty(COMMONS_EXEC_DEBUG, "false"));
+ }
+
+ /**
+ * Determine if lenient mode is enabled.
+ *
+ * @return true if lenient mode is enabled
+ */
+ public static boolean isLenientEnabled() {
+ return "true".equalsIgnoreCase(System.getProperty(COMMONS_EXEC_LENIENT, "true"));
+ }
+
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/util/MapUtilTest.java b/ApacheCommonsExec/src/org/apache/commons/exec/util/MapUtilTest.java
new file mode 100644
index 0000000..5e040a7
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/util/MapUtilTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.apache.commons.exec.util;
+
+import org.apache.commons.exec.environment.EnvironmentUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+public class MapUtilTest extends TestCase
+{
+ /**
+ * Test copying of map
+ */
+ public void testCopyMap() throws Exception {
+
+ HashMap procEnvironment = new HashMap();
+ procEnvironment.put("JAVA_HOME", "/usr/opt/java");
+
+ Map result = MapUtils.copy(procEnvironment);
+ assertTrue(result.size() == 1);
+ assertTrue(procEnvironment.size() == 1);
+ assertEquals("/usr/opt/java", result.get("JAVA_HOME"));
+
+ result.remove("JAVA_HOME");
+ assertTrue(result.size() == 0);
+ assertTrue(procEnvironment.size() == 1);
+ }
+
+ /**
+ * Test merging of maps
+ */
+ public void testMergeMap() throws Exception {
+
+ Map procEnvironment = EnvironmentUtils.getProcEnvironment();
+ HashMap applicationEnvironment = new HashMap();
+
+ applicationEnvironment.put("appMainClass", "foo.bar.Main");
+ Map result = MapUtils.merge(procEnvironment, applicationEnvironment);
+ assertTrue((procEnvironment.size() + applicationEnvironment.size()) == result.size());
+ assertEquals("foo.bar.Main", result.get("appMainClass"));
+ }
+
+ /**
+ * Test prefixing of map
+ */
+ public void testPrefixMap() throws Exception {
+
+ HashMap procEnvironment = new HashMap();
+ procEnvironment.put("JAVA_HOME", "/usr/opt/java");
+
+ Map result = MapUtils.prefix(procEnvironment, "env");
+ assertTrue(procEnvironment.size() == result.size());
+ assertEquals("/usr/opt/java", result.get("env.JAVA_HOME"));
+ }
+}
\ No newline at end of file
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/util/MapUtils.java b/ApacheCommonsExec/src/org/apache/commons/exec/util/MapUtils.java
new file mode 100644
index 0000000..f31763f
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/util/MapUtils.java
@@ -0,0 +1,105 @@
+/*
+ * 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.apache.commons.exec.util;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * Helper classes to manipulate maps to pass substition map to the
+ * CommandLine. This class is not part of the public API and
+ * could change without warning.
+ *
+ * @author Siegfried Goeschl
+ */
+public class MapUtils
+{
+ /**
+ * Clones a map.
+ *
+ * @param source the source map
+ * @return the clone of the source map
+ */
+ public static Map copy(Map source) {
+
+ if(source == null) {
+ return null;
+ }
+
+ Map result = new HashMap();
+ result.putAll(source);
+ return result;
+ }
+
+ /**
+ * Clones a map and prefixes the keys in the clone, e.g.
+ * for mapping "JAVA_HOME" to "env.JAVA_HOME" to simulate
+ * the behaviour of ANT.
+ *
+ * @param source the source map
+ * @param prefix the prefix used for all names
+ * @return the clone of the source map
+ */
+ public static Map prefix(Map source, String prefix) {
+
+ if(source == null) {
+ return null;
+ }
+
+ Map result = new HashMap();
+
+ Iterator iter = source.entrySet().iterator();
+
+ while(iter.hasNext()) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ Object key = entry.getKey();
+ Object value = entry.getValue();
+ result.put(prefix + '.' + key.toString(), value);
+ }
+
+ return result;
+ }
+
+ /**
+ * Clones the lhs map and add all things from the
+ * rhs map.
+ *
+ * @param lhs the first map
+ * @param rhs the second map
+ * @return the merged map
+ */
+ public static Map merge(Map lhs, Map rhs) {
+
+ Map result = null;
+
+ if((lhs == null) || (lhs.size() == 0)) {
+ result = copy(rhs);
+ }
+ else if((rhs == null) || (rhs.size() == 0)) {
+ result = copy(lhs);
+ }
+ else {
+ result = copy(lhs);
+ result.putAll(rhs);
+ }
+
+ return result;
+ }
+}
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/util/StringUtilTest.java b/ApacheCommonsExec/src/org/apache/commons/exec/util/StringUtilTest.java
new file mode 100644
index 0000000..ce0f6c6
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/util/StringUtilTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.apache.commons.exec.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+public class StringUtilTest extends TestCase
+{
+ /**
+ * Test no string substitution
+ */
+ public void testNoStringSubstitution() throws Exception
+ {
+ Map vars = new HashMap();
+ vars.put("foo", "FOO");
+ vars.put("bar", "BAR");
+
+ assertEquals("This is a FOO & BAR test", StringUtils.stringSubstitution("This is a FOO & BAR test", vars, true).toString());
+ }
+
+ /**
+ * Test a default string substitution, e.g. all placeholders
+ * are expanded.
+ */
+ public void testDefaultStringSubstitution() throws Exception
+ {
+ Map vars = new HashMap();
+ vars.put("foo", "FOO");
+ vars.put("bar", "BAR");
+
+ assertEquals("This is a FOO & BAR test", StringUtils.stringSubstitution("This is a ${foo} & ${bar} test", vars, true).toString());
+ assertEquals("This is a FOO & BAR test", StringUtils.stringSubstitution("This is a ${foo} & ${bar} test", vars, false).toString());
+ }
+
+ /**
+ * Test an incomplete string substitution where not all placeholders
+ * are expanded.
+ */
+ public void testIncompleteSubstitution() throws Exception {
+
+ Map vars = new HashMap();
+ vars.put("foo", "FOO");
+
+ assertEquals("This is a FOO & ${bar} test", StringUtils.stringSubstitution("This is a ${foo} & ${bar} test", vars, true).toString());
+
+ try
+ {
+ StringUtils.stringSubstitution("This is a ${foo} & ${bar} test", vars, false).toString();
+ fail();
+ }
+ catch(RuntimeException e)
+ {
+ // nothing to do
+ }
+ }
+
+ /**
+ * Test a erroneous template.
+ */
+ public void testErroneousTemplate() throws Exception
+ {
+ Map vars = new HashMap();
+ vars.put("foo", "FOO");
+
+ assertEquals("This is a FOO & ${}} test", StringUtils.stringSubstitution("This is a ${foo} & ${}} test", vars, true).toString());
+ }
+}
\ No newline at end of file
diff --git a/ApacheCommonsExec/src/org/apache/commons/exec/util/StringUtils.java b/ApacheCommonsExec/src/org/apache/commons/exec/util/StringUtils.java
new file mode 100644
index 0000000..cb3d527
--- /dev/null
+++ b/ApacheCommonsExec/src/org/apache/commons/exec/util/StringUtils.java
@@ -0,0 +1,258 @@
+/*
+ * 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.apache.commons.exec.util;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.io.File;
+
+/**
+ * Supplement of commons-lang, the stringSubstitution() was in a simpler
+ * implementation available in an older commons-lang implementation.
+ *
+ * This class is not part of the public API and could change without
+ * warning.
+ *
+ * @author Siegfried Goeschl
+ */
+public class StringUtils {
+
+ private static final String SINGLE_QUOTE = "\'";
+ private static final String DOUBLE_QUOTE = "\"";
+ private static final char SLASH_CHAR = '/';
+ private static final char BACKSLASH_CHAR = '\\';
+
+ /**
+ * Perform a series of substitutions. The substitutions
+ * are performed by replacing ${variable} in the target
+ * string with the value of provided by the key "variable"
+ * in the provided hash table.
+ *
+ * A key consists of the following characters:
+ *
+ *
+ *
+ * @param argStr the argument string to be processed
+ * @param vars name/value pairs used for substitution
+ * @param isLenient ignore a key not found in vars or throw a RuntimeException?
+ * @return String target string with replacements.
+ */
+ public static StringBuffer stringSubstitution(String argStr, Map vars, boolean isLenient) {
+
+ StringBuffer argBuf = new StringBuffer();
+
+ if (argStr == null || argStr.length() == 0) {
+ return argBuf;
+ }
+
+ if (vars == null || vars.size() == 0) {
+ return argBuf.append(argStr);
+ }
+
+ int argStrLength = argStr.length();
+
+ for (int cIdx = 0; cIdx < argStrLength;) {
+
+ char ch = argStr.charAt(cIdx);
+ char del = ' ';
+
+ switch (ch) {
+
+ case '$':
+ StringBuffer nameBuf = new StringBuffer();
+ del = argStr.charAt(cIdx + 1);
+ if (del == '{') {
+ cIdx++;
+
+ for (++cIdx; cIdx < argStr.length(); ++cIdx) {
+ ch = argStr.charAt(cIdx);
+ if (ch == '_' || ch == '.' || ch == '-' || ch == '+' || Character.isLetterOrDigit(ch))
+ nameBuf.append(ch);
+ else
+ break;
+ }
+
+ if (nameBuf.length() >= 0) {
+
+ String value;
+ Object temp = vars.get(nameBuf.toString());
+
+ if(temp instanceof File) {
+ // for a file we have to fix the separator chars to allow
+ // cross-platform compatibility
+ value = fixFileSeparatorChar(((File) temp).getAbsolutePath());
+ }
+ else {
+ value = (temp != null ? temp.toString() : null);
+ }
+
+ if (value != null) {
+ argBuf.append(value);
+ } else {
+ if (isLenient) {
+ // just append the unresolved variable declaration
+ argBuf.append("${").append(nameBuf.toString()).append("}");
+ } else {
+ // complain that no variable was found
+ throw new RuntimeException("No value found for : " + nameBuf);
+ }
+ }
+
+ del = argStr.charAt(cIdx);
+
+ if (del != '}') {
+ throw new RuntimeException("Delimiter not found for : " + nameBuf);
+ }
+ }
+
+ cIdx++;
+ }
+ else {
+ argBuf.append(ch);
+ ++cIdx;
+ }
+
+ break;
+
+ default:
+ argBuf.append(ch);
+ ++cIdx;
+ break;
+ }
+ }
+
+ return argBuf;
+ }
+
+ /**
+ * Split a string into an array of strings based
+ * on a separator.
+ *
+ * @param input what to split
+ * @param splitChar what to split on
+ * @return the array of strings
+ */
+ public static String[] split(String input, String splitChar) {
+ StringTokenizer tokens = new StringTokenizer(input, splitChar);
+ List strList = new ArrayList();
+ while (tokens.hasMoreTokens()) {
+ strList.add(tokens.nextToken());
+ }
+ return (String[]) strList.toArray(new String[strList.size()]);
+ }
+
+ /**
+ * Fixes the file separator char for the target platform
+ * using the following replacement.
+ *
+ *
+ *
+ *
+ * @param arg the argument to fix
+ * @return the transformed argument
+ */
+ public static String fixFileSeparatorChar(String arg) {
+ return arg.replace(SLASH_CHAR, File.separatorChar).replace(
+ BACKSLASH_CHAR, File.separatorChar);
+ }
+
+ /**
+ * Concatenates an array of string using a separator.
+ *
+ * @param strings the strings to concatenate
+ * @param separator the separator between two strings
+ * @return the concatenated strings
+ */
+ public static String toString(String[] strings, String separator) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < strings.length; i++) {
+ if (i > 0) {
+ sb.append(separator);
+ }
+ sb.append(strings[i]);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Put quotes around the given String if necessary.
+ *
+ [if-any logo][end]
+ PGP
link downloads the OpenPGP compatible signature from our main site.
+ The MD5
link downloads the checksum from the main site.
+
+
+
+
+ commons-exec-1.1-bin.tar.gz
+ md5
+ pgp
+
+
+ commons-exec-1.1-bin.zip
+ md5
+ pgp
+
+
+
+
+ commons-exec-1.1-src.tar.gz
+ md5
+ pgp
+
+
+ commons-exec-1.1-src.zip
+ md5
+ pgp
+
+
+
+ Please also remember these points: +
+ For more information on subversion and creating patches see the + Apache Contributors Guide. +
+ ++ You may also find these links useful: +
+ + + + diff --git a/ApacheCommonsExec/src/site/xdoc/mail-lists.xml b/ApacheCommonsExec/src/site/xdoc/mail-lists.xml new file mode 100644 index 0000000..29ecbcd --- /dev/null +++ b/ApacheCommonsExec/src/site/xdoc/mail-lists.xml @@ -0,0 +1,202 @@ + + + ++ Commons Exec shares mailing lists with all the other + Commons Components. + To make it easier for people to only read messages related to components they are interested in, + the convention in Commons is to prefix the subject line of messages with the component's name, + for example: +
+ Questions related to the usage of Commons Exec should be posted to the
+ User List.
+
+ The Developer List
+ is for questions and discussion related to the development of Commons Exec.
+
+ Please do not cross-post; developers are also subscribed to the user list.
+
+ Note: please don't send patches or attachments to any of the mailing lists. + Patches are best handled via the Issue Tracking system. + Otherwise, please upload the file to a public server and include the URL in the mail. +
+
+ Please prefix the subject line of any messages for Commons Exec
+ with [exec] - thanks!
+
+
+
Name | +Subscribe | +Unsubscribe | +Post | +Archive | +Other Archives | +
---|---|---|---|---|---|
+ Commons User List
+ + Questions on using Commons Exec. + + |
+ Subscribe | +Unsubscribe | +Post | +mail-archives.apache.org | +markmail.org + www.mail-archive.com + news.gmane.org + |
+
+ Commons Developer List
+ + Discussion of development of Commons Exec. + + |
+ Subscribe | +Unsubscribe | +Post | +mail-archives.apache.org | +markmail.org + www.mail-archive.com + news.gmane.org + |
+
+ Commons Issues List
+ + Only for e-mails automatically generated by the issue tracking system. + + |
+ Subscribe | +Unsubscribe | +read only | +mail-archives.apache.org | +markmail.org + www.mail-archive.com + |
+
+ Commons Commits List
+ + Only for e-mails automatically generated by the source control sytem. + + |
+ Subscribe | +Unsubscribe | +read only | +mail-archives.apache.org | +markmail.org + www.mail-archive.com + |
+
+ Other mailing lists which you may find useful include: +
+ +Name | +Subscribe | +Unsubscribe | +Post | +Archive | +Other Archives | +
---|---|---|---|---|---|
+ Apache Announce List
+ + General announcements of Apache project releases. + + |
+ Subscribe | +Unsubscribe | +read only | +mail-archives.apache.org | +markmail.org + old.nabble.com + www.mail-archive.com + news.gmane.org + |
+
The following tables contains the test results of running the comons-exec regression tests + on different OS/JVMs
+Operating System | +JVM | +Status | +Reporter | +
---|---|---|---|
Linux (2.6.34.7-56.fc1, x86, 64 bit) | +java version "1.6.0_20" Java(TM) SE Runtime Environment (build + 1.6.0_20-b02) Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01, mixed mode) |
+ Passed | +James Carman | +
Linux (2.6.35-gentoo-r9, x86, 64 bit) | +java version "1.6.0_21" Java(TM) SE Runtime Environment (build + 1.6.0_21-b06) Java HotSpot(TM) 64-Bit Server VM (build 17.0-b16, mixed mode) |
+ Passed | +Jörg Schaible | +
Linux (2.6.35-gentoo-r9, x86, 64 bit) | +java version "1.5.0_22" Java(TM) 2 Runtime Environment, Standard Edition (build + 1.5.0_22-b03) Java HotSpot(TM) 64-Bit Server VM (build 1.5.0_22-b03, mixed + mode) |
+ Passed | +Jörg Schaible | +
Linux (2.6.35-gentoo-r9, x86, 64 bit) | +java version "1.4.2-03" Java(TM) 2 Runtime Environment, Standard Edition (build + Blackdown-1.4.2-03) Java HotSpot(TM) 64-Bit Server VM (build Blackdown-1.4.2-03, + mixed mode) |
+ Failed DefaultExecutorTest#testEnvironmentVariables DefaultExecutorTest#testAddEnvironmentVariables |
+ Jörg Schaible | +
Linux (2.6.35-gentoo-r9, x86, 64 bit) | +java version "1.5.0" Java(TM) 2 Runtime Environment, Standard Edition (build pxa64dev-20100813 (SR12 FP1 + )) IBM J9 VM (build 2.3, J2RE 1.5.0 IBM J9 2.3 Linux + amd64-64 j9vmxa6423-20100808 (JIT enabled) |
+ Passed | +Jörg Schaible | +
Linux (2.6.35-gentoo-r9, x86, 64 bit) | +java version "1.6.0" Java(TM) SE Runtime Environment (build + pxa6460sr8fp1-20100624_01(SR8 FP1)) IBM J9 VM (build 2.4, JRE 1.6.0 IBM J9 2.4 + Linux amd64-64 jvmxa6460sr8ifx-20100609_59383 (JIT enabled, AOT enabled) |
+ Passed | +Jörg Schaible | +
Linux (2.6.35-gentoo-r9, x86, 64 bit) | +java version "1.6.0_18" OpenJDK Runtime Environment (IcedTea6 1.8.1) (Gentoo + build 1.6.0_18-b18) OpenJDK 64-Bit Server VM (build 16.0-b13, mixed mode) |
+ Passed | +Jörg Schaible | +
Linux (2.6.35-gentoo-r9, x86, 64 bit) | +java version "1.5.0" JamVM version 1.5.4 Copyright (C) 2003-2010 Robert + Lougher GPL2 Execution Engine: inline-threaded interpreter Compiled with: gcc + 4.4.3 |
+ Failed DefaultExecutorTest#testExecuteAsyncWithTimelyUserTermination |
+ Jörg Schaible | +
Operating System | +JVM | +Status | +Reporter | +
---|---|---|---|
Mac OS X (Darwin Kernel Version 10.4.0) | +java version "1.6.0_20" Java(TM) SE Runtime Environment (build + 1.6.0_20-b02) Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01, mixed mode) |
+ Passed | +James Carman | +
Mac OS X (Darwin Kernel Version 10.4.0) | +java version "1.5.0_24" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_24-b02-357-10M3065) Java HotSpot(TM) Client VM (build 1.5.0_24-149, mixed mode, sharing) |
+ Passed | +Simon Tripodi | +
Operating System | +JVM | +Status | +Reporter | +
---|---|---|---|
Windows 7 Professional 64 bit | +java version "1.6.0_20" Java(TM) SE Runtime Environment (build 1.6.0_20-b02) Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01, mixed mode) |
+ Passed | +James Carman | +
Windows 7 Enterprise | +java version "1.6.0_21" | +Passed | +Niall Pemberton | +
Windows 7 Enterprise | +java version "1.5.0_22-b03" | +Passed | +Niall Pemberton | +
Windows 7 Enterprise | +java version "1.4.2_19-b04" | +Passed | +Niall Pemberton | +
Windows 7 Enterprise | +java version "1.3.1_18-b01" | +Passed | +Niall Pemberton | +
Windows Vista Business | +java version "1.6.0_21" Java(TM) SE Runtime Environment (build 1.6.0_21-b07) Java HotSpot(TM) 64-Bit Server VM (build 17.0-b17, mixed mode) |
+ Passed | +Gary Gregory | +
Operating System | +JVM | +Status | +Reporter | +
---|---|---|---|
OpenVMS V8.3 (AlphaServer ES40 - 4 CPUs) | +Fast VM (build 1.5.0-4, build J2SDK.v.1.5.0:03/31/2008-15:26, native threads, jit_150) | +Fails tests which try to terminate a subprocess
+
|
+ Sebastian Bazley | +
OpenVMS V8.3-1H1 (Integrity HP rx2600 (1.30GHz/3.0MB) - 2 CPUs) | +Java HotSpot(TM) Server VM (build 1.5.0-2 12/02/2006-21:30 IA64, mixed mode) | +Fails tests which try to terminate a subprocess
+
|
+ Sebastian Bazley | +