add missing files for AMQP-58 (Support for caching of OtpConnections)

This commit is contained in:
markpollack
2010-09-27 15:22:21 -04:00
23 changed files with 1234 additions and 588 deletions

View File

@@ -28,4 +28,8 @@ public class OtpAuthException extends OtpException {
super(cause);
}
public OtpAuthException(String msg, com.ericsson.otp.erlang.OtpAuthException cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed 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.springframework.erlang.connection;
import java.io.IOException;
import com.ericsson.otp.erlang.OtpAuthException;
import com.ericsson.otp.erlang.OtpErlangExit;
import com.ericsson.otp.erlang.OtpErlangList;
import com.ericsson.otp.erlang.OtpErlangObject;
/**
* A simple interface that is used to wrap access to the OtpConnection class in order to support
* caching of OptConnections via method interception.
*
* Note: The surface area of the API is all that is required to implement administrative functionality
* for the Spring AMQP admin project. To access the underlying OtpConnection, use the method getTargetConnection
* on the interface ConnectionProxy that is implemented by DefaultConnection.
*
* @author Mark Pollack
*
*/
public interface Connection {
/**
* Close the connection to the remote node.
*/
void close();
/**
* Send an RPC request to the remote Erlang node. This convenience function
* creates the following message and sends it to 'rex' on the remote node:
*
* <pre>
* { self, { call, Mod, Fun, Args, user } }
* </pre>
*
* <p>
* Note that this method has unpredicatble results if the remote node is not
* an Erlang node.
* </p>
*
* @param mod
* the name of the Erlang module containing the function to
* be called.
* @param fun
* the name of the function to call.
* @param args
* a list of Erlang terms, to be used as arguments to the
* function.
*
* @exception java.io.IOException
* if the connection is not active or a communication
* error occurs.
*/
void sendRPC(final String mod, final String fun, final OtpErlangList args) throws IOException;
/**
* Receive an RPC reply from the remote Erlang node. This convenience
* function receives a message from the remote node, and expects it to have
* the following format:
*
* <pre>
* { rex, Term }
* </pre>
*
* @return the second element of the tuple if the received message is a
* two-tuple, otherwise null. No further error checking is
* performed.
*
* @exception java.io.IOException
* if the connection is not active or a communication
* error occurs.
*
* @exception OtpErlangExit
* if an exit signal is received from a process on the
* peer node.
*
* @exception OtpAuthException
* if the remote node sends a message containing an
* invalid cookie.
*/
OtpErlangObject receiveRPC() throws IOException, OtpErlangExit, OtpAuthException;
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed 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.springframework.erlang.connection;
import com.ericsson.otp.erlang.OtpConnection;
/**
* Subinterface of {@link Connection} to be implemented by Connection proxies.
* Allows access to the underlying target Connection
*
* @author Mark Pollack
*
*/
public interface ConnectionProxy extends Connection {
OtpConnection getTargetConnection();
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed 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.springframework.erlang.connection;
import java.io.IOException;
import com.ericsson.otp.erlang.OtpAuthException;
import com.ericsson.otp.erlang.OtpConnection;
import com.ericsson.otp.erlang.OtpErlangExit;
import com.ericsson.otp.erlang.OtpErlangList;
import com.ericsson.otp.erlang.OtpErlangObject;
/**
* Basic implementation of {@link ConnectionProxy} that delegates to an underlying OtpConnection.
* @author Mark Pollack
*
*/
public class DefaultConnection implements ConnectionProxy {
private OtpConnection otpConnection;
public DefaultConnection(OtpConnection otpConnection) {
this.otpConnection = otpConnection;
}
public void close() {
otpConnection.close();
}
public void sendRPC(String mod, String fun, OtpErlangList args)
throws IOException {
otpConnection.sendRPC(mod, fun, args);
}
public OtpErlangObject receiveRPC() throws IOException, OtpErlangExit,
OtpAuthException {
return otpConnection.receiveRPC();
}
public OtpConnection getTargetConnection() {
return this.otpConnection;
}
}

View File

@@ -87,9 +87,8 @@ public class SimpleConnectionFactory implements ConnectionFactory, InitializingB
public SimpleConnectionFactory(String selfNodeName, String cookie, String peerNodeName) {
this.selfNodeName = selfNodeName;
this(selfNodeName, peerNodeName);
this.cookie = cookie;
this.peerNodeName = peerNodeName;
}
public SimpleConnectionFactory(String selfNodeName, String peerNodeName) {

View File

@@ -0,0 +1,253 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed 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.springframework.erlang.connection;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.erlang.OtpIOException;
import org.springframework.util.Assert;
import com.ericsson.otp.erlang.OtpAuthException;
import com.ericsson.otp.erlang.OtpPeer;
import com.ericsson.otp.erlang.OtpSelf;
/**
* A {@link ConnectionFactory} implementation that returns the same Connections from all
* {@link #createConnection()} calls, and ignores calls to {@link Connection#close()}.
*
* Provides a more traditional API to creating a connection to a remote erlang
* node than the JInterface API.
*
* <p>
* The following is taken from the JInterface javadocs that describe the valid
* node names that can be used. These naming constraints apply to the string
* values you pass into the node names in SimpleConnectionFactory's constructor.
* <p>
* About nodenames: Erlang nodenames consist of two components, an alivename and
* a hostname separated by '@'. Additionally, there are two nodename formats:
* short and long. Short names are of the form "alive@hostname", while long
* names are of the form "alive@host.fully.qualified.domainname". Erlang has
* special requirements regarding the use of the short and long formats, in
* particular they cannot be mixed freely in a network of communicating nodes,
* however Jinterface makes no distinction. See the Erlang documentation for
* more information about nodenames.
* </p>
*
* <p>
* The constructors for the AbstractNode classes will create names exactly as
* you provide them as long as the name contains '@'. If the string you provide
* contains no '@', it will be treated as an alivename and the name of the local
* host will be appended, resulting in a shortname. Nodenames longer than 255
* characters will be truncated without warning.
* </p>
*
* <p>
* Upon initialization, this class attempts to read the file .erlang.cookie in
* the user's home directory, and uses the trimmed first line of the file as the
* default cookie by those constructors lacking a cookie argument. If for any
* reason the file cannot be found or read, the default cookie will be set to
* the empty string (""). The location of a user's home directory is determined
* using the system property "user.home", which may not be automatically set on
* all platforms.
* </p>
*
* @author Mark Pollack
*/
public class SingleConnectionFactory implements ConnectionFactory,
InitializingBean {
protected final Log logger = LogFactory.getLog(getClass());
private String selfNodeName;
private String cookie;
private String peerNodeName;
private OtpSelf otpSelf;
private OtpPeer otpPeer;
/** Raw JInterface Connection */
private Connection targetConnection;
/** Proxy Connection */
private Connection connection;
/** Synchronization monitor for the shared Connection */
private final Object connectionMonitor = new Object();
public SingleConnectionFactory(String selfNodeName, String cookie,
String peerNodeName) {
this.selfNodeName = selfNodeName;
this.cookie = cookie;
this.peerNodeName = peerNodeName;
}
public SingleConnectionFactory(String selfNodeName, String peerNodeName) {
this.selfNodeName = selfNodeName;
this.peerNodeName = peerNodeName;
}
public Connection createConnection() throws UnknownHostException,
OtpAuthException {
synchronized (this.connectionMonitor) {
if (this.connection == null) {
try {
initConnection();
} catch (IOException e) {
throw new OtpIOException("failed to connect from '"
+ this.selfNodeName + "' to peer node '"
+ this.peerNodeName + "'", e);
}
}
return this.connection;
}
}
public void initConnection() throws IOException, OtpAuthException {
synchronized (this.connectionMonitor) {
if (this.targetConnection != null) {
closeConnection(this.targetConnection);
}
this.targetConnection = doCreateConnection();
prepareConnection(this.targetConnection);
if (logger.isInfoEnabled()) {
logger.info("Established shared Rabbit Connection: "
+ this.targetConnection);
}
this.connection = getSharedConnectionProxy(this.targetConnection);
}
}
/**
* Close the given Connection.
*
* @param connection
* the Connection to close
*/
protected void closeConnection(Connection connection) {
if (logger.isDebugEnabled()) {
logger.debug("Closing shared Rabbit Connection: "
+ this.targetConnection);
}
try {
// TODO there are other close overloads close(int closeCode,
// java.lang.String closeMessage, int timeout)
connection.close();
} catch (Throwable ex) {
logger.debug("Could not close shared Rabbit Connection", ex);
}
}
/**
* Create a JInterface Connection via this class's ConnectionFactory.
*
* @return the new Otp Connection
* @throws OtpAuthException
*/
protected Connection doCreateConnection() throws IOException,
OtpAuthException {
return new DefaultConnection(otpSelf.connect(otpPeer));
}
protected void prepareConnection(Connection con) throws IOException {
}
/**
* Wrap the given OtpConnection with a proxy that delegates every method
* call to it but suppresses close calls. This is useful for allowing
* application code to handle a special framework Connection just like an
* ordinary Connection from a Rabbit ConnectionFactory.
*
* @param target
* the original Connection to wrap
* @return the wrapped Connection
*/
protected Connection getSharedConnectionProxy(Connection target) {
List<Class<?>> classes = new ArrayList<Class<?>>(1);
classes.add(Connection.class);
return (Connection) Proxy.newProxyInstance(
Connection.class.getClassLoader(),
classes.toArray(new Class[classes.size()]),
new SharedConnectionInvocationHandler(target));
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() {
Assert.isTrue(this.selfNodeName != null || this.peerNodeName != null,
"'selfNodeName' or 'peerNodeName' is required");
try {
if (this.cookie == null) {
this.otpSelf = new OtpSelf(this.selfNodeName);
} else {
this.otpSelf = new OtpSelf(this.selfNodeName, this.cookie);
}
} catch (IOException e) {
throw new OtpIOException(e);
}
this.otpPeer = new OtpPeer(this.peerNodeName);
}
private class SharedConnectionInvocationHandler implements
InvocationHandler {
private final Connection target;
public SharedConnectionInvocationHandler(Connection target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (method.getName().equals("equals")) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
} else if (method.getName().equals("hashCode")) {
// Use hashCode of Connection proxy.
return System.identityHashCode(proxy);
} else if (method.getName().equals("toString")) {
return "Shared Otp Connection: " + this.target;
} else if (method.getName().equals("close")) {
// Handle close method: don't pass the call on.
return null;
}
try {
return method.invoke(this.target, args);
} catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
}

View File

@@ -28,7 +28,6 @@ import org.springframework.erlang.connection.Connection;
import org.springframework.erlang.connection.ConnectionFactory;
import com.ericsson.otp.erlang.OtpAuthException;
import com.ericsson.otp.erlang.OtpConnection;
/**
* @author Mark Pollack

View File

@@ -23,6 +23,7 @@ import org.springframework.erlang.OtpIOException;
import org.springframework.erlang.UncategorizedOtpException;
import org.springframework.util.Assert;
import com.ericsson.otp.erlang.OtpAuthException;
import com.ericsson.otp.erlang.OtpConnection;
/**
@@ -47,6 +48,9 @@ public class ErlangUtils {
if (ex instanceof IOException) {
return new OtpIOException((IOException) ex);
}
if (ex instanceof OtpAuthException) {
return new org.springframework.erlang.OtpAuthException((OtpAuthException) ex);
}
//fallback
return new UncategorizedOtpException(ex);
}

View File

@@ -16,389 +16,404 @@
package org.springframework.util.exec;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.StringReader;
import java.io.ByteArrayOutputStream;
import java.util.*;
import java.util.StringTokenizer;
import java.util.Vector;
/* Derived from ant exec task. All the 'backward compat with jdk1.1, 1.2'
removed. Since jdk1.3 supports working dir, no need for scripts.
removed. Since jdk1.3 supports working dir, no need for scripts.
All ant-specific code has been removed as well, this is a completely
independent component.
Costin
*/
All ant-specific code has been removed as well, this is a completely
independent component.
Costin
*/
/**
* Runs an external program.
*
*
* @author thomas.haas@softwired-inc.com
*/
public class Execute {
/** Invalid exit code. **/
public final static int INVALID = Integer.MAX_VALUE;
/** Invalid exit code. **/
public final static int INVALID = Integer.MAX_VALUE;
private String[] cmdl = null;
private String[] env = null;
private int exitValue = INVALID;
private ExecuteStreamHandler streamHandler;
private ExecuteWatchdog watchdog;
private File workingDirectory = null;
private boolean newEnvironment = false;
private String[] cmdl = null;
private String[] env = null;
private int exitValue = INVALID;
private ExecuteStreamHandler streamHandler;
private ExecuteWatchdog watchdog;
private File workingDirectory = null;
private boolean newEnvironment = false;
private Process process;
private static Vector<String> procEnvironment = null;
private static Vector<String> procEnvironment = null;
/**
* Find the list of environment variables for this process.
*/
public static synchronized Vector<String> getProcEnvironment() {
if (procEnvironment != null) return procEnvironment;
/**
* Find the list of environment variables for this process.
*/
public static synchronized Vector<String> getProcEnvironment() {
if (procEnvironment != null)
return procEnvironment;
procEnvironment = new Vector<String>();
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Execute exe = new Execute(new PumpStreamHandler(out));
exe.setCommandline(getProcEnvCommand());
// Make sure we do not recurse forever
exe.setNewenvironment(true);
int retval = exe.execute();
if ( retval != 0 ) {
// Just try to use what we got
}
procEnvironment = new Vector<String>();
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Execute exe = new Execute(new PumpStreamHandler(out));
exe.setCommandline(getProcEnvCommand());
// Make sure we do not recurse forever
exe.setNewenvironment(true);
int retval = exe.execute();
if (retval != 0) {
// Just try to use what we got
}
BufferedReader in =
new BufferedReader(new StringReader(out.toString()));
String var = null;
String line, lineSep = System.getProperty("line.separator");
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 = lineSep + line;
}
else {
var += lineSep + line;
}
}
else {
// New env var...append the previous one if we have it.
if (var != null) {
procEnvironment.addElement(var);
}
var = line;
}
}
// Since we "look ahead" before adding, there's one last env var.
procEnvironment.addElement(var);
}
catch (Exception exc) {
exc.printStackTrace();
// Just try to see how much we got
}
return procEnvironment;
}
BufferedReader in = new BufferedReader(new StringReader(
out.toString()));
String var = null;
String line, lineSep = System.getProperty("line.separator");
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 = lineSep + line;
} else {
var += lineSep + line;
}
} else {
// New env var...append the previous one if we have it.
if (var != null) {
procEnvironment.addElement(var);
}
var = line;
}
}
// Since we "look ahead" before adding, there's one last env var.
procEnvironment.addElement(var);
} catch (Exception exc) {
exc.printStackTrace();
// Just try to see how much we got
}
return procEnvironment;
}
private static String[] getProcEnvCommand() {
if ( Os.isFamily("os/2") ) {
// OS/2 - use same mechanism as Windows 2000
// Not sure
String[] cmd = {"cmd", "/c", "set" };
return cmd;
}
else if ( Os.isFamily("windows") ) {
String[] cmd = {"cmd", "/c", "set" };
return cmd;
}
else if ( Os.isFamily("unix") ) {
// Generic UNIX
// Alternatively one could use: /bin/sh -c env
String[] cmd = {"/usr/bin/env"};
return cmd;
}
else if ( Os.isFamily("netware") ) {
String[] cmd = {"env"};
return cmd;
}
else {
// MAC OS 9 and previous
// TODO: I have no idea how to get it, someone must fix it
String[] cmd = null;
return cmd;
}
}
private static String[] getProcEnvCommand() {
if (Os.isFamily("os/2")) {
// OS/2 - use same mechanism as Windows 2000
// Not sure
String[] cmd = { "cmd", "/c", "set" };
return cmd;
} else if (Os.isFamily("windows")) {
String[] cmd = { "cmd", "/c", "set" };
return cmd;
} else if (Os.isFamily("unix")) {
// Generic UNIX
// Alternatively one could use: /bin/sh -c env
String[] cmd = { "/usr/bin/env" };
return cmd;
} else if (Os.isFamily("netware")) {
String[] cmd = { "env" };
return cmd;
} else {
// MAC OS 9 and previous
// TODO: I have no idea how to get it, someone must fix it
String[] cmd = null;
return cmd;
}
}
/**
* Creates a new execute object using <code>PumpStreamHandler</code> for
* stream handling.
*/
public Execute() {
this(new PumpStreamHandler(), null);
}
/**
* Creates a new execute object using <code>PumpStreamHandler</code> for
* stream handling.
*/
public Execute() {
this(new PumpStreamHandler(), null);
}
/**
* Creates a new execute object.
*
* @param streamHandler
* the stream handler used to handle the input and output streams
* of the subprocess.
*/
public Execute(ExecuteStreamHandler streamHandler) {
this(streamHandler, null);
}
/**
* Creates a new execute object.
*
* @param streamHandler the stream handler used to handle the input and
* output streams of the subprocess.
*/
public Execute(ExecuteStreamHandler streamHandler) {
this(streamHandler, null);
}
/**
* Creates a new execute object.
*
* @param streamHandler
* the stream handler used to handle the input and output streams
* of the subprocess.
* @param watchdog
* a watchdog for the subprocess or <code>null</code> to to
* disable a timeout for the subprocess.
*/
public Execute(ExecuteStreamHandler streamHandler, ExecuteWatchdog watchdog) {
this.streamHandler = streamHandler;
this.watchdog = watchdog;
}
/**
* Creates a new execute object.
*
* @param streamHandler the stream handler used to handle the input and
* output streams of the subprocess.
* @param watchdog a watchdog for the subprocess or <code>null</code> to
* to disable a timeout for the subprocess.
*/
public Execute(ExecuteStreamHandler streamHandler, ExecuteWatchdog watchdog) {
this.streamHandler = streamHandler;
this.watchdog = watchdog;
}
/**
* Returns the commandline used to create a subprocess.
*
* @return the commandline used to create a subprocess
*/
public String[] getCommandline() {
return cmdl;
}
public String getCommandLineString() {
return array2string(getCommandline());
}
/**
* Returns the commandline used to create a subprocess.
*
* @return the commandline used to create a subprocess
*/
public String[] getCommandline() {
return cmdl;
}
/**
* Sets the commandline of the subprocess to launch.
*
* @param commandline
* the commandline of the subprocess to launch
*/
public void setCommandline(String[] commandline) {
cmdl = commandline;
}
public String getCommandLineString() {
return array2string(getCommandline());
}
/**
* Set whether to propagate the default environment or not.
*
* @param newenv
* whether to propagate the process environment.
*/
public void setNewenvironment(boolean newenv) {
newEnvironment = newenv;
}
/**
* Sets the commandline of the subprocess to launch.
*
* @param commandline the commandline of the subprocess to launch
*/
public void setCommandline(String[] commandline) {
cmdl = commandline;
}
/**
* Returns the environment used to create a subprocess.
*
* @return the environment used to create a subprocess
*/
public String[] getEnvironment() {
if (env == null || newEnvironment)
return env;
return patchEnvironment();
}
/**
* Set whether to propagate the default environment or not.
*
* @param newenv whether to propagate the process environment.
*/
public void setNewenvironment(boolean newenv) {
newEnvironment = newenv;
}
/**
* Sets the environment variables for the subprocess to launch.
*
* @param env
* array of Strings, each element of which has an environment
* variable settings in format <em>key=value</em>
*/
public void setEnvironment(String[] env) {
this.env = env;
}
/**
* Returns the environment used to create a subprocess.
*
* @return the environment used to create a subprocess
*/
public String[] getEnvironment() {
if (env == null || newEnvironment) return env;
return patchEnvironment();
}
/**
* Sets the working directory of the process to execute.
*
* <p>
* This is emulated using the antRun scripts unless the OS is Windows NT in
* which case a cmd.exe is spawned, or MRJ and setting user.dir works, or
* JDK 1.3 and there is official support in java.lang.Runtime.
*
* @param wd
* the working directory of the process.
*/
public void setWorkingDirectory(File wd) {
workingDirectory = wd;
}
/**
* Runs a process defined by the command line and returns its exit status.
*
* @return the exit status of the subprocess or <code>INVALID</code>
* @throws Exception
* if launching of the subprocess failed
*/
public int execute() throws Exception {
process = Runtime.getRuntime().exec(getCommandline(), getEnvironment(),
workingDirectory);
try {
streamHandler.setProcessInputStream(process.getOutputStream());
streamHandler.setProcessOutputStream(process.getInputStream());
streamHandler.setProcessErrorStream(process.getErrorStream());
} catch (IOException e) {
process.destroy();
throw e;
}
streamHandler.start();
/**
* Sets the environment variables for the subprocess to launch.
*
* @param env array of Strings, each element of which has
* an environment variable settings in format <em>key=value</em>
*/
public void setEnvironment(String[] env) {
this.env = env;
}
if (watchdog != null)
watchdog.start(process, Thread.currentThread());
/**
* Sets the working directory of the process to execute.
*
* <p>This is emulated using the antRun scripts unless the OS is
* Windows NT in which case a cmd.exe is spawned,
* or MRJ and setting user.dir works, or JDK 1.3 and there is
* official support in java.lang.Runtime.
*
* @param wd the working directory of the process.
*/
public void setWorkingDirectory(File wd) {
workingDirectory = wd;
}
if (log.isTraceEnabled())
log.trace("Waiting process ");
waitFor(process);
process = null;
// costin
boolean wait=true;
public void setWait( boolean b ) {
wait=b;
}
if (log.isTraceEnabled())
log.trace("End waiting, stop threads ");
if (watchdog != null)
watchdog.stop();
if (log.isTraceEnabled())
log.trace("Watchdog stopped ");
streamHandler.stop();
if (log.isTraceEnabled())
log.trace("Stream handler stopped ");
if (watchdog != null) {
Exception ex = watchdog.getException();
if (ex != null)
throw ex;
}
int exit = getExitValue();
Process process;
/**
* Runs a process defined by the command line and returns its exit status.
*
* @return the exit status of the subprocess or <code>INVALID</code>
* @throws Exception if launching of the subprocess failed
*/
public int execute() throws Exception {
process =
Runtime.getRuntime().exec( getCommandline(), getEnvironment(),
workingDirectory );
try {
streamHandler.setProcessInputStream(process.getOutputStream());
streamHandler.setProcessOutputStream(process.getInputStream());
streamHandler.setProcessErrorStream(process.getErrorStream());
} catch (IOException e) {
process.destroy();
throw e;
}
streamHandler.start();
if (log.isDebugEnabled()) {
log.debug("Done exit=" + exit + " " + getCommandLineString());
}
return exit;
}
if (watchdog != null) watchdog.start(process,
Thread.currentThread());
public void kill() {
if (process != null) {
process.destroy();
}
}
if( log.isTraceEnabled() ) log.trace("Waiting process ");
waitFor(process);
private String array2string(String sa[]) {
if (sa == null)
return "null";
StringBuffer sb = new StringBuffer();
for (int i = 0; i < sa.length; i++)
sb.append(sa[i]).append(" ");
return sb.toString();
}
if( log.isTraceEnabled() ) log.trace("End waiting, stop threads ");
if (watchdog != null) watchdog.stop();
if( log.isTraceEnabled() ) log.trace("Watchdog stopped ");
streamHandler.stop();
if( log.isTraceEnabled() ) log.trace("Stream handler stopped ");
if (watchdog != null) {
Exception ex=watchdog.getException();
if( ex!=null )
throw ex;
}
int exit= getExitValue();
protected void waitFor(Process process) {
try {
process.waitFor();
setExitValue(process.exitValue());
} catch (InterruptedException e) {
log.info("waitFor() interrupted ");
}
}
if( log.isDebugEnabled() ) {
log.debug("Done exit=" + exit + " " + getCommandLineString());
}
return exit;
}
protected void setExitValue(int value) {
exitValue = value;
}
private String array2string( String sa[]) {
if( sa==null ) return "null";
StringBuffer sb=new StringBuffer();
for( int i=0; i<sa.length; i++ ) sb.append(sa[i]).append(" ");
return sb.toString();
}
/**
* query the exit value of the process.
*
* @return the exit value, 1 if the process was killed, or Project.INVALID
* if no exit value has been received
*/
public int getExitValue() {
return exitValue;
}
protected void waitFor(Process process) {
try {
process.waitFor();
setExitValue(process.exitValue());
} catch (InterruptedException e) {
log.info("waitFor() interrupted ");
}
}
protected void setExitValue(int value) {
exitValue = value;
}
/**
* query the exit value of the process.
* @return the exit value, 1 if the process was killed,
* or Project.INVALID if no exit value has been received
*/
public int getExitValue() {
return exitValue;
}
/**
* Patch the current environment with the new values from the user.
* @return the patched environment
*/
private String[] patchEnvironment() {
@SuppressWarnings("unchecked")
/**
* Patch the current environment with the new values from the user.
*
* @return the patched environment
*/
private String[] patchEnvironment() {
@SuppressWarnings("unchecked")
Vector<String> osEnv = (Vector<String>) getProcEnvironment().clone();
for (int i = 0; i < env.length; i++) {
int pos = env[i].indexOf('=');
// Get key including "="
String key = env[i].substring(0, pos+1);
int size = osEnv.size();
for (int j = 0; j < size; j++) {
if ((osEnv.elementAt(j)).startsWith(key)) {
osEnv.removeElementAt(j);
break;
}
}
osEnv.addElement(env[i]);
}
String[] result = new String[osEnv.size()];
osEnv.copyInto(result);
return result;
}
for (int i = 0; i < env.length; i++) {
int pos = env[i].indexOf('=');
// Get key including "="
String key = env[i].substring(0, pos + 1);
int size = osEnv.size();
for (int j = 0; j < size; j++) {
if ((osEnv.elementAt(j)).startsWith(key)) {
osEnv.removeElementAt(j);
break;
}
}
osEnv.addElement(env[i]);
}
String[] result = new String[osEnv.size()];
osEnv.copyInto(result);
return result;
}
public static int execute(Vector<String> envVars, String cmd, File baseDir ) {
Vector<String> v = new Vector<String>();
StringTokenizer st=new StringTokenizer( cmd, " " );
while( st.hasMoreTokens() ) {
v.addElement( st.nextToken() );
}
public static int execute(Vector<String> envVars, String cmd, File baseDir) {
Vector<String> v = new Vector<String>();
StringTokenizer st = new StringTokenizer(cmd, " ");
while (st.hasMoreTokens()) {
v.addElement(st.nextToken());
}
return execute( envVars, v, baseDir );
}
public static int execute(Vector<String> envVars, Vector<String> cmd, File baseDir) {
return execute( envVars, cmd, baseDir, 10000 /* default time to wait */);
}
return execute(envVars, v, baseDir);
}
/** Wrapper for common execution patterns
* @param envVars Environment variables to execute with (optional)
* @param cmd a vector of the commands to execute
* @param baseDir the base directory to run from (optional)
* @param timeToWait milliseconds to wait for completion
*/
public static int execute(Vector<String> envVars, Vector<String> cmd, File baseDir, int timeToWait) {
try {
// We can collect the out or provide in if needed
ExecuteWatchdog watchdog=new ExecuteWatchdog( timeToWait );
watchdog.setDontkill( true );
PumpStreamHandler out=new PumpStreamHandler();
Execute exec=new Execute(out,watchdog);
public static int execute(Vector<String> envVars, Vector<String> cmd,
File baseDir) {
return execute(envVars, cmd, baseDir, 10000 /* default time to wait */);
}
String cmdA[]=new String[ cmd.size() ];
cmd.toArray( cmdA );
if( log.isDebugEnabled() ) {
StringBuffer sb=new StringBuffer();
for(int i=0; i<cmdA.length; i++ ) {
sb.append(cmdA[i] + " " );
}
log.debug( "Exec: " + sb.toString());
}
exec.setCommandline( cmdA );
/**
* Wrapper for common execution patterns
*
* @param envVars
* Environment variables to execute with (optional)
* @param cmd
* a vector of the commands to execute
* @param baseDir
* the base directory to run from (optional)
* @param timeToWait
* milliseconds to wait for completion
*/
public static int execute(Vector<String> envVars, Vector<String> cmd,
File baseDir, int timeToWait) {
try {
// We can collect the out or provide in if needed
ExecuteWatchdog watchdog = new ExecuteWatchdog(timeToWait);
watchdog.setDontkill(true);
PumpStreamHandler out = new PumpStreamHandler();
Execute exec = new Execute(out, watchdog);
if( envVars!=null ) {
String env[]=new String[envVars.size()];
envVars.toArray( env );
exec.setEnvironment( env );
}
String cmdA[] = new String[cmd.size()];
cmd.toArray(cmdA);
if (log.isDebugEnabled()) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < cmdA.length; i++) {
sb.append(cmdA[i] + " ");
}
log.debug("Exec: " + sb.toString());
}
exec.setCommandline(cmdA);
exec.setNewenvironment( false );
if( baseDir!=null)
exec.setWorkingDirectory( baseDir );
if (envVars != null) {
String env[] = new String[envVars.size()];
envVars.toArray(env);
exec.setEnvironment(env);
}
exec.execute();
int status=exec.getExitValue();
log.debug("Exit value " + status );
return status;
} catch( Exception ex ) {
// ex.printStackTrace();
System.err.println("An error has occurred in Execute.");
return -1;
}
}
exec.setNewenvironment(false);
if (baseDir != null)
exec.setWorkingDirectory(baseDir);
exec.execute();
int status = exec.getExitValue();
log.debug("Exit value " + status);
return status;
} catch (Exception ex) {
// ex.printStackTrace();
System.err.println("An error has occurred in Execute.");
return -1;
}
}
private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
.getLog(Execute.class);
private static org.apache.commons.logging.Log log=
org.apache.commons.logging.LogFactory.getLog( Execute.class );
}