org.overlord.sramp.events.jms.JMSEventProducer.java Source code

Java tutorial

Introduction

Here is the source code for org.overlord.sramp.events.jms.JMSEventProducer.java

Source

/*
 * Copyright 2014 JBoss Inc
 *
 * 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.overlord.sramp.events.jms;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.broker.BrokerService;
import org.apache.commons.lang.StringUtils;
import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.BaseArtifactType;
import org.overlord.sramp.common.SrampConfig;
import org.overlord.sramp.common.SrampConstants;
import org.overlord.sramp.common.ontology.SrampOntology;
import org.overlord.sramp.events.ArtifactUpdateEvent;
import org.overlord.sramp.events.EventProducer;
import org.overlord.sramp.events.OntologyUpdateEvent;
import org.overlord.sramp.events.jms.i18n.Messages;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.ArrayList;
import java.util.List;

/**
 * Provides a JMS implementation of the {@link EventProducer}.
 *
 * During {@link #startup()}, we check for the existence of a JMS
 * "ConnectionFactory" and any configured topics/queues, both through JNDI
 * names. If they exist, it means we're in a JavaEE environment and things are
 * correctly configured. We simply use the existing JMS framework and the
 * pre-existing topics/queues.
 *
 * Otherwise, we assume we're on a non-JavaEE server (EAP without
 * standalone-full, etc). We then create an embedded ActiveMQ broker over a TCP
 * port, then programmatically create all topics/queues. External clients can
 * then connect to it in one of two ways: 1.) Simply use the ActiveMQ libs and
 * API 2.) The ActiveMQ broker provides a lightweight JNDI implementation and
 * automatically exposes the ConnectionFactory (literally named
 * "ConnectionFactory"). To expose the topics/queues, the *client app* needs to
 * include a jndi.properties file (and ActiveMQ jar) on the classpath. The
 * contents should contain something like
 * "topic.[jndi name] = [activemq topic name]". [jndi name] is then available to
 * the client. Other than that properties file, the client is able to use
 * generic JNDI and JMS without any ActiveMQ APIs.
 *
 * @author Brett Meyer
 */
public class JMSEventProducer implements EventProducer {

    public static final String JMS_TYPE_ARTIFACT_CREATED = "sramp:artifactCreated"; //$NON-NLS-1$
    public static final String JMS_TYPE_ARTIFACT_UPDATED = "sramp:artifactUpdated"; //$NON-NLS-1$
    public static final String JMS_TYPE_ARTIFACT_DELETED = "sramp:artifactDeleted"; //$NON-NLS-1$
    public static final String JMS_TYPE_ONTOLOGY_CREATED = "sramp:ontologyCreated"; //$NON-NLS-1$
    public static final String JMS_TYPE_ONTOLOGY_UPDATED = "sramp:ontologyUpdated"; //$NON-NLS-1$
    public static final String JMS_TYPE_ONTOLOGY_DELETED = "sramp:ontologyDeleted"; //$NON-NLS-1$

    private static Logger LOG = LoggerFactory.getLogger(JMSEventProducer.class);

    private Connection connection = null;

    private Session session = null;

    private final List<Destination> destinations = new ArrayList<Destination>();

    @Override
    public void startup() {
        if (SrampConfig.isJmsEnabled()) {
            try {
                String connectionFactoryName = SrampConfig.getConfigProperty(
                        SrampConstants.SRAMP_CONFIG_EVENT_JMS_CONNECTIONFACTORY, "ConnectionFactory"); //$NON-NLS-1$

                // Note that both properties end up doing the same thing.  Technically, we could combine both into one
                // single sramp.config.events.jms.destinations, but leaving them split for readability.
                String topicNamesProp = SrampConfig.getConfigProperty(SrampConstants.SRAMP_CONFIG_EVENT_JMS_TOPICS,
                        ""); //$NON-NLS-1$
                String[] topicNames = new String[0];
                if (StringUtils.isNotEmpty(topicNamesProp)) {
                    topicNames = topicNamesProp.split(","); //$NON-NLS-1$
                }
                String queueNamesProp = SrampConfig.getConfigProperty(SrampConstants.SRAMP_CONFIG_EVENT_JMS_QUEUES,
                        ""); //$NON-NLS-1$
                String[] queueNames = new String[0];
                if (StringUtils.isNotEmpty(queueNamesProp)) {
                    queueNames = queueNamesProp.split(","); //$NON-NLS-1$
                }

                try {
                    // First, see if a ConnectionFactory and Topic/Queue exists on JNDI.  If so, assume JMS is properly
                    // setup in a Java EE container and simply use it.

                    ConnectionFactory connectionFactory = (ConnectionFactory) jndiLookup(connectionFactoryName);
                    connection = connectionFactory.createConnection();
                    session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

                    for (String topicName : topicNames) {
                        Topic topic = (Topic) jndiLookup(topicName);
                        destinations.add(topic);
                    }

                    for (String queueName : queueNames) {
                        Queue queue = (Queue) jndiLookup(queueName);
                        destinations.add(queue);
                    }
                } catch (NamingException e) {
                    // Otherwise, JMS wasn't setup. Assume we need to start an embedded
                    // ActiveMQ broker and create the destinations.

                    String bindAddress = "tcp://localhost:" //$NON-NLS-1$
                            + SrampConfig.getConfigProperty(SrampConstants.SRAMP_CONFIG_EVENT_JMS_PORT, "61616"); //$NON-NLS-1$

                    LOG.warn(Messages.i18n.format("org.overlord.sramp.events.jms.embedded_broker", bindAddress)); //$NON-NLS-1$

                    session = null;
                    destinations.clear();

                    BrokerService broker = new BrokerService();
                    broker.addConnector(bindAddress);
                    broker.start();

                    // Event though we added a TCP connector, above, ActiveMQ also exposes the broker over the "vm"
                    // protocol. It optimizes performance for connections on the same JVM.
                    ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://localhost"); //$NON-NLS-1$
                    initActiveMQ(connectionFactory, topicNames, queueNames);
                }
            } catch (Exception e) {
                LOG.error(e.getMessage(), e);
            }
        }
    }

