View Javadoc

1   /*
2    * $Id: JmxAgent.java 7976 2007-08-21 14:26:13Z dirk.olmes $
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.umo.UMOEndpoint,name=" + name);
431                     }
432                     ObjectName on = jmxSupport.getObjectName(
433                                                     jmxSupport.getDomainName() +
434                                                     ":type=org.mule.Endpoint,component=" +
435                                                     jmxSupport.escape(mBean.getComponentName()) +
436                                                     ",name=" + name);
437                     mBeanServer.registerMBean(mBean, on);
438                     registeredMBeans.add(on);
439                     logger.info("Registered Endpoint Service with name: " + on);
440                 }
441             } else {
442                 logger.warn("Connector: " + connector
443                         + " is not an istance of AbstractConnector, cannot obtain Endpoint MBeans from it");
444             }
445 
446         }
447     }
448 
449     protected void registerConnectorServices() throws
450                                                 MalformedObjectNameException,
451                                                 NotCompliantMBeanException,
452                                                 MBeanRegistrationException,
453                                                 InstanceAlreadyExistsException
454     {
455         Iterator iter = MuleManager.getInstance().getConnectors().values().iterator();
456         while (iter.hasNext()) {
457             UMOConnector connector = (UMOConnector) iter.next();
458             ConnectorServiceMBean mBean = new ConnectorService(connector);
459             final String rawName = mBean.getName();
460             final String name = jmxSupport.escape(rawName);
461             final String stringName = jmxSupport.getDomainName() + ":type=org.mule.Connector,name=" + name;
462             if (logger.isDebugEnabled()) {
463                 logger.debug("Attempting to register service with name: " + stringName);
464             }
465             ObjectName oName = jmxSupport.getObjectName(stringName);
466             mBeanServer.registerMBean(mBean, oName);
467             registeredMBeans.add(oName);
468             logger.info("Registered Connector Service with name " + oName);
469         }
470     }
471 
472     /**
473      * @return Returns the createServer.
474      */
475     public boolean isCreateServer()
476     {
477         return createServer;
478     }
479 
480     /**
481      * @param createServer The createServer to set.
482      */
483     public void setCreateServer(boolean createServer)
484     {
485         this.createServer = createServer;
486     }
487 
488     /**
489      * @return Returns the locateServer.
490      */
491     public boolean isLocateServer()
492     {
493         return locateServer;
494     }
495 
496     /**
497      * @param locateServer The locateServer to set.
498      */
499     public void setLocateServer(boolean locateServer)
500     {
501         this.locateServer = locateServer;
502     }
503 
504     /**
505      * @return Returns the connectorServerUrl.
506      */
507     public String getConnectorServerUrl()
508     {
509         return connectorServerUrl;
510     }
511 
512     /**
513      * @param connectorServerUrl The connectorServerUrl to set.
514      */
515     public void setConnectorServerUrl(String connectorServerUrl)
516     {
517         this.connectorServerUrl = connectorServerUrl;
518     }
519 
520     /**
521      * @return Returns the enableStatistics.
522      */
523     public boolean isEnableStatistics()
524     {
525         return enableStatistics;
526     }
527 
528     /**
529      * @param enableStatistics The enableStatistics to set.
530      */
531     public void setEnableStatistics(boolean enableStatistics)
532     {
533         this.enableStatistics = enableStatistics;
534     }
535 
536     /**
537      * @return Returns the mBeanServer.
538      */
539     public MBeanServer getMBeanServer()
540     {
541         return mBeanServer;
542     }
543 
544     /**
545      * @param mBeanServer The mBeanServer to set.
546      */
547     public void setMBeanServer(MBeanServer mBeanServer)
548     {
549         this.mBeanServer = mBeanServer;
550     }
551 
552     /**
553      * Getter for property 'connectorServerProperties'.
554      *
555      * @return Value for property 'connectorServerProperties'.
556      */
557     public Map getConnectorServerProperties() {
558         return connectorServerProperties;
559     }
560 
561     /**
562      * Setter for property 'connectorServerProperties'. Set to
563      * {@code null} to use defaults ({@link #DEFAULT_CONNECTOR_SERVER_PROPERTIES}).
564      * Pass in an empty map to use no parameters. Passing a non-empty map will
565      * replace defaults.
566      *
567      * @param connectorServerProperties Value to set for property 'connectorServerProperties'.
568      */
569     public void setConnectorServerProperties(Map connectorServerProperties) {
570         this.connectorServerProperties = connectorServerProperties;
571     }
572 
573 
574     /**
575      * Getter for property 'jmxSupportFactory'.
576      *
577      * @return Value for property 'jmxSupportFactory'.
578      */
579     public JmxSupportFactory getJmxSupportFactory()
580     {
581         return jmxSupportFactory;
582     }
583 
584     /**
585      * Setter for property 'jmxSupportFactory'.
586      *
587      * @param jmxSupportFactory Value to set for property 'jmxSupportFactory'.
588      */
589     public void setJmxSupportFactory(JmxSupportFactory jmxSupportFactory)
590     {
591         this.jmxSupportFactory = jmxSupportFactory;
592     }
593 
594 
595     /**
596      * Setter for property 'credentials'.
597      *
598      * @param newCredentials Value to set for property 'credentials'.
599      */
600     public void setCredentials(final Map newCredentials)
601     {
602         this.credentials.clear();
603         if (newCredentials != null && !newCredentials.isEmpty())
604         {
605             this.credentials.putAll(newCredentials);
606         }
607     }
608 }