All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kieker.analysis.plugin.reader.jms.JMSReader Maven / Gradle / Ivy

There is a newer version: 1.15.4
Show newest version
/***************************************************************************
 * Copyright 2013 Kieker Project (http://kieker-monitoring.net)
 *
 * 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 kieker.analysis.plugin.reader.jms;

import java.io.Serializable;
import java.util.Hashtable;
import java.util.concurrent.CountDownLatch;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageFormatException;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;

import kieker.analysis.IProjectContext;
import kieker.analysis.plugin.annotation.OutputPort;
import kieker.analysis.plugin.annotation.Plugin;
import kieker.analysis.plugin.annotation.Property;
import kieker.analysis.plugin.reader.AbstractReaderPlugin;
import kieker.common.configuration.Configuration;
import kieker.common.logging.Log;
import kieker.common.logging.LogFactory;
import kieker.common.record.IMonitoringRecord;

/**
 * Reads monitoring records from a (remote or local) JMS queue.
 * 
 * 
 * @author Andre van Hoorn, Matthias Rohr
 * 
 * @since 0.95a
 */
@Plugin(description = "A reader which reads records from a (remove or local) JMS queue",
		dependencies = "This plugin needs the file 'javax.jms-*.jar'.",
		outputPorts = {
			@OutputPort(name = JMSReader.OUTPUT_PORT_NAME_RECORDS, eventTypes = { IMonitoringRecord.class }, description = "Output Port of the JMSReader")
		},
		configuration = {
			@Property(name = JMSReader.CONFIG_PROPERTY_NAME_PROVIDERURL, defaultValue = "tcp://127.0.0.1:61616/"),
			@Property(name = JMSReader.CONFIG_PROPERTY_NAME_DESTINATION, defaultValue = "queue1"),
			@Property(name = JMSReader.CONFIG_PROPERTY_NAME_FACTORYLOOKUP, defaultValue = "org.apache.activemq.jndi.ActiveMQInitialContextFactory")
		})
public final class JMSReader extends AbstractReaderPlugin {

	/** The name of the output port delivering the received records. */
	public static final String OUTPUT_PORT_NAME_RECORDS = "monitoringRecords";

	/** The name of the configuration determining the JMS provider URL. */
	public static final String CONFIG_PROPERTY_NAME_PROVIDERURL = "jmsProviderUrl";
	/** The name of the configuration determining the JMS destination (e.g. queue1). */
	public static final String CONFIG_PROPERTY_NAME_DESTINATION = "jmsDestination";
	/** The name of the configuration determining the name of the used JMS factory. */
	public static final String CONFIG_PROPERTY_NAME_FACTORYLOOKUP = "jmsFactoryLookupName";

	static final Log LOG = LogFactory.getLog(JMSReader.class); // NOPMD package for inner class

	private final String jmsProviderUrl;
	private final String jmsDestination;
	private final String jmsFactoryLookupName;
	private final CountDownLatch cdLatch = new CountDownLatch(1);

