org.grouter.common.jms.TopicSenderDestination.java Source code

Java tutorial

Introduction

Here is the source code for org.grouter.common.jms.TopicSenderDestination.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.grouter.common.jms;

import static org.grouter.common.jndi.ServiceLocatorContextAware.*;

import java.io.*;
import java.util.*;
import java.lang.IllegalStateException;

import javax.jms.*;
import javax.naming.*;

import org.apache.commons.lang.builder.*;
import org.apache.log4j.*;
import org.grouter.common.jndi.JNDIUtils;
import org.grouter.common.jndi.ServiceLocatorException;

/**
 * See {@link org.grouter.common.jms.AbstractDestination} and use abstract interface to concrete
 * implementations.
 *
 * @author Georges Polyzois
 */
public class TopicSenderDestination extends AbstractSenderDestination {
    // The logger.
    private static Logger logger = Logger.getLogger(TopicSenderDestination.class);
    // Connection to the Topic.
    private TopicConnection topicConnection;
    // Sender to the Topic.
    protected TopicPublisher topicPublisher;
    // Session to the Topic.
    private TopicSession topicSession;
    // Used for request / reply on the same session.
    private TemporaryTopic temporaryTopic;

    /**
     * Constructor for use of a transactional (commit/rollback) way of handling
     * acknowledge of messages (code needs to use session to handle this).
     * See other constructor if you are using acknowledge mode based messaging
     * where AcknowledgeMode is needed.
     *
     * @param topicName              the JNDI name of the destination queue.
     * @param topicConnectionFactory the JNDI name of the Queue connection factory to use,
     *                               if null provided then the default factory will be used specified in {@link org.grouter.common.jms.AbstractDestination}
     * @param rebindBehavior         a RebindBeahvior specifies how we are supposed
     *                               to rebind to this destination {@link org.grouter.common.jms.RebindBehavior}. If null
     *                               specified an EternalRebind will be used.
     * @param context                provide the context to use for binding to the destination
     *                               (@see javax.naming.Context).
     * @param timeToLive             set to 0 if no time to live should be uses - TTL is specified
     *                               in millisecons!!!
     */
    public TopicSenderDestination(String topicName, String topicConnectionFactory, RebindBehavior rebindBehavior,
            Context context, long timeToLive) {
        init(topicName, topicConnectionFactory, rebindBehavior, context, timeToLive, AcknowledgeMode.NONE, true,
                false);
    }

    /**
     * Constructor for use with an acknowledge mode option specified. JMS message provider
     * will take care of acknowledging messages based on mode.
     *
     * @param topicName              the JNDI name of the destination queue.
     * @param topicConnectionFactory the JNDI name of the Queue connection factory to use,
     *                               if null provided then the default factory will be used specified in {@link org.grouter.common.jms.AbstractDestination}
     * @param rebindBehavior         a RebindBeahvior specifies how we are supposed
     *                               to rebind to this destination {@link org.grouter.common.jms.RebindBehavior}. If null
     *                               specified an EternalRebind will be used.
     * @param context                provide the context to use for binding to the destination
     *                               {@link javax.naming.Context}. If null provided then the default will be used -
     *                               depends on your environemnt (jndi.properties, -Djava..., or InitialContext(new Hashtable())).
     * @param timeToLive             set to 0 if no time to live should be used - TTL is specified
     *                               in millisecons!!!
     * @param ackmode                this maps direclty to {@link javax.jms.Session} and the different types of
     *                               acknowledge modes existing there.
     */
    @SuppressWarnings({ "SameParameterValue", "SameParameterValue", "SameParameterValue", "SameParameterValue" })
    public TopicSenderDestination(String topicName, String topicConnectionFactory, RebindBehavior rebindBehavior,
            Context context, long timeToLive, AcknowledgeMode ackmode) {

        init(topicName, topicConnectionFactory, rebindBehavior, context, timeToLive, ackmode, false, false);
    }

