org.carewebframework.amqp.rabbitmq.GlobalEventDispatcher.java Source code

Java tutorial

Introduction

Here is the source code for org.carewebframework.amqp.rabbitmq.GlobalEventDispatcher.java

Source

/**
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
 * If a copy of the MPL was not distributed with this file, You can obtain one at
 * http://mozilla.org/MPL/2.0/.
 * 
 * This Source Code Form is also subject to the terms of the Health-Related Additional
 * Disclaimer of Warranty and Limitation of Liability available at
 * http://www.carewebframework.org/licensing/disclaimer.
 */
package org.carewebframework.amqp.rabbitmq;

import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.carewebframework.api.event.AbstractGlobalEventDispatcher;

import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;

/**
 * This class is responsible for communicating with the global messaging server (in this case, an
 * AMQP server). It interacts with the local event manager and is responsible for dispatching
 * (publishing) events to be distributing globally to the messaging server and receiving subscribed
 * events from the same and passing them on to the local event dispatcher for local distribution.
 */
public class GlobalEventDispatcher extends AbstractGlobalEventDispatcher implements MessageListener {

    private static final Log log = LogFactory.getLog(GlobalEventDispatcher.class);

    private final Map<String, Subscriber> subscribers = Collections
            .synchronizedMap(new HashMap<String, Subscriber>());

    private Broker broker;

    /**
     * Create the global event dispatcher.
     */
    public GlobalEventDispatcher() {
        super();
    }

    @Override
    protected String getNodeId() {
        return broker.getExchange().getName();
    }

    /**
     * Cleanup this instance.
     */
    @Override
    public void destroy() {
        super.destroy();
        removeSubscriptions();
    }

    /**
     * Remove all remote subscriptions.
     */
    private void removeSubscriptions() {
        for (final SimpleMessageListenerContainer subscriber : this.subscribers.values()) {
            try {
                subscriber.stop();
            } catch (final Throwable e) {
                log.debug("Error closing subscriber", e);//is level appropriate - previously hidden exception -afranken
            }
        }

        this.subscribers.clear();
    }

    /**
     * Queue a subscription request.
     * 
     * @see org.carewebframework.api.event.IGlobalEventDispatcher#subscribeRemoteEvent(java.lang.String,
     *      boolean)
     */
    @Override
    public void subscribeRemoteEvent(final String eventName, final boolean subscribe) {
        try {
            if (subscribe) {
                doHostSubscribe(eventName);
            } else {
                doHostUnsubscribe(eventName);
            }
        } catch (final AmqpException e) {
            log.error(e);
        }
    }

    /**
     * Registers an event subscription with the global event manager. Note that the global event
     * manager has no knowledge of each event's individual subscribers - only that the event of a
     * given name has subscribers. This is because the global event manager need only dispatch
     * events to the local event manager. The local event manager will then dispatch events to the
     * individual subscribers.
     * 
     * @param eventName Name of event.
     * @throws AmqpException RabbitMQ exception.
     */
    private void doHostSubscribe(final String eventName) throws AmqpException {

        if (this.subscribers.get(eventName) != null) {
            if (log.isDebugEnabled()) {
                log.debug(String.format("Already subscribed to Topic[%s]", eventName));
            }
            return;
        }
        if (log.isDebugEnabled()) {
            log.debug(String.format("Subscribing to Topic[%s]", eventName));
        }

        broker.declareEventQueue(eventName);
        Subscriber subscriber = new Subscriber(publisherInfo);
        subscriber.setMessageListener(this);
        subscriber.setConnectionFactory(broker.getRabbitTemplate().getConnectionFactory());
        subscriber.setQueueNames(eventName);
        subscriber.start();
        subscribers.put(eventName, subscriber);
    }

    /**
     * Removes an event subscription with the global event manager.
     * 
     * @param eventName Name of event
     * @throws AmqpException RabbitMQ exception.
     */
    private void doHostUnsubscribe(final String eventName) throws AmqpException {
        final Subscriber subscriber = this.subscribers.remove(eventName);

        if (subscriber != null) {
            log.debug(String.format("Unsubscribing Subscriber[%s] for Topic [%s].", subscriber, eventName));
            subscriber.stop();
        }
    }

    /**
     * @see org.carewebframework.api.event.IGlobalEventDispatcher#fireRemoteEvent(java.lang.String,
     *      java.io.Serializable, java.lang.String)
     */
    @Override
    public void fireRemoteEvent(final String eventName, final Serializable eventData, final String recipients) {
        try {
            doFireRemoteEvent(eventName, eventData, recipients);
        } catch (final AmqpException e) {
            log.error("Error firing remote event.", e);
        }
    }

    /**
     * Publishes the specified event to the messaging server.
     * 
     * @param eventName Name of the event.
     * @param eventData Data object associated with the event.
     * @param recipients List of recipients for the event (null or empty string means all
     *            subscribers).
     * @throws AmqpException RabbitMQ exception.
     */
    private void doFireRemoteEvent(final String eventName, final Object eventData, final String recipients)
            throws AmqpException {
        broker.sendEvent(eventName, eventData, publisherInfo.getEndpointId(), recipients);
    }

    /**
     * This is the callback for messages received from the AMQP server.
     * 
     * @param message Message received from the AMQP server.
     */
    @Override
    public void onMessage(final Message message) {
        if (log.isDebugEnabled()) {
            log.debug("Message received: " + message);
        }

        processMessage(message);
    }

    /**
     * Process a dequeued message by forwarding it to the local event manager for local delivery. If
     * the message is a ping request, send the response.
     * 
     * @param message Message to process.
     */
    protected void processMessage(final Message message) {
        try {
            String eventName = message.getMessageProperties().getReceivedRoutingKey();
            Object eventData = broker.getRabbitTemplate().getMessageConverter().fromMessage(message);
            localEventDelivery(eventName, eventData);
        } catch (final Exception e) {
            log.error("Error during local dispatch of global event.", e);
        }
    }

    /**
     * Override to do any special setup prior to processing of messages.
     * 
     * @return True if OK to proceed.
     */
    @Override
    protected boolean beginMessageProcessing() {
        return true;
    }

    /**
     * Override to do any special teardown after processing of messages.
     */
    @Override
    protected void endMessageProcessing() {

    }

    /**
     * Sets the AMQP broker.
     * 
     * @param broker The AMQP broker.
     */
    public void setBroker(Broker broker) {
        this.broker = broker;
    }

}