org.openmrs.event.EventEngine.java Source code

Java tutorial

Introduction

Here is the source code for org.openmrs.event.EventEngine.java

Source

/**
 * The contents of this file are subject to the OpenMRS Public License
 * Version 1.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://license.openmrs.org
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
 */
package org.openmrs.event;

import java.io.File;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.OpenmrsObject;
import org.openmrs.api.APIException;
import org.openmrs.util.OpenmrsUtil;
import org.springframework.jms.connection.SingleConnectionFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;

/**
 * Used by {@link Event}.
 */
public class EventEngine {

    protected final static String DELIMITER = ":";

    protected static Log log = LogFactory.getLog(Event.class);

    protected JmsTemplate jmsTemplate = null;

    protected Map<String, TopicSubscriber> subscribers = new HashMap<String, TopicSubscriber>();

    protected SingleConnectionFactory connectionFactory;

    /**
     * @see Event#fireAction(String, OpenmrsObject)
     */
    public void fireAction(String action, final Object object) {
        Destination key = getDestination(object.getClass(), action);
        fireEvent(key, object);
    }

    /**
     * @see Event#fireEvent(Destination, OpenmrsObject)
     */
    public void fireEvent(final Destination dest, final Object object) {
        EventMessage eventMessage = new EventMessage();
        if (object instanceof OpenmrsObject) {
            eventMessage.put("uuid", ((OpenmrsObject) object).getUuid());
        }
        eventMessage.put("classname", object.getClass().getName());
        eventMessage.put("action", getAction(dest));

        doFireEvent(dest, eventMessage);
    }

    /**
     * @see Event#fireEvent(String, EventMessage)
     */
    public void fireEvent(String topicName, EventMessage eventMessage) {
        if (StringUtils.isBlank(topicName)) {
            throw new APIException("Topic name cannot be null or blank");
        }
        doFireEvent(getDestination(topicName), eventMessage);
    }

    /**
     * @param dest
     * @param eventMessage
     */
    private void doFireEvent(final Destination dest, final EventMessage eventMessage) {

        initializeIfNeeded();

        jmsTemplate.send(dest, new MessageCreator() {

            @Override
            public Message createMessage(Session session) throws JMSException {
                if (log.isInfoEnabled())
                    log.info("Sending data " + eventMessage);

                MapMessage mapMessage = session.createMapMessage();
                if (eventMessage != null) {
                    for (Map.Entry<String, Serializable> entry : eventMessage.entrySet()) {
                        mapMessage.setObject(entry.getKey(), entry.getValue());
                    }
                }

                return mapMessage;
            }
        });
    }