    /**
     * Constructor for use with an acknowledge mode option specified. JMS message provider
     * will take care of acknowledging messages based on mode.
     *
     * @param topicName              the JNDI name of the destination queue.
     * @param topicConnectionFactory the JNDI name of the Queue connection factory to use,
     *                               if null provided then the default factory will be used specified in {@link org.grouter.common.jms.AbstractDestination}
     * @param rebindBehavior         a RebindBeahvior specifies how we are supposed
     *                               to rebind to this destination {@link org.grouter.common.jms.RebindBehavior}. If null
     *                               specified an EternalRebind will be used.
     * @param context                provide the context to use for binding to the destination
     *                               {@link javax.naming.Context}. If null provided then the default will be used -
     *                               depends on your environemnt (jndi.properties, -Djava..., or InitialContext(new Hashtable())).
     * @param timeToLive             set to 0 if no time to live should be used - TTL is specified
     *                               in millisecons!!!
     * @param ackmode                this maps direclty to {@link javax.jms.Session} and the different types of
     *                               acknowledge modes existing there.
     * @param useTemporaryQueue      will create a temporary topic for this session
     */
    public TopicSenderDestination(String topicName, String topicConnectionFactory, RebindBehavior rebindBehavior,
            Context context, long timeToLive, AcknowledgeMode ackmode, boolean useTemporaryQueue) {
        init(topicName, topicConnectionFactory, rebindBehavior, context, timeToLive, ackmode, false,
                useTemporaryQueue);
    }

    /**
     * Initializer used by constructors.
     *
     * @param topicName              String
     * @param topicConnectionFactory String
     * @param theRebindBehavior      RebindBehavior
     * @param thecontext             Context
     * @param timeToLive             long
     * @param ackmode                AcknowledgeMode
     * @param isTransactional        boolean
     * @param useTemporaryTopic      boolean
     */
    private void init(String topicName, String topicConnectionFactory, RebindBehavior theRebindBehavior,
            Context thecontext, long timeToLive, AcknowledgeMode ackmode, boolean isTransactional,
            boolean useTemporaryTopic) {
        int mappedacknowledgeMode = getAcknowledgeMode(ackmode);
        if (thecontext == null) {
            try {
                context = new InitialContext();
            } catch (NamingException ex) {
                logger.error("Failed creating default InitialContext.", ex);
                //can not do anything without a context
                return;
            }
        } else {
            context = thecontext;
        }
        useTemporaryReplyDestination = useTemporaryTopic;
        JNDIUtils.printJNDI(context, logger);
        destinationName = topicName;
        this.acknowledgeMode = mappedacknowledgeMode;
        this.isTransactional = isTransactional;
        this.timeToLive = timeToLive;
        try {
            serviceLocatorContextAware = getInstance();
        } catch (ServiceLocatorException ex) {
            logger.warn(ex, ex);
        }
        if (topicConnectionFactory != null) {
            connectionFactory = topicConnectionFactory;
        }
        // Set default rebind behavior - can be changed dynamically using setter
        if (theRebindBehavior != null) {
            rebindBehavior = theRebindBehavior;
        }
        // Register an exceptionlistener
        exceptionListener = new SystemJMSExceptionListenerHandler(this);
    }

    @Override
    public void unbind() {
        if (topicConnection == null) {
            logger.error(
                    "Connection to destination topic was null - " + "can not close null connection, returning.");
        } else {
            try {
                topicConnection.setExceptionListener(null);
                topicConnection.stop();
                if (temporaryTopic != null) {
                    temporaryTopic.delete();
                }
            } catch (JMSException e) {
                logger.error("Could not stop connection to destination.", e);
            } finally {
                try {
                    if (topicConnection != null) {
                        topicConnection.close();
                    }
                } catch (JMSException e1) {
                    logger.error("Could not close topic connection!");
                }
            }
            topicConnection = null;
            temporaryTopic = null;
        }
    }

    /**
     * Retrieves the session for this destination. This method can be used if you
     * are handling trasacted sessions and need to explicitly get hold of the session
     * to do a commit or rollback.
     *
     * @return TopicSession
     */
    public TopicSession getTopicSession() {
        return topicSession;
    }

    @Override
    public void bind() {
        try {
            // Find ConnectionFactory
            final TopicConnectionFactory topicConnectionFactory = getInstance()
                    .getTopicConnectionFactory(connectionFactory, context);
            // Get queue
            final Topic topic = getInstance().getTopic(destinationName, context);
            // Create conneciton to queue
            topicConnection = topicConnectionFactory.createTopicConnection();
            // Register an exceptionlistener
            topicConnection.setExceptionListener(exceptionListener);
            topicSession = topicConnection.createTopicSession(isTransactional, acknowledgeMode);
            topicPublisher = topicSession.createPublisher(topic);
            if (timeToLive > 0) {
                topicPublisher.setTimeToLive(timeToLive);
            }
            if (useTemporaryReplyDestination) {
                temporaryTopic = topicSession.createTemporaryTopic();
                logger.debug("TemporaryTopic created for this session " + temporaryTopic);
            }
            topicConnection.start();
            logger.info("Bound to destination " + destinationName);
        } catch (JMSException e) {
            logger.error("Got exception with JMS provider during bind to destination " + destinationName
                    + ". Error code : " + e.getErrorCode(), e);
            rebind(this);
        } catch (ServiceLocatorException ex) {
            logger.error("Got exception with JMS provider during bind to destination " + destinationName + ".", ex);
            rebind(this);
        }
    }

