View Javadoc

1   /*
2    * $Id: JmxAgent.java 8791 2007-10-02 17:19:44Z aperepel $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  package org.mule.management.agents;
11  
12  import org.mule.MuleManager;
13  import org.mule.MuleRuntimeException;
14  import org.mule.config.i18n.CoreMessages;
15  import org.mule.impl.internal.notifications.ManagerNotification;
16  import org.mule.impl.internal.notifications.ManagerNotificationListener;
17  import org.mule.impl.internal.notifications.NotificationException;
18  import org.mule.management.i18n.ManagementMessages;
19  import org.mule.management.mbeans.ComponentService;
20  import org.mule.management.mbeans.ComponentServiceMBean;
21  import org.mule.management.mbeans.ConnectorService;
22  import org.mule.management.mbeans.ConnectorServiceMBean;
23  import org.mule.management.mbeans.EndpointService;
24  import org.mule.management.mbeans.EndpointServiceMBean;
25  import org.mule.management.mbeans.ModelService;
26  import org.mule.management.mbeans.ModelServiceMBean;
27  import org.mule.management.mbeans.MuleConfigurationService;
28  import org.mule.management.mbeans.MuleConfigurationServiceMBean;
29  import org.mule.management.mbeans.MuleService;
30  import org.mule.management.mbeans.MuleServiceMBean;
31  import org.mule.management.mbeans.StatisticsService;
32  import org.mule.management.support.AutoDiscoveryJmxSupportFactory;
33  import org.mule.management.support.JmxSupport;
34  import org.mule.management.support.JmxSupportFactory;
35  import org.mule.management.support.SimplePasswordJmxAuthenticator;
36  import org.mule.providers.AbstractConnector;
37  import org.mule.umo.UMOException;
38  import org.mule.umo.lifecycle.InitialisationException;
39  import org.mule.umo.manager.UMOAgent;
40  import org.mule.umo.manager.UMOManager;
41  import org.mule.umo.manager.UMOServerNotification;
42  import org.mule.umo.model.UMOModel;
43  import org.mule.umo.provider.UMOConnector;
44  import org.mule.umo.provider.UMOMessageReceiver;
45  import org.mule.util.ClassUtils;
46  import org.mule.util.StringUtils;
47  
48  import java.util.ArrayList;
49  import java.util.Collections;
50  import java.util.HashMap;
51  import java.util.Iterator;
52  import java.util.List;
53  import java.util.Map;
54  
55  import javax.management.InstanceAlreadyExistsException;
56  import javax.management.MBeanRegistrationException;
57  import javax.management.MBeanServer;
58  import javax.management.MBeanServerFactory;
59  import javax.management.MalformedObjectNameException;
60  import javax.management.NotCompliantMBeanException;
61  import javax.management.ObjectName;
62  import javax.management.remote.JMXAuthenticator;
63  import javax.management.remote.JMXConnectorServer;
64  import javax.management.remote.JMXConnectorServerFactory;
65  import javax.management.remote.JMXServiceURL;
66  import javax.management.remote.rmi.RMIConnectorServer;
67  
68  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
69  import org.apache.commons.logging.Log;
70  import org.apache.commons.logging.LogFactory;
71  
72  /**
73   * <code>JmxAgent</code> registers MUle Jmx management beans with an MBean
74   * server.
75   */
76  public class JmxAgent implements UMOAgent
77  {
78      public static final String DEFAULT_REMOTING_URI = "service:jmx:rmi:///jndi/rmi://localhost:1099/server";
79      // populated with values below in a static initializer
80      public static final Map DEFAULT_CONNECTOR_SERVER_PROPERTIES;
81  
82      /**
83       * Default JMX Authenticator to use for securing remote access.
84       */
85      public static final String DEFAULT_JMX_AUTHENTICATOR = SimplePasswordJmxAuthenticator.class.getName();
86  
87      /**
88       * Logger used by this class
89       */
90      protected static final Log logger = LogFactory.getLog(JmxAgent.class);
91  
92      /**
93       * Should MBeanServer be discovered.
94       */
95      protected boolean locateServer = true;
96  
97      private String name = "JMX Agent";
98      private boolean createServer = true;
99      private String connectorServerUrl;
100     private MBeanServer mBeanServer;
101     private JMXConnectorServer connectorServer;
102     private Map connectorServerProperties = null;
103     private boolean enableStatistics = true;
104     private List registeredMBeans = new ArrayList();
105     private final AtomicBoolean serverCreated = new AtomicBoolean(false);
106     private final AtomicBoolean initialized = new AtomicBoolean(false);
107 
108     private JmxSupportFactory jmxSupportFactory = AutoDiscoveryJmxSupportFactory.getInstance();
109     private JmxSupport jmxSupport = jmxSupportFactory.getJmxSupport();
110 
111     /**
112      * Default constructor.
113      */
114     public JmxAgent ()
115     {
116         connectorServerProperties = new HashMap(DEFAULT_CONNECTOR_SERVER_PROPERTIES);
117     }
118 
119     /**
120      * Username/password combinations for JMX Remoting
121      * authentication.
122      */
123     private Map credentials = new HashMap();
124 
125     static {
126         Map props = new HashMap(1);
127         props.put(RMIConnectorServer.JNDI_REBIND_ATTRIBUTE, "true");
128         DEFAULT_CONNECTOR_SERVER_PROPERTIES = Collections.unmodifiableMap(props);
129     }
130 
131     /** {@inheritDoc}
132     *
133     * @see org.mule.umo.manager.UMOAgent#getName()
134     */
135     public String getName()
136     {
137         return this.name;
138     }
139 
140     /** {@inheritDoc}
141      *
142      * @see org.mule.umo.manager.UMOAgent#setName(java.lang.String)
143      */
144     public void setName(String name)
145     {
146         this.name = name;
147     }
148 
149     /** {@inheritDoc}
150      *
151      * @see org.mule.umo.manager.UMOAgent#getDescription()
152      */
153     public String getDescription()
154     {
155         if (connectorServerUrl != null)
156         {
157             return "JMX Agent: " + connectorServerUrl;
158         }
159         else
160         {
161             return "JMX Agent";
162         }
163     }
164 
165     /** 
166      * {@inheritDoc}
167      * 
168      * @see org.mule.umo.lifecycle.Initialisable#initialise()
169      */
170     public void initialise() throws InitialisationException
171     {
172         if (initialized.get()) {
173             return;
174         }
175         if (mBeanServer == null && !locateServer && !createServer) {
176             throw new InitialisationException(ManagementMessages.createOrLocateShouldBeSet(), this);
177         }
178         if (mBeanServer == null && locateServer) {
179             List l = MBeanServerFactory.findMBeanServer(null);
180             if (l != null && l.size() > 0) {
181                 mBeanServer = (MBeanServer) l.get(0);
182             }
183         }
184         if (mBeanServer == null && createServer) {
185             mBeanServer = MBeanServerFactory.createMBeanServer();
186             serverCreated.set(true);
187         }
188         if (mBeanServer == null) {
189             throw new InitialisationException(ManagementMessages.cannotLocateOrCreateServer(), this);
190         }
191         if (connectorServerUrl != null) {
192             try {
193                 JMXServiceURL url = new JMXServiceURL(connectorServerUrl);
194                 if (connectorServerProperties == null)
195                 {
196                     connectorServerProperties = new HashMap(DEFAULT_CONNECTOR_SERVER_PROPERTIES);
197                 }
198                 // TODO custom authenticator may have its own security config, refactor
199                 if (!credentials.isEmpty())
200                 {
201                     JMXAuthenticator jmxAuthenticator = (JMXAuthenticator) ClassUtils.instanciateClass(
202                                                                     DEFAULT_JMX_AUTHENTICATOR, ClassUtils.NO_ARGS);
203                     // TODO support for custom authenticators 
204                     ((SimplePasswordJmxAuthenticator) jmxAuthenticator).setCredentials(credentials);
205                     connectorServerProperties.put(JMXConnectorServer.AUTHENTICATOR, jmxAuthenticator);
206                 }
207                 connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, connectorServerProperties, mBeanServer);
208             } catch (Exception e) {
209                 throw new InitialisationException(CoreMessages.failedToCreate("Jmx Connector"), e, this);
210             }
211         }
212 
213         // We need to register all the services once the server has initialised
214         ManagerNotificationListener l = new ManagerNotificationListener() {
215             public void onNotification(UMOServerNotification notification)
216             {
217                 if (notification.getAction() == ManagerNotification.MANAGER_STARTED_MODELS) {
218                     try {
219                         registerWrapperService();
220                         registerStatisticsService();
221                         registerMuleService();
222                         registerConfigurationService();
223                         registerModelServices();
224                         registerComponentServices();
225                         registerEndpointServices();
226                         registerConnectorServices();
227                     } catch (Exception e) {
228                         throw new MuleRuntimeException(CoreMessages.objectFailedToInitialise("MBeans"), e);
229                     }
230                 }
231             }
232         };
233 
234         try
235         {
236             UMOManager manager = MuleManager.getInstance();
237 
238 
239             if (StringUtils.isBlank(manager.getId()))
240             {
241                 // TODO i18n the message properly
242                 throw new IllegalArgumentException(
243                         "Manager ID is mandatory when running with JmxAgent. Give your Mule configuration a valid ID.");
244             }
245             MuleManager.getInstance().registerListener(l);
246         } catch (NotificationException e) {
247             throw new InitialisationException(e, this);
248         }
249         initialized.compareAndSet(false, true);
250     }
251 
252     /** {@inheritDoc}
253      * (non-Javadoc)
254      * 
255      * @see org.mule.umo.lifecycle.Startable#start()
256      */
257     public void start() throws UMOException
258     {
259         if (connectorServer != null) {
260             try {
261                 logger.info("Starting JMX agent connector Server");
262                 connectorServer.start();
263             } catch (Exception e) {
264                 throw new JmxManagementException(CoreMessages.failedToStart("Jmx Connector"), e);
265             }
266         }
267     }
268 
269     /** {@inheritDoc}
270      * (non-Javadoc)
271      * 
272      * @see org.mule.umo.lifecycle.Stoppable#stop()
273      */
274     public void stop() throws UMOException
275     {
276         if (connectorServer != null) {
277             try {
278                 connectorServer.stop();
279             } catch (Exception e) {
280                 throw new JmxManagementException(CoreMessages.failedToStop("Jmx Connector"), e);
281             }
282         }
283     }
284 
285     /** {@inheritDoc}
286      * (non-Javadoc)
287      * 
288      * @see org.mule.umo.lifecycle.Disposable#dispose()
289      */
290     public void dispose()
291     {
292         if (mBeanServer != null) {
293             for (Iterator iterator = registeredMBeans.iterator(); iterator.hasNext();) {
294                 ObjectName objectName = (ObjectName) iterator.next();
295                 try {
296                     mBeanServer.unregisterMBean(objectName);
297                 } catch (Exception e) {
298                     logger.warn("Failed to unregister MBean: " + objectName + ". Error is: " + e.getMessage());
299                 }
300             }
301             if (serverCreated.get()) {
302                 MBeanServerFactory.releaseMBeanServer(mBeanServer);
303             }
304             mBeanServer = null;
305         }
306 
307         initialized.set(false);
308     }
309 
310     /** {@inheritDoc}
311      * (non-Javadoc)
312      * 
313      * @see org.mule.umo.manager.UMOAgent#registered()
314      */
315     public void registered()
316     {
317         // nothing to do
318     }
319 
320     /** {@inheritDoc}
321      * (non-Javadoc)
322      * 
323      * @see org.mule.umo.manager.UMOAgent#unregistered()
324      */
325     public void unregistered()
326     {
327         // nothing to do
328     }
329 
330     /**
331      * Register a Java Service Wrapper agent.
332      * @throws UMOException if registration failed
333      */
334     protected void registerWrapperService() throws UMOException
335     {
336         // WrapperManager to support restarts
337         final WrapperManagerAgent wmAgent = new WrapperManagerAgent();
338         final UMOManager manager = MuleManager.getInstance();
339         if (manager.lookupAgent(wmAgent.getName()) == null)
340         {
341             manager.registerAgent(wmAgent);
342         }
343     }
344 
345 
346     protected void registerStatisticsService() throws NotCompliantMBeanException, MBeanRegistrationException,
347             InstanceAlreadyExistsException, MalformedObjectNameException
348     {
349         ObjectName on = jmxSupport.getObjectName(jmxSupport.getDomainName() + ":type=org.mule.Statistics,name=AllStatistics");
350         StatisticsService mBean = new StatisticsService();
351         mBean.setManager(MuleManager.getInstance());
352         mBean.setEnabled(isEnableStatistics());
353         logger.debug("Registering statistics with name: " + on);
354         mBeanServer.registerMBean(mBean, on);
355         registeredMBeans.add(on);
356     }
357 
358     protected void registerModelServices() throws NotCompliantMBeanException, MBeanRegistrationException,
359             InstanceAlreadyExistsException, MalformedObjectNameException
360     {
361         for (Iterator iterator = MuleManager.getInstance().getModels().values().iterator(); iterator.hasNext();)
362         {
363             UMOModel model = (UMOModel) iterator.next();
364             ModelServiceMBean serviceMBean = new ModelService(model);
365             String rawName = serviceMBean.getName() + "(" + serviceMBean.getType() + ")";
366             String name = jmxSupport.escape(rawName);
367             ObjectName on = jmxSupport.getObjectName(jmxSupport.getDomainName() + ":type=org.mule.Model,name=" + name);
368             logger.debug("Registering model with name: " + on);
369             mBeanServer.registerMBean(serviceMBean, on);
370             registeredMBeans.add(on);
371         }
372     }
373 
374     protected void registerMuleService() throws NotCompliantMBeanException, MBeanRegistrationException,
375             InstanceAlreadyExistsException, MalformedObjectNameException
376     {
377         ObjectName on = jmxSupport.getObjectName(jmxSupport.getDomainName() + ":type=org.mule.ManagementContext,name=MuleServerInfo");
378         MuleServiceMBean serviceMBean = new MuleService();
379         logger.debug("Registering mule with name: " + on);
380         mBeanServer.registerMBean(serviceMBean, on);
381         registeredMBeans.add(on);
382     }
383 
384     protected void registerConfigurationService() throws NotCompliantMBeanException, MBeanRegistrationException,
385             InstanceAlreadyExistsException, MalformedObjectNameException
386     {
387         ObjectName on = jmxSupport.getObjectName(jmxSupport.getDomainName() + ":type=org.mule.Configuration,name=GlobalConfiguration");
388         MuleConfigurationServiceMBean serviceMBean = new MuleConfigurationService();
389         logger.debug("Registering configuration with name: " + on);
390         mBeanServer.registerMBean(serviceMBean, on);
391         registeredMBeans.add(on);
392     }
393 
394     protected void registerComponentServices() throws NotCompliantMBeanException, MBeanRegistrationException,
395             InstanceAlreadyExistsException, MalformedObjectNameException
396     {
397         for (Iterator iterator = MuleManager.getInstance().getModels().values().iterator(); iterator.hasNext();)
398         {
399             UMOModel model = (UMOModel) iterator.next();
400             Iterator iter = model.getComponentNames();
401 
402             String rawName;
403             while (iter.hasNext()) {
404                 rawName = iter.next().toString();
405                 final String name = jmxSupport.escape(rawName);
406                 ObjectName on = jmxSupport.getObjectName(jmxSupport.getDomainName() + ":type=org.mule.Component,name=" + name);
407                 ComponentServiceMBean serviceMBean = new ComponentService(rawName);
408                 logger.debug("Registering component with name: " + on);
409                 mBeanServer.registerMBean(serviceMBean, on);
410                 registeredMBeans.add(on);
411             }
412         }
413 
414     }
415 
416     protected void registerEndpointServices() throws NotCompliantMBeanException, MBeanRegistrationException,
417             InstanceAlreadyExistsException, MalformedObjectNameException
418     {
419         Iterator iter = MuleManager.getInstance().getConnectors().values().iterator();
420         UMOConnector connector;
421         while (iter.hasNext()) {
422             connector = (UMOConnector) iter.next();
423             if (connector instanceof AbstractConnector) {
424                 for (Iterator iterator = ((AbstractConnector) connector).getReceivers().values().iterator(); iterator.hasNext();) {
425                     EndpointServiceMBean mBean = new EndpointService((UMOMessageReceiver) iterator.next());
426                     final String rawName = mBean.getName();
427                     final String name = jmxSupport.escape(rawName);
428                     if (logger.isInfoEnabled()) {
429                         logger.info("Attempting to register service with name: " + jmxSupport.getDomainName() +
430                                                     ":type=org.mule.Endpoint,component=" +
431                                                     jmxSupport.escape(mBean.getComponentName()) +
432                                                     ",name=" + name);
433                     }
434                     ObjectName on = jmxSupport.getObjectName(
435                                                     jmxSupport.getDomainName() +
436                                                     ":type=org.mule.Endpoint,component=" +
437                                                     jmxSupport.escape(mBean.getComponentName()) +
438                                                     ",name=" + name);
439                     mBeanServer.registerMBean(mBean, on);
440                     registeredMBeans.add(on);
441                     logger.info("Registered Endpoint Service with name: " + on);
442                 }
443             } else {
444                 logger.warn("Connector: " + connector
445                         + " is not an istance of AbstractConnector, cannot obtain Endpoint MBeans from it");
446             }
447 
448         }
449     }
450 
451     protected void registerConnectorServices() throws
452                                                 MalformedObjectNameException,
453                                                 NotCompliantMBeanException,
454                                                 MBeanRegistrationException,
455                                                 InstanceAlreadyExistsException
456     {
457         Iterator iter = MuleManager.getInstance().getConnectors().values().iterator();
458         while (iter.hasNext()) {
459             UMOConnector connector = (UMOConnector) iter.next();
460             ConnectorServiceMBean mBean = new ConnectorService(connector);
461             final String rawName = mBean.getName();
462             final String name = jmxSupport.escape(rawName);
463             final String stringName = jmxSupport.getDomainName() + ":type=org.mule.Connector,name=" + name;
464             if (logger.isDebugEnabled()) {
465                 logger.debug("Attempting to register service with name: " + stringName);
466             }
467             ObjectName oName = jmxSupport.getObjectName(stringName);
468             mBeanServer.registerMBean(mBean, oName);
469             registeredMBeans.add(oName);
470             logger.info("Registered Connector Service with name " + oName);
471         }
472     }
473 
474     /**
475      * @return Returns the createServer.
476      */
477     public boolean isCreateServer()
478     {
479         return createServer;
480     }
481 
482     /**
483      * @param createServer The createServer to set.
484      */
485     public void setCreateServer(boolean createServer)
486     {
487         this.createServer = createServer;
488     }
489 
490     /**
491      * @return Returns the locateServer.
492      */
493     public boolean isLocateServer()
494     {
495         return locateServer;
496     }
497 
498     /**
499      * @param locateServer The locateServer to set.
500      */
501     public void setLocateServer(boolean locateServer)
502     {
503         this.locateServer = locateServer;
504     }
505 
506     /**
507      * @return Returns the connectorServerUrl.
508      */
509     public String getConnectorServerUrl()
510     {
511         return connectorServerUrl;
512     }
513 
514     /**
515      * @param connectorServerUrl The connectorServerUrl to set.
516      */
517     public void setConnectorServerUrl(String connectorServerUrl)
518     {
519         this.connectorServerUrl = connectorServerUrl;
520     }
521 
522     /**
523      * @return Returns the enableStatistics.
524      */
525     public boolean isEnableStatistics()
526     {
527         return enableStatistics;
528     }
529 
530     /**
531      * @param enableStatistics The enableStatistics to set.
532      */
533     public void setEnableStatistics(boolean enableStatistics)
534     {
535         this.enableStatistics = enableStatistics;
536     }
537 
538     /**
539      * @return Returns the mBeanServer.
540      */
541     public MBeanServer getMBeanServer()
542     {
543         return mBeanServer;
544     }
545 
546     /**
547      * @param mBeanServer The mBeanServer to set.
548      */
549     public void setMBeanServer(MBeanServer mBeanServer)
550     {
551         this.mBeanServer = mBeanServer;
552     }
553 
554     /**
555      * Getter for property 'connectorServerProperties'.
556      *
557      * @return Value for property 'connectorServerProperties'.
558      */
559     public Map getConnectorServerProperties() {
560         return connectorServerProperties;
561     }
562 
563     /**
564      * Setter for property 'connectorServerProperties'. Set to
565      * {@code null} to use defaults ({@link #DEFAULT_CONNECTOR_SERVER_PROPERTIES}).
566      * Pass in an empty map to use no parameters. Passing a non-empty map will
567      * replace defaults.
568      *
569      * @param connectorServerProperties Value to set for property 'connectorServerProperties'.
570      */
571     public void setConnectorServerProperties(Map connectorServerProperties) {
572         this.connectorServerProperties = connectorServerProperties;
573     }
574 
575 
576     /**
577      * Getter for property 'jmxSupportFactory'.
578      *
579      * @return Value for property 'jmxSupportFactory'.
580      */
581     public JmxSupportFactory getJmxSupportFactory()
582     {
583         return jmxSupportFactory;
584     }
585 
586     /**
587      * Setter for property 'jmxSupportFactory'.
588      *
589      * @param jmxSupportFactory Value to set for property 'jmxSupportFactory'.
590      */
591     public void setJmxSupportFactory(JmxSupportFactory jmxSupportFactory)
592     {
593         this.jmxSupportFactory = jmxSupportFactory;
594     }
595 
596 
597     /**
598      * Setter for property 'credentials'.
599      *
600      * @param newCredentials Value to set for property 'credentials'.
601      */
602     public void setCredentials(final Map newCredentials)
603     {
604         this.credentials.clear();
605         if (newCredentials != null && !newCredentials.isEmpty())
606         {
607             this.credentials.putAll(newCredentials);
608         }
609     }
610 }