    private synchronized void initializeIfNeeded() {
        if (jmsTemplate == null) {
            log.info("creating connection factory");
            String dataDirectory = new File(OpenmrsUtil.getApplicationDataDirectory(), "activemq-data")
                    .getAbsolutePath();
            try {
                dataDirectory = URLEncoder.encode(dataDirectory, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException("Failed to encode URI", e);
            }
            String brokerURL = "vm://localhost?broker.persistent=true&broker.dataDirectory=" + dataDirectory;
            ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory(brokerURL);
            connectionFactory = new SingleConnectionFactory(cf); // or CachingConnectionFactory ?
            jmsTemplate = new JmsTemplate(connectionFactory);
        } else {
            log.trace("messageListener already defined");
        }
    }

    /**
     * @see Event#subscribe(Class, String, EventListener)
     */
    public void subscribe(Class<?> clazz, String action, EventListener listener) {
        if (action != null) {
            Destination dest = getDestination(clazz, action);
            subscribe(dest, listener);
        } else {
            for (Event.Action a : Event.Action.values()) {
                subscribe(getDestination(clazz, a.toString()), listener);
            }
        }
    }

    /**
     * @see Event#subscribe(String, EventListener)
     */
    public void subscribe(String topicName, EventListener listener) {
        if (StringUtils.isBlank(topicName)) {
            throw new APIException("Topic name cannot be null or blank");
        }
        subscribe(getDestination(topicName), listener);
    }

    /**
     * @see Event#unsubscribe(Class, org.openmrs.event.Event.Action, EventListener)
     */
    public void unsubscribe(Class<?> clazz, Event.Action action, EventListener listener) {
        if (action != null) {
            unsubscribe(getDestination(clazz, action.toString()), listener);
        } else {
            for (Event.Action a : Event.Action.values()) {
                unsubscribe(getDestination(clazz, a.toString()), listener);
            }
        }
    }

    /**
     * @see Event#unsubscribe(String, EventListener)
     */
    public void unsubscribe(String topicName, EventListener listener) {
        if (StringUtils.isBlank(topicName)) {
            throw new APIException("Topic name cannot be null or blank");
        }
        unsubscribe(getDestination(topicName), listener);
    }

    /**
     * @see Event#getDestination(Class, String)
     */
    public Destination getDestination(final Class<?> clazz, final String action) {
        return getDestination(action.toString() + DELIMITER + clazz.getName());
    }

    /**
     * @see Event#getDestination(String)
     */
    public Destination getDestination(final String topicName) {
        return new Topic() {

            @Override
            public String getTopicName() throws JMSException {
                return topicName;
            }
        };
    }

    /**
     * @see Event#subscribe(Destination, EventListener)
     */
    public void subscribe(Destination destination, final EventListener listenerToRegister) {

        initializeIfNeeded();

        TopicConnection conn;
        Topic topic = (Topic) destination;

        try {
            conn = (TopicConnection) jmsTemplate.getConnectionFactory().createConnection();
            TopicSession session = conn.createTopicSession(false, TopicSession.AUTO_ACKNOWLEDGE);
            TopicSubscriber subscriber = session.createSubscriber(topic);
            subscriber.setMessageListener(new MessageListener() {

                @Override
                public void onMessage(Message message) {
                    listenerToRegister.onMessage(message);
                }
            });

            //Check if this is a duplicate and remove it
            String key = topic.getTopicName() + DELIMITER + listenerToRegister.getClass().getName();
            if (subscribers.containsKey(key)) {
                unsubscribe(destination, listenerToRegister);
            }

            subscribers.put(key, subscriber);
            conn.start();

        } catch (JMSException e) {
            // TODO Auto-generated catch block. Do something smarter here.
            e.printStackTrace();
        }

        //      List<EventListener> currentListeners = listeners.get(key);
        //
        //      if (currentListeners == null) {
        //         currentListeners = new ArrayList<EventListener>();
        //         currentListeners.add(listenerToRegister);
        //         listeners.put(key, currentListeners);
        //         if (log.isInfoEnabled())
        //            log.info("subscribed: " + listenerToRegister + " to key: "
        //                  + key);
        //
        //      } else {
        //         // prevent duplicates because of weird spring loading
        //         String listernToRegisterName = listenerToRegister.getClass()
        //               .getName();
        //         Iterator<EventListener> iterator = currentListeners.iterator();
        //         while (iterator.hasNext()) {
        //            EventListener lstnr = iterator.next();
        //            if (lstnr.getClass().getName().equals(listernToRegisterName))
        //               iterator.remove();
        //         }
        //
        //         if (log.isInfoEnabled())
        //            log.info("subscribing: " + listenerToRegister + " to key: "
        //                  + key);
        //
        //         currentListeners.add(listenerToRegister);
        //      }

    }

    /**
     * @see Event#unsubscribe(Destination, EventListener)
     */
    public void unsubscribe(Destination dest, EventListener listener) {

        initializeIfNeeded();

        if (dest != null) {
            Topic topic = (Topic) dest;
            try {
                String key = topic.getTopicName() + DELIMITER + listener.getClass().getName();
                if (subscribers.get(key) != null)
                    subscribers.get(key).close();

                subscribers.remove(key);
            } catch (JMSException e) {
                log.error("Failed to unsubscribe from the specified destination:", e);
            }
        }
    }

    /**
     * @see Event#setSubscription(SubscribableEventListener)
     */
    public void setSubscription(SubscribableEventListener listenerToRegister) {

        // loop over each object and each action to register
        for (Class<? extends OpenmrsObject> objectClass : listenerToRegister.subscribeToObjects()) {
            for (String action : listenerToRegister.subscribeToActions()) {
                Destination key = getDestination(objectClass, action);
                subscribe(key, listenerToRegister);
            }
        }
    }

    /**
     * @see Event#unsetSubscription(SubscribableEventListener)
     */
    public void unsetSubscription(SubscribableEventListener listenerToRegister) {
        for (Class<? extends OpenmrsObject> objectClass : listenerToRegister.subscribeToObjects()) {
            for (String action : listenerToRegister.subscribeToActions()) {
                Destination key = getDestination(objectClass, action);
                unsubscribe(key, listenerToRegister);
            }
        }
    }

    protected String getAction(final Destination dest) {
        if (dest instanceof Topic) {
            // look for delimiter and get string before that
            String topicName;
            try {
                topicName = ((Topic) dest).getTopicName();
            } catch (JMSException e) {
                // TODO fail hard here? document this in javadoc too
                return null;
            }
            int index = topicName.indexOf(DELIMITER);
            if (index < 0) {
                // uh, what? TODO: document this
                return null;
            }

            return topicName.substring(0, index);

        } else {
            // what kind of Destination is this if not a Topic??
            // TODO: document this
            return null;
        }
    }

    /**
     * Closes the underlying shared connection which will close the broker too under the hood
     */
    public void shutdown() {
        if (log.isDebugEnabled())
            log.debug("Shutting down JMS shared connection...");

        if (connectionFactory != null) {
            connectionFactory.destroy();
        }
    }
}