/*
* Copyright 2009 Laurent Bovet, Swiss Post IT
*
* 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 hermes.ext.imq;
import hermes.Domain;
import hermes.Hermes;
import hermes.HermesAdmin;
import hermes.browser.HermesBrowser;
import hermes.config.DestinationConfig;
import hermes.ext.HermesAdminSupport;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.Topic;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Support for Sun MQ (aka Java MQ or even Open MQ, technically named imq).
*
* @author bovetl
* @version $Revision$
* @since 01.00.00.00
*/
public class ImqAdmin extends HermesAdminSupport implements HermesAdmin, MessageListener {
static final String DEST_LIST_TOPIC_NAME = "mq.metrics.destination_list";
static final String QUEUE_METRICS_TOPIC_PREFIX = "mq.metrics.destination.queue.";
static final String TOPIC_METRICS_TOPIC_PREFIX = "mq.metrics.destination.topic.";
private final ConnectionFactory connectionFactory;
private Connection connection;
private Session session;
private MessageConsumer destListTopicSubscriber;
private final Map<String, MessageConsumer> destMetricTopicSubscribers = new HashMap<String, MessageConsumer>();
private final Map<String, Long> messageCounts = new HashMap<String, Long>();
private final Map<String, Map<String, Long>> stats = new HashMap<String, Map<String, Long>>();
private List<DestinationConfig> destinations = new ArrayList<DestinationConfig>();
private static Log LOG = LogFactory.getLog(ImqAdminFactory.class);
private final Object destListGuard = new Object();
private final Object destMetricGuard = new Object();
public ImqAdmin(Hermes hermes, ConnectionFactory connectionFactory) {
super(hermes);
this.connectionFactory = connectionFactory;
LOG.debug("Building ImqAdmin");
try {
connection = this.connectionFactory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic destListTopic = session.createTopic(DEST_LIST_TOPIC_NAME);
destListTopicSubscriber = session.createConsumer(destListTopic);
destListTopicSubscriber.setMessageListener(this);
} catch (JMSException e) {
throw new RuntimeException(e);
}
}
@Override
public Collection<DestinationConfig> discoverDestinationConfigs() throws JMSException {
LOG.debug("Discovering destination(s)");
try {
synchronized (destListGuard) {
destListGuard.wait();
}
} catch (InterruptedException e) {
}
LOG.debug("Discovered " + destinations.size() + " destination(s)");
return destinations;
}
@Override
public int getDepth(DestinationConfig destinationConfig) throws JMSException {
LOG.debug("Getting depth");
try {
subscribeToDestMetricTopic(destinationConfig.getShortName(), destinationConfig.getDomain());
synchronized (destMetricGuard) {
destMetricGuard.wait(5000);
}
} catch (InterruptedException e) {
}
Long result = messageCounts.get(destinationConfig.getShortName());
LOG.debug("Got depth for " + destinationConfig.getShortName() + ": " + result);
if (result == null) {
throw new RuntimeException("Timeout: Got no data from metric topic.");
}
clearDestMetricTopicSubscribers();
return result.intValue();
}
@Override
public Map<String, Long> getStatistics(DestinationConfig destination) throws JMSException {
LOG.debug("Getting statistics for " + destination);
try {
synchronized (destMetricGuard) {
subscribeToDestMetricTopic(destination.getShortName(), destination.getDomain());
destMetricGuard.wait(5000);
}
} catch (InterruptedException e) {
}
Map<String, Long> result = stats.get(destination.getShortName());
clearDestMetricTopicSubscribers();
if (result == null) {
throw new RuntimeException("Timeout: Got no data from metric topic.");
}
return result;
}
@Override
public void close() throws JMSException {
LOG.debug("Closing IMQ Session");
clearDestMetricTopicSubscribers();
destListTopicSubscriber.close();
session.close();
connection.close();
}
@Override
public void onMessage(Message msg) {
try {
MapMessage mapMsg = (MapMessage) msg;
String type = mapMsg.getStringProperty("type");
LOG.debug("Got admin message from broker of type: " + type);
if (type.equals(DEST_LIST_TOPIC_NAME)) {
List<DestinationConfig> result = new ArrayList<DestinationConfig>();
for (@SuppressWarnings("unchecked")
Enumeration e = mapMsg.getMapNames(); e.hasMoreElements();) {
String name = (String) e.nextElement();
@SuppressWarnings("unchecked")
Map<String, String> object = (Map<String, String>) mapMsg.getObject(name);
DestinationConfig dest = HermesBrowser.getConfigDAO().createDestinationConfig();
dest.setName(object.get("name"));
dest.setShortName(object.get("name"));
dest.setDomain("queue".equals(object.get("type")) ? Domain.QUEUE.getId() : ("topic".equals(object.get("type")) ? Domain.TOPIC.getId() : Domain.UNKNOWN.getId()));
result.add(dest);
}
Collections.sort(result, new Comparator<DestinationConfig>() {
@Override
public int compare(DestinationConfig o1, DestinationConfig o2) {
return o1.getShortName().compareTo(o2.getShortName());
}
});
destinations = result;
synchronized (destListGuard) {
destListGuard.notifyAll();
}
} else if (type.startsWith(QUEUE_METRICS_TOPIC_PREFIX)) {
LOG.debug("Got queue metrics: " + type);
String queueName = type.substring(QUEUE_METRICS_TOPIC_PREFIX.length());
messageCounts.put(queueName, mapMsg.getLong("numMsgs"));
HashMap<String, Long> map = new HashMap<String, Long>();
@SuppressWarnings("unchecked")
Enumeration<String> e = mapMsg.getMapNames();
while (e.hasMoreElements()) {
String name = e.nextElement();
map.put(name, mapMsg.getLong(name));
}
stats.put(queueName, map);
LOG.debug("Stored stats for: " + queueName);
synchronized (destMetricGuard) {
destMetricGuard.notifyAll();
}
} else if (type.startsWith(TOPIC_METRICS_TOPIC_PREFIX)) {
LOG.debug("Got topic metrics: " + type);
String topicName = type.substring(TOPIC_METRICS_TOPIC_PREFIX.length());
messageCounts.put(topicName, mapMsg.getLong("numMsgs"));
HashMap<String, Long> map = new HashMap<String, Long>();
@SuppressWarnings("unchecked")
Enumeration<String> e = mapMsg.getMapNames();
while (e.hasMoreElements()) {
String name = e.nextElement();
map.put(name, mapMsg.getLong(name));
}
stats.put(topicName, map);
LOG.debug("Stored stats for: " + topicName);
synchronized (destMetricGuard) {
destMetricGuard.notifyAll();
}
}
} catch (JMSException e) {
throw new RuntimeException(e);
}
}
private MessageConsumer subscribeToDestMetricTopic(String destName, int domain) throws JMSException {
LOG.debug("Subscribing to " + destName);
String topicName = null;
if (domain == Domain.QUEUE.getId()) {
topicName = QUEUE_METRICS_TOPIC_PREFIX + destName;
} else if (domain == Domain.TOPIC.getId()) {
topicName = TOPIC_METRICS_TOPIC_PREFIX + destName;
}
Topic destListTopic = session.createTopic(topicName);
MessageConsumer subscriber = session.createConsumer(destListTopic);
subscriber.setMessageListener(this);
LOG.debug("Created subscriber " + subscriber + " listening to " + topicName);
return subscriber;
}
private void clearDestMetricTopicSubscribers() throws JMSException {
for (MessageConsumer subscriber : destMetricTopicSubscribers.values()) {
LOG.debug("Closing subscriber: " + subscriber);
subscriber.close();
}
destMetricTopicSubscribers.clear();
}
}