	/**
	 * Creates a new instance of this class using the given parameters.
	 * 
	 * @param configuration
	 *            The configuration used to initialize the whole reader. Keep in mind that the configuration should contain the following properties:
	 *            
    *
  • The property {@link #CONFIG_PROPERTY_NAME_PROVIDERURL}, e.g. {@code tcp://localhost:3035/} *
  • The property {@link #CONFIG_PROPERTY_NAME_DESTINATION}, e.g. {@code queue1} *
  • The property {@link #CONFIG_PROPERTY_NAME_FACTORYLOOKUP}, e.g. {@code org.exolab.jms.jndi.InitialContextFactory} *
* @param projectContext * The project context for this component. * * @throws IllegalArgumentException * If one of the properties is empty. */ public JMSReader(final Configuration configuration, final IProjectContext projectContext) throws IllegalArgumentException { super(configuration, projectContext); // Initialize the reader bases on the given configuration. this.jmsProviderUrl = configuration.getStringProperty(CONFIG_PROPERTY_NAME_PROVIDERURL); this.jmsDestination = configuration.getStringProperty(CONFIG_PROPERTY_NAME_DESTINATION); this.jmsFactoryLookupName = configuration.getStringProperty(CONFIG_PROPERTY_NAME_FACTORYLOOKUP); // simple sanity check if ((this.jmsProviderUrl.length() == 0) || (this.jmsDestination.length() == 0) || (this.jmsFactoryLookupName.length() == 0)) { throw new IllegalArgumentException("JMSReader has not sufficient parameters. jmsProviderUrl ('" + this.jmsProviderUrl + "'), jmsDestination ('" + this.jmsDestination + "'), or factoryLookupName ('" + this.jmsFactoryLookupName + "') is null"); } } /** * A call to this method is a blocking call. * * @return true if the method succeeds, false otherwise. */ public boolean read() { boolean retVal = true; Connection connection = null; try { final Hashtable properties = new Hashtable(); // NOPMD NOCS (InitialContext expects Hashtable) properties.put(Context.INITIAL_CONTEXT_FACTORY, this.jmsFactoryLookupName); // JMS initialization properties.put(Context.PROVIDER_URL, this.jmsProviderUrl); final Context context = new InitialContext(properties); final ConnectionFactory factory = (ConnectionFactory) context.lookup("ConnectionFactory"); connection = factory.createConnection(); final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); Destination destination; try { // As a first step, try a JNDI lookup (this seems to fail with ActiveMQ sometimes) destination = (Destination) context.lookup(this.jmsDestination); } catch (final NameNotFoundException exc) { // JNDI lookup failed, try manual creation (this seems to fail with ActiveMQ/HornetQ sometimes) destination = session.createQueue(this.jmsDestination); if (destination == null) { // LOG.error("Failed to lookup queue '" + this.jmsDestination + "' via JNDI: " + exc.getMessage() + " AND failed to create queue"); throw exc; // will be catched below to abort the read method } } LOG.info("Listening to destination:" + destination + " at " + this.jmsProviderUrl + " !\n***\n\n"); final MessageConsumer receiver = session.createConsumer(destination); receiver.setMessageListener(new JMSMessageListener()); // start the connection to enable message delivery connection.start(); LOG.info("JMSReader started and waits for incoming monitoring events!"); this.block(); LOG.info("Woke up by shutdown"); } catch (final Exception ex) { // NOPMD NOCS (IllegalCatchCheck) LOG.error("Error in read()", ex); retVal = false; } finally { try { if (connection != null) { connection.close(); } } catch (final JMSException ex) { LOG.error("Failed to close JMS", ex); } } return retVal; } private final void block() { Runtime.getRuntime().addShutdownHook(new Thread() { @Override public final void run() { JMSReader.this.unblock(); } }); try { this.cdLatch.await(); } catch (final InterruptedException e) { // ignore } } final void unblock() { // NOPMD (package visible for inner class) this.cdLatch.countDown(); } final boolean deliverIndirect(final String outputPortName, final Object data) { // NOPMD (package visible for inner class) return super.deliver(outputPortName, data); } /** * {@inheritDoc} */ public void terminate(final boolean error) { LOG.info("Shutdown of JMSReader requested."); this.unblock(); } /** * {@inheritDoc} */ @Override public Configuration getCurrentConfiguration() { final Configuration configuration = new Configuration(); configuration.setProperty(CONFIG_PROPERTY_NAME_PROVIDERURL, this.jmsProviderUrl); configuration.setProperty(CONFIG_PROPERTY_NAME_DESTINATION, this.jmsDestination); configuration.setProperty(CONFIG_PROPERTY_NAME_FACTORYLOOKUP, this.jmsFactoryLookupName); return configuration; } /** * The MessageListener will read onMessage each time a message comes in. */ private final class JMSMessageListener implements MessageListener { public JMSMessageListener() { // empty default constructor } public void onMessage(final Message jmsMessage) { if (jmsMessage == null) { LOG.warn("Received null message"); } else { if (jmsMessage instanceof ObjectMessage) { try { final ObjectMessage om = (ObjectMessage) jmsMessage; final Serializable omo = om.getObject(); if ((omo instanceof IMonitoringRecord) && (!JMSReader.this.deliverIndirect(OUTPUT_PORT_NAME_RECORDS, omo))) { LOG.error("deliverRecord returned false"); } } catch (final MessageFormatException ex) { LOG.error("Error delivering record", ex); } catch (final JMSException ex) { LOG.error("Error delivering record", ex); } catch (final Exception ex) { // NOPMD NOCS (catch Exception) LOG.error("Error delivering record", ex); } } else { LOG.warn("Received message of invalid type: " + jmsMessage.getClass().getName()); } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy