org.addsimplicity.anicetus.io.jms.JMSDeliveryAdapter.java Source code

Java tutorial

Introduction

Here is the source code for org.addsimplicity.anicetus.io.jms.JMSDeliveryAdapter.java

Source

/**
 * Copyright 2008-2009 Dan Pritchett
 * 
 * Licensed 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.addsimplicity.anicetus.io.jms;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.jms.ConnectionFactory;
import javax.jms.Destination;

import org.addsimplicity.anicetus.entity.GlobalInfo;
import org.addsimplicity.anicetus.io.DeliveryAdapter;
import org.addsimplicity.anicetus.io.ExceptionHandler;
import org.addsimplicity.anicetus.io.SystemErrorExceptionHandler;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MessageConverter;

/**
 * The JMS delivery adapter publishes telemetry to a JMS topic or queue. The
 * delivery is done using Spring's JMS templates. Telemetry is published on a
 * separate thread, asynchronously to the primary application flows. This is
 * done to minimize any overhead that may be introduced into the main
 * application processing.
 * 
 * Telemetry artifacts are queued and delivered by worker threads. The
 * application can control the number of worker threads as well as the size of
 * the queue. Additionally, the behavior if the queue overruns can be
 * controlled. Telemetry can either be discarded if there is no further queue
 * space or the calling thread will be used to delivery the artifact.
 * 
 * Messages are converted to the desired format using a Spring message converter
 * implementation.
 * 
 * @author Dan Pritchett (driveawedge@yahoo.com)
 * 
 */
public class JMSDeliveryAdapter implements DeliveryAdapter, InitializingBean {
    class DeliveryTask implements Runnable {
        GlobalInfo m_telemetry;

        public DeliveryTask(GlobalInfo telemetry) {
            super();
            m_telemetry = telemetry;
        }

        public void run() {
            try {
                m_template.convertAndSend(m_telemetry);
            } catch (Throwable t) {
                m_handler.exceptionCaught(t);
            }
        }

    }

    private ConnectionFactory m_connectionFactory;

    private Destination m_destination;
    private ExceptionHandler m_handler = new SystemErrorExceptionHandler();
    private MessageConverter m_messageConverter;
    private JmsTemplate m_template;
    private int m_maxDeliveryThreads = 2;
    private int m_maxDeliveryQueue = Integer.MAX_VALUE;
    private RejectedExecutionHandler m_rejectionHandler = new ThreadPoolExecutor.DiscardPolicy();
    private ExecutorService m_executor;

    /**
     * Called by Spring once all properties have been set. This method will
     * establish the connection to the JMS broker.
     */
    public void afterPropertiesSet() throws Exception {
        BlockingQueue<Runnable> q = new LinkedBlockingQueue<Runnable>(m_maxDeliveryQueue);
        m_executor = new ThreadPoolExecutor(1, m_maxDeliveryThreads, 60l, TimeUnit.SECONDS, q,
                new DeliveryThreadFactory(), m_rejectionHandler);

        m_template = new JmsTemplate(m_connectionFactory);
        m_template.setDefaultDestination(m_destination);
        m_template.setMessageConverter(m_messageConverter);
    }

    /**
     * Get the current connection factory used to connect to the JMS broker.
     * 
     * @return the connection factory.
     */
    public ConnectionFactory getConnectionFactory() {
        return m_connectionFactory;
    }

    /**
     * Get the current destination.
     * 
     * @return the current destination.
     */
    public Destination getDestination() {
        return m_destination;
    }

    /**
     * Get the current policy for queue overruns.
     * 
     * @return the current overrun policy.
     */
    public boolean getDiscardOverrun() {
        return m_rejectionHandler instanceof ThreadPoolExecutor.DiscardPolicy;
    }

    /**
     * Get the maximum size of the delivery queue.
     * 
     * @return the maximum queue size.
     */
    public int getMaxDeliveryQueue() {
        return m_maxDeliveryQueue;
    }

    /**
     * Get the maximum number of delivery threads.
     * 
     * @return the maximum number of delivery threads
     */
    public int getMaxDeliveryThreads() {
        return m_maxDeliveryThreads;
    }

    /**
     * Get the current message converter.
     * 
     * @return the message converter.
     */
    public MessageConverter getMessageConverter() {
        return m_messageConverter;
    }

    /**
     * Send the telemetry to the JMS topic. The telemetry is queued for delivery
     * and this method will return immediately unless discarding messages is
     * disabled and the queue is full.
     * 
     * @param telemetry
     *          The telemetry to send.
     * 
     * @seeorg.addsimplicity.aniticus.support.DeliveryAdapter#sendSession(org. 
     *                                                                         addsimplicity
     *                                                                         .
     *                                                                         aniticus
     *                                                                         .
     *                                                                         entity
     *                                                                         .
     *                                                                         Session
     *                                                                         )
     */
    public void sendTelemetry(GlobalInfo telemetry) {
        m_executor.submit(new DeliveryTask(telemetry));
    }

    /**
     * Set the JMS connection factory that will be used to connect to the broker.
     * 
     * @param connectionFactory
     *          The factory used to connect to the broker.
     */
    public void setConnectionFactory(ConnectionFactory connectionFactory) {
        m_connectionFactory = connectionFactory;
    }

    /**
     * Set the destination topic or queue for delivering telemetry.
     * 
     * @param destination
     *          The topic for publishing messages.
     */
    public void setDestination(Destination destination) {
        m_destination = destination;
    }

    /**
     * Setting discard to true will cause telemetry to be dropped if the delivery
     * queue is full. This is the default setting.
     * 
     * @param discard
     *          True to discard telemetry if the queue is full.
     */
    public void setDiscardOverrun(boolean discard) {
        if (discard) {
            m_rejectionHandler = new ThreadPoolExecutor.DiscardPolicy();
        } else {
            m_rejectionHandler = new ThreadPoolExecutor.CallerRunsPolicy();
        }
    }

    /**
     * The exception handler that will be invoked if a delivery error occurs. Note
     * that the handler may be called on a different thread from the thread that
     * sets it.
     * 
     * @param handler
     *          The exception handler.
     * 
     * @see org.addsimplicity.aniticus.support.DeliveryAdapter#setExceptionHandler(org.addsimplicity.aniticus.support.ExceptionHandler)
     */
    public void setExceptionHandler(ExceptionHandler handler) {
        m_handler = handler;
    }

    /**
     * The delivery queue holds telemetry to be delivered. By default the queue
     * size is unlimited.
     * 
     * @param maxDeliveryQueue
     *          The maximum number of telemetry events that will be queued.
     */
    public void setMaxDeliveryQueue(int maxDeliveryQueue) {
        m_maxDeliveryQueue = maxDeliveryQueue;
    }

    /**
     * Delivery threads publish events to the JMS topic. By default a maximum of 2
     * threads are used.
     * 
     * @param maxDeliveryThreads
     *          The maximum threads that will be used for delivering telemetry.
     */
    public void setMaxDeliveryThreads(int maxDeliveryThreads) {
        m_maxDeliveryThreads = maxDeliveryThreads;
    }

    /**
     * The message converter is responsible for translating the telemetry artifact
     * to the JMS message structure.
     * 
     * @param messageConverter
     *          The message converter.
     */
    public void setMessageConverter(MessageConverter messageConverter) {
        m_messageConverter = messageConverter;
    }

}