AMQP-26 renamed 'spring-otp' to 'spring-erlang'
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.otp.erlang;
|
||||
|
||||
import com.ericsson.otp.erlang.OtpErlangTuple;
|
||||
|
||||
/**
|
||||
* Exception thrown when an 'badrpc' is received from an Erlang RPC call.
|
||||
* @author Mark Pollack
|
||||
*
|
||||
*/
|
||||
public class ErlangBadRpcException extends OtpException {
|
||||
|
||||
private OtpErlangTuple reasonTuple;
|
||||
|
||||
public ErlangBadRpcException(String reason) {
|
||||
super(reason);
|
||||
}
|
||||
|
||||
public ErlangBadRpcException(OtpErlangTuple tuple) {
|
||||
super(tuple.toString());
|
||||
this.reasonTuple = tuple;
|
||||
}
|
||||
|
||||
public OtpErlangTuple getReasonTuple() {
|
||||
return reasonTuple;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.otp.erlang;
|
||||
|
||||
import com.ericsson.otp.erlang.OtpErlangTuple;
|
||||
|
||||
/**
|
||||
* Exception thrown when an 'error' is received from an Erlang RPC call
|
||||
* @author Mark Pollack
|
||||
*
|
||||
*/
|
||||
public class ErlangErrorRpcException extends OtpException {
|
||||
|
||||
private OtpErlangTuple reasonTuple;
|
||||
|
||||
public ErlangErrorRpcException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ErlangErrorRpcException(OtpErlangTuple tuple) {
|
||||
super(tuple.toString());
|
||||
this.reasonTuple = tuple;
|
||||
}
|
||||
|
||||
public OtpErlangTuple getReasonTuple() {
|
||||
return reasonTuple;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.otp.erlang;
|
||||
|
||||
/**
|
||||
* Runtime exception mirroring the OTP OtpAuthException.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public class OtpAuthException extends OtpException {
|
||||
|
||||
public OtpAuthException(com.ericsson.otp.erlang.OtpAuthException cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.otp.erlang;
|
||||
|
||||
/**
|
||||
* Base RuntimeException for errors that occur when executing OTP operations.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public class OtpException extends RuntimeException {
|
||||
|
||||
public OtpException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public OtpException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public OtpException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.otp.erlang;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* RuntimeException wrapper for an {@link IOException} which
|
||||
* can be commonly thrown from OTP operations.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class OtpIOException extends OtpException {
|
||||
|
||||
public OtpIOException(IOException cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public OtpIOException(String message, IOException cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.otp.erlang;
|
||||
|
||||
/**
|
||||
* A "catch-all" exception type within the OtpException hierarchy
|
||||
* when no more specific cause is known.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public class UncategorizedOtpException extends OtpException {
|
||||
|
||||
public UncategorizedOtpException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public UncategorizedOtpException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.otp.erlang.connection;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import com.ericsson.otp.erlang.OtpAuthException;
|
||||
import com.ericsson.otp.erlang.OtpConnection;
|
||||
|
||||
/**
|
||||
* An interface based ConnectionFactory for creating {@link OtpConnection}s.
|
||||
*
|
||||
* <p>NOTE: The Rabbit API contains a ConnectionFactory class (same name).
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public interface ConnectionFactory {
|
||||
|
||||
OtpConnection createConnection() throws UnknownHostException, OtpAuthException, IOException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.otp.erlang.connection;
|
||||
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import com.ericsson.otp.erlang.OtpConnection;
|
||||
|
||||
/**
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public class ConnectionFactoryUtils {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ConnectionFactoryUtils.class);
|
||||
|
||||
|
||||
/**
|
||||
* Release the given Connection by closing it.
|
||||
*/
|
||||
public static void releaseConnection(OtpConnection con, ConnectionFactory cf) {
|
||||
if (con == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
con.close();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.debug("Could not close Otp Connection", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.otp.erlang.connection;
|
||||
|
||||
import com.ericsson.otp.erlang.OtpPeer;
|
||||
import com.ericsson.otp.erlang.OtpSelf;
|
||||
|
||||
/**
|
||||
* Encapsulate properties to create a OtpConnection
|
||||
* @author Mark Pollack
|
||||
*
|
||||
*/
|
||||
public class ConnectionParameters {
|
||||
|
||||
private OtpSelf otpSelf;
|
||||
|
||||
private OtpPeer otpPeer;
|
||||
|
||||
public ConnectionParameters(OtpSelf otpSelf, OtpPeer otpPeer) {
|
||||
//TODO assert not null...
|
||||
this.otpSelf = otpSelf;
|
||||
this.otpPeer = otpPeer;
|
||||
}
|
||||
|
||||
public OtpSelf getOtpSelf() {
|
||||
return otpSelf;
|
||||
}
|
||||
|
||||
public OtpPeer getOtpPeer() {
|
||||
return otpPeer;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.otp.erlang.connection;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.otp.erlang.OtpIOException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.ericsson.otp.erlang.OtpAuthException;
|
||||
import com.ericsson.otp.erlang.OtpConnection;
|
||||
import com.ericsson.otp.erlang.OtpPeer;
|
||||
import com.ericsson.otp.erlang.OtpSelf;
|
||||
|
||||
/**
|
||||
* @author Mark Pollack
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class SimpleConnectionFactory implements ConnectionFactory, InitializingBean {
|
||||
|
||||
private String selfNodeName;
|
||||
|
||||
private String cookie;
|
||||
|
||||
private String peerNodeName;
|
||||
|
||||
private OtpSelf otpSelf;
|
||||
|
||||
private OtpPeer otpPeer;
|
||||
|
||||
|
||||
public SimpleConnectionFactory(String selfNodeName, String cookie, String peerNodeName) {
|
||||
this.selfNodeName = selfNodeName;
|
||||
this.cookie = cookie;
|
||||
this.peerNodeName = peerNodeName;
|
||||
}
|
||||
|
||||
public SimpleConnectionFactory(String selfNodeName, String peerNodeName) {
|
||||
this.selfNodeName = selfNodeName;
|
||||
this.peerNodeName = peerNodeName;
|
||||
}
|
||||
|
||||
|
||||
public OtpConnection createConnection() throws UnknownHostException, OtpAuthException, IOException {
|
||||
try {
|
||||
return otpSelf.connect(otpPeer);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new OtpIOException("failed to connect from '" + this.selfNodeName
|
||||
+ "' to peer node '" + this.peerNodeName + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.otp.erlang.core;
|
||||
|
||||
/**
|
||||
* Describes an Erlang application. Only three fields are supported as that is the level
|
||||
* of information that rabbitmq returns when performing a status request.
|
||||
*
|
||||
* See http://www.erlang.org/doc/man/app.html for full details
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*
|
||||
*/
|
||||
public class Application {
|
||||
|
||||
private String description;
|
||||
|
||||
private String id;
|
||||
|
||||
private String version;
|
||||
|
||||
public Application(String description, String id, String version) {
|
||||
super();
|
||||
this.description = description;
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Application [description=" + description + ", id=" + id
|
||||
+ ", version=" + version + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.otp.erlang.core;
|
||||
|
||||
import com.ericsson.otp.erlang.OtpConnection;
|
||||
|
||||
/**
|
||||
* Basic callback for use in ErlangTemplate
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public interface ConnectionCallback<T> {
|
||||
|
||||
/**
|
||||
* Execute any number of operations against the supplied OTP connection,
|
||||
* possibly returning a result.
|
||||
*/
|
||||
T doInConnection(OtpConnection connection) throws Exception; //Not sure everything it throws
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.otp.erlang.core;
|
||||
|
||||
import org.springframework.otp.erlang.OtpException;
|
||||
import org.springframework.otp.erlang.support.converter.ErlangConverter;
|
||||
|
||||
import com.ericsson.otp.erlang.OtpErlangList;
|
||||
import com.ericsson.otp.erlang.OtpErlangObject;
|
||||
|
||||
/**
|
||||
* Operations to perform against OTP/Erlang
|
||||
* @author Mark Pollack
|
||||
*
|
||||
*/
|
||||
public interface ErlangOperations {
|
||||
|
||||
<T> T execute(ConnectionCallback<T> action) throws OtpException;
|
||||
|
||||
OtpErlangObject executeErlangRpc(String module, String function, OtpErlangList args) throws OtpException;
|
||||
|
||||
|
||||
OtpErlangObject executeErlangRpc(String module, String function, OtpErlangObject... args) throws OtpException;
|
||||
|
||||
|
||||
OtpErlangObject executeRpc(String module, String function, Object... args) throws OtpException;
|
||||
|
||||
Object executeAndConvertRpc(String module, String function, ErlangConverter converterToUse, Object... args) throws OtpException;
|
||||
|
||||
Object executeAndConvertRpc(String module, String function, Object... args) throws OtpException;
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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.otp.erlang.core;
|
||||
|
||||
import org.springframework.otp.erlang.ErlangErrorRpcException;
|
||||
import org.springframework.otp.erlang.ErlangBadRpcException;
|
||||
import org.springframework.otp.erlang.OtpException;
|
||||
import org.springframework.otp.erlang.connection.ConnectionFactory;
|
||||
import org.springframework.otp.erlang.support.ErlangAccessor;
|
||||
import org.springframework.otp.erlang.support.ErlangUtils;
|
||||
import org.springframework.otp.erlang.support.converter.ErlangConverter;
|
||||
import org.springframework.otp.erlang.support.converter.SimpleErlangConverter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.ericsson.otp.erlang.*;
|
||||
|
||||
|
||||
/**
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public class ErlangTemplate extends ErlangAccessor implements ErlangOperations {
|
||||
|
||||
|
||||
private volatile ErlangConverter erlangConverter = new SimpleErlangConverter();
|
||||
|
||||
public ErlangTemplate(ConnectionFactory connectionFactory) {
|
||||
setConnectionFactory(connectionFactory);
|
||||
afterPropertiesSet();
|
||||
}
|
||||
|
||||
public OtpErlangObject executeErlangRpc(final String module, final String function, final OtpErlangList args) {
|
||||
return execute(new ConnectionCallback<OtpErlangObject>() {
|
||||
public OtpErlangObject doInConnection(OtpConnection connection) throws Exception {
|
||||
logger.debug("Sending RPC for module [" + module + "] function [" + function + "] args [" + args);
|
||||
connection.sendRPC(module, function, args);
|
||||
//TODO consider dedicated response object.
|
||||
OtpErlangObject response = connection.receiveRPC();
|
||||
logger.debug("Response received = " + response.toString());
|
||||
handleResponseError(module, function, response);
|
||||
return response;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void handleResponseError(String module, String function, OtpErlangObject result) {
|
||||
//{badrpc,{'EXIT',{undef,[{rabbit_access_control,list_users,[[]]},{rpc,'-handle_call/3-fun-0-',5}]}}}
|
||||
|
||||
if (result instanceof OtpErlangTuple) {
|
||||
OtpErlangTuple msg = (OtpErlangTuple)result;
|
||||
OtpErlangObject[] elements = msg.elements();
|
||||
if (msg.elementAt(0) instanceof OtpErlangAtom)
|
||||
{
|
||||
OtpErlangAtom responseAtom = (OtpErlangAtom)msg.elementAt(0);
|
||||
//TODO consider error handler strategy.
|
||||
if (responseAtom.atomValue().equals("badrpc")) {
|
||||
if (msg.elementAt(1) instanceof OtpErlangTuple) {
|
||||
throw new ErlangBadRpcException( (OtpErlangTuple)msg.elementAt(1));
|
||||
} else {
|
||||
throw new ErlangBadRpcException( msg.elementAt(1).toString());
|
||||
}
|
||||
} else if (responseAtom.atomValue().equals("error")) {
|
||||
if (msg.elementAt(1) instanceof OtpErlangTuple) {
|
||||
throw new ErlangErrorRpcException( (OtpErlangTuple)msg.elementAt(1));
|
||||
} else {
|
||||
throw new ErlangErrorRpcException( msg.elementAt(1).toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public OtpErlangObject executeErlangRpc(String module, String function, OtpErlangObject... args) {
|
||||
return executeRpc(module, function, new OtpErlangList(args));
|
||||
}
|
||||
|
||||
public OtpErlangObject executeRpc(String module, String function, Object... args) {
|
||||
return executeErlangRpc(module, function, (OtpErlangList) erlangConverter.toErlang(args));
|
||||
}
|
||||
|
||||
public Object executeAndConvertRpc(String module, String function, ErlangConverter converterToUse, Object... args) {
|
||||
return converterToUse.fromErlang(executeRpc(module, function, converterToUse.toErlang(args)));
|
||||
}
|
||||
|
||||
public Object executeAndConvertRpc(String module, String function, Object... args) {
|
||||
return erlangConverter.fromErlangRpc(module, function, executeErlangRpc(module, function, (OtpErlangList)erlangConverter.toErlang(args)));
|
||||
}
|
||||
|
||||
public ErlangConverter getErlangConverter() {
|
||||
return erlangConverter;
|
||||
}
|
||||
|
||||
public void setErlangConverter(ErlangConverter erlangConverter) {
|
||||
this.erlangConverter = erlangConverter;
|
||||
}
|
||||
|
||||
public <T> T execute(ConnectionCallback<T> action) throws OtpException {
|
||||
|
||||
Assert.notNull(action, "Callback object must not be null");
|
||||
OtpConnection con = null;
|
||||
try {
|
||||
con = createConnection();
|
||||
return action.doInConnection(con);
|
||||
}
|
||||
catch (OtpException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw convertOtpAccessException(ex);
|
||||
}
|
||||
finally {
|
||||
org.springframework.otp.erlang.connection.ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the specified checked exception to
|
||||
* a Spring runtime exception equivalent.
|
||||
* <p>The default implementation delegates to the
|
||||
* {@link org.springframework.otp.erlang.support.ErlangUtils#convertOtpAccessException} method.
|
||||
* @param ex the original checked {@link Exception} to convert
|
||||
* @return the Spring runtime wrapping <code>ex</code>
|
||||
* @see org.springframework.otp.erlang.support.ErlangUtils#convertOtpAccessException
|
||||
*/
|
||||
protected OtpException convertOtpAccessException(Exception ex) {
|
||||
return ErlangUtils.convertOtpAccessException(ex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Describes an Erlang node.
|
||||
*/
|
||||
package org.springframework.otp.erlang.core;
|
||||
|
||||
/**
|
||||
* Simple description class for an Erlang node.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*
|
||||
*/
|
||||
public class Node {
|
||||
|
||||
private String name;
|
||||
|
||||
public Node(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Node [name=" + name + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.otp.erlang.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.otp.erlang.OtpException;
|
||||
import org.springframework.otp.erlang.connection.ConnectionFactory;
|
||||
|
||||
import com.ericsson.otp.erlang.OtpAuthException;
|
||||
import com.ericsson.otp.erlang.OtpConnection;
|
||||
|
||||
/**
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public abstract class ErlangAccessor implements InitializingBean {
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private ConnectionFactory connectionFactory;
|
||||
|
||||
protected OtpConnection createConnection() throws UnknownHostException, OtpAuthException, IOException {
|
||||
return getConnectionFactory().createConnection();
|
||||
}
|
||||
|
||||
public void setConnectionFactory(ConnectionFactory connectionFactory) {
|
||||
this.connectionFactory = connectionFactory;
|
||||
}
|
||||
|
||||
public ConnectionFactory getConnectionFactory() {
|
||||
return this.connectionFactory;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
if (getConnectionFactory() == null) {
|
||||
throw new IllegalArgumentException("Property 'connectionFactory' is required");
|
||||
}
|
||||
}
|
||||
|
||||
protected OtpException convertOtpAccessException(Exception ex) {
|
||||
return ErlangUtils.convertOtpAccessException(ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.otp.erlang.support;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.otp.erlang.OtpException;
|
||||
import org.springframework.otp.erlang.OtpIOException;
|
||||
import org.springframework.otp.erlang.UncategorizedOtpException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.ericsson.otp.erlang.OtpConnection;
|
||||
|
||||
/**
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public class ErlangUtils {
|
||||
|
||||
/**
|
||||
* Close the given Connection.
|
||||
* @param con the Connection to close if necessary
|
||||
* (if this is <code>null</code>, the call will be ignored)
|
||||
*/
|
||||
public static void releaseConnection(OtpConnection con) {
|
||||
if (con == null) {
|
||||
return;
|
||||
}
|
||||
con.close();
|
||||
}
|
||||
|
||||
public static OtpException convertOtpAccessException(Exception ex) {
|
||||
Assert.notNull(ex, "Exception must not be null");
|
||||
if (ex instanceof IOException) {
|
||||
return new OtpIOException((IOException) ex);
|
||||
}
|
||||
//fallback
|
||||
return new UncategorizedOtpException(ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.otp.erlang.support.converter;
|
||||
|
||||
import org.springframework.otp.erlang.OtpException;
|
||||
|
||||
/**
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public class ErlangConversionException extends OtpException {
|
||||
|
||||
public ErlangConversionException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ErlangConversionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.otp.erlang.support.converter;
|
||||
|
||||
import com.ericsson.otp.erlang.OtpErlangObject;
|
||||
|
||||
/**
|
||||
* Converter between Java and Erlang Types. Additional support for converting results from RPC calls.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public interface ErlangConverter {
|
||||
|
||||
/**
|
||||
* Convert a Java object to a Erlang data type.
|
||||
* @param object the object to convert
|
||||
* @return the Erlang data type
|
||||
* @throws ErlangConversionException in case of conversion failure
|
||||
*/
|
||||
OtpErlangObject toErlang(Object object) throws ErlangConversionException;
|
||||
|
||||
/**
|
||||
* Convert from a Erlang data type to a Java object.
|
||||
* @param erlangObject the Elang object to convert
|
||||
* @return the converted Java object
|
||||
* @throws ErlangConversionException in case of conversion failure
|
||||
*/
|
||||
Object fromErlang(OtpErlangObject erlangObject) throws ErlangConversionException;
|
||||
|
||||
Object fromErlangRpc(String module, String function, OtpErlangObject erlangObject) throws ErlangConversionException;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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.otp.erlang.support.converter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.ericsson.otp.erlang.OtpErlangAtom;
|
||||
import com.ericsson.otp.erlang.OtpErlangBinary;
|
||||
import com.ericsson.otp.erlang.OtpErlangBoolean;
|
||||
import com.ericsson.otp.erlang.OtpErlangByte;
|
||||
import com.ericsson.otp.erlang.OtpErlangChar;
|
||||
import com.ericsson.otp.erlang.OtpErlangDouble;
|
||||
import com.ericsson.otp.erlang.OtpErlangFloat;
|
||||
import com.ericsson.otp.erlang.OtpErlangInt;
|
||||
import com.ericsson.otp.erlang.OtpErlangList;
|
||||
import com.ericsson.otp.erlang.OtpErlangLong;
|
||||
import com.ericsson.otp.erlang.OtpErlangObject;
|
||||
import com.ericsson.otp.erlang.OtpErlangPid;
|
||||
import com.ericsson.otp.erlang.OtpErlangRangeException;
|
||||
import com.ericsson.otp.erlang.OtpErlangShort;
|
||||
import com.ericsson.otp.erlang.OtpErlangString;
|
||||
|
||||
/**
|
||||
* Converter that supports the basic types and arrays.
|
||||
* @author Mark Pollack
|
||||
*
|
||||
*/
|
||||
public class SimpleErlangConverter implements ErlangConverter {
|
||||
|
||||
public Object fromErlang(OtpErlangObject erlangObject)
|
||||
throws ErlangConversionException {
|
||||
//TODO support arrays.
|
||||
return convertErlangToBasicType(erlangObject);
|
||||
}
|
||||
|
||||
public Object fromErlangRpc(String module, String function,
|
||||
OtpErlangObject erlangObject) throws ErlangConversionException {
|
||||
return this.fromErlang(erlangObject);
|
||||
}
|
||||
|
||||
public OtpErlangObject toErlang(Object obj)
|
||||
throws ErlangConversionException {
|
||||
if (obj instanceof OtpErlangObject) {
|
||||
return (OtpErlangObject) obj;
|
||||
}
|
||||
if (obj instanceof Object[]) {
|
||||
Object[] objectsToConvert = (Object[]) obj;
|
||||
if (objectsToConvert.length != 0) {
|
||||
ArrayList<OtpErlangObject> tempList = new ArrayList<OtpErlangObject>();
|
||||
|
||||
for (Object objectToConvert : objectsToConvert) {
|
||||
OtpErlangObject erlangObject = convertBasicTypeToErlang(objectToConvert);
|
||||
tempList.add(erlangObject);
|
||||
}
|
||||
OtpErlangObject ia[] = new OtpErlangObject[tempList.size()];
|
||||
return new OtpErlangList(tempList.toArray(ia));
|
||||
} else {
|
||||
return new OtpErlangList();
|
||||
}
|
||||
} else {
|
||||
return convertBasicTypeToErlang(obj);
|
||||
}
|
||||
}
|
||||
|
||||
protected OtpErlangObject convertBasicTypeToErlang(Object obj) {
|
||||
if (obj instanceof byte[]) {
|
||||
return new OtpErlangBinary((byte[]) obj);
|
||||
} else if (obj instanceof Boolean) {
|
||||
return new OtpErlangBoolean((Boolean) obj);
|
||||
} else if (obj instanceof Byte) {
|
||||
return new OtpErlangByte((Byte) obj);
|
||||
} else if (obj instanceof Character) {
|
||||
return new OtpErlangChar((Character) obj);
|
||||
} else if (obj instanceof Double) {
|
||||
return new OtpErlangDouble((Double) obj);
|
||||
} else if (obj instanceof Float) {
|
||||
return new OtpErlangFloat((Float) obj);
|
||||
} else if (obj instanceof Integer) {
|
||||
return new OtpErlangInt((Integer) obj);
|
||||
} else if (obj instanceof Long) {
|
||||
return new OtpErlangLong((Long) obj);
|
||||
} else if (obj instanceof Short) {
|
||||
return new OtpErlangShort((Short) obj);
|
||||
} else if (obj instanceof String) {
|
||||
return new OtpErlangString((String) obj);
|
||||
} else {
|
||||
throw new ErlangConversionException(
|
||||
"Could not convert Java object of type [" + obj.getClass()
|
||||
+ "] to an Erlang data type.");
|
||||
}
|
||||
}
|
||||
|
||||
protected Object convertErlangToBasicType(OtpErlangObject erlangObject) {
|
||||
try {
|
||||
if (erlangObject instanceof OtpErlangBinary) {
|
||||
return ((OtpErlangBinary) erlangObject).binaryValue();
|
||||
} else if (erlangObject instanceof OtpErlangAtom) {
|
||||
return ((OtpErlangAtom) erlangObject).atomValue();
|
||||
} else if (erlangObject instanceof OtpErlangBinary) {
|
||||
return ((OtpErlangBinary) erlangObject).binaryValue();
|
||||
} else if (erlangObject instanceof OtpErlangBoolean) {
|
||||
return extractBoolean(erlangObject);
|
||||
} else if (erlangObject instanceof OtpErlangByte) {
|
||||
return ((OtpErlangByte) erlangObject).byteValue();
|
||||
} else if (erlangObject instanceof OtpErlangChar) {
|
||||
return ((OtpErlangChar) erlangObject).charValue();
|
||||
} else if (erlangObject instanceof OtpErlangDouble) {
|
||||
return ((OtpErlangDouble) erlangObject).doubleValue();
|
||||
} else if (erlangObject instanceof OtpErlangFloat) {
|
||||
return ((OtpErlangFloat) erlangObject).floatValue();
|
||||
} else if (erlangObject instanceof OtpErlangInt) {
|
||||
return ((OtpErlangInt) erlangObject).intValue();
|
||||
} else if (erlangObject instanceof OtpErlangLong) {
|
||||
return ((OtpErlangLong) erlangObject).longValue();
|
||||
} else if (erlangObject instanceof OtpErlangShort) {
|
||||
return ((OtpErlangShort) erlangObject).shortValue();
|
||||
} else if (erlangObject instanceof OtpErlangString) {
|
||||
return ((OtpErlangString) erlangObject).stringValue();
|
||||
} else if (erlangObject instanceof OtpErlangPid) {
|
||||
return ((OtpErlangPid) erlangObject).toString();
|
||||
} else {
|
||||
throw new ErlangConversionException(
|
||||
"Could not convert Erlang object ["
|
||||
+ erlangObject.getClass() + "] to Java type.");
|
||||
}
|
||||
} catch (OtpErlangRangeException e) {
|
||||
throw new ErlangConversionException(
|
||||
"Could not convert Erlang object ["
|
||||
+ erlangObject.getClass() + "] to Java type.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean extractBoolean(OtpErlangObject erlangObject) {
|
||||
return ((OtpErlangBoolean) erlangObject).booleanValue();
|
||||
}
|
||||
|
||||
public static String extractPid(OtpErlangObject value) {
|
||||
return ((OtpErlangPid)value).toString();
|
||||
}
|
||||
|
||||
public static long extractLong(OtpErlangObject value) {
|
||||
return ((OtpErlangLong)value).longValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.util.exec;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Class to execute processes in the background. These processes
|
||||
* will not exit once the controlling Java process exits.
|
||||
*/
|
||||
public class Background {
|
||||
/**
|
||||
* Execute the command (and its args, ala Runtime.exec), sending the
|
||||
* output && error streams to the void.
|
||||
*/
|
||||
public static void exec(String[] cmd)
|
||||
throws IOException
|
||||
{
|
||||
File devNull;
|
||||
if(Os.isFamily("unix"))
|
||||
devNull = new File("/dev/null");
|
||||
else if (Os.isFamily("windows"))
|
||||
devNull = new File("NUL");
|
||||
else
|
||||
throw new IllegalStateException("Unhandled Java environment");
|
||||
exec(cmd, devNull, false, devNull, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command (and its args, ala Runtime.exec)
|
||||
*
|
||||
* @param outFile File to send standard out from the process to
|
||||
* @param appendOut If true, append the file with standard out,
|
||||
* else truncate or create a new file
|
||||
* @param errFile File to send standard err from the process to
|
||||
* @param appendErr If true, append the file with standard error,
|
||||
* else truncate or create a new file
|
||||
*/
|
||||
public static void exec(String[] cmd,
|
||||
File outFile, boolean appendOut,
|
||||
File errFile, boolean appendErr)
|
||||
throws IOException
|
||||
{
|
||||
if(Os.isFamily("unix"))
|
||||
execUnix(cmd, outFile, appendOut, errFile, appendErr);
|
||||
else if (Os.isFamily("windows"))
|
||||
execWin(cmd, outFile, appendOut, errFile, appendErr);
|
||||
else
|
||||
throw new IllegalStateException("Unhandled Java environment");
|
||||
}
|
||||
|
||||
private static void execUnix(String[] cmd,
|
||||
File outFile, boolean appendOut,
|
||||
File errFile, boolean appendErr)
|
||||
throws IOException
|
||||
{
|
||||
StringBuffer escaped;
|
||||
String[] execCmd;
|
||||
Runtime r;
|
||||
|
||||
escaped = new StringBuffer();
|
||||
for(int i=0; i<cmd.length; i++){
|
||||
escaped.append(Escape.escape(cmd[i]));
|
||||
escaped.append(" ");
|
||||
}
|
||||
|
||||
execCmd = new String[] {
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
escaped.toString() +
|
||||
(appendOut == true ? ">>" : ">") +
|
||||
Escape.escape(outFile.getAbsolutePath()) +
|
||||
" 2" + (appendErr == true ? ">>" : " >") +
|
||||
Escape.escape(errFile.getAbsolutePath()) +
|
||||
" </dev/null &"
|
||||
};
|
||||
|
||||
Process p = Runtime.getRuntime().exec(execCmd);
|
||||
try {
|
||||
p.waitFor();
|
||||
} catch(Exception exc){
|
||||
throw new IOException("Unable to properly background process: " +
|
||||
exc.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void execWin(String[] cmd,
|
||||
File outFile, boolean appendOut,
|
||||
File errFile, boolean appendErr)
|
||||
throws IOException
|
||||
{
|
||||
String[] logargs;
|
||||
String[] execCmd;
|
||||
ArrayList tmpCmd = new ArrayList();
|
||||
Runtime r;
|
||||
|
||||
tmpCmd.add("cmd");
|
||||
tmpCmd.add("/c");
|
||||
tmpCmd.add("start");
|
||||
tmpCmd.add("/b");
|
||||
tmpCmd.add("\"\"");
|
||||
tmpCmd.add("/MIN");
|
||||
for(int i=0; i<cmd.length; i++){
|
||||
tmpCmd.add(cmd[i]);
|
||||
}
|
||||
tmpCmd.add((appendOut == true ? ">>" : ">") +
|
||||
Escape.escape(outFile.getAbsolutePath()));
|
||||
tmpCmd.add((outFile.equals(errFile) ? " 2&" : " 2") +
|
||||
(appendErr == true ? ">>" : " >") +
|
||||
Escape.escape(errFile.getAbsolutePath()));
|
||||
|
||||
Process p = Runtime.getRuntime().exec((String [])tmpCmd.toArray(cmd));
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Background.exec(new String[] {"javaq", "foo bar", "bar" },
|
||||
new File("garfo"), true, new File("barfo"), true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.util.exec;
|
||||
|
||||
public class Escape {
|
||||
private static char[] enlargeArray(char[] in){
|
||||
char[] res;
|
||||
|
||||
res = new char[in.length * 2];
|
||||
System.arraycopy(in, 0, res, 0, in.length);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape a string by quoting the magical elements
|
||||
* (such as whitespace, quotes, slashes, etc.)
|
||||
*/
|
||||
public static String escape(String in){
|
||||
char[] inChars, outChars, resChars;
|
||||
int numOut;
|
||||
|
||||
inChars = new char[in.length()];
|
||||
outChars = new char[inChars.length];
|
||||
in.getChars(0, inChars.length, inChars, 0);
|
||||
numOut = 0;
|
||||
|
||||
for(int i=0; i<inChars.length; i++){
|
||||
if(outChars.length - numOut < 5){
|
||||
outChars = enlargeArray(outChars);
|
||||
}
|
||||
|
||||
if(Character.isWhitespace(inChars[i]) ||
|
||||
inChars[i] == '\\' ||
|
||||
inChars[i] == '\'' ||
|
||||
inChars[i] == '\"' ||
|
||||
inChars[i] == '&' ||
|
||||
inChars[i] == ';')
|
||||
{
|
||||
outChars[numOut++] = '\\';
|
||||
outChars[numOut++] = inChars[i];
|
||||
} else {
|
||||
outChars[numOut++] = inChars[i];
|
||||
}
|
||||
}
|
||||
|
||||
return new String(outChars, 0, numOut);
|
||||
}
|
||||
|
||||
public static void main(String[] args){
|
||||
System.out.println(Escape.escape("foo bar"));
|
||||
System.out.println(Escape.escape("\\\"foo' bar\""));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
* 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.util.exec;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.StringReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/* 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.
|
||||
|
||||
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;
|
||||
|
||||
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 static Vector procEnvironment = null;
|
||||
|
||||
/**
|
||||
* Find the list of environment variables for this process.
|
||||
*/
|
||||
public static synchronized Vector getProcEnvironment() {
|
||||
if (procEnvironment != null) return procEnvironment;
|
||||
|
||||
procEnvironment = new Vector();
|
||||
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;
|
||||
}
|
||||
|
||||
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 osname =
|
||||
System.getProperty("os.name").toLowerCase(Locale.US);
|
||||
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.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the commandline of the subprocess to launch.
|
||||
*
|
||||
* @param commandline the commandline of the subprocess to launch
|
||||
*/
|
||||
public void setCommandline(String[] commandline) {
|
||||
cmdl = commandline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to propagate the default environment or not.
|
||||
*
|
||||
* @param newenv whether to propagate the process environment.
|
||||
*/
|
||||
public void setNewenvironment(boolean newenv) {
|
||||
newEnvironment = newenv;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 environment variables for the subprocess to launch.
|
||||
*
|
||||
* @param commandline 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
// costin
|
||||
|
||||
boolean wait=true;
|
||||
public void setWait( boolean b ) {
|
||||
wait=b;
|
||||
}
|
||||
|
||||
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>
|
||||
* @exception java.io.IOExcpetion The exception is thrown, 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 (watchdog != null) watchdog.start(process,
|
||||
Thread.currentThread());
|
||||
|
||||
if( log.isTraceEnabled() ) log.trace("Waiting process ");
|
||||
waitFor(process);
|
||||
|
||||
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();
|
||||
|
||||
if( log.isDebugEnabled() ) {
|
||||
log.debug("Done exit=" + exit + " " + getCommandLineString());
|
||||
}
|
||||
return exit;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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() {
|
||||
Vector osEnv = (Vector) 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 (((String)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 envVars, String cmd, File baseDir ) {
|
||||
Vector v=new Vector();
|
||||
StringTokenizer st=new StringTokenizer( cmd, " " );
|
||||
while( st.hasMoreTokens() ) {
|
||||
v.addElement( st.nextElement() );
|
||||
}
|
||||
|
||||
return execute( envVars, v, baseDir );
|
||||
}
|
||||
|
||||
public static int execute( Vector envVars, Vector cmd, File baseDir) {
|
||||
return execute( envVars, cmd, baseDir, 10000 /* default time to wait */);
|
||||
}
|
||||
|
||||
/** 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 envVars, Vector 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);
|
||||
|
||||
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 );
|
||||
|
||||
if( envVars!=null ) {
|
||||
String env[]=new String[envVars.size()];
|
||||
envVars.toArray( env );
|
||||
exec.setEnvironment( env );
|
||||
}
|
||||
|
||||
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 );
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.util.exec;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Used by <code>Execute</code> to handle input and output stream of
|
||||
* subprocesses.
|
||||
*
|
||||
* @author thomas.haas@softwired-inc.com
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
void stop();
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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.util.exec;
|
||||
|
||||
|
||||
/**
|
||||
* Destroys a process running for too long.
|
||||
* For example:
|
||||
* <pre>
|
||||
* ExecuteWatchdog watchdog = new ExecuteWatchdog(30000);
|
||||
* Execute exec = new Execute(myloghandler, watchdog);
|
||||
* exec.setCommandLine(mycmdline);
|
||||
* int exitvalue = exec.execute();
|
||||
* if (exitvalue != SUCCESS && watchdog.killedProcess()){
|
||||
* // it was killed on purpose by the watchdog
|
||||
* }
|
||||
* </pre>
|
||||
|
||||
* @author thomas.haas@softwired-inc.com
|
||||
* @author <a href="mailto:sbailliez@imediation.com">Stephane Bailliez</a>
|
||||
* @see Execute
|
||||
*/
|
||||
public class ExecuteWatchdog implements Runnable {
|
||||
|
||||
/** the process to execute and watch for duration */
|
||||
private Process process;
|
||||
|
||||
/** timeout duration. Once the process running time exceeds this it should be killed */
|
||||
private int timeout;
|
||||
|
||||
/** say whether or not the watchog is currently monitoring a process */
|
||||
private boolean watch = false;
|
||||
|
||||
/** exception that might be thrown during the process execution */
|
||||
private Exception caught = null;
|
||||
|
||||
/** say whether or not the process was killed due to running overtime */
|
||||
private boolean killedProcess = false;
|
||||
|
||||
Thread execThread;
|
||||
private boolean dontkill=false;
|
||||
/**
|
||||
* Creates a new watchdog with a given timeout.
|
||||
*
|
||||
* @param timeout the timeout for the process in milliseconds. It must be greather than 0.
|
||||
*/
|
||||
public ExecuteWatchdog(int timeout) {
|
||||
if (timeout < 1) {
|
||||
throw new IllegalArgumentException("timeout lesser than 1.");
|
||||
}
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
|
||||
public void setDontkill( boolean b ) {
|
||||
dontkill=b;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <tt>null</tt>
|
||||
* @throws IllegalStateException thrown if a process is still being monitored.
|
||||
*/
|
||||
public synchronized void start(Process process, Thread execThread) {
|
||||
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;
|
||||
final Thread thread = new Thread(this, "WATCHDOG");
|
||||
this.execThread=execThread;
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the watcher. It will notify all threads possibly waiting on this object.
|
||||
*/
|
||||
public synchronized void stop() {
|
||||
watch = false;
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Watches the process and terminates it, if it runs for to long.
|
||||
*/
|
||||
public synchronized void run() {
|
||||
try {
|
||||
// This isn't a Task, don't have a Project object to log.
|
||||
// project.log("ExecuteWatchdog: timeout = "+timeout+" msec", Project.MSG_VERBOSE);
|
||||
final long until = System.currentTimeMillis() + timeout;
|
||||
long now;
|
||||
while (watch && until > (now = System.currentTimeMillis())) {
|
||||
try {
|
||||
wait(until - now);
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
|
||||
// if we are here, either someone stopped the watchdog,
|
||||
// we are on timeout and the process must be killed, or
|
||||
// we are on timeout and the process has already stopped.
|
||||
try {
|
||||
// We must check if the process was not stopped
|
||||
// before being here
|
||||
process.exitValue();
|
||||
} catch (IllegalThreadStateException e){
|
||||
// the process is not terminated, if this is really
|
||||
// a timeout and not a manual stop then kill it.
|
||||
//System.out.println("ExecuteWatchdog: timeout");
|
||||
if (watch){
|
||||
killedProcess = true;
|
||||
if( ! dontkill ) {
|
||||
//System.out.println("ExecuteWatchdog: destroying process");
|
||||
process.destroy();
|
||||
}
|
||||
if( execThread != null ) {
|
||||
execThread.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
caught = e;
|
||||
} finally {
|
||||
cleanUp();
|
||||
}
|
||||
//System.out.println("ExecuteWatchdog: done");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* reset the monitor flag and the process.
|
||||
*/
|
||||
protected void cleanUp() {
|
||||
watch = false;
|
||||
process = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 BuildException a wrapped exception over the one that was silently
|
||||
* swallowed and stored during the process run.
|
||||
*/
|
||||
// public void checkException() throws BuildException {
|
||||
// if (caught != null) {
|
||||
// throw new BuildException("Exception in ExecuteWatchdog.run: "
|
||||
// + caught.getMessage(), caught);
|
||||
// }
|
||||
// }
|
||||
|
||||
public Exception getException() {
|
||||
return caught;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether or not the watchdog is still monitoring the process.
|
||||
* @return <tt>true</tt> if the process is still running, otherwise <tt>false</tt>.
|
||||
*/
|
||||
public boolean isWatching(){
|
||||
return watch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the last process run was killed on timeout or not.
|
||||
* @return <tt>true</tt> if the process was killed otherwise <tt>false</tt>.
|
||||
*/
|
||||
public boolean killedProcess(){
|
||||
return killedProcess;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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.util.exec;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Condition that tests the OS type.
|
||||
*
|
||||
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
|
||||
* @author <a href="mailto:umagesh@apache.org">Magesh Umasankar</a>
|
||||
*/
|
||||
public class Os {
|
||||
private static final String osName =
|
||||
System.getProperty("os.name").toLowerCase(Locale.US);
|
||||
private static final String osArch =
|
||||
System.getProperty("os.arch").toLowerCase(Locale.US);
|
||||
private static final String osVersion =
|
||||
System.getProperty("os.version").toLowerCase(Locale.US);
|
||||
private static final String pathSep = System.getProperty("path.separator");
|
||||
|
||||
/**
|
||||
* Determines if the OS on which Ant is executing matches the
|
||||
* given OS family.
|
||||
*
|
||||
* @param f The OS family type desired<br />
|
||||
* Possible values:<br />
|
||||
* <ul><li>dos</li>
|
||||
* <li>mac</li>
|
||||
* <li>netware</li>
|
||||
* <li>os/2</li>
|
||||
* <li>unix</li>
|
||||
* <li>windows</li></ul>
|
||||
* @since 1.5
|
||||
*/
|
||||
public static boolean isFamily(String family) {
|
||||
return isOs(family, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the OS on which Ant is executing matches the
|
||||
* given OS name.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public static boolean isName(String name) {
|
||||
return isOs(null, name, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the OS on which Ant is executing matches the
|
||||
* given OS architecture.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public static boolean isArch(String arch) {
|
||||
return isOs(null, null, arch, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the OS on which Ant is executing matches the
|
||||
* given OS version.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public static boolean isVersion(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
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public static boolean isOs(String family, String name, String arch,
|
||||
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("windows")) {
|
||||
isFamily = osName.indexOf("windows") > -1;
|
||||
} else if (family.equals("os/2")) {
|
||||
isFamily = osName.indexOf("os/2") > -1;
|
||||
} else if (family.equals("netware")) {
|
||||
isFamily = osName.indexOf("netware") > -1;
|
||||
} else if (family.equals("dos")) {
|
||||
isFamily = pathSep.equals(";") && !isFamily("netware");
|
||||
} else if (family.equals("mac")) {
|
||||
isFamily = osName.indexOf("mac") > -1;
|
||||
} else if (family.equals("unix")) {
|
||||
isFamily = pathSep.equals(":")
|
||||
&& (!isFamily("mac") || osName.endsWith("x"));
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
"Don\'t know how to detect os family \""
|
||||
+ family + "\"");
|
||||
}
|
||||
}
|
||||
if (name != null) {
|
||||
isName = name.equals(osName);
|
||||
}
|
||||
if (arch != null) {
|
||||
isArch = arch.equals(osArch);
|
||||
}
|
||||
if (version != null) {
|
||||
isVersion = version.equals(osVersion);
|
||||
}
|
||||
retValue = isFamily && isName && isArch && isVersion;
|
||||
}
|
||||
return retValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.util.exec;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Copies standard output and error of subprocesses to standard output and
|
||||
* error of the parent process.
|
||||
*
|
||||
* TODO: standard input of the subprocess is not implemented.
|
||||
*
|
||||
* @author thomas.haas@softwired-inc.com
|
||||
*/
|
||||
public class PumpStreamHandler implements ExecuteStreamHandler {
|
||||
|
||||
private Thread inputThread;
|
||||
private Thread errorThread;
|
||||
|
||||
private OutputStream out, err;
|
||||
boolean running=false;
|
||||
|
||||
public PumpStreamHandler(OutputStream out, OutputStream err) {
|
||||
this.out = out;
|
||||
this.err = err;
|
||||
}
|
||||
|
||||
public PumpStreamHandler(OutputStream outAndErr) {
|
||||
this(outAndErr, outAndErr);
|
||||
}
|
||||
|
||||
public PumpStreamHandler() {
|
||||
this(System.out, System.err);
|
||||
}
|
||||
|
||||
public void setProcessOutputStream(InputStream is) {
|
||||
createProcessOutputPump(is, out);
|
||||
}
|
||||
|
||||
|
||||
public void setProcessErrorStream(InputStream is) {
|
||||
createProcessErrorPump(is, err);
|
||||
}
|
||||
|
||||
|
||||
public void setProcessInputStream(OutputStream os) {
|
||||
}
|
||||
|
||||
|
||||
public void start() {
|
||||
inputThread.start();
|
||||
errorThread.start();
|
||||
running=true;
|
||||
}
|
||||
|
||||
|
||||
public void stop() {
|
||||
if( ! running ) return;
|
||||
try {
|
||||
//if( log.isDebugEnabled() ) log.debug("Joining it");
|
||||
// inputThread.interrupt();
|
||||
inputThread.join(1000);
|
||||
//if( log.isDebugEnabled() ) log.debug("Joined" );
|
||||
} catch(InterruptedException e) {}
|
||||
try {
|
||||
//if( log.isDebugEnabled() ) log.debug("Joining it");
|
||||
// errorThread.interrupt();
|
||||
errorThread.join(1000);
|
||||
//if( log.isDebugEnabled() ) log.debug("Joined" );
|
||||
} catch(InterruptedException e) {}
|
||||
try {
|
||||
err.flush();
|
||||
} catch (IOException e) {}
|
||||
try {
|
||||
out.flush();
|
||||
} catch (IOException e) {}
|
||||
running=false;
|
||||
}
|
||||
|
||||
protected OutputStream getErr() {
|
||||
return err;
|
||||
}
|
||||
|
||||
protected OutputStream getOut() {
|
||||
return out;
|
||||
}
|
||||
|
||||
protected void createProcessOutputPump(InputStream is, OutputStream os) {
|
||||
inputThread = createPump(is, os);
|
||||
}
|
||||
|
||||
protected void createProcessErrorPump(InputStream is, OutputStream os) {
|
||||
errorThread = createPump(is, os);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a stream pumper to copy the given input stream to the given output stream.
|
||||
*/
|
||||
protected Thread createPump(InputStream is, OutputStream os) {
|
||||
final Thread result = new Thread(new StreamPumper(is, os));
|
||||
result.setDaemon(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static org.apache.commons.logging.Log log=
|
||||
org.apache.commons.logging.LogFactory.getLog( PumpStreamHandler.class );
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.util.exec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Copies all data from an input stream to an output stream.
|
||||
*
|
||||
* @author thomas.haas@softwired-inc.com
|
||||
*/
|
||||
public class StreamPumper implements Runnable {
|
||||
|
||||
// TODO: make SIZE and SLEEP instance variables.
|
||||
// TODO: add a status flag to note if an error occured in run.
|
||||
|
||||
private final static int SLEEP = 5;
|
||||
private final static int SIZE = 128;
|
||||
private InputStream is;
|
||||
private OutputStream os;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new stream pumper.
|
||||
*
|
||||
* @param is input stream to read data from
|
||||
* @param os output stream to write data to.
|
||||
*/
|
||||
public StreamPumper(InputStream is, OutputStream os) {
|
||||
this.is = is;
|
||||
this.os = os;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
final byte[] buf = new byte[SIZE];
|
||||
|
||||
int length;
|
||||
try {
|
||||
while ((length = is.read(buf)) > 0) {
|
||||
os.write(buf, 0, length);
|
||||
try {
|
||||
Thread.sleep(SLEEP);
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
} catch(IOException e) {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user