/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.communication.transport.jms.common;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import de.rcenvironment.core.communication.common.CommunicationException;
import de.rcenvironment.core.communication.model.NetworkRequest;
import de.rcenvironment.core.communication.model.NetworkResponse;
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 all incoming messages after the initial two-way handshake. These requests contain the actual
* network communication. Requests are quickly dispatched to the shared thread pool to avoid congestion in this consumer thread.
*
* @author Robert Mischke
*/
public final class RequestInboxConsumer extends AbstractJmsQueueConsumer implements Runnable {
// TODO >7.0.0: set to a much lower value once relay forwarding is asynchronous as well
private static final long SLOW_DISPATCH_LOGGING_THRESHOLD_MSEC = 25 * 1000; // 25 sec
private final MessageChannelEndpointHandler endpointHandler;
private final AsyncTaskService threadPool = ConcurrencyUtils.getAsyncTaskService();
public RequestInboxConsumer(String queueName, Connection connection, MessageChannelEndpointHandler endpointHandler)
throws JMSException {
super(connection, queueName);
this.endpointHandler = endpointHandler;
}
@Override
@TaskDescription("JMS Network Transport: Incoming request listener")
public void run() {
super.run();
}
@Override
protected void dispatchMessage(final Message message, final Connection jmsConnection) {
threadPool.execute(new Runnable() {
@Override
@TaskDescription("JMS Network Transport: Dispatch incoming request")
public void run() {
try {
Session responseSession = jmsConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
try {
dispatchMessageInternal(message, responseSession);
} finally {
if (responseSession != null) {
responseSession.close();
}
}
} catch (JMSException e) {
// do not log stacktrace, as it contains no additional information
log.error("JMS exception in response session for request from queue " + queueName + ": " + e.toString());
}
}
});
}
private void dispatchMessageInternal(Message message, Session session) {
String messageType;
try {
messageType = message.getStringProperty(JmsProtocolConstants.MESSAGE_FIELD_MESSAGE_TYPE);
} catch (JMSException e) {
log.warn("Received message with undefined message type");
return;
}
NetworkRequest request;
try {
if (JmsProtocolConstants.MESSAGE_TYPE_REQUEST.equals(messageType)) {
request = JmsProtocolUtils.createNetworkRequestFromMessage(message);
// on the messaging level, senders and receivers are identified by instance session ids
String senderIdString = request.accessMetaData().getSenderIdString();
long startTime = System.currentTimeMillis();
NetworkResponse response = endpointHandler.onRawRequestReceived(request, senderIdString);
long durationMsec = System.currentTimeMillis() - startTime;
// TODO move slow dispatch logging to transport-neutral code - misc_ro, May 2015
if (durationMsec > SLOW_DISPATCH_LOGGING_THRESHOLD_MSEC) {
log.debug(StringUtils.format("Slow dispatch (%,d msec) for incoming request of type %s", durationMsec,
request.getMessageType()));
}
try {
Message jmsResponse = JmsProtocolUtils.createMessageFromNetworkResponse(response, session);
final String messageId = message.getJMSMessageID();
// sanity check
if (messageId == null) {
log.error("Unexpected state: null JMS message id");
return; // no graceful handling possible
}
jmsResponse.setJMSCorrelationID(messageId); // set correlation id for non-blocking response handling
JmsProtocolUtils.sendWithTransientProducer(session, jmsResponse, message.getJMSReplyTo());
} catch (JMSException e) {
log.debug(StringUtils
.format("Error sending JMS response after successful request dispatch; most likely, the remote side "
+ "has closed the connection after sending the request (request type: %s, exception: %s)",
request.getMessageType(), e.toString()));
}
} else if (JmsProtocolConstants.MESSAGE_TYPE_CHANNEL_CLOSING.equals(messageType)) {
String closingChannelId = message.getStringProperty(JmsProtocolConstants.MESSAGE_FIELD_CHANNEL_ID);
endpointHandler.onInboundChannelClosing(closingChannelId);
} else {
log.warn("Received message of unhandled type " + messageType + " from queue " + queueName);
}
} catch (JMSException e) {
log.warn("Error while dispatching message of type " + messageType, e);
} catch (CommunicationException e) {
log.warn("Error while dispatching message of type " + messageType, e);
}
}
}