com.aol.advertising.qiao.emitter.AMQEmitter.java Source code

Java tutorial

Introduction

Here is the source code for com.aol.advertising.qiao.emitter.AMQEmitter.java

Source

/****************************************************************************
 * Copyright (c) 2015 AOL Inc.
 * @author:     ytung05
 *
 * 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 com.aol.advertising.qiao.emitter;

import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;

import com.aol.advertising.qiao.emitter.handler.DefaultAMQMessageCreator;
import com.aol.advertising.qiao.emitter.handler.IJmsMessageCreator;
import com.aol.advertising.qiao.exception.ConfigurationException;
import com.aol.advertising.qiao.exception.QiaoOperationException;
import com.aol.advertising.qiao.management.ConnectionController;
import com.aol.advertising.qiao.management.ConnectionController.ConnectionType;
import com.aol.advertising.qiao.management.IConnectionControlListener;
import com.aol.advertising.qiao.management.IStatsCalculatorAware;
import com.aol.advertising.qiao.management.IStatsCollectable;
import com.aol.advertising.qiao.management.metrics.IStatisticsStore;
import com.aol.advertising.qiao.management.metrics.PubStats;
import com.aol.advertising.qiao.management.metrics.PubStats.StatType;
import com.aol.advertising.qiao.management.metrics.StatsCalculator;
import com.aol.advertising.qiao.management.metrics.StatsEnum;
import com.aol.advertising.qiao.management.metrics.StatsEvent;
import com.aol.advertising.qiao.management.metrics.StatsEvent.StatsOp;
import com.aol.advertising.qiao.util.IntervalMetric;
import com.aol.advertising.qiao.util.StatsUtils;

/**
 * AMQEmitter sends data to ActiveMQ queue or topic. A message factory class can
 * be set via the msgCreator property to create a JMS Message object for each
 * data item. If none is specified, DefaultAMQMessageCreator is used.
 */
