/* * 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.artificer.events.jms; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang.StringUtils; import org.artificer.common.ArtificerConfig; import org.artificer.common.ArtificerConstants; import org.artificer.common.ontology.ArtificerOntology; import org.artificer.events.ArtifactUpdateEvent; import org.artificer.events.EventProducer; import org.artificer.events.OntologyUpdateEvent; import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.BaseArtifactType; 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. * * @author Brett Meyer */ public class JMSEventProducer implements EventProducer { public static final String JMS_TYPE_ARTIFACT_CREATED = "artificer:artifactCreated"; public static final String JMS_TYPE_ARTIFACT_UPDATED = "artificer:artifactUpdated"; public static final String JMS_TYPE_ARTIFACT_DELETED = "artificer:artifactDeleted"; public static final String JMS_TYPE_ONTOLOGY_CREATED = "artificer:ontologyCreated"; public static final String JMS_TYPE_ONTOLOGY_UPDATED = "artificer:ontologyUpdated"; public static final String JMS_TYPE_ONTOLOGY_DELETED = "artificer:ontologyDeleted"; 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 (ArtificerConfig.isJmsEnabled()) { try { String connectionFactoryName = ArtificerConfig.getConfigProperty( ArtificerConstants.ARTIFICER_CONFIG_EVENT_JMS_CONNECTIONFACTORY, "ConnectionFactory"); // 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 = ArtificerConfig.getConfigProperty(ArtificerConstants.ARTIFICER_CONFIG_EVENT_JMS_TOPICS, ""); String[] topicNames = new String[0]; if (StringUtils.isNotEmpty(topicNamesProp)) { topicNames = topicNamesProp.split(","); } String queueNamesProp = ArtificerConfig.getConfigProperty(ArtificerConstants.ARTIFICER_CONFIG_EVENT_JMS_QUEUES, ""); String[] queueNames = new String[0]; if (StringUtils.isNotEmpty(queueNamesProp)) { queueNames = queueNamesProp.split(","); } // See if a ConnectionFactory and Topic/Queue exists on JNDI. If so, assume JMS is properly // setup in a Java EE container and 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 (Exception e) { LOG.error(e.getMessage(), e); } } } @Override public void artifactCreated(BaseArtifactType artifact) { if (ArtificerConfig.isJmsEnabled()) { publishEvent(artifact, JMS_TYPE_ARTIFACT_CREATED); } } @Override public void artifactUpdated(BaseArtifactType updatedArtifact, BaseArtifactType oldArtifact) { if (ArtificerConfig.isJmsEnabled()) { ArtifactUpdateEvent event = new ArtifactUpdateEvent(updatedArtifact, oldArtifact); publishEvent(event, JMS_TYPE_ARTIFACT_UPDATED); } } @Override public void artifactDeleted(BaseArtifactType artifact) { if (ArtificerConfig.isJmsEnabled()) { publishEvent(artifact, JMS_TYPE_ARTIFACT_DELETED); } } @Override public void ontologyCreated(ArtificerOntology ontology) { if (ArtificerConfig.isJmsEnabled()) { publishEvent(ontology, JMS_TYPE_ONTOLOGY_CREATED); } } @Override public void ontologyUpdated(ArtificerOntology updatedOntology, ArtificerOntology oldOntology) { if (ArtificerConfig.isJmsEnabled()) { OntologyUpdateEvent event = new OntologyUpdateEvent(updatedOntology, oldOntology); publishEvent(event, JMS_TYPE_ONTOLOGY_UPDATED); } } @Override public void ontologyDeleted(ArtificerOntology ontology) { if (ArtificerConfig.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"); return jndiContext.lookup(name); } catch (NamingException e) { // EAP (no namespace) Context jndiContext = (Context) initContext.lookup("java:"); return jndiContext.lookup(name); } } @Override public void shutdown() { if (ArtificerConfig.isJmsEnabled()) { try { session.close(); } catch (Exception e) { } try { connection.close(); } catch (Exception e) { } } } }