/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.communication.transport.jms.common; import java.io.IOException; import java.net.ProtocolException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeoutException; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.JMSException; import javax.jms.Session; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import de.rcenvironment.core.communication.channel.MessageChannelIdFactory; import de.rcenvironment.core.communication.channel.ServerContactPoint; import de.rcenvironment.core.communication.common.CommunicationException; import de.rcenvironment.core.communication.common.InstanceNodeSessionId; import de.rcenvironment.core.communication.model.InitialNodeInformation; import de.rcenvironment.core.communication.model.NetworkContactPoint; import de.rcenvironment.core.communication.transport.spi.BrokenMessageChannelListener; import de.rcenvironment.core.communication.transport.spi.HandshakeInformation; import de.rcenvironment.core.communication.transport.spi.MessageChannel; import de.rcenvironment.core.communication.transport.spi.MessageChannelEndpointHandler; import de.rcenvironment.core.communication.transport.spi.NetworkTransportProvider; import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils; import de.rcenvironment.toolkit.modules.concurrency.api.AsyncTaskService; /** * Abstract base class for JMS transport providers. This base class provides the aspects of a JMS transport provider that are independent of * the JMS implementation used. * * @author Robert Mischke */ public abstract class AbstractJmsTransportProvider implements NetworkTransportProvider { protected final Map<ServerContactPoint, JmsBroker> serverEndpoints = new HashMap<ServerContactPoint, JmsBroker>(); protected final MessageChannelIdFactory connectionIdFactory; protected final Log log = LogFactory.getLog(getClass()); protected final AsyncTaskService threadPool = ConcurrencyUtils.getAsyncTaskService(); protected final JmsArtifactFactory artifactFactory; /** * Common JMS implementation of the {@link RemoteInitiatedMessageChannelFactory} interface. * * @author Robert Mischke */ public class RemoteInitiatedMessageChannelFactoryImpl implements RemoteInitiatedMessageChannelFactory { @Override public JmsMessageChannel createRemoteInitiatedMessageChannel(InitialNodeInformation receivingNodeInformation, JMSHandshakeInformation remoteHandshakeInformation, JMSHandshakeInformation ownHandshakeInformation, ServerContactPoint associatedSCP, Connection localJmsConnection, Session session) throws JMSException, ProtocolException { InitialNodeInformation remoteNodeInformation = remoteHandshakeInformation.getInitialNodeInformation(); String connectionId = connectionIdFactory.generateId(false); JmsMessageChannel remoteInitiatedConnection = new RemoteInitiatedJmsMessageChannel(receivingNodeInformation.getInstanceNodeSessionId(), localJmsConnection, associatedSCP); remoteInitiatedConnection.setRemoteNodeInformation(remoteNodeInformation); remoteInitiatedConnection.setAssociatedMirrorChannelId(remoteHandshakeInformation.getChannelId()); // FIXME add proper token remoteInitiatedConnection.setShutdownSecurityToken("passive." + remoteNodeInformation.getInstanceNodeSessionIdString()); remoteInitiatedConnection.setChannelId(connectionId); remoteInitiatedConnection.setInitiatedByRemote(true); // initialize the temporary queues created by the server RequestResponseQueuesManager tempQueueManager = new RequestResponseQueuesManager(); String serverQueueInfo = tempQueueManager.initServerSide(session, remoteHandshakeInformation.getTemporaryQueueInformation()); ownHandshakeInformation.setTemporaryQueueInformation(serverQueueInfo); // note: these are not part of the remote-initiated channel, but of the server port // TODO move out of this method? (note: replaced with single shared inbox consumer for now) // String incomingRequestQueueName = tempQueueManager.getC2BRequestQueue(); // threadPool.execute( // new RequestInboxConsumer(incomingRequestQueueName, localJmsConnection, associatedSCP.getEndpointHandler()), // StringUtils.format("C2B Request Inbox Consumer for channel %s @ %s", remoteHandshakeInformation.getChannelId(), // incomingRequestQueueName)); String outgoingRequestQueueName = tempQueueManager.getB2CRequestQueue(); String incomingResponseQueueName = tempQueueManager.getB2CResponseQueue(); remoteInitiatedConnection.setupNonBlockingRequestSending(outgoingRequestQueueName, incomingResponseQueueName); return remoteInitiatedConnection; } } public AbstractJmsTransportProvider(MessageChannelIdFactory connectionIdFactory, JmsArtifactFactory artifactFactory) { this.connectionIdFactory = connectionIdFactory; this.artifactFactory = artifactFactory; } @Override public MessageChannel connect(NetworkContactPoint ncp, InitialNodeInformation ownNodeInformation, String ownProtocolVersion, boolean allowInverseConnection, MessageChannelEndpointHandler inverseConnectionEndpointHandler, BrokenMessageChannelListener brokenConnectionListener) throws CommunicationException { SelfInitiatedJmsMessageChannel newChannel = null; try { try { ConnectionFactory connectionFactory = artifactFactory.createConnectionFactory(ncp); InstanceNodeSessionId localNodeId = ownNodeInformation.getInstanceNodeSessionId(); newChannel = new SelfInitiatedJmsMessageChannel(localNodeId, connectionFactory, brokenConnectionListener); newChannel.setChannelId(connectionIdFactory.generateId(true)); newChannel.connectToJmsBroker(); log.debug("Connected to JMS broker; sending initial handshake with identity '" + localNodeId + "'"); JMSHandshakeInformation ownHandshakeInformation = new JMSHandshakeInformation(); ownHandshakeInformation.setProtocolVersionString(ownProtocolVersion); ownHandshakeInformation.setInitialNodeInformation(ownNodeInformation); ownHandshakeInformation.setChannelId(newChannel.getChannelId()); // this throws a CommunicationException in case of protocol version mismatch HandshakeInformation remoteHandshakeInformation = newChannel.performInitialHandshake(ownHandshakeInformation, inverseConnectionEndpointHandler); InitialNodeInformation remoteNodeInformation = remoteHandshakeInformation.getInitialNodeInformation(); newChannel.setRemoteNodeInformation(remoteNodeInformation); log.debug("Successfully performed JMS handshake with remote node " + remoteNodeInformation.getLogDescription()); // basic check against duplicate node ids; does not guard against non-neighbor nodes // with same id if (remoteNodeInformation.getInstanceNodeSessionId().isSameInstanceNodeAs(localNodeId)) { throw new CommunicationException("Invalid setup: Remote and local node share the same instance node id: " + localNodeId.getInstanceNodeIdString()); } newChannel.markAsEstablished(); return newChannel; } catch (IOException e) { throw new CommunicationException("Failed to initiate JMS connection", e); } catch (RuntimeException e) { throw new CommunicationException("Failed to establish JMS connection", e); } catch (JMSException e) { throw new CommunicationException("Failed to establish JMS connection. Reason: " + e.toString()); // compress stacktrace } catch (TimeoutException e) { throw new CommunicationException("Timeout while establishing JMS connection", e); } } catch (CommunicationException e) { if (newChannel != null) { newChannel.onClosedOrBroken(); } throw e; } } @Override public boolean supportsRemoteInitiatedConnections() { return true; } @Override public void startServer(ServerContactPoint scp) throws CommunicationException { JmsBroker broker = artifactFactory.createBroker(scp, new RemoteInitiatedMessageChannelFactoryImpl()); // CHECKSTYLE:DISABLE (IllegalCatch) - ActiveMQ method declares "throws Exception" try { broker.start(); } catch (Exception e) { throw new CommunicationException("Failed to start JMS broker for SCP " + scp, e); } // CHECKSTYLE:ENABLE (IllegalCatch) synchronized (serverEndpoints) { serverEndpoints.put(scp, broker); } } @Override public void stopServer(ServerContactPoint scp) { final JmsBroker broker; synchronized (serverEndpoints) { broker = serverEndpoints.get(scp); } broker.stop(); } }