@ManagedResource
public class AMQEmitter extends AbstractDataEmitter
        implements IStatsCollectable, IConnectionControlListener, IStatsCalculatorAware {
    class Producer {
        Session session;
        MessageProducer producer;

        Producer(Session session, MessageProducer producer) {
            this.session = session;
            this.producer = producer;
        }

        public void send(Message msg) throws JMSException {
            producer.send(msg);
        }

        public void close() {
            try {
                producer.close();
                session.close();
            } catch (JMSException e) {
            }
        }
    }

    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    private IJmsMessageCreator<Object> msgCreator;
    private ActiveMQConnectionFactory connectionFactory;
    private String destinationName;
    private Destination destination;
    private boolean isQueue = true;
    private int numProducer = 1; // one producer per session per connection
    private boolean transacted = false;
    private int ackMode = Session.AUTO_ACKNOWLEDGE;
    private int deliveryMode;
    private String brokerURL;

    private List<Connection> connList;

    //
    private ConnectionController connectionController;

    private AtomicBoolean isInitialized = new AtomicBoolean(false);

    private ThreadLocal<Producer> localProducer = new ThreadLocal<Producer>();

    private Deque<Producer> producersStack = new ConcurrentLinkedDeque<Producer>();
    private List<Producer> producersPool = new ArrayList<Producer>();

    private String statDispatchedKey = "amq_dispatched";
    private AtomicLong numDispatched = new AtomicLong(0);

    private String statKeyDispatchTime = "amq_" + StatsEnum.EMIT_TIME.value() + "_nano";
    private IntervalMetric stats;
    private StatsCalculator statsCalculator;

    @Override
    public void init() throws Exception {
        if (isInitialized.compareAndSet(false, true)) {
            _validate();

            stats = new IntervalMetric(statKeyDispatchTime);

            _registerStatsCollector();
            _registerStatsCalculator();

            logger.info("brokerURL=" + brokerURL);
            if (brokerURL != null)
                connectionFactory.setBrokerURL(brokerURL);

            connList = new ArrayList<Connection>(numProducer);

            createJmsResources();

            if (connectionController != null) {
                connectionController.register(this, ConnectionType.OUTBOUND);
            }
        }
    }

    private void _validate() {
        if (destinationName == null || destinationName.trim().length() == 0)
            throw new ConfigurationException("Missing property destinationName");

        if (msgCreator == null)
            msgCreator = new DefaultAMQMessageCreator();
    }

    private void _registerStatsCollector() {

        final String clzname = this.getClass().getSimpleName();
        if (statsCollector != null)
            statsCollector.register(new Callable<Void>() {
                @Override
                public Void call() {
                    if (numDispatched.get() > 0) {
                        eventPublisher.publishEvent(new StatsEvent(this, clzname, funnelId, StatsOp.INCRBY,
                                statDispatchedKey, numDispatched.getAndSet(0)));

                    }

                    eventPublisher.publishEvent(new StatsEvent(this, clzname, funnelId, StatsOp.INCRBY_INTVL_STAT,
                            statKeyDispatchTime, stats.getAndReset()));

                    return null;
                }
            });

    }

    private void _registerStatsCalculator() {
        if (statsCalculator != null) {
            IStatisticsStore statsStore = StatsUtils.getStatsStore(funnelId);
            if (statsStore == null)
                throw new ConfigurationException(funnelId + " statistics store does not exist");

            Map<String, PubStats> counter_keys = new LinkedHashMap<String, PubStats>();

            PubStats pstats = new PubStats(StatType.INTERVAL_METRIC, statKeyDispatchTime, false, true, false,
                    false);
            counter_keys.put(pstats.getMetric(), pstats);

            statsCalculator.register(statsCalculator.new CalcCallable(statsStore, counter_keys));
        }

    }

    /**
     * Create JMS connectins, sessions, producers, destination objects. (one
     * producer per connection)
     *
     * @throws Exception
     */
    protected void createJmsResources() throws Exception {

        Connection conn;
        Session session;
        MessageProducer producer;

        // create connections
        logger.info("creating producers...");
        for (int i = 0; i < numProducer; i++) {
            conn = connectionFactory.createConnection();
            connList.add(conn);

            // create sessions for a connection
            session = conn.createSession(transacted, ackMode);

            if (destination == null) {
                if (isQueue) {
                    destination = session.createQueue(destinationName);
                } else {
                    destination = session.createTopic(destinationName);
                }
            }

            producer = session.createProducer(destination);
            producer.setDeliveryMode(deliveryMode);

            Producer p = new Producer(session, producer);
            producersPool.add(p);
            producersStack.add(p);
        }

        logger.info("AMQ producer(s) created, count=" + producersStack.size());
    }

    private Producer getProducer() throws QiaoOperationException {
        Producer producer = localProducer.get();
        if (producer == null) {
            producer = producersStack.poll();
            if (producer == null)
                throw new QiaoOperationException("No producer available");

            localProducer.set(producer);
        }

        return producer;
    }

    @Override
    public void process(Object data) {
        try {
            Producer producer = getProducer();
            Message msg = msgCreator.createMessage(producer.session, data);
            if (msg != null) {
                if (logger.isDebugEnabled())
                    logger.debug("send> " + data.toString());

                long ts_start = System.nanoTime();
                producer.send(msg);
                long dur = System.nanoTime() - ts_start;
                stats.update(dur);

                numDispatched.incrementAndGet();
            }
        } catch (JMSException e) {
            logger.warn(e.getClass().getName() + ": " + e.getMessage());
            running = false;
        } catch (QiaoOperationException e) {
            logger.error(e.getMessage(), e);
            running = false;
        } catch (Throwable t) {
            logger.error(t.getMessage(), t);
            running = false;
        }

    }

    @Override
    public void shutdown() {
        running = false;
        for (Producer p : producersPool) {
            p.close();
        }
    }

    @Override
    public void removeThreadLocal() {
        Producer producer = localProducer.get();
        if (producer != null) {
            producer.close();
            localProducer.remove();
        }
    }

    @ManagedOperation
    public void resetConnections() {
        producersStack.clear();

        for (Connection conn : connList) {
            try {
                conn.close();
            } catch (Exception e) {
                logger.error(e.getMessage());
            }
        }

        connList.clear();

        try {
            createJmsResources();
            this.start();
            logger.info(this.getClass().getSimpleName() + " re-started");
        } catch (Exception e) {
            logger.error("failed to create JMS resources - " + e.getClass().getName() + ": " + e.getMessage(), e);
        }

    }

    @Override
    public void reset() {
        resetConnections();
    }

    /**
     * Required to properly create the message from an event.
     *
     * @param msgCreator
     */

    public void setMsgCreator(IJmsMessageCreator<Object> msgCreator) {
        this.msgCreator = msgCreator;
    }

    public void setConnectionFactory(ActiveMQConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
    }

    public void setDestinationName(String destinationName) {
        this.destinationName = destinationName;
    }

    // public void setNumConnections(int numConnections)
    // {
    //     this.numConnections = numConnections;
    // }

    public void setTransacted(boolean transacted) {
        this.transacted = transacted;
    }

    public void setAckMode(int ackMode) {
        this.ackMode = ackMode;
    }

    public void setDeliveryMode(int deliveryMode) {
        this.deliveryMode = deliveryMode;
    }

    public void setBrokerURL(String brokerURL) {
        this.brokerURL = brokerURL;
    }

    public void setStatDispatchedKey(String statDispatchedKey) {
        this.statDispatchedKey = statDispatchedKey;
    }

    public void setConnectionController(ConnectionController connectionController) {
        this.connectionController = connectionController;
    }

    @ManagedAttribute
    public int getNumProducers() {
        return numProducer;
    }

    @ManagedAttribute
    public String getDestinationName() {
        return destinationName;
    }

    @ManagedAttribute
    public String getBrokerURL() {
        return brokerURL;
    }

    @ManagedAttribute
    public boolean isQueue() {
        return isQueue;
    }

    @ManagedAttribute
    public boolean isTransacted() {
        return transacted;
    }

    @ManagedAttribute
    public int getAckMode() {
        return ackMode;
    }

    @ManagedAttribute
    public int getDeliveryMode() {
        return deliveryMode;
    }

    @ManagedAttribute
    @Override
    public boolean isRunning() {
        return running && !isSuspended();
    }

    @Override
    public void suspend() {
        if (isSuspended.compareAndSet(false, true)) {
            localProducer = new ThreadLocal<Producer>();
        }
    }

    @Override
    public void resume() {
        if (isSuspended.compareAndSet(true, false)) {
            for (Producer p : producersPool) {
                producersStack.add(p);
            }
        }
    }

    @Override
    public boolean isSuspended() {
        return isSuspended.get();
    }

    public void setStatKeyDispatchTime(String statKeyDispatchTime) {
        this.statKeyDispatchTime = statKeyDispatchTime;
    }

    @Override
    public void setStatsCalculator(StatsCalculator statsCalculator) {
        this.statsCalculator = statsCalculator;
    }

    @Override
    public void setEmitterThreadCount(int threadCount) {
        this.numProducer = threadCount;
    }

}