/* * Copyright 2003,2004 Peter Lee, 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.weblogic; 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.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import javax.jms.Destination; import javax.jms.JMSException; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.AttributeNotFoundException; import javax.management.InstanceNotFoundException; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.QueryExp; import javax.management.ReflectionException; import javax.naming.Context; import javax.naming.NamingException; import org.apache.log4j.Logger; import weblogic.management.MBeanHome; import weblogic.management.WebLogicObjectName; import weblogic.management.runtime.JMSDestinationRuntimeMBean; /** * Administration plugin for WebLogicJMS. * * WebLogicJMSAdmin supports: (i) queue depth, (ii) display of all runtime JMS * Destination statistics, and (iii) automatic queue discovery via WebLogic JMX. * For the administration features to work correctly, WebLogicJMSAdminFactory * must be properly configured. * * @author leepops@sourceforge.net last changed by: $Author $ * @version $Id: WebLogicJMSAdmin.java,v 1.9 2005/08/15 20:37:23 colincrist Exp $ */ public class WebLogicJMSAdmin extends HermesAdminSupport implements HermesAdmin { private final static Logger log = Logger.getLogger(WebLogicJMSAdmin.class); private final String[] JMS_DEST_MONITOR_ATTRIB_NAMES = { "BytesCurrentCount", "BytesHighCount", "BytesPendingCount", "BytesReceivedCount", "BytesThresholdTime", "ConsumersCurrentCount", "ConsumersHighCount", "ConsumersTotalCount", "DestinationType", "MessagesCurrentCount", "MessagesHighCount", "MessagesPendingCount", "MessagesReceivedCount", "MessagesThresholdTime" }; private final String JMS_DEST_NAME_ATTRIB = "Name"; private final String JMS_DEST_JNDI_NAME_ATTRIB = "JNDIName"; private final String[] JMS_DEST_CONFIG_ATTRIB_NAMES = { JMS_DEST_NAME_ATTRIB, JMS_DEST_JNDI_NAME_ATTRIB }; private final String JMX_QUEUE_TYPE = "JMSQueue"; private final String JMX_TOPIC_TYPE = "JMSTopic"; private Context context; private WebLogicJMSAdminFactory factory; /** * JMS admin session for WebLogic Server JMS. */ public WebLogicJMSAdmin( Hermes hermes, WebLogicJMSAdminFactory factory, Context context) { super(hermes); this.context = context; this.factory = factory; } /* (non-Javadoc) * @see hermes.HermesAdmin#getDepth(javax.jms.Destination) */ public synchronized int getDepth(DestinationConfig dConfig) throws JMSException { Map stats = getStatistics(dConfig); Long i1 = (Long)stats.get("MessagesCurrentCount"); long i2 = i1.intValue(); return (int)i2; } /* (non-Javadoc) * @see hermes.HermesAdmin#close() */ public void close() throws JMSException { // nop } public synchronized Collection getStatistics(Collection destinations) throws JMSException { final List rval = new ArrayList(); for (Iterator iter = destinations.iterator(); iter.hasNext();) { rval.add(getStatistics((Destination)iter.next())); } return rval; } public Map getStatistics(DestinationConfig dConfig) throws JMSException { try { return getStatistics(getHermes().getDestination(dConfig.getName(), Domain.getDomain(dConfig.getDomain()))) ; } catch (NamingException ex) { throw new HermesException(ex) ; } } private synchronized Map getStatistics(Destination destination) throws JMSException { Map stats = null; try { JMSDestinationRuntimeMBean bean = getMBean(destination); AttributeList attributes = bean.getAttributes(JMS_DEST_MONITOR_ATTRIB_NAMES); stats = new TreeMap(); for (Iterator i = attributes.iterator(); i.hasNext();) { Attribute attribute = (Attribute)i.next(); stats.put(attribute.getName(), attribute.getValue()); } log.debug(stats); return stats; } catch (InstanceNotFoundException e) { log.error("InstanceNotFoundException - ", e); throw new HermesException(e); } catch (MalformedObjectNameException e) { log.error("MalformedObjectNameException - ", e); throw new HermesException(e); } } public synchronized Collection discoverDestinationConfigs() throws JMSException { // // If accessing via JNDI then get the destinations from the context. if (getHermes().getConnectionFactory() instanceof JNDIConnectionFactory) { return super.discoverDestinationConfigs() ; } MBeanHome home = getHome(this.context); // get the MBeanServer interface MBeanServer homeServer = home.getMBeanServer(); ObjectName queueMBeanQuery = null; ObjectName topicMBeanQuery = null; try { queueMBeanQuery = getJmsQueueMBeanQuery(); topicMBeanQuery = getJmsTopicMBeanQuery(); } catch (MalformedObjectNameException e) { log.error("MalformedObjectNameException - ", e); throw new RuntimeException(e); } Collection queues = discoverDestinationConfigs( homeServer, queueMBeanQuery, Domain.QUEUE); Collection topics = discoverDestinationConfigs( homeServer, topicMBeanQuery, Domain.TOPIC); Collection rval = new ArrayList(queues.size() + topics.size()); rval.addAll(queues); rval.addAll(topics); return rval; } private Collection discoverDestinationConfigs( MBeanServer homeServer, ObjectName query, Domain domain) { //Retrieve a list of MBeans with object names that include QueryExp queryExpr = null; Set mBeans = null; mBeans = homeServer.queryNames(query, queryExpr); Collection rval = new ArrayList(mBeans.size()); for (Iterator i = mBeans.iterator(); i.hasNext();) { ObjectName name = (ObjectName)i.next(); log.debug("Matches to the MBean query: " + name.toString()); String jndiName = null; String destName = null; try { AttributeList attributes = homeServer.getAttributes( name, JMS_DEST_CONFIG_ATTRIB_NAMES); destName = (String)getAttributeEx(attributes, JMS_DEST_NAME_ATTRIB); jndiName = (String)getAttributeEx(attributes, JMS_DEST_JNDI_NAME_ATTRIB); } catch (AttributeNotFoundException e) { log.error("AttributeNotFoundException - ", e); throw new RuntimeException(e); } catch (InstanceNotFoundException e) { log.error("InstanceNotFoundException - ", e); throw new RuntimeException(e); } catch (ReflectionException e) { log.error("ReflectionException - ", e); throw new RuntimeException(e); } DestinationConfig dConfig = new DestinationConfig(); dConfig.setName(jndiName); dConfig.setShortName(destName); dConfig.setDomain(domain.getId()); rval.add(dConfig); } return rval; } /** * Lookup the MBeanHome from JNDI as needed. * * @return * @throws JMSException */ protected MBeanHome getHome(Context context) throws JMSException { try { log.debug("JNDI admin binding: " + getMBeanHomeJndiName()); return (MBeanHome)context.lookup(getMBeanHomeJndiName()); } catch (NamingException e) { log.error("NamingException - ", e); throw new HermesException(e); } } /** * Returns the MBean for the JMSDestinationRuntime object representing the relevant JMS destination. * @param destination * @return * @throws InstanceNotFoundException * @throws MalformedObjectNameException * @throws JMSException */ private JMSDestinationRuntimeMBean getMBean(Destination destination) throws InstanceNotFoundException, MalformedObjectNameException, JMSException { MBeanHome home = getHome(this.context); ObjectName name = getJmsDestMBeanName(destination); JMSDestinationRuntimeMBean bean = (JMSDestinationRuntimeMBean)home.getMBean(name); log.debug("Found MBean: " + bean); return bean; } /** * Returns the JNDI binding name for the MBeanHome for the relevant WebLogic server. * The binding name will be of the form: weblogic.management.home.>server< * @return */ private String getMBeanHomeJndiName() { return MBeanHome.JNDI_NAME + "." + this.factory.getWebLogicServer(); } /** * Returns a JMX ObjectName to the JMSDestinationRuntime object representing the runtime MBean * of the destination. The object name is of the form: * >domain<:JMSServerRuntime=>jmsserver<,Location=>server<,Name=>queue<,ServerRuntime=>server<,Type=JMSDestinationRuntime * * @param destination the JMS destination * @return * @throws MalformedObjectNameException */ private ObjectName getJmsDestMBeanName(Destination destination) throws MalformedObjectNameException { StringBuffer buff = new StringBuffer(this.factory.getWebLogicDomain()); buff.append(":JMSServerRuntime="); buff.append(this.factory.getJmsServer()); buff.append(",Location="); buff.append(this.factory.getWebLogicServer()); buff.append(",Name="); buff.append(destination.toString()); buff.append(",ServerRuntime="); buff.append(this.factory.getWebLogicServer()); buff.append(",Type=JMSDestinationRuntime"); String s = buff.toString(); log.debug("Constructed JMSDestination MBean name: " + s); return new WebLogicObjectName(s); } /** * @return JMX ObjectName query matching JMS queue destinations * @throws MalformedObjectNameException in the case of internal error */ private ObjectName getJmsQueueMBeanQuery() throws MalformedObjectNameException { return getJmsDestMBeanQuery(JMX_QUEUE_TYPE); } /** * @return JMX ObjectName query matching JMS topic destinations * @throws MalformedObjectNameException in the case of internal error */ private ObjectName getJmsTopicMBeanQuery() throws MalformedObjectNameException { return getJmsDestMBeanQuery(JMX_TOPIC_TYPE); } /** * @return JMX ObjectName query matching JMS topic destinations. The query is of the form: * >domain<:JMSServer=>jmsserver<,Type=>jmsdestinationtype< * @throws MalformedObjectNameException in the case of internal error */ private ObjectName getJmsDestMBeanQuery(String destType) throws MalformedObjectNameException { StringBuffer buff = new StringBuffer(this.factory.getWebLogicDomain()); buff.append(":JMSServer="); buff.append(this.factory.getJmsServer()); buff.append(",Type="); buff.append(destType); buff.append(",*"); String s = buff.toString(); return new ObjectName(s); } private static Object getAttributeEx(AttributeList attributes, String name) throws AttributeNotFoundException { Object o = getAttribute(attributes, name); if (o == null) throw new AttributeNotFoundException( "Attribute " + name + " not found."); return o; } private static Object getAttribute(AttributeList attributes, String name) { Object rval = null; for (Iterator i = attributes.iterator(); i.hasNext();) { Attribute a = (Attribute)i.next(); if (a.getName().equals(name)) rval = a.getValue(); } return rval; } }