/*
* 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.util.concurrent.TimeoutException;
import javax.jms.ConnectionFactory;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.Session;
import de.rcenvironment.core.communication.channel.MessageChannelState;
import de.rcenvironment.core.communication.common.CommunicationException;
import de.rcenvironment.core.communication.common.InstanceNodeSessionId;
import de.rcenvironment.core.communication.transport.spi.BrokenMessageChannelListener;
import de.rcenvironment.core.communication.transport.spi.HandshakeInformation;
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.TaskDescription;
/**
* Represents a self-initiated JMS connection, ie a connection that was established from the local node to a remote node.
*
* @author Robert Mischke
*/
public class SelfInitiatedJmsMessageChannel extends AbstractJmsMessageChannel {
private static final int INITIAL_HANDSHAKE_TIMEOUT_MSEC = 15 * 1000;
private ConnectionFactory connectionFactory;
private BrokenMessageChannelListener brokenConnectionListener;
// private TemporaryQueue remoteInitiatedRequestInboxQueue;
public SelfInitiatedJmsMessageChannel(InstanceNodeSessionId localNodeId, ConnectionFactory connectionFactory,
BrokenMessageChannelListener brokenConnectionListener) {
super(localNodeId);
this.connectionFactory = connectionFactory;
this.brokenConnectionListener = brokenConnectionListener;
}
void connectToJmsBroker() throws JMSException {
connection = connectionFactory.createConnection();
connection.setExceptionListener(new ExceptionListener() {
@Override
public void onException(JMSException exception) {
log.warn(StringUtils.format("Asynchronous JMS exception in outgoing connection %s: %s ", getChannelId(),
exception.toString()));
// DO NOT automatically assume a broken connection on an async exception; this is covered by "health checking" - misc_ro
// brokenConnectionListener.onChannelBroken(SelfInitiatedJmsMessageChannel.this);
}
});
connection.start();
}
@Override
protected void onClosedOrBroken() {
log.debug("Closing self-initiated channel " + getChannelId());
super.onClosedOrBroken();
final boolean isActiveShutdown = getState() == MessageChannelState.CLOSED;
log.debug("Triggering asynchronous JMS disconnect of message channel " + getChannelId());
threadPool.execute(new Runnable() {
@Override
@TaskDescription("JMS Network Transport: Asynchronous disconnect")
public void run() {
tearDownJmsConnection(isActiveShutdown);
}
}, getChannelId());
}
private void tearDownJmsConnection(boolean isActiveShutdown) {
// on a clean shutdown, send "goodbye" message and wait
if (isActiveShutdown) {
sendShutdownMessageToRemoteRequestInbox();
try {
Thread.sleep(JmsProtocolConstants.WAIT_AFTER_SENDING_SHUTDOWN_MESSAGE_MSEC);
} catch (InterruptedException e1) {
// log and proceed; still try to close the connection
log.warn("Interrupted between sending the shutdown notice and closing the JMS connection");
}
}
try {
// note: this should automatically clean up (discard) the remoteInitiatedRequestInboxQueue
if (connection != null) {
connection.close();
} else {
log.debug("No JMS connection for channel " + getChannelId() + " when asked to tear it down");
}
} catch (JMSException e) {
log.debug("Exception while closing JMS connection", e);
}
}
HandshakeInformation performInitialHandshake(JMSHandshakeInformation ownHandshakeInformation,
MessageChannelEndpointHandler remoteInitiatedConnectionEndpointHandler) throws JMSException,
CommunicationException, TimeoutException, IOException {
Session initialSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
try {
Queue initialInbox = initialSession.createQueue(JmsProtocolConstants.QUEUE_NAME_INITIAL_BROKER_INBOX);
// remoteInitiatedRequestInboxQueue = initialSession.createTemporaryQueue();
// String remoteInitiatedRequestInboxQueueName = remoteInitiatedRequestInboxQueue.getQueueName();
RequestResponseQueuesManager tempQueueManager = new RequestResponseQueuesManager();
String clientQueueInfo = tempQueueManager.initClientSide(initialSession);
ownHandshakeInformation.setTemporaryQueueInformation(clientQueueInfo);
// create request message
Message handshakeRequestMessage = JmsProtocolUtils.createHandshakeMessage(ownHandshakeInformation, initialSession);
// perform handshake
ObjectMessage handshakeResponseMessage =
(ObjectMessage) performBlockingJmsRequestResponse(initialSession, handshakeRequestMessage, initialInbox,
INITIAL_HANDSHAKE_TIMEOUT_MSEC);
// extract the response, expecting the protocol set in the local HandshakeInformation
JMSHandshakeInformation remoteHandshakeInformation =
JmsProtocolUtils.parseHandshakeMessage(handshakeResponseMessage, ownHandshakeInformation.getProtocolVersionString());
failOnIncompatibleVersions(remoteHandshakeInformation.getProtocolVersionString(),
ownHandshakeInformation.getProtocolVersionString());
tempQueueManager.finishClientSide(remoteHandshakeInformation.getTemporaryQueueInformation());
// associate outgoing channel with the id of "mirror" channel
setAssociatedMirrorChannelId(remoteHandshakeInformation.getChannelId());
// spawn incoming request listener
// note: this listener is not part of the message channel, so it must be closed explicitly
// TODO clarify ownership
ConcurrencyUtils.getAsyncTaskService().execute(
new RequestInboxConsumer(tempQueueManager.getB2CRequestQueue(), connection, remoteInitiatedConnectionEndpointHandler),
StringUtils.format("B2C Request Inbox Consumer for channel %s @ %s", remoteHandshakeInformation.getChannelId(),
tempQueueManager.getB2CRequestQueue()));
String outgoingRequestQueueName = tempQueueManager.getC2BRequestQueue();
String incomingResponseQueueName = tempQueueManager.getC2BResponseQueue();
setupNonBlockingRequestSending(outgoingRequestQueueName, incomingResponseQueueName);
return remoteHandshakeInformation;
} finally {
initialSession.close();
}
}
}