com.atomikos.jms.extra.AbstractJmsSenderTemplate Maven / Gradle / Ivy
/**
* Copyright (C) 2000-2020 Atomikos
*
* LICENSE CONDITIONS
*
* See http://www.atomikos.com/Main/WhichLicenseApplies for details.
*/
package com.atomikos.jms.extra;
import java.io.Serializable;
import java.util.Map;
import java.util.Map.Entry;
import javax.jms.BytesMessage;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.Topic;
import javax.transaction.Status;
import javax.transaction.SystemException;
import com.atomikos.icatch.jta.UserTransactionManager;
import com.atomikos.jms.AtomikosConnectionFactoryBean;
import com.atomikos.jms.internal.AtomikosJMSException;
import com.atomikos.jms.internal.AtomikosTransactionRequiredJMSException;
import com.atomikos.logging.Logger;
import com.atomikos.logging.LoggerFactory;
/**
* Common functionality for the sender templates.
*
*/
public abstract class AbstractJmsSenderTemplate implements JmsSenderTemplate
{
private static final Logger LOGGER = LoggerFactory.createLogger(AbstractJmsSenderTemplate.class);
private AtomikosConnectionFactoryBean connectionFactoryBean;
private String user;
private String password;
private Destination destination;
private String destinationName;
private Destination replyToDestination;
private String replyToDestinationName;
private int deliveryMode;
private int priority;
private long timeToLive;
private boolean inited;
protected AbstractJmsSenderTemplate()
{
// set default values according to Sun's JMS javadocs
setTimeToLive ( 0 );
setDeliveryMode ( javax.jms.DeliveryMode.PERSISTENT );
setPriority ( 4 );
}
protected abstract Session getOrRefreshSession ( Connection c ) throws JMSException;
protected abstract Connection getOrReuseConnection() throws JMSException;
protected abstract void afterUseWithoutErrors ( Connection c , Session s ) throws JMSException;
protected void destroy ( Connection c , Session s)
throws JMSException {
try {
if ( s != null ) s.close();
} catch ( JMSException warn ) {
LOGGER.logWarning ( this + ": error closing session" , warn);
}
try {
if ( c != null ) c.close();
} catch ( JMSException warn ) {
LOGGER.logWarning ( this + ": error closing connection" , warn);
}
}
protected synchronized Connection refreshConnection() throws JMSException {
Connection connection = null;
if ( getDestinationName() == null )
throw new JMSException ( "Please call setDestination or setDestinationName first!" );
if ( user != null ) {
connection = connectionFactoryBean.createConnection (
user, password );
} else {
connection = connectionFactoryBean.createConnection ();
}
connection.start ();
return connection;
}
/**
* Initializes the session for sending.
* Call this method first.
*/
public void init() throws JMSException
{
if ( ! inited ) {
if ( connectionFactoryBean == null ) throw new IllegalStateException ( "Property 'atomikosConnectionFactoryBean' must be set first!" );
if ( getDestinationName() == null ) {
throw new IllegalStateException ( "Property 'destination' or 'destinationName' must be set first!" );
}
StringBuffer msg = new StringBuffer();
msg.append ( this + ":configured with [" );
msg.append ( "user=" ).append ( getUser() ).append ( ", " );
msg.append ( "password=" ).append ( password ).append ( ", " );
msg.append ( "deliveryMode=" ).append ( getDeliveryMode() ).append ( ", " );
msg.append ( "timeToLive=" ).append ( getTimeToLive() ).append ( ", " );
msg.append ( "priority=" ).append ( getPriority() ).append ( ", " );
msg.append ( "destination=" ).append( getDestinationName() ).append ( ", " );
msg.append ( "replyToDestination=" ).append ( getReplyToDestinationName() );
msg.append ( "]" );
if ( LOGGER.isTraceEnabled() ) LOGGER.logTrace ( msg.toString() );
inited = true;
}
}
private void retrieveDestinationIfNecessary() throws JMSException
{
if ( getDestination() == null ) {
String dName = getDestinationName();
RetrieveDestinationCallback cb = new RetrieveDestinationCallback ( dName );
executeCallbackInternal ( cb );
setDestination ( cb.getDestination() );
}
}
private void retrieveReplyToDestinationIfNecessary() throws JMSException
{
if ( getReplyToDestination() == null ) {
String dName = getReplyToDestinationName();
if ( dName != null ) {
RetrieveDestinationCallback cb = new RetrieveDestinationCallback ( dName );
executeCallbackInternal ( cb );
setReplyToDestination ( cb.getDestination() );
}
}
}
/**
* Sets the connection factory to use. Required.
* @param connectionFactory
*/
public void setAtomikosConnectionFactoryBean(AtomikosConnectionFactoryBean connectionFactory) {
this.connectionFactoryBean = connectionFactory;
}
public AtomikosConnectionFactoryBean getAtomikosConnectionFactoryBean() {
return connectionFactoryBean;
}
public Destination getDestination() {
return destination;
}
/**
* Sets the (provider-specific) destination name in order
* to lookup the destination (rather than providing one directly).
*
* Required, unless you set the destination directly.
*
* @param destinationName
*/
public void setDestinationName ( String destinationName )
{
this.destinationName = destinationName;
}
/**
* Sets the destination to send to. Required, unless
* you set the destinationName instead.
*
* @param destination
*/
public void setDestination(Destination destination) {
this.destination = destination;
}
private String getName(Destination d, String destinationName ) {
String ret = destinationName;
if ( ret == null ) {
if ( d instanceof Queue ) {
Queue q = ( Queue ) d;
try {
ret = q.getQueueName();
} catch ( JMSException e ) {
if ( LOGGER.isTraceEnabled() ) LOGGER.logTrace ( this + ": error retrieving queue name" , e );
}
} else if ( d instanceof Topic ) {
Topic t = ( Topic ) d;
try {
ret = t.getTopicName();
} catch ( JMSException e ) {
if ( LOGGER.isTraceEnabled() ) LOGGER.logTrace ( this + ": error retrieving topic name" , e );
}
}
}
return ret;
}
protected String getDestinationName() {
return getName ( getDestination() , destinationName );
}
protected String getReplyToDestinationName() {
return getName ( getReplyToDestination() , replyToDestinationName );
}
/**
* @return The user to connect with, or null if no explicit authentication
* is to be used.
*/
public String getUser() {
return user;
}
/**
* If this session is used for sending request/reply messages, then this
* property indicates the destination where the replies are to be sent (optional). The
* session uses this to set the JMSReplyTo header accordingly. This property
* can be omitted if no reply is needed.
*
*
* The replyToDestination should be in the same JMS vendor domain as the send
* queue. To cross domains, configure a bridge for both the request and the
* reply channels.
*/
public void setReplyToDestination(Destination destination)
{
this.replyToDestination = destination;
}
/**
* Sets the provider-specific replyToDestinationName. Optional.
*
* @param replyToDestinationName
*/
public void setReplyToDestinationName ( String replyToDestinationName )
{
this.replyToDestinationName = replyToDestinationName;
}
/**
* Gets the replyToDestination.
*
* @return
*/
public Destination getReplyToDestination() {
return replyToDestination;
}
/**
* Set the password for explicit authentication (optional).
* This is only required if
* the user has also been set.
*
* @param password
* The password.
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Set the user to use for explicit authentication (optional). If no explicit
* authentication is required then this method should not be called.
*
* @param user
*/
public void setUser(String user) {
this.user = user;
}
protected void executeCallbackInternal (
JmsSenderTemplateCallback callback ) throws JMSException {
init();
Session session = null;
Connection conn = null;
try {
conn = getOrReuseConnection();
session = getOrRefreshSession ( conn );
if ( LOGGER.isTraceEnabled() ) LOGGER.logTrace ( "Calling callback..." );
callback.doInJmsSession ( session );
if ( LOGGER.isTraceEnabled() ) LOGGER.logTrace ( "Callback done!" );
afterUseWithoutErrors ( conn , session );
} catch ( AtomikosTransactionRequiredJMSException notx ) {
destroy ( conn , session );
String msg = "The JMS session you are using requires a JTA transaction context for the calling thread and none was found." + "\n" +
"Please correct your code to start a JTA transaction before sending any message.";
LOGGER.logWarning ( msg );
AtomikosTransactionRequiredJMSException.throwAtomikosTransactionRequiredJMSException ( msg );
} catch ( JMSException e ) {
e.printStackTrace();
destroy ( conn , session );
String msg = this + ": error in sending JMS message";
AtomikosJMSException.throwAtomikosJMSException( msg , e );
}
}
/* (non-Javadoc)
* @see com.atomikos.jms.extra.JmsSenderTemplate#executeCallback(com.atomikos.jms.extra.JmsSenderTemplateCallback)
*/
public void executeCallback(JmsSenderTemplateCallback callback) throws JMSException {
init();
retrieveDestinationIfNecessary();
retrieveReplyToDestinationIfNecessary();
UserTransactionManager tm = new UserTransactionManager ();
try {
if ( tm.getStatus () != Status.STATUS_ACTIVE )
throw new JMSException ( "This method requires an active transaction!" );
} catch ( SystemException e ) {
LOGGER.logError ( this +": error in getting transaction status", e );
throw new RuntimeException ( e.getMessage () );
}
executeCallbackInternal ( callback );
}
/**
* @return The deliverymode for messages sent in this session.
*/
public int getDeliveryMode() {
return deliveryMode;
}
/**
* @return The priority for messages sent in this session.
*/
public int getPriority() {
return priority;
}
/**
* @return The timeToLive for messages sent in this session.
*/
public long getTimeToLive() {
return timeToLive;
}
/**
*
* Set the deliverymode for messages sent in this session (optional). Defaults to
* persistent.
*
* @param
*/
public void setDeliveryMode(int i) {
deliveryMode = i;
}
/**
* Set the priority for messages sent in this session (optional). Defaults to 4.
*
* @param
*/
public void setPriority(int i) {
priority = i;
}
/**
* Set the time to live for messages sent in this session (optional). Defaults to 0.
*
* @param
*/
public void setTimeToLive(long l) {
timeToLive = l;
}
/* (non-Javadoc)
* @see com.atomikos.jms.extra.JmsSenderTemplate#sendTextMessage(java.lang.String)
*/
@Override
public void sendTextMessage(String content) throws JMSException {
retrieveDestinationIfNecessary();
retrieveReplyToDestinationIfNecessary();
MessageCallback cb = new MessageCallback ( getDestination() , getReplyToDestination() , getDeliveryMode() , getPriority() , getTimeToLive() ) {
@Override
Message createMessage(Session session) throws JMSException {
return session.createTextMessage(content);
}
};
executeCallback ( cb );
}
/* (non-Javadoc)
* @see com.atomikos.jms.extra.JmsSenderTemplate#sendMapMessage(java.util.Map)
*/
@Override
public void sendMapMessage(Map content) throws JMSException {
retrieveDestinationIfNecessary();
retrieveReplyToDestinationIfNecessary();
MessageCallback cb = new MessageCallback ( getDestination() , getReplyToDestination() , getDeliveryMode() , getPriority() , getTimeToLive() ) {
@Override
Message createMessage(Session session) throws JMSException {
MapMessage message = session.createMapMessage();
for (Entry element : content.entrySet()) {
message.setObject(element.getKey(), element.getValue());
}
return message;
}
};
executeCallback ( cb );
}
/* (non-Javadoc)
* @see com.atomikos.jms.extra.JmsSenderTemplate#sendObjectMessage(java.io.Serializable)
*/
@Override
public void sendObjectMessage(Serializable content) throws JMSException {
retrieveDestinationIfNecessary();
retrieveReplyToDestinationIfNecessary();
MessageCallback cb = new MessageCallback ( getDestination() , getReplyToDestination() , getDeliveryMode() , getPriority() , getTimeToLive() ) {
@Override
Message createMessage(Session session) throws JMSException {
return session.createObjectMessage(content);
}
};
executeCallback ( cb );
}
/* (non-Javadoc)
* @see com.atomikos.jms.extra.JmsSenderTemplate#sendBytesMessage(byte[])
*/
@Override
public void sendBytesMessage(byte[] content) throws JMSException {
retrieveDestinationIfNecessary();
retrieveReplyToDestinationIfNecessary();
MessageCallback cb = new MessageCallback ( getDestination() , getReplyToDestination() , getDeliveryMode() , getPriority() , getTimeToLive() ) {
@Override
Message createMessage(Session session) throws JMSException {
BytesMessage msg = session.createBytesMessage();
msg.writeBytes ( content );
return msg;
}
};
executeCallback ( cb );
}
/**
* Closes all resources.
*/
public void close() {
try {
Connection c = getOrReuseConnection();
Session s = getOrRefreshSession(c);
destroy(c, s);
} catch (JMSException e) {
LOGGER.logWarning ( this + ": error closing" , e );
}
connectionFactoryBean.close();
}
}