    @Override
    public void sendMessage(String message) {
        try {
            ObjectMessage msg = this.topicSession.createObjectMessage(message);
            setJMSHeader(msg);
            topicPublisher.send(msg);
            logger.debug("Message sent to destination : " + destinationName);
        } catch (JMSException ex) {
            logger.error("Failed sending message to JMS provider using destination " + destinationName
                    + ". Error message : " + ex.getMessage());
        } catch (IllegalStateException ex) {
            logger.error("Failed sending message to JMS provider using destination " + destinationName
                    + ". Error message : " + ex.getMessage());
        }
    }

    @Override
    public void rebind(AbstractDestination dest) {
        rebindBehavior.rebind(this);
    }

    @Override
    public synchronized void sendMessage(Serializable message, HashMap<String, String> headerProperties) {
        try {
            ObjectMessage msg = createMessage(message, headerProperties);
            setJMSHeader(msg);
            topicPublisher.send(msg, this.acknowledgeMode, this.messagePriority, this.timeToLive);
            logger.debug("Message sent to destination : " + destinationName);
        } catch (JMSException ex) {
            logger.error("Failed sending message to JMS provider using destination " + destinationName
                    + ". Error message : " + ex.getMessage());
        } catch (IllegalStateException ex) {
            logger.error("Failed sending message to JMS provider using destination " + destinationName
                    + ". Error message : " + ex.getMessage(), ex);
        }
    }

    @Override
    public synchronized void sendMessage(Message message, int deliveryMode, int messagePriority, long timeToLive) {
        try {
            setJMSHeader(message);
            topicPublisher.send(message, deliveryMode, messagePriority, timeToLive);
            logger.debug("Message sent to destination : " + destinationName);
        } catch (JMSException ex) {
            logger.error("Failed sending message to JMS provider using destination " + destinationName
                    + ". Error message : " + ex.getMessage(), ex);
        } catch (IllegalStateException ex) {
            logger.error("Failed sending message to JMS provider using destination " + destinationName
                    + ". Error message : " + ex.getMessage(), ex);
        }
    }

    @Override
    public Session getSession() {
        return topicSession;
    }

    @Override
    public synchronized void sendMessage(Message message) {
        try {
            setJMSHeader(message);
            topicPublisher.send(message);
            logger.debug("Message sent to destination : " + destinationName);
        } catch (JMSException ex) {
            logger.error("Failed sending message to JMS provider using destination " + destinationName
                    + ". Error message : " + ex.getMessage(), ex);
        } catch (IllegalStateException ex) {
            logger.error("Failed sending message to JMS provider using destination " + destinationName
                    + ". Error message : " + ex.getMessage(), ex);
        }
    }

    /**
     * <b>See documentation in {@link org.grouter.common.jms.AbstractSenderDestination#sendMessage(java.io.Serializable,int,int,long,java.util.HashMap)}.</b><br>
     * <br>
     */
    public void sendMessage(Serializable message, int deliveryMode, int messagePriority, long timeToLive,
            HashMap<String, String> headerProperties) {
        try {
            ObjectMessage msg = createMessage(message, headerProperties);
            setJMSHeader(msg);
            topicPublisher.send(msg, deliveryMode, messagePriority, timeToLive);
            logger.debug("Message sent to destination : " + destinationName);
        } catch (JMSException ex) {
            logger.error("Failed sending message to JMS provider using destination " + destinationName
                    + ". Error message : " + ex.getMessage(), ex);
        } catch (IllegalStateException ex) {
            logger.error("Failed sending message to JMS provider using destination " + destinationName
                    + ". Error message : " + ex.getMessage(), ex);
        }
    }

