add missing files for AMQP-58 (Support for caching of OtpConnections)
This commit is contained in:
@@ -28,4 +28,8 @@ public class OtpAuthException extends OtpException {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public OtpAuthException(String msg, com.ericsson.otp.erlang.OtpAuthException cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user