kieker.analysis.plugin.reader.jms.JMSReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kieker Show documentation
Show all versions of kieker Show documentation
Kieker: Application Performance Monitoring and Dynamic Software Analysis
/***************************************************************************
* 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());
}
}
}
}
}