    @Override
    public void sendMessage(Serializable message) {
        try {
            ObjectMessage msg = createMessage(message, null);
            topicPublisher.send(msg);
            logger.debug("Message sent to destination : " + destinationName);
        } catch (JMSException ex) {
            logger.error("Failed sending message to JMS provider using destination " + destinationName
                    + ". Error message : " + ex.getMessage(), ex);
        } catch (IllegalStateException ex) {
            logger.error("Failed sending message to JMS provider using destination " + destinationName
                    + ". Error message : " + ex.getMessage(), ex);
        }
    }

    /**
     * <b>See documentation in {@link org.grouter.common.jms.AbstractSenderDestination#sendMessage(java.io.Serializable,java.util.HashMap)}.</b><br>
     * <br>
     *
     * @param message a serializable object instance
     * @param headerProperties properties to store in header for JMS message
     *
     * @return ObjectMessage an object message
     */
    private ObjectMessage createMessage(Serializable message, HashMap<String, String> headerProperties) {
        ObjectMessage msg = null;
        try {
            msg = topicSession.createObjectMessage(message);
            msg.clearProperties();
            if (headerProperties != null) {
                for (String key : headerProperties.keySet()) {
                    String value = headerProperties.get(key);
                    msg.setStringProperty(key, value);
                }
            }
        } catch (JMSException e) {
            logger.warn("Failed setting header for message.", e);
        }
        return msg;
    }

    /**
     * Set header for JMS message. Only JMSReplyTo, JMSCorrelationID and JMSType can be set using setters.
     *
     * @param msg Message
     * @throws javax.jms.JMSException  a JMSException
     */
    private void setJMSHeader(Message msg) throws JMSException {
        if (useTemporaryReplyDestination && temporaryTopic != null) {
            logger.debug("Using a temporary destination for this session.");
            msg.setJMSReplyTo(temporaryTopic);
        }
    }

    /**
     * <br>     for listener...
     */
    /*    public void sendReplyToTemporaryDestination(Message request)
        {
    TemporaryTopic replyTopic = null;
    TopicPublisher tempsender = null;
    String temporaryDestinationName = null;
    try
    {
        if (request.getJMSReplyTo() == null)
        {
            throw new IllegalStateException(
                    "The sender of this message has not entered a JMSReplyTo field - " +
                            "impossible to send reply on temporary destination!!");
        }
        temporaryDestinationName = request.getJMSReplyTo().toString();
        request.setJMSCorrelationID(request.getJMSMessageID());
        logger.debug("JMSCorrelationID was set!!!" +
                request.getJMSCorrelationID());
        replyTopic = (TemporaryTopic) request.getJMSReplyTo();
        tempsender = topicSession.createPublisher(replyTopic);
        logger.debug("Created a tempsender and sending reply to " +
                replyTopic);
        tempsender.send(request);
    }
    catch (JMSException ex)
    {
        //ignore
        logger.warn("Failed sending reply on temporary destination : " + temporaryDestinationName);
    }
    finally
    {
        try
        {
            if (tempsender != null)
            {
                tempsender.close();
            }
            if (replyTopic != null)
            {
                replyTopic.delete();
            }
        } catch (JMSException ex1)
        {
            //ignore
        }
    }
        }
    */

    /**
     * Getter for temporary destination.
     *
     * @return TemporaryTopic
     */

    public TemporaryTopic getTemporaryDestination() {
        if (useTemporaryReplyDestination) {
            return temporaryTopic;
        } else {
            throw new IllegalStateException("You have used this destination in a wrong way. Have you "
                    + "used correct constructor for temporary destinations?");
        }
    }

    @Override
    public Message waitAndGetReplyFromTemporaryDestination(long waitForMs) {
        TopicSubscriber receiver = null;
        try {
            if (!useTemporaryReplyDestination) {
                throw new IllegalStateException("You have used this destination in a wrong way. Have you "
                        + "used correct constructor for temporary destinations?");
            }
            receiver = topicSession.createSubscriber(getTemporaryDestination());
            return receiver.receive(waitForMs);
        } catch (JMSException ex) {
            logger.warn("Waiting for reply on temp topic failed", ex);
            return null;
        } finally {
            try {
                if (receiver != null) {
                    receiver.close();
                }
            } catch (JMSException ex1) {
                //ignore
            }
        }
    }

    /**
     * Reflection to string - uses org.apache.commons.lang.builder.ToStringBuilder.
     *
     * @return String
     * @see org.apache.commons.lang.builder.ToStringBuilder#reflectionToString
     */
    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

}