Near Infinity

Monitoring JMS Resources Using WebLogic MBeans

By Sara Bevels

May 16, 2011

Recently, I needed to enhance some existing code to apply optional filters when retrieving the status of application-created JMS queues/topics (resources) from WebLogic.  These filters would be based upon a pre-defined set of categories for the JMS resources in our application.  The existing code used the deprecated MBeanHome interface to connect to the WebLogic MBean server, retrieved all "JMSServerRuntime" MBeans, and returned the status of only application-created JMS resources through the use of nested iterations over the returned MBean set.  Since I just needed to add a filter on top of the returned MBean set, the easiest route would have been to add an iteration over the returned JMSServerRuntimeMBeans to return only the ones that matched the provided filter.  However, since the existing performance was already slower than desired, I knew that adding an iteration would degrade it even further.  As a result, I decided to leverage MBeanServerConnection queryNames when retrieving the MBeans directly from WebLogic to allow WebLogic to do the filtering for me instead of iterating over the results after the MBeans are returned.  Also, I figured I'd refactor the existing code to no longer use deprecated WebLogic APIs in favor of the standard JMX programming model.

Here is an example of the existing code.  It performs the following steps:
1.  Connect to the MBeanServer using MBeanHome  
2.  Retrieve all MBeans of type "JMSServerRuntime"
3.  Iterate over the set to find the MBean for the JMS Server created by the application
4.  Retrieve all JMS destinations for the application-created JMS Server, and then iterate over them to retrieve each destination

