/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.communication.transport.jms.common;
import java.net.ProtocolException;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import de.rcenvironment.core.communication.channel.ServerContactPoint;
import de.rcenvironment.core.communication.model.InitialNodeInformation;
import de.rcenvironment.core.communication.transport.spi.MessageChannelEndpointHandler;
import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.toolkit.modules.concurrency.api.AsyncTaskService;
import de.rcenvironment.toolkit.modules.concurrency.api.TaskDescription;
/**
* A single-threaded consumer that listens for initial protocol handshake requests. These requests are the first messages that should be
* sent by a remote node after it has established a (network level) connection to the local broker.
*
* @author Robert Mischke
*/
public final class InitialInboxConsumer extends AbstractJmsQueueConsumer implements Runnable {
private final MessageChannelEndpointHandler endpointHandler;
private ServerContactPoint associatedSCP;
private RemoteInitiatedMessageChannelFactory passiveConnectionFactory;
private final AsyncTaskService threadPool = ConcurrencyUtils.getAsyncTaskService();
private final String expectedProtocolVersion;
public InitialInboxConsumer(Connection localJmsConnection, ServerContactPoint scp,
RemoteInitiatedMessageChannelFactory passiveConnectionFactory) throws JMSException {
super(localJmsConnection, JmsProtocolConstants.QUEUE_NAME_INITIAL_BROKER_INBOX);
this.associatedSCP = scp;
this.endpointHandler = scp.getEndpointHandler();
this.expectedProtocolVersion = scp.getExpectedProtocolVersion();
this.passiveConnectionFactory = passiveConnectionFactory;
}
@Override
@TaskDescription("JMS Network Transport: Incoming connection listener")
public void run() {
super.run();
}
@Override
protected void dispatchMessage(final Message message, final Connection connection) {
threadPool.execute(new Runnable() {
@Override
@TaskDescription("JMS Network Transport: Dispatch initial handshake request")
public void run() {
try {
Session responseSession = jmsConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
try {
dispatchMessageInternal(message, responseSession, connection);
} finally {
if (responseSession != null) {
responseSession.close();
}
}
} catch (JMSException e) {
log.error("JMS exception in response session for request from queue " + queueName, e);
}
}
});
}
private void dispatchMessageInternal(Message message, Session session, Connection connection) {
String messageType;
try {
messageType = message.getStringProperty(JmsProtocolConstants.MESSAGE_FIELD_MESSAGE_TYPE);
} catch (JMSException e) {
log.warn("Received message with undefined message type");
return;
}
try {
if (JmsProtocolConstants.MESSAGE_TYPE_INITIAL.equals(messageType)) {
handleHandshakeRequest(message, session, connection);
} else {
log.warn("Received message of unhandled type " + messageType + " from queue " + queueName);
}
} catch (JMSException | ProtocolException e) {
log.warn(StringUtils.format("Error while dispatching message of type %s: %s", messageType, e.toString())); // log without
// stacktrace
}
}
private void handleHandshakeRequest(Message message, Session session, Connection connection) throws JMSException,
ProtocolException {
JMSHandshakeInformation remoteHandshakeInformation =
JmsProtocolUtils.parseHandshakeMessage(message, expectedProtocolVersion);
Message jmsResponse;
if (remoteHandshakeInformation.matchesVersion(expectedProtocolVersion)) {
InitialNodeInformation remoteNodeInformation = remoteHandshakeInformation.getInitialNodeInformation();
InitialNodeInformation ownNodeInformation = endpointHandler.exchangeNodeInformation(remoteNodeInformation);
// create handshake response so the connection factory can set its temporary queue information
JMSHandshakeInformation ownHandshakeInformation = new JMSHandshakeInformation();
log.debug("Received initial handshake request from " + remoteNodeInformation);
// create the remote-initiated connection
// TODO (review: document as general approach?) do so before sending the response
JmsMessageChannel remoteInitiatedChannel =
passiveConnectionFactory.createRemoteInitiatedMessageChannel(ownNodeInformation, remoteHandshakeInformation,
ownHandshakeInformation, associatedSCP, connection, session);
remoteInitiatedChannel.markAsEstablished();
endpointHandler.onRemoteInitiatedChannelEstablished(remoteInitiatedChannel, associatedSCP);
// create full response
ownHandshakeInformation.setProtocolVersionString(expectedProtocolVersion);
ownHandshakeInformation.setChannelId(remoteInitiatedChannel.getChannelId());
ownHandshakeInformation.setInitialNodeInformation(ownNodeInformation);
// ownHandshakeInformation.setClientGeneratedTemporaryQueueNames(JmsProtocolConstants.QUEUE_NAME_C2B_REQUEST_INBOX);
log.debug("Remote-initiated connection established, sending handshake response to " + remoteNodeInformation);
jmsResponse = JmsProtocolUtils.createHandshakeMessage(ownHandshakeInformation, session);
} else {
// respond with a reduced handshake response containing only the version information
JMSHandshakeInformation ownHandshakeInformation = new JMSHandshakeInformation();
ownHandshakeInformation.setProtocolVersionString(expectedProtocolVersion);
log.debug("Received handshake request with an incompatible version ('" + remoteHandshakeInformation.getProtocolVersionString()
+ "'); sending minimal response");
jmsResponse = JmsProtocolUtils.createHandshakeMessage(ownHandshakeInformation, session);
}
// send response
JmsProtocolUtils.sendWithTransientProducer(session, jmsResponse, message.getJMSReplyTo());
}
}