/*--- formatted by Jindent 2.1, (www.c-lab.de/~jindent) ---*/
/**
* ***************************************************************
* The LEAP libraries, when combined with certain JADE platform components,
* provide a run-time environment for enabling FIPA agents to execute on
* lightweight devices running Java. LEAP and JADE teams have jointly
* designed the API for ease of integration and hence to take advantage
* of these dual developments and extensions so that users only see
* one development platform and a
* single homogeneous set of APIs. Enabling deployment to a wide range of
* devices whilst still having access to the full development
* environment and functionalities that JADE provides.
* Copyright (C) 2001 Telecom Italia LAB S.p.A.
* Copyright (C) 2001 Siemens AG.
*
* GNU Lesser General Public License
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation,
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* **************************************************************
*/
package jade.imtp.leap;
import jade.core.PlatformManager;
import jade.core.Node;
import jade.core.IMTPException;
import jade.core.Profile;
import jade.core.UnreachableException;
import jade.mtp.TransportAddress;
import jade.util.leap.Iterator;
import jade.util.leap.ArrayList;
import jade.util.leap.List;
import jade.util.leap.Map;
import jade.util.leap.HashMap;
import jade.util.Logger;
/**
* This class provides the implementation of a command
* dispatcher. The command dispatcher misses support for multiple remote
* objects, multiple ICPs and command routing.
*
* <p>The command dispatcher is based on an implementation written by
* Michael Watzke and Giovanni Caire (TILAB), 09/11/2000.</p>
*
* @author Tobias Schaefer
* @version 1.0
*/
class CommandDispatcher implements StubHelper, ICP.Listener {
private static final String MAIN_PROTO_CLASS = "main-proto-class";
/**
* The default name for new instances of the class
* <tt>CommandDispatcher</tt> that have not get an unique name by
* their container yet.
*/
protected static final String DEFAULT_NAME = "Default";
private static boolean enableMultiplePlatforms;
/**
* The default singleton instance of the command dispatcher.
*/
protected static CommandDispatcher defaultCommandDispatcher;
/**
* The singleton map of command dispatchers in case more than one must be present in the same JVM.
*/
protected static Map dispatchers = new HashMap();
private String platformName;
/**
* The unique name of this command dispatcher used to avoid loops in
* the forwarding mechanism.
*/
protected String name;
/**
* The transport address of the default router. Commands that cannot
* be dispatched directly will be sent to this address.
*/
protected TransportAddress routerTA = null;
/**
* This hashtable maps the IDs of the objects remotized by this
* command dispatcher to the skeletons for these objects. It is used
* when a command is received from a remote JVM.
*/
protected Map skeletons = new HashMap();
/**
* This hashtable maps the objects remotized by this command
* dispatcher to their IDs. It is used when a stub of a remotized
* object must be built to be sent to a remote JVM.
*/
protected Map ids = new HashMap();
/**
* A counter that is used for determining IDs for remotized objects.
* Everytime a new object is registered by the command dispatcher it
* gets the value of this field as ID and the field is increased.
*/
protected int nextID;
/**
* The pool of ICP objects used by this command dispatcher to
* actually send/receive data over the network. It is a table that
* associates a <tt>String</tt> representing a protocol (e.g. "http")
* to a list of ICPs supporting that protocol.
*/
protected Map icps = new HashMap();
/**
* The transport addresses the ICPs managed by this command
* dispatcher are listening for commands on.
*/
protected List addresses = new ArrayList();
/**
* The URLs corresponding to the local transport addresses.
*/
protected List urls = new ArrayList();
/**
The stub for the platform service manager. This stub will be
shared by all nodes within this Java virtual Machine.
*/
private PlatformManager thePlatformManager = null;
private Logger myLogger = Logger.getMyLogger(getClass().getName());;
static {
enableMultiplePlatforms = "true".equals(System.getProperty("jade.enable.multiple.platforms"));
}
/**
* Returns a reference to the singleton instance of the CommandDispatcher for the indicated platform.
* Such instance is created if necessary.
* If no platform name is specified or the enableMultiplePlatforms flag is not set, then use the default
* CommandDispatcher
* @return the singleton instance of the CommandDispatcher for the indicated platform.
*/
public synchronized static final CommandDispatcher getDispatcher(String name) throws IMTPException {
System.out.println("Retrieving CommandDispatcher for platform "+name);
if (enableMultiplePlatforms) {
if (name != null) {
CommandDispatcher cd = (CommandDispatcher) dispatchers.get(name);
if (cd == null) {
cd = new CommandDispatcher();
cd.setPlatformName(name);
dispatchers.put(name, cd);
}
return cd;
}
else {
throw new IMTPException("No platform name specified and enable-multiple-platforms mode activated");
}
}
else {
// Use the default CommandDispatcher anyway
if (defaultCommandDispatcher == null) {
defaultCommandDispatcher = new CommandDispatcher();
defaultCommandDispatcher.setPlatformName(name);
}
return defaultCommandDispatcher;
}
}
private synchronized static void removeDispatcher(String name) {
if (enableMultiplePlatforms) {
dispatchers.remove(name);
}
}
private void setPlatformName(String name) {
platformName = name;
}
/**
* A sole constructor. To get a command dispatcher the constructor
* should not be called directly but the static <tt>create</tt> and
* <tt>getDispatcher</tt> methods should be used. Thereby the
* existence of a singleton instance of the command dispatcher will
* be guaranteed.
*/
private CommandDispatcher() {
// Set a temporary name. Will be substituted as soon as the first
// container attached to this CommandDispatcher will receive a
// unique name from the main.
name = DEFAULT_NAME;
nextID = 1;
}
synchronized PlatformManager getPlatformManagerProxy(Profile p) throws IMTPException {
if(thePlatformManager == null) {
PlatformManagerStub stub = new PlatformManagerStub(platformName);
TransportAddress mainTA = initMainTA(p);
stub.bind(this);
stub.addTA(mainTA);
setPlatformManager(stub);
}
return thePlatformManager;
}
// No need to synchronize it as it is always called within synchronized blocks
private void setPlatformManager(PlatformManager pm) throws IMTPException {
thePlatformManager = pm;
String actualPlatformName = thePlatformManager.getPlatformName();
if (platformName != null) {
// PlatformName already set --> Check that it is consistent with the actual name of the platform
if (!platformName.equals(actualPlatformName)) {
throw new IMTPException("Wrong platform name "+platformName+". It should be "+actualPlatformName);
}
}
else {
platformName = actualPlatformName;
}
}
// This is only called when reconnecting to a new Master Main Container --> No need to check again the platformName
synchronized void setPlatformManagerProxy(PlatformManager pm) {
thePlatformManager = pm;
}
public PlatformManager getPlatformManagerStub(String addr) throws IMTPException {
// Try to translate the address into a TransportAddress
// using a protocol supported by this CommandDispatcher
try {
PlatformManagerStub stub = new PlatformManagerStub(platformName);
TransportAddress ta = stringToAddr(addr);
stub.bind(this);
stub.addTA(ta);
return stub;
}
catch (DispatcherException de) {
throw new IMTPException("Invalid address for a Platform Manager", de);
}
}
public void addAddressToStub(Stub target, String toAdd) {
try {
TransportAddress ta = stringToAddr(toAdd);
target.addTA(ta);
}
catch(DispatcherException de) {
de.printStackTrace();
}
}
public void removeAddressFromStub(Stub target, String toRemove) {
try {
TransportAddress ta = stringToAddr(toRemove);
target.removeTA(ta);
}
catch(DispatcherException de) {
de.printStackTrace();
}
}
public void clearStubAddresses(Stub target) {
target.clearTAs();
}
/**
* Sets the transport address of the default router used for the
* forwarding mechanism.
*
* @param url the URL of the default router.
*/
void setRouterAddress(String url) {
if (url != null) {
// The default router must be directly reachable -->
// Its URL can be converted into a TransportAddress by
// the ICP registered to this CommandDispatcher
try {
TransportAddress ta = stringToAddr(url);
if (routerTA != null && !routerTA.equals(ta)) {
if(myLogger.isLoggable(Logger.WARNING))
myLogger.log(Logger.WARNING,"Transport address of current router has been changed");
}
routerTA = ta;
}
catch (Exception e) {
// Just print a warning: default (i.e. main TA) will be used
if(myLogger.isLoggable(Logger.WARNING))
myLogger.log(Logger.WARNING,"Can't initialize router address");
}
}
}
/**
* This method dispatches the specified command to the first address
* (among those specified) to which dispatching succeeds.
*
* @param destTAs a list of transport addresses where the command
* dispatcher should try to dispatch the command.
* @param command the command that is to be dispatched.
* @return a response command from the receiving container.
* @throws DispatcherException if an error occurs during dispatching.
* @throws UnreachableException if none of the destination addresses
* is reachable.
*/
public Command dispatchCommand(List destTAs,
Command command) throws DispatcherException, UnreachableException {
// DEBUG
//TransportAddress ta = (TransportAddress) destTAs.get(0);
//System.out.println("Dispatching command of type " + command.getCode() + " to "+ta.getHost()+":"+ta.getPort());
Command response = null;
//#J2ME_EXCLUDE_BEGIN
if (isLocal(destTAs)) {
Integer id = new Integer(command.getObjectID());
Skeleton skel = (Skeleton) skeletons.get(id);
if (skel != null) {
response = skel.processCommand(command);
}
}
//#J2ME_EXCLUDE_END
if (response == null) {
try {
response = dispatchSerializedCommand(destTAs, serializeCommand(command), command.getRequireFreshConnection(), name);
}
catch (LEAPSerializationException lse) {
throw new DispatcherException("Error serializing command "+command+" ["+lse.getMessage()+"]");
}
}
// If the dispatched command was an ADD_NODE --> get the
// name from the response and use it as the name of the CommandDispatcher
if (command.getCode() == Command.ADD_NODE && name.equals(DEFAULT_NAME)) {
name = (String) response.getParamAt(0);
}
return response;
}
private boolean isLocal(List destTAs) {
try {
TransportAddress ta1 = (TransportAddress) addresses.get(0);
TransportAddress ta2 = (TransportAddress) destTAs.get(0);
return (ta1.getHost().equals(ta2.getHost()) && ta1.getPort().equals(ta2.getPort()) && ta2.getFile() == null);
}
catch (Exception e) {
return false;
}
}
/**
* Dispatches the specified serialized command to one of the
* specified transport addresses (the first where dispatching
* succeeds) directly or through the router.
*
* @param destTAs a list of transport addresses where the command
* dispatcher should try to dispatch the command.
* @param commandPayload the serialized command that is to be
* dispatched.
* @param origin a <tt>String</tt> object describing the origin of
* the command to be dispatched.
* @return a response command from the receiving container.
* @throws DispatcherException if an error occurs during dispatching.
* @throws UnreachableException if none of the destination addresses
* is reachable.
*/
private Command dispatchSerializedCommand(List destTAs, byte[] commandPayload, boolean requireFreshConnection, String origin)
throws DispatcherException, UnreachableException {
// Be sure that the destination addresses are correctly specified
if (destTAs == null || destTAs.size() == 0) {
throw new DispatcherException("no destination address specified.");
}
byte[] responsePayload = null;
try {
// Try to dispatch the command directly
responsePayload = dispatchDirectly(destTAs, commandPayload, requireFreshConnection);
}
catch (UnreachableException ue) {
// Direct dispatching failed --> Try through the router
if (myLogger.isLoggable(Logger.FINE)) {
myLogger.log(Logger.FINE, "Destination unreachable. Dispatch command through router.", ue);
}
if (routerTA != null) {
responsePayload = dispatchThroughRouter(destTAs, commandPayload, requireFreshConnection, origin);
}
else {
throw ue;
}
}
// Deserialize the response
try {
Command response = deserializeCommand(responsePayload);
// Check whether some exceptions to be handled by the
// CommandDispatcher occurred on the remote site
checkRemoteExceptions(response);
return response;
}
catch (LEAPSerializationException lse) {
throw new DispatcherException("error deserializing response ["+lse.getMessage()+"].");
}
}
/**
* Dispatches the specified serialized command to one of the
* specified transport addresses (the first where dispatching
* succeeds) directly.
*
* @param destTAs a list of transport addresses where the command
* dispatcher should try to dispatch the command.
* @param commandPayload the serialized command that is to be
* dispatched.
* @return a serialized response command from the receiving
* container.
* @throws UnreachableException if none of the destination addresses
* is reachable.
*/
private byte[] dispatchDirectly(List destTAs, byte[] commandPayload, boolean requireFreshConnection) throws UnreachableException {
// Loop on destinaltion addresses (No need to check again
// that the list of addresses is not-null and not-empty)
UnreachableException lastException = null;
for (int i = 0; i < destTAs.size(); i++) {
try {
return send((TransportAddress) destTAs.get(i), commandPayload, requireFreshConnection);
}
catch (UnreachableException ue) {
lastException = ue;
// Can't send command to this address --> try the next one
// DEBUG
//TransportAddress ta = (TransportAddress)destTAs.get(i);
//System.out.println("Sending command to " + ta.getProto() + "://" + ta.getHost() + ":" + ta.getPort() + " failed [" + ue.getMessage() + "]");
//if (i < destTAs.size() - 1)
// System.out.println("Try next address");
}
}
// Sending failed to all addresses --> Throw the last exception
throw lastException;
}
/**
* Dispatches the specified serialized command to one of the
* specified transport addresses (the first where dispatching
* succeeds) through the router.
*
* @param destTAs a list of transport addresses where the command
* dispatcher should try to dispatch the command.
* @param commandPayload the serialized command that is to be
* dispatched.
* @param origin a <tt>String</tt> object describing the origin of
* the command to be dispatched.
* @return a serialized response command from the receiving
* container.
* @throws DispatcherException if an error occurs during dispatching.
* @throws UnreachableException if none of the destination addresses
* is reachable.
*/
private byte[] dispatchThroughRouter(List destTAs, byte[] commandPayload, boolean requireFreshConnection, String origin) throws DispatcherException, UnreachableException {
// Build a FORWARD command
Command forward = new Command(Command.FORWARD);
forward.addParam(commandPayload);
forward.addParam(destTAs);
forward.addParam(origin);
try {
return send(routerTA, serializeCommand(forward), requireFreshConnection);
}
catch (LEAPSerializationException lse) {
throw new DispatcherException("error serializing FORWARD command ["+lse.getMessage()+"].");
}
}
/**
* Checks whether some exceptions to be handled by the command
* dispatcher occurred on the remote site. If this is the case the
* command dispatcher throws the corresponding exception locally.
*
* @param response the resonse comman from the receiving container.
* @throws DispatcherException if an error occurs on the remote site
* during dispatching.
* @throws UnreachableException if the destination address is not
* reachable.
*/
protected void checkRemoteExceptions(Command response)
throws DispatcherException, UnreachableException {
if (response.getCode() == Command.ERROR) {
String exception = (String) response.getParamAt(0);
// DispatcherException (some error occurred in the remote
// CommandDispatcher) --> throw a DispatcherException.
if (exception.equals("jade.imtp.leap.DispatcherException")) {
throw new DispatcherException("DispatcherException in remote site. "+response.getParamAt(1));
}
else // UnreachableException (the Command was sent to the router,
// but the final destination was unreachable from there)
// --> throw an UnreachableException
if (exception.equals("jade.core.UnreachableException")) {
throw new UnreachableException((String) response.getParamAt(1));
}
}
}
/**
* Serializes a <tt>Command</tt> object into a <tt>byte</tt> array.
*
* @param command the command to be serialized.
* @return the serialized command.
* @throws LEAPSerializationException if the command cannot be
* serialized.
*/
protected byte[] serializeCommand(Command command) throws LEAPSerializationException {
DeliverableDataOutputStream ddout = new DeliverableDataOutputStream(this);
ddout.serializeCommand(command);
return ddout.getSerializedByteArray();
}
/**
* Deserializes a <tt>Command</tt> object from a <tt>byte</tt> array.
*
* @param data the <tt>byte</tt> array containing serialized command.
* @return the deserialized command.
* @throws LEAPSerializationException if the command cannot be
* deserialized.
*/
protected Command deserializeCommand(byte[] data) throws LEAPSerializationException {
return new DeliverableDataInputStream(data, this).deserializeCommand();
}
/**
* Builds a command that carries an exception.
*
* @param exception the exception to be carried.
* @return the command carrying the exception.
*/
protected Command buildExceptionResponse(Exception exception) {
Command response = new Command(Command.ERROR);
response.addParam(exception.getClass().getName());
response.addParam(exception.getMessage());
return response;
}
private TransportAddress initMainTA(Profile p) throws IMTPException {
TransportAddress mainTA = null;
try {
String mainURL = p.getParameter(LEAPIMTPManager.MAIN_URL, null);
// DEBUG
//System.out.println("Main URL is "+mainURL);
// Try to translate the mainURL into a TransportAddress
// using a protocol supported by this CommandDispatcher
try {
mainTA = stringToAddr(mainURL);
}
catch (DispatcherException de) {
// Failure --> A suitable protocol class may be explicitly
// indicated in the profile (otherwise rethrow the exception)
String mainTPClass = p.getParameter(MAIN_PROTO_CLASS, null);
if (mainTPClass != null) {
TransportProtocol tp = (TransportProtocol) Class.forName(mainTPClass).newInstance();
mainTA = tp.stringToAddr(mainURL);
}
else {
throw de;
}
}
// If the router TA was not set --> use the mainTA as default
//if (routerTA == null) {
// routerTA = mainTA;
//}
return mainTA;
}
catch (Exception e) {
throw new IMTPException("Error getting Main Container address", e);
}
}
/**
* Adds (and activates) an ICP to this command dispatcher.
*
* @param peer the ICP to add.
* @param args the arguments required by the ICP for the activation.
* These arguments are ICP specific.
*/
public void addICP(ICP peer, String peerID, Profile p) {
try {
// Activate the peer.
TransportAddress ta = peer.activate(this, peerID, p);
// Add the listening address to the list of local addresses.
TransportProtocol tp = peer.getProtocol();
String url = tp.addrToString(ta);
if (myLogger.isLoggable(Logger.FINE)) {
myLogger.log(Logger.FINE, "ICP "+peerID+" of class "+peer.getClass().getName()+" activated. Address is: "+url);
}
addresses.add(ta);
urls.add(url);
// Put the peer in the table of local ICPs.
String proto = tp.getName().toLowerCase();
List list = (List) icps.get(proto);
if (list == null) {
icps.put(proto, (list = new ArrayList()));
}
list.add(peer);
}
catch (ICPException icpe) {
// Print a warning.
myLogger.log(Logger.WARNING, "Error adding ICP "+peer+"["+icpe.getMessage()+"].");
}
}
TransportProtocol getProtocol(String protoName) {
List list = (List) icps.get(protoName.toLowerCase());
if (list != null && list.size() > 0) {
ICP icp = (ICP) list.get(0);
return icp.getProtocol();
}
return null;
}
/**
* Returns the ID of the specified remotized object.
*
* @param remoteObject the object whose ID should be returned.
* @return the ID of the reomte object.
* @throws RuntimeException if the specified object is not
* remotized by this command dispatcher.
*/
public int getID(Object remoteObject) throws IMTPException {
Integer id = (Integer) ids.get(remoteObject);
if (id != null) {
return id.intValue();
}
throw new IMTPException("specified object is not remotized by this command dispatcher.");
}
/**
* Returns the list of local addresses.
*
* @return the list of local addresses.
*/
public List getLocalTAs() {
return addresses;
}
/**
* Returns the list of URLs corresponding to the local addresses.
*
* @return the list of URLs corresponding to the local addresses.
*/
public List getLocalURLs() {
return urls;
}
/**
* Converts an URL into a transport address using the transport
* protocol supported by the ICPs currently installed in the command
* dispatcher. If there is no ICP installed to the command dispatcher
* or their transport protocols are not able to convert the specified
* URL a <tt>DispatcherException</tt> is thrown.
*
* @param url a <tt>String</tt> object specifying the URL to convert.
* @return the converted URL.
* @throws DispatcherException if there is no ICP installed to the
* command dispatcher or the transport protocols of the ICPs
* are not able to convert the specified URL.
*/
protected TransportAddress stringToAddr(String url) throws DispatcherException {
Iterator peers = icps.values().iterator();
while (peers.hasNext()) {
// Try to convert the url using the TransportProtocol
// supported by this ICP.
try {
// There can be more than one peer supporting the same
// protocol. Use the first one.
return ((ICP) ((List) peers.next()).get(0)).getProtocol().stringToAddr(url);
}
catch (Throwable t) {
// Do nothing and try the next one.
}
}
// If we reach this point the url can't be converted.
throw new DispatcherException("can't convert URL "+url+".");
}
/**
* Registers the specified skeleton to the command dispatcher.
*
* @param skeleton a skeleton to be managed by the command
* dispatcher.
* @param remoteObject the remote object related to the specified
* skeleton.
*/
public synchronized void registerSkeleton(Skeleton skeleton, Object remotizedObject) throws IMTPException {
Integer id = null;
if(remotizedObject instanceof PlatformManager) {
id = new Integer(0);
name = "Service-Manager";
setPlatformManager((PlatformManager) remotizedObject);
}
else {
id = new Integer((int) (System.currentTimeMillis() & 0xffffff));
}
if (myLogger.isLoggable(Logger.FINE)) {
myLogger.log(Logger.FINE, "Registering skeleton "+skeleton+" for remotized object "+remotizedObject+". ID is "+id);
}
skeletons.put(id, skeleton);
ids.put(remotizedObject, id);
}
/**
* Deregisters the specified remote object from the command dispatcher.
*
* @param remoteObject the remote object related to the specified
* skeleton.
*/
public synchronized void deregisterSkeleton(final Object remoteObject) {
if (myLogger.isLoggable(Logger.FINE)) {
myLogger.log(Logger.FINE, "Deregistering skeleton for remotized object "+remoteObject);
}
if (skeletons.size() == 1) {
// This is the only skeleton --> The problem described below (in the "else" clause)
// can't happen. Moreover the JVM is likely going to exit --> We can't delay the skeleton deregistration
removeRemoteObject(remoteObject);
}
else {
// Hack: If the PlatformManager monitoring this node is in the same
// JVM it needs some time to broadcast the termination of this node
// to its replicas (if any) --> asynchronously deregister the skeleton after
// a while
Thread t = new Thread() {
public void run() {
try {
Thread.sleep(1000);
}
catch (InterruptedException ie) {}
removeRemoteObject(remoteObject);
}
};
t.start();
}
}
private synchronized void removeRemoteObject(Object remoteObject) {
Object id = ids.remove(remoteObject);
if (id != null) {
if (myLogger.isLoggable(Logger.FINE)) {
myLogger.log(Logger.FINE, "Asynchronous deregisteration of skeleton for remotized object "+remoteObject+". ID is "+id);
}
skeletons.remove(id);
}
// When there are no more skeletons, shutdown the CommandDispatcher (this closes all ICPs)
if (ids.isEmpty()) {
if (myLogger.isLoggable(Logger.FINE)) {
myLogger.log(Logger.FINE, "All skeletons deregistered. Shutting down.");
}
shutDown();
}
}
public Stub buildLocalStub(Object remotizedObject) throws IMTPException {
Stub stub = null;
if (remotizedObject instanceof Node) {
stub = new NodeStub(getID(remotizedObject), platformName);
}
else if (remotizedObject instanceof PlatformManager) {
stub = new PlatformManagerStub(platformName);
}
else {
throw new IMTPException("can't create a stub for object "+remotizedObject+".");
}
stub.bind(this);
// Add the local addresses.
Iterator it = addresses.iterator();
while (it.hasNext()) {
stub.addTA((TransportAddress) it.next());
}
return stub;
}
/**
* Selects a suitable peer and sends the specified serialized command
* to the specified transport address.
*
* @param ta the transport addresses where the command should be
* sent.
* @param commandPayload the serialized command that is to be
* sent.
* @return a serialized response command from the receiving
* container.
* @throws UnreachableException if the destination address is not
* reachable.
*/
private byte[] send(TransportAddress ta, byte[] commandPayload, boolean requireFreshConnection) throws UnreachableException {
// Get the ICPs suitable for the given TransportAddress.
List list = (List) icps.get(ta.getProto().toLowerCase());
if (list == null) {
throw new UnreachableException("no ICP suitable for protocol "+ta.getProto()+".");
}
ICPException lastException = null;
for (int i = 0; i < list.size(); i++) {
try {
return ((ICP) list.get(i)).deliverCommand(ta, commandPayload, requireFreshConnection);
}
catch (ICPException icpe) {
lastException = icpe;
// DEBUG
// Print a warning and try next address
//System.out.println("Warning: can't deliver command to "+ta+". "+icpe.getMessage());
}
}
throw new UnreachableException("ICPException delivering command to address "+ta+".", lastException);
}
/**
* Shuts the command dispatcher down and deactivates the local ICPs.
*/
private void shutDown() {
Iterator peersKeys = icps.keySet().iterator();
while (peersKeys.hasNext()) {
List list = (List) icps.get(peersKeys.next());
for (int i = 0; i < list.size(); i++) {
try {
// This call interrupts the listening thread of this peer
// and waits for its completion.
((ICP) list.get(i)).deactivate();
}
catch (ICPException icpe) {
// Do nothing as this means that this peer had never been activated.
}
}
list.clear();
}
icps.clear();
urls.clear();
addresses.clear();
thePlatformManager = null;
name = DEFAULT_NAME;
nextID = 1;
removeDispatcher(platformName);
platformName = null;
}
// /////////////////////////////////////////
// ICP.Listener INTERFACE
// /////////////////////////////////////////
/**
* Handles a received (still serialized) command object, i.e.
* deserialize it and launch processing of the command.
*
* @param commandPayload the command to be deserialized and
* processed.
* @return a <tt>byte</tt> array containing the serialized response
* command.
* @throws LEAPSerializationException if the command cannot be
* (de-)serialized.
*/
public byte[] handleCommand(byte[] commandPayload) throws LEAPSerializationException {
try {
// Deserialize the incoming command.
Command command = deserializeCommand(commandPayload);
Command response = null;
// DEBUG
//System.out.println("Received command of type " + command.getCode());
if (command.getCode() == Command.FORWARD) {
// DEBUG
// System.out.println("Routing command");
// If this is a FORWARD command then handle it directly.
byte[] originalPayload = (byte[]) command.getParamAt(0);
List destTAs = (List) command.getParamAt(1);
String origin = (String) command.getParamAt(2);
if (origin.equals(name)) {
// The forwarding mechanism is looping.
response = buildExceptionResponse(new UnreachableException("destination unreachable (and forward loop)."));
}
else {
try {
response = dispatchSerializedCommand(destTAs, originalPayload, false, origin);
}
catch (UnreachableException ue) {
response = buildExceptionResponse(ue);
}
}
}
else {
// If this is a normal Command, let the proper Skeleton
// process it.
Integer id = new Integer(command.getObjectID());
Skeleton s = (Skeleton) skeletons.get(id);
if (s != null) {
response = s.processCommand(command);
}
else {
response = buildExceptionResponse(new DispatcherException("No skeleton for object-id "+id));
}
}
return serializeCommand(response);
}
catch (LEAPSerializationException lse) {
lse.printStackTrace();
// Note that if the call below throws an exception this is not handled by
// the CommandDispatcher. However this should never happen as an exception
// response should always be serializable.
return serializeCommand(buildExceptionResponse(new DispatcherException(lse.toString())));
}
catch (Exception e) {
// Note that if the call below throws an exception this is not handled by
// the CommandDispatcher. However this should never happen as an exception
// response should always be serializable.
return serializeCommand(buildExceptionResponse(new DispatcherException(e.toString())));
}
}
}