<div><span class="Apple-style-span" style="font-family: monospace; white-space: pre; ">/**</span></div><div>&nbsp;&nbsp;* Obtain the status of JMS queues and topics</div><div>&nbsp;&nbsp;* @return List of all JMSDestinationRuntimeMBean objects (one per destination)</div><div>&nbsp; */</div><div>private static List&lt;JMSDestinationRuntimeMBean&gt; getJMSStatus()</div><div>{</div><div>    List&lt;JMSDestinationRuntimeMBean&gt; destinations = new ArrayList&lt;JMSDestinationRuntimeMBean&gt;();</div><div>&nbsp;&nbsp; &nbsp;try</div><div>&nbsp;&nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp;  // Look up the management bean home for the admin server</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Context ctx = new InitialContext();</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;MBeanHome home = (MBeanHome) ctx.lookup(MBeanHome.ADMIN_JNDI_NAME);</div><div><br /></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;// Get all JMS server MBeans</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Set mbeanSet = home.getMBeansByType("JMSServerRuntime");</div><div><br /></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;// Iterate over the set to find the the JMS Server used by our application</div><div><span class="Apple-tab-span" style="white-space:pre">	</span>Iterator mBeanIterator = mbeanSet.iterator();</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;while ( mBeanIterator.hasNext() )</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  JMSServerRuntimeMBean server = (JMSServerRuntimeMBean) mBeanIterator.next();</div><div><br /></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// If it's our application-created JMS server, then get all of its destinations</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;String name = server.getName().toUpperCase();</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if ( name.startsWith("JMSServerName") ) //insert the name of the JMS server used by the application</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  JMSDestinationRuntimeMBean[] dest = server.getDestinations();</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;for ( int i=0; i &lt; dest.length; i++ )</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;        //NOTE: This is where we would add another iteration to determine if the destination meets any provided filters</div><div><span class="Apple-tab-span" style="white-space:pre">		</span>    destinations.add(dest[i]);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;}</div><div>&nbsp;&nbsp; &nbsp;}</div><div>&nbsp;&nbsp; &nbsp;catch (Exception ex)</div><div>&nbsp;&nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;LOG.warn("Could not retrieve JMS statistics for all destinations", ex);</div><div>&nbsp;&nbsp; &nbsp;}</div><div><br /></div><div>&nbsp;&nbsp; &nbsp;return(destinations);</div><div>}</div><div><br /></div>
Based on my analysis and testing, I found the performance of this method to be degraded in relation to the number of MBeans returned by WebLogic and the nested iterations over the returned MBean set.  So instead of retrieving MBeans using MBeanHome getMBeansByType, I decided to try queryNames from the MBeanServerConnection interface.  The queryNames method returns the MBean names from the MBean server and takes an ObjectName and QueryExp as parameters.  Since the ObjectName identifies the MBean names to be retrieved, I used "Type=JMSDestinationRuntime".  The QueryExp parameter was where I was hoping WebLogic would do the filtering for me.  After doing some more testing, I found thats since WebLogic performed the filtering before returning the MBean set, the results were returned much faster than retrieving the full list of MBeans.  The only drawback of this approach was that queryNames returns the ObjectNames for the MBeans selected and not all the attributes needed by our application for the status of the JMS queues/topics.  An additional query to the MBean server is necessary to retrieve the missing attributes.  Even with this additional query, the enhanced code returned the results faster than the existing code.

Here is an example of the updated code.  It performs the following steps:
1.  Create the QueryExp based upon the provided filters
2.  Connect to the WebLogic Admin MBeanServer using JMXConnector.   
3.  Retrieve MBeans (using QueryNames) of type "JMSServerRuntime" and the QueryExp
4.  Check that all MBeans have the application-created JMSServerRuntime
5.  Retrieve all JMS destinations for the application-created JMS Server, and then iterate over them to retrieve each destination

<div>&nbsp;&nbsp; &nbsp;/**</div><div>&nbsp;&nbsp; &nbsp;* Obtain the status of JMS queues and topics</div><div>&nbsp;&nbsp; &nbsp;* @param jmsNameList required leading list of names of queue/topic (if null then all queues/topics returned)</div><div>&nbsp;&nbsp; &nbsp;* @return List of all JMSStatus objects (one per destination)</div><div>&nbsp;&nbsp; &nbsp;*/</div><div>&nbsp;&nbsp; &nbsp;public static List&lt;JMSStatus&gt; getJMSStatusByNamedList(List&lt;String&gt; jmsNameList)</div><div>&nbsp;&nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;QueryExp fullQuery = null;</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;if (jmsNameList != null)</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;QueryExp query = null;</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;for (String jmsName : jmsNameList)</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;AttributeValueExp attribute = Query.attr(mBeanName);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;StringValueExp name = Query.value(jmsName);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;query = Query.initialSubString(attribute, name);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fullQuery = (fullQuery != null) ? Query.or(fullQuery, query) : query;</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;}</div><div><br /></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;return getJMSStatusByQuery(fullQuery);</div><div><br /></div><div>&nbsp;&nbsp; &nbsp;}</div><div><br /></div><div>&nbsp;&nbsp; &nbsp;/**</div><div>&nbsp;&nbsp; &nbsp;* Obtain the status of JMS queues and topics</div><div>&nbsp;&nbsp; &nbsp;* @param query optional QueryExp to filter the returned jms destinations (if null then all queues/topics returned)</div><div>&nbsp;&nbsp; &nbsp;* @return List of all JMSStatus objects (one per destination) sorted by name</div><div>&nbsp;&nbsp; &nbsp;*/</div><div>&nbsp;&nbsp; &nbsp;private static List&lt;JMSStatus&gt; getJMSStatusByQuery(QueryExp query)</div><div>&nbsp;&nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;List&lt;JMSStatus&gt; destinations = new ArrayList&lt;JMSStatus&gt;();</div><div><span class="Apple-tab-span" style="white-space:pre">	</span>private String[] mBeanAttributes = {"Name", "DestinationType", "MessagesCurrentCount",</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"MessagesPendingCount", "MessagesReceivedCount", "MessagesHighCount"};</div><div><br /></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;MBeanServerConnection connection = null;</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;JMXConnector connector = null;</div><div><br /></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;try</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;connector = initConnection();</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;connection = connector.getMBeanServerConnection();</div><div><br /></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Set&lt;ObjectName&gt; mbeanSet = connection.queryNames(new ObjectName("*:*,Type=JMSDestinationRuntime"), query);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;for (ObjectName mbean : mbeanSet)</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (mbean.getKeyProperty("JMSServerRuntime").toLowerCase().startsWith("JMSServerName")) // Name of the application-created JMS Server name</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;AttributeList attributes = connection.getAttributes(mbean, mBeanAttributes);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (attributes.size() == mBeanAttributes.length)</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{</div><div><span class="Apple-tab-span" style="white-space:pre">			</span>JMSStatus stat = new JMSStatus(</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(Attribute) attributes.get(0),</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(Attribute) attributes.get(1),</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(Attribute) attributes.get(2),</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(Attribute) attributes.get(3),</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(Attribute) attributes.get(4),</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(Attribute) attributes.get(5),</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;mbean.getKeyProperty("JMSServerRuntime"),</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;mbean.getKeyProperty("ServerRuntime"));</div><div><br /></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;destinations.add(stat);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;}</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;catch (Exception ex)</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;LOG.warn("Could not retrieve JMS statistics for all destinations", ex);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;}</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;finally</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (connector != null)</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;try {</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;connector.close();</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;} catch (IOException ex) {</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;LOG.warn("Could not close the MBean Server connection", ex);</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;}</div><div><br /></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;return(destinations);</div><div><br /></div><div>&nbsp;&nbsp; &nbsp;}</div><div><br /></div><div>&nbsp;&nbsp; &nbsp;/**</div><div>&nbsp;&nbsp; &nbsp;* Initialize the JMX connection to the WebLogic Admin MBeanServer.</div><div>&nbsp;&nbsp; &nbsp;* @return JMXConnector for the MBeanServer connection</div><div>&nbsp;&nbsp; &nbsp;*/</div><div>&nbsp;&nbsp; &nbsp;private static JMXConnector initConnection() throws IOException {</div><div><br /></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;JMXServiceURL serviceURL = new JMXServiceURL(protocol, host, port, jndiPath); // Update using the appropriate protocol, host, port, jndiPath</div><div><br /></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Map&lt;String,Object&gt; env = new HashMap&lt;String,Object&gt;();</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;env.put(Context.SECURITY_PRINCIPAL, "login"); &nbsp;// update with admin user login</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;env.put(Context.SECURITY_CREDENTIALS, "password"); &nbsp;// update with admin user password</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;env.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES, "weblogic.management.remote");</div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;env.put("jmx.remote.x.request.waiting.timeout", 10000);</div><div><br /></div><div>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;return JMXConnectorFactory.connect(serviceURL, env);</div><div>&nbsp;&nbsp; }</div>