blob: 3a0ce33d0b86c8add0f1070aaaee5274ff994af7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.rt.server.jms;
import java.util.Date;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import org.eclipse.core.runtime.Platform;
import org.eclipse.scout.commons.DateUtility;
import org.eclipse.scout.commons.TypeCastUtility;
import org.eclipse.scout.commons.annotations.ConfigProperty;
import org.eclipse.scout.commons.annotations.Order;
import org.eclipse.scout.commons.exception.ProcessingException;
import org.eclipse.scout.commons.logger.IScoutLogger;
import org.eclipse.scout.commons.logger.ScoutLogManager;
import org.eclipse.scout.rt.server.services.common.clustersync.IClusterSynchronizationService;
import org.eclipse.scout.service.SERVICES;
/**
* Base class for a JNDI configured scout service for JMS. See {@link AbstractJndiService} for configuring example.
* <p>
* This class is thread save.
* <p>
* Some notes about JMS objects:
* <ul>
* <li>A {@link Connection} is a is a relatively heavyweight object. It supports concurrent use. Therefore it should be
* shared.
* <li> {@link Session}, {@link MessageConsumer} and {@link MessageProducer} are lightweight objects and should not be
* used by different threads.
* <li>To receive new messages one should not use the method
* {@link MessageConsumer#setMessageListener(javax.jms.MessageListener)} as this is a J2EE container private method.
* Instead one should use the synchronous methods like {@link MessageConsumer#receive(long)}
* </ul>
*
* @param <T>
* the type of message that should be sent and received
*/
public abstract class AbstractJmsService<T> extends AbstractJndiService {
private static IScoutLogger LOG = ScoutLogManager.getLogger(AbstractJmsService.class);
private String m_connectionFactory;
private String m_destination;
private Connection m_connection;
@ConfigProperty(ConfigProperty.STRING)
@Order(10)
protected String getConfiguredConnectionFactory() {
return null;
}
@ConfigProperty(ConfigProperty.STRING)
@Order(20)
protected String getConfiguredDestination() {
return null;
}
public String getConnectionFactory() {
return m_connectionFactory;
}
public void setConnectionFactory(String connectionFactory) {
m_connectionFactory = connectionFactory;
}
public String getDestination() {
return m_destination;
}
public void setDestination(String destination) {
m_destination = destination;
}
@Override
protected void initConfig() {
super.initConfig();
setConnectionFactory(getConfiguredConnectionFactory());
setDestination(getConfiguredDestination());
}
protected ConnectionFactory lookupConnectionFactory() throws ProcessingException {
return lookup(getConnectionFactory(), ConnectionFactory.class);
}
protected Destination lookupDestination() throws ProcessingException {
return lookup(getDestination(), Destination.class);
}
protected boolean isEnabled() {
return getConnectionFactory() != null && getDestination() != null;
}
@SuppressWarnings("unchecked")
protected Class<T> getMessageType() {
return TypeCastUtility.getGenericsParameterClass(this.getClass(), AbstractJmsService.class);
}
protected IJmsMessageSerializer<T> createMessageSerializer() {
return new JmsMessageSerializer<T>(getMessageType());
}
protected synchronized Connection getConnection() {
return m_connection;
}
protected synchronized void setupConnection() throws ProcessingException {
closeConnection();
ConnectionFactory connectionFactory = lookupConnectionFactory();
Connection con;
try {
con = connectionFactory.createConnection();
}
catch (JMSException e) {
throw new ProcessingException("Failed creating JMS connection", e);
}
String clientId = null;
try {
// try to set clientId; might fail, ignore if happens
clientId = createClientId();
con.setClientID(clientId);
}
catch (Exception e) {
LOG.info("WARNING - Failed to set clientID '{0}' for consumer connection, possibly because of running in application container: {1}", clientId, e.getMessage());
LOG.trace("Full Exception:", e);
}
m_connection = con;
}
protected synchronized void closeConnection() throws ProcessingException {
Connection connection = m_connection;
if (connection != null) {
m_connection = null;
try {
connection.close();
}
catch (JMSException e) {
throw new ProcessingException("Failed closing JMS connection", e);
}
}
}
protected String createClientId() {
String serverVersion = null;
try {
serverVersion = Platform.getProduct().getDefiningBundle().getVersion().toString();
}
catch (Exception e) {
LOG.warn("Cannot determine server version", e);
}
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName()).append(" ");
if (serverVersion != null) {
sb.append(serverVersion).append(" ");
}
sb.append("nodeId=").append(SERVICES.getService(IClusterSynchronizationService.class).getNodeId()).append(" ");
sb.append("registered at ");
sb.append(DateUtility.format(new Date(), "yyyy-MM-dd HH:mm:ss,SSS"));
return sb.toString();
}
}