    private void initActiveMQ(ConnectionFactory connectionFactory, String[] topicNames, String[] queueNames)
            throws Exception {
        connection = connectionFactory.createConnection();
        connection.start();
        session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

        for (String topicName : topicNames) {
            destinations.add(session.createTopic(topicName));
        }

        for (String queueName : queueNames) {
            destinations.add(session.createQueue(queueName));
        }
    }

    @Override
    public void artifactCreated(BaseArtifactType artifact) {
        if (SrampConfig.isJmsEnabled()) {
            publishEvent(artifact, JMS_TYPE_ARTIFACT_CREATED);
        }
    }

    @Override
    public void artifactUpdated(BaseArtifactType updatedArtifact, BaseArtifactType oldArtifact) {
        if (SrampConfig.isJmsEnabled()) {
            ArtifactUpdateEvent event = new ArtifactUpdateEvent(updatedArtifact, oldArtifact);
            publishEvent(event, JMS_TYPE_ARTIFACT_UPDATED);
        }
    }

    @Override
    public void artifactDeleted(BaseArtifactType artifact) {
        if (SrampConfig.isJmsEnabled()) {
            publishEvent(artifact, JMS_TYPE_ARTIFACT_DELETED);
        }
    }

    @Override
    public void ontologyCreated(SrampOntology ontology) {
        if (SrampConfig.isJmsEnabled()) {
            publishEvent(ontology, JMS_TYPE_ONTOLOGY_CREATED);
        }
    }

    @Override
    public void ontologyUpdated(SrampOntology updatedOntology, SrampOntology oldOntology) {
        if (SrampConfig.isJmsEnabled()) {
            OntologyUpdateEvent event = new OntologyUpdateEvent(updatedOntology, oldOntology);
            publishEvent(event, JMS_TYPE_ONTOLOGY_UPDATED);
        }
    }

    @Override
    public void ontologyDeleted(SrampOntology ontology) {
        if (SrampConfig.isJmsEnabled()) {
            publishEvent(ontology, JMS_TYPE_ONTOLOGY_DELETED);
        }
    }

    private void publishEvent(Object payload, String type) {
        for (Destination destination : destinations) {
            MessageProducer producer = null;
            try {
                producer = session.createProducer(destination);
                TextMessage textMessage = session.createTextMessage();
                textMessage.setJMSType(type);

                ObjectMapper mapper = new ObjectMapper();
                String text = mapper.writeValueAsString(payload);
                textMessage.setText(text);

                producer.send(textMessage);
            } catch (Exception e) {
                LOG.error(e.getMessage(), e);
            } finally {
                if (producer != null) {
                    try {
                        producer.close();
                    } catch (Exception e) {
                    }
                }
            }
        }
    }

    private Object jndiLookup(String name) throws NamingException {
        Context initContext = new InitialContext();
        try {
            Context jndiContext = (Context) initContext.lookup("java:comp/env"); //$NON-NLS-1$
            return jndiContext.lookup(name);
        } catch (NamingException e) {
            // EAP (no namespace)
            Context jndiContext = (Context) initContext.lookup("java:"); //$NON-NLS-1$
            return jndiContext.lookup(name);
        }
    }

    @Override
    public void shutdown() {
        if (SrampConfig.isJmsEnabled()) {
            try {
                session.close();
            } catch (Exception e) {
            }
            try {
                connection.close();
            } catch (Exception e) {
            }
        }
    }
}