View Javadoc

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