/*
* Copyright 2003,2004 Colin Crist
*
* 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.hornetq;
import hermes.Domain;
import hermes.Hermes;
import hermes.HermesAdmin;
import hermes.HermesException;
import hermes.JNDIConnectionFactory;
import hermes.config.DestinationConfig;
import hermes.ext.HermesAdminSupport;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.naming.NamingException;
import org.apache.log4j.Logger;
import org.hornetq.api.core.management.ObjectNameBuilder;
import org.hornetq.api.jms.management.JMSQueueControl;
import org.hornetq.api.jms.management.TopicControl;
/**
* @author David Cole : dlcole@gmail.com
*/
public class HornetQAdmin extends HermesAdminSupport implements HermesAdmin {
private static final Logger log = Logger.getLogger(HornetQAdmin.class);
private final JNDIConnectionFactory jndiCF;
private JMXConnector jmxConnector;
private final HornetQAdminFactory factory;
private MBeanServerConnection mBeanServer = null;
/**
* Constructor
*/
public HornetQAdmin(HornetQAdminFactory factory, Hermes hermes, JNDIConnectionFactory jndiCF, ConnectionFactory cf) throws JMSException {
super(hermes);
this.jndiCF = jndiCF;
this.factory = factory;
}
/**
* Creates an MBeanServerConnection by using either the jmxUrl to create a
* JMXConnector or will default to JNDI using the <br>
* The connection will be created using the jmxURL if configured on the
* HornetQAdminFactory. <br>
* An example jmxUrl for JBoss would look like:
* service:jmx:rmi:///jndi/rmi://localhost:1090/jmxconnector <br>
* If the jmxUrl is not set, the method will attempt to obtain access to the
* MBeanServerConnection via JNDI using the mBeanServerConnectionJndi
* property. An example for JBoss would be: jmx/invoker/RMIAdaptor<br>
* One of the two properties must be set to function properly: jmxURL or
* mBeanServerConnectionJndi
*
* @return MBeanServerConnection
* @throws IOException
* @throws JMSException
* @throws HermesException
*/
private MBeanServerConnection getMBeanServerConnection() throws IOException, JMSException, HermesException {
if (mBeanServer == null) {
if (factory.getJmxUrl() != null) {
if (jmxConnector == null) {
log.info("Creating JMXConnector using jmxUrl: " + factory.getJmxUrl());
// Create a JMX Connector to connect to the server's
// MBeanServer
try {
jmxConnector = JMXConnectorFactory.connect(new JMXServiceURL(factory.getJmxUrl()), new HashMap());
mBeanServer = jmxConnector.getMBeanServerConnection();
} catch (Exception ex) {
throw new HermesException("Error creating the JMXConnector. Check the jmxUrl in the Admin Factory. An example for JBoss would look like: "
+ "service:jmx:rmi:///jndi/rmi://localhost:1090/jmxconnector", ex);
}
}
} else if (factory.getMBeanServerConnectionJndi() != null) {
try {
mBeanServer = (MBeanServerConnection) jndiCF.createContext().lookup(factory.getMBeanServerConnectionJndi());
} catch (NamingException e) {
throw new HermesException("Error accessing the MBeanServerConnection via JNDI using: " + factory.getMBeanServerConnectionJndi() + ". "
+ "Please make sure the mBeanServerConnectionJndi is configured properly. " + "An example for JBoss would be: jmx/invoker/RMIAdaptor", e);
}
} else {
throw new HermesException("You must set either the jmxUrl or the jmxAdapterJndi. jmxUrl example: " + "service:jmx:rmi:///jndi/rmi://localhost:1090/jmxconnector. "
+ "jmxAdapterJndi example: jmx/invoker/RMIAdaptor");
}
}
return mBeanServer;
}
private JMSQueueControl getQueueControl(String queueName) throws Exception {
log.debug("Creating JMSQueueControl for queue: " + queueName);
MBeanServerConnection mbsc = getMBeanServerConnection();
// Construct an ObjectName for JMX access to the Queue
ObjectName on = ObjectNameBuilder.DEFAULT.getJMSQueueObjectName(queueName);
// Create a JMSQueueControl proxy to manage the queue on the server
JMSQueueControl queueControl = MBeanServerInvocationHandler.newProxyInstance(mbsc, on, JMSQueueControl.class, false);
return queueControl;
}
private TopicControl getTopicControl(String topicName) throws Exception {
log.debug("Creating TopicControl for topic: " + topicName);
MBeanServerConnection mbsc = getMBeanServerConnection();
// Construct an ObjectName for JMX access to the Queue
ObjectName on = ObjectNameBuilder.DEFAULT.getJMSTopicObjectName(topicName);
// Create a JMSQueueControl proxy to manage the queue on the server
TopicControl topicControl = MBeanServerInvocationHandler.newProxyInstance(mbsc, on, TopicControl.class, false);
return topicControl;
}
/**
* @see hermes.ProviderExtensionSession#size(javax.jms.Destination)
*/
@Override
public int getDepth(DestinationConfig dConfig) throws JMSException {
try {
String dest = getRealDestinationName(dConfig);
long depth = 0;
if (dConfig.getDomain() == Domain.QUEUE.getId()) {
log.debug("Looking for depth of queue: " + dest);
JMSQueueControl queueControl = getQueueControl(dest);
depth = queueControl.getMessageCount();
} else {
log.debug("Looking for depth of queue: " + dest);
TopicControl topicControl = getTopicControl(dest);
depth = topicControl.getMessageCount();
}
log.debug("Found the depth of (" + depth + ") for: " + dest);
return (int) depth;
} catch (HermesException ex) {
throw ex;
} catch (Exception e) {
log.error(e.getMessage(), e);
close();
throw new HermesException(e);
}
}
/**
* @see hermes.ProviderExtensionSession#close()
*/
@Override
public void close() throws JMSException {
closeJMXConnector();
mBeanServer = null;
}
private void closeJMXConnector() {
if (jmxConnector != null) {
try {
jmxConnector.close();
} catch (IOException e) {
log.error("Error closing the JMXConnector: " + e.getMessage(), e);
}
jmxConnector = null;
}
}
@Override
public Map getStatistics(DestinationConfig dConfig) throws JMSException {
final Map stats = new LinkedHashMap();
try {
String destination = getRealDestinationName(dConfig);
log.debug("Generating statistics for : " + destination);
if (dConfig.getDomain() == Domain.QUEUE.getId()) {
JMSQueueControl queueControl = getQueueControl(destination);
stats.put("QueueName", queueControl.getName());
stats.put("ScheduledCount", queueControl.getScheduledCount());
stats.put("MessagesAdded", queueControl.getMessagesAdded());
stats.put("MessageCount", queueControl.getMessageCount());
stats.put("DeliveringCount", queueControl.getDeliveringCount());
stats.put("ConsumerCount", queueControl.getConsumerCount());
} else {
TopicControl topicControl = getTopicControl(destination);
stats.put("TopicName", topicControl.getName());
stats.put("MessageCount", topicControl.getMessageCount());
stats.put("DurableMessageCount", topicControl.getDurableMessageCount());
stats.put("NonDurableMessageCount", topicControl.getNonDurableMessageCount());
stats.put("SubscriptionCount", topicControl.getSubscriptionCount());
stats.put("DurableSubscriptionCount", topicControl.getDurableSubscriptionCount());
stats.put("NonDurableSubscriptionCount", topicControl.getNonDurableSubscriptionCount());
}
log.debug("Statistics of destination: " + destination + " are:\n" + stats);
} catch (Exception e) {
throw new HermesException(e);
}
return stats;
}
@Override
public int truncate(DestinationConfig dConfig) throws JMSException {
try {
String destination = getRealDestinationName(dConfig);
if (dConfig.getDomain() == Domain.QUEUE.getId()) {
JMSQueueControl queueControl = getQueueControl(destination);
return queueControl.removeMessages(null);
} else {
throw new HermesException("JBoss does not support truncating a durable subscription");
}
} catch (HermesException ex) {
throw ex;
} catch (Exception e) {
log.error(e.getMessage(), e);
close();
throw new HermesException(e);
}
}
}