1
2
3
4
5
6
7
8
9
10 package org.mule.module.management.agent;
11
12 import org.mule.AbstractAgent;
13 import org.mule.api.MuleException;
14 import org.mule.api.MuleRuntimeException;
15 import org.mule.api.context.notification.MuleContextNotificationListener;
16 import org.mule.api.lifecycle.InitialisationException;
17 import org.mule.api.model.Model;
18 import org.mule.api.service.Service;
19 import org.mule.api.transport.Connector;
20 import org.mule.api.transport.MessageReceiver;
21 import org.mule.config.i18n.CoreMessages;
22 import org.mule.context.notification.MuleContextNotification;
23 import org.mule.context.notification.NotificationException;
24 import org.mule.module.management.i18n.ManagementMessages;
25 import org.mule.module.management.mbean.ConnectorService;
26 import org.mule.module.management.mbean.ConnectorServiceMBean;
27 import org.mule.module.management.mbean.EndpointService;
28 import org.mule.module.management.mbean.EndpointServiceMBean;
29 import org.mule.module.management.mbean.ModelService;
30 import org.mule.module.management.mbean.ModelServiceMBean;
31 import org.mule.module.management.mbean.MuleConfigurationService;
32 import org.mule.module.management.mbean.MuleConfigurationServiceMBean;
33 import org.mule.module.management.mbean.MuleService;
34 import org.mule.module.management.mbean.MuleServiceMBean;
35 import org.mule.module.management.mbean.ServiceService;
36 import org.mule.module.management.mbean.ServiceServiceMBean;
37 import org.mule.module.management.mbean.StatisticsService;
38 import org.mule.module.management.mbean.StatisticsServiceMBean;
39 import org.mule.module.management.support.AutoDiscoveryJmxSupportFactory;
40 import org.mule.module.management.support.JmxSupport;
41 import org.mule.module.management.support.JmxSupportFactory;
42 import org.mule.module.management.support.SimplePasswordJmxAuthenticator;
43 import org.mule.transport.AbstractConnector;
44 import org.mule.util.ClassUtils;
45 import org.mule.util.StringUtils;
46
47 import java.lang.management.ManagementFactory;
48 import java.net.URI;
49 import java.rmi.RemoteException;
50 import java.rmi.registry.LocateRegistry;
51 import java.rmi.registry.Registry;
52 import java.rmi.server.ExportException;
53 import java.util.Collections;
54 import java.util.HashMap;
55 import java.util.Map;
56 import java.util.Set;
57
58 import javax.management.InstanceAlreadyExistsException;
59 import javax.management.MBeanRegistrationException;
60 import javax.management.MBeanServer;
61 import javax.management.MBeanServerFactory;
62 import javax.management.MalformedObjectNameException;
63 import javax.management.NotCompliantMBeanException;
64 import javax.management.ObjectName;
65 import javax.management.remote.JMXAuthenticator;
66 import javax.management.remote.JMXConnectorServer;
67 import javax.management.remote.JMXConnectorServerFactory;
68 import javax.management.remote.JMXServiceURL;
69 import javax.management.remote.rmi.RMIConnectorServer;
70
71 import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
72
73 import org.apache.commons.logging.Log;
74 import org.apache.commons.logging.LogFactory;
75
76
77
78
79 public class JmxAgent extends AbstractAgent
80 {
81 public static final String NAME = "jmx-agent";
82
83 public static final String DEFAULT_REMOTING_URI = "service:jmx:rmi:///jndi/rmi://localhost:1099/server";
84
85
86 public static final Map<String, String> DEFAULT_CONNECTOR_SERVER_PROPERTIES;
87
88
89
90
91 public static final String DEFAULT_JMX_AUTHENTICATOR = SimplePasswordJmxAuthenticator.class.getName();
92
93
94
95
96 protected static final Log logger = LogFactory.getLog(JmxAgent.class);
97
98
99
100
101 protected boolean locateServer = true;
102
103 protected boolean containerMode = true;
104
105
106 private boolean createServer = false;
107 private String connectorServerUrl;
108 private MBeanServer mBeanServer;
109 private JMXConnectorServer connectorServer;
110 private Map<String, Object> connectorServerProperties = null;
111 private boolean enableStatistics = true;
112 private final AtomicBoolean serverCreated = new AtomicBoolean(false);
113 private final AtomicBoolean initialized = new AtomicBoolean(false);
114
115 private JmxSupportFactory jmxSupportFactory = AutoDiscoveryJmxSupportFactory.getInstance();
116 private JmxSupport jmxSupport = jmxSupportFactory.getJmxSupport();
117
118
119 private Registry rmiRegistry;
120 private boolean createRmiRegistry = true;
121
122
123
124 private Map<String, String> credentials = new HashMap<String, String>();
125
126 static
127 {
128 Map<String, String> props = new HashMap<String, String>(1);
129 props.put(RMIConnectorServer.JNDI_REBIND_ATTRIBUTE, "true");
130 DEFAULT_CONNECTOR_SERVER_PROPERTIES = Collections.unmodifiableMap(props);
131 }
132
133 public JmxAgent()
134 {
135 super(NAME);
136 connectorServerProperties = new HashMap<String, Object>(DEFAULT_CONNECTOR_SERVER_PROPERTIES);
137 }
138
139 @Override
140 public String getDescription()
141 {
142 if (connectorServerUrl != null)
143 {
144 return name + ": " + connectorServerUrl;
145 }
146 else
147 {
148 return "JMX Agent";
149 }
150 }
151
152
153
154
155 public void initialise() throws InitialisationException
156 {
157 if (initialized.get())
158 {
159 return;
160 }
161
162 this.containerMode = muleContext.getConfiguration().isContainerMode();
163
164 try
165 {
166 Object agent = muleContext.getRegistry().lookupObject(this.getClass());
167
168 if (agent == this && this.initialized.get())
169 {
170 if (logger.isDebugEnabled())
171 {
172 logger.debug("Found an existing JMX agent in the registry, we're done here.");
173 }
174 return;
175 }
176 }
177 catch (Exception e)
178 {
179 throw new InitialisationException(e, this);
180 }
181
182
183 if (mBeanServer == null && createServer)
184 {
185
186 mBeanServer = MBeanServerFactory.createMBeanServer();
187 serverCreated.set(true);
188 }
189
190 if (mBeanServer == null && locateServer)
191 {
192 mBeanServer = ManagementFactory.getPlatformMBeanServer();
193 }
194
195 if (mBeanServer == null)
196 {
197 throw new InitialisationException(ManagementMessages.cannotLocateOrCreateServer(), this);
198 }
199
200 if (StringUtils.isBlank(muleContext.getConfiguration().getId()))
201 {
202
203 throw new IllegalArgumentException(
204 "Manager ID is mandatory when running with JmxAgent. Give your Mule configuration a valid ID.");
205 }
206
207 try
208 {
209
210 muleContext.registerListener(new MuleContextStartedListener());
211
212 muleContext.registerListener(new MuleContextStoppedListener());
213 }
214 catch (NotificationException e)
215 {
216 throw new InitialisationException(e, this);
217 }
218 initialized.compareAndSet(false, true);
219 }
220
221 protected void initRMI() throws Exception
222 {
223 String connectUri = (connectorServerUrl != null ? connectorServerUrl : DEFAULT_REMOTING_URI);
224 if (connectUri.contains("jmx:rmi"))
225 {
226 int i = connectUri.lastIndexOf("rmi://");
227 URI uri = new URI(connectUri.substring(i));
228 if (rmiRegistry == null)
229 {
230 try
231 {
232 if (isCreateRmiRegistry())
233 {
234 try
235 {
236 rmiRegistry = LocateRegistry.createRegistry(uri.getPort());
237 }
238 catch (ExportException e)
239 {
240 logger.info("Registry on " + uri + " already bound. Attempting to use that instead");
241 rmiRegistry = LocateRegistry.getRegistry(uri.getHost(), uri.getPort());
242 }
243 }
244 else
245 {
246 rmiRegistry = LocateRegistry.getRegistry(uri.getHost(), uri.getPort());
247 }
248 }
249 catch (RemoteException e)
250 {
251 throw new InitialisationException(e, this);
252 }
253 }
254 }
255 }
256
257 public void start() throws MuleException
258 {
259 try
260 {
261
262 initRMI();
263 if (connectorServerUrl == null)
264 {
265 return;
266 }
267
268 logger.info("Creating and starting JMX agent connector Server");
269 JMXServiceURL url = new JMXServiceURL(connectorServerUrl);
270 if (connectorServerProperties == null)
271 {
272 connectorServerProperties = new HashMap<String, Object>(DEFAULT_CONNECTOR_SERVER_PROPERTIES);
273 }
274
275
276 if (!credentials.isEmpty())
277 {
278 JMXAuthenticator jmxAuthenticator = (JMXAuthenticator) ClassUtils.instanciateClass(DEFAULT_JMX_AUTHENTICATOR);
279
280 ((SimplePasswordJmxAuthenticator) jmxAuthenticator).setCredentials(credentials);
281 connectorServerProperties.put(JMXConnectorServer.AUTHENTICATOR, jmxAuthenticator);
282 }
283 connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url,
284 connectorServerProperties,
285 mBeanServer);
286 connectorServer.start();
287 }
288 catch (ExportException e)
289 {
290 throw new JmxManagementException(CoreMessages.failedToStart("Jmx Agent"), e);
291 }
292 catch (Exception e)
293 {
294 throw new JmxManagementException(CoreMessages.failedToStart("Jmx Agent"), e);
295 }
296 }
297
298 public void stop() throws MuleException
299 {
300 if (connectorServer != null)
301 {
302 try
303 {
304 connectorServer.stop();
305 }
306 catch (Exception e)
307 {
308 throw new JmxManagementException(CoreMessages.failedToStop("Jmx Connector"), e);
309 }
310 }
311 }
312
313
314
315
316 public void dispose()
317 {
318 unregisterMBeansIfNecessary();
319 if (serverCreated.get())
320 {
321 MBeanServerFactory.releaseMBeanServer(mBeanServer);
322 }
323 mBeanServer = null;
324 serverCreated.compareAndSet(true, false);
325 initialized.set(false);
326 }
327
328
329
330
331
332
333 protected void registerWrapperService() throws MuleException
334 {
335
336 final WrapperManagerAgent wmAgent = new WrapperManagerAgent();
337 if (muleContext.getRegistry().lookupAgent(wmAgent.getName()) == null)
338 {
339 muleContext.getRegistry().registerAgent(wmAgent);
340 }
341 }
342
343 protected void registerStatisticsService() throws NotCompliantMBeanException, MBeanRegistrationException,
344 InstanceAlreadyExistsException, MalformedObjectNameException
345 {
346 ObjectName on = jmxSupport.getObjectName(String.format("%s:%s", jmxSupport.getDomainName(muleContext, !containerMode), StatisticsServiceMBean.DEFAULT_JMX_NAME));
347 StatisticsService service = new StatisticsService();
348 service.setMuleContext(muleContext);
349 service.setEnabled(isEnableStatistics());
350 ClassloaderSwitchingMBeanWrapper mBean = new ClassloaderSwitchingMBeanWrapper(service, StatisticsServiceMBean.class, muleContext.getExecutionClassLoader());
351 logger.debug("Registering statistics with name: " + on);
352 mBeanServer.registerMBean(mBean, on);
353 }
354
355 protected void registerModelServices() throws NotCompliantMBeanException, MBeanRegistrationException,
356 InstanceAlreadyExistsException, MalformedObjectNameException
357 {
358 for (Model model : muleContext.getRegistry().lookupObjects(Model.class))
359 {
360 ModelServiceMBean service = new ModelService(model);
361 String rawName = service.getName() + "(" + service.getType() + ")";
362 String name = jmxSupport.escape(rawName);
363 final String jmxName = String.format("%s:%s%s", jmxSupport.getDomainName(muleContext, !containerMode), ModelServiceMBean.DEFAULT_JMX_NAME_PREFIX, name);
364 ObjectName on = jmxSupport.getObjectName(jmxName);
365 ClassloaderSwitchingMBeanWrapper mBean = new ClassloaderSwitchingMBeanWrapper(service, ModelServiceMBean.class, muleContext.getExecutionClassLoader());
366 logger.debug("Registering model with name: " + on);
367 mBeanServer.registerMBean(mBean, on);
368 }
369 }
370
371 protected void registerMuleService() throws NotCompliantMBeanException, MBeanRegistrationException,
372 InstanceAlreadyExistsException, MalformedObjectNameException
373 {
374 ObjectName on = jmxSupport.getObjectName(String.format("%s:%s", jmxSupport.getDomainName(muleContext, !containerMode), MuleServiceMBean.DEFAULT_JMX_NAME));
375 if (muleContext.getConfiguration().isContainerMode() && mBeanServer.isRegistered(on))
376 {
377
378 return;
379 }
380 MuleService service = new MuleService(muleContext);
381 ClassloaderSwitchingMBeanWrapper serviceMBean = new ClassloaderSwitchingMBeanWrapper(service, MuleServiceMBean.class, muleContext.getExecutionClassLoader());
382 logger.debug("Registering mule with name: " + on);
383 mBeanServer.registerMBean(serviceMBean, on);
384 }
385
386 protected void registerConfigurationService() throws NotCompliantMBeanException, MBeanRegistrationException,
387 InstanceAlreadyExistsException, MalformedObjectNameException
388 {
389 ObjectName on = jmxSupport.getObjectName(String.format("%s:%s", jmxSupport.getDomainName(muleContext, !containerMode), MuleConfigurationServiceMBean.DEFAULT_JMX_NAME));
390 MuleConfigurationServiceMBean service = new MuleConfigurationService(muleContext.getConfiguration());
391 ClassloaderSwitchingMBeanWrapper mBean = new ClassloaderSwitchingMBeanWrapper(service, MuleConfigurationServiceMBean.class, muleContext.getExecutionClassLoader());
392 logger.debug("Registering configuration with name: " + on);
393 mBeanServer.registerMBean(mBean, on);
394 }
395
396 protected void registerServiceServices() throws NotCompliantMBeanException, MBeanRegistrationException,
397 InstanceAlreadyExistsException, MalformedObjectNameException
398 {
399 for (Service service : muleContext.getRegistry().lookupObjects(Service.class))
400 {
401 final String rawName = service.getName();
402 final String name = jmxSupport.escape(rawName);
403 final String jmxName = String.format("%s:%s%s", jmxSupport.getDomainName(muleContext, !containerMode), ServiceServiceMBean.DEFAULT_JMX_NAME_PREFIX, name);
404 ObjectName on = jmxSupport.getObjectName(jmxName);
405 ServiceServiceMBean serviceMBean = new ServiceService(rawName, muleContext);
406 ClassloaderSwitchingMBeanWrapper wrapper = new ClassloaderSwitchingMBeanWrapper(serviceMBean, ServiceServiceMBean.class, muleContext.getExecutionClassLoader());
407 logger.debug("Registering service with name: " + on);
408 mBeanServer.registerMBean(wrapper, on);
409 }
410 }
411
412 protected void registerEndpointServices() throws NotCompliantMBeanException, MBeanRegistrationException,
413 InstanceAlreadyExistsException, MalformedObjectNameException
414 {
415 for (Connector connector : muleContext.getRegistry().lookupObjects(Connector.class))
416 {
417 if (connector instanceof AbstractConnector)
418 {
419 for (MessageReceiver messageReceiver : ((AbstractConnector) connector).getReceivers().values())
420 {
421 EndpointServiceMBean service = new EndpointService(messageReceiver);
422
423 String fullName = buildFullyQualifiedEndpointName(service, connector);
424 if (logger.isInfoEnabled())
425 {
426 logger.info("Attempting to register service with name: " + fullName);
427 }
428
429 ObjectName on = jmxSupport.getObjectName(fullName);
430 ClassloaderSwitchingMBeanWrapper mBean = new ClassloaderSwitchingMBeanWrapper(service, EndpointServiceMBean.class, muleContext.getExecutionClassLoader());
431 mBeanServer.registerMBean(mBean, on);
432 if (logger.isInfoEnabled())
433 {
434 logger.info("Registered Endpoint Service with name: " + on);
435 }
436 }
437 }
438 else
439 {
440 logger.warn("Connector: " + connector
441 + " is not an istance of AbstractConnector, cannot obtain Endpoint MBeans from it");
442 }
443 }
444 }
445
446 protected String buildFullyQualifiedEndpointName(EndpointServiceMBean mBean, Connector connector)
447 {
448 String rawName = jmxSupport.escape(mBean.getName());
449
450 StringBuilder fullName = new StringBuilder(128);
451 fullName.append(jmxSupport.getDomainName(muleContext, !containerMode));
452 fullName.append(":type=Endpoint,service=");
453 fullName.append(jmxSupport.escape(mBean.getComponentName()));
454 fullName.append(",connector=");
455 fullName.append(connector.getName());
456 fullName.append(",name=");
457 fullName.append(rawName);
458 return fullName.toString();
459 }
460
461 protected void registerConnectorServices() throws MalformedObjectNameException,
462 NotCompliantMBeanException, MBeanRegistrationException, InstanceAlreadyExistsException
463 {
464 for (Connector connector : muleContext.getRegistry().lookupObjects(Connector.class))
465 {
466 ConnectorServiceMBean service = new ConnectorService(connector);
467 final String rawName = service.getName();
468 final String name = jmxSupport.escape(rawName);
469 final String jmxName = String.format("%s:%s%s", jmxSupport.getDomainName(muleContext, !containerMode), ConnectorServiceMBean.DEFAULT_JMX_NAME_PREFIX, name);
470 if (logger.isDebugEnabled())
471 {
472 logger.debug("Attempting to register service with name: " + jmxName);
473 }
474 ObjectName oName = jmxSupport.getObjectName(jmxName);
475 ClassloaderSwitchingMBeanWrapper mBean = new ClassloaderSwitchingMBeanWrapper(service, ConnectorServiceMBean.class, muleContext.getExecutionClassLoader());
476 mBeanServer.registerMBean(mBean, oName);
477 logger.info("Registered Connector Service with name " + oName);
478 }
479 }
480
481 public boolean isCreateServer()
482 {
483 return createServer;
484 }
485
486 public void setCreateServer(boolean createServer)
487 {
488 this.createServer = createServer;
489 }
490
491 public boolean isLocateServer()
492 {
493 return locateServer;
494 }
495
496 public void setLocateServer(boolean locateServer)
497 {
498 this.locateServer = locateServer;
499 }
500
501 public String getConnectorServerUrl()
502 {
503 return connectorServerUrl;
504 }
505
506 public void setConnectorServerUrl(String connectorServerUrl)
507 {
508 this.connectorServerUrl = connectorServerUrl;
509 }
510
511 public boolean isEnableStatistics()
512 {
513 return enableStatistics;
514 }
515
516 public void setEnableStatistics(boolean enableStatistics)
517 {
518 this.enableStatistics = enableStatistics;
519 }
520
521 public MBeanServer getMBeanServer()
522 {
523 return mBeanServer;
524 }
525
526 public void setMBeanServer(MBeanServer mBeanServer)
527 {
528 this.mBeanServer = mBeanServer;
529 }
530
531 public Map<String, Object> getConnectorServerProperties()
532 {
533 return connectorServerProperties;
534 }
535
536
537
538
539
540
541
542
543 public void setConnectorServerProperties(Map<String, Object> connectorServerProperties)
544 {
545 this.connectorServerProperties = connectorServerProperties;
546 }
547
548 public JmxSupportFactory getJmxSupportFactory()
549 {
550 return jmxSupportFactory;
551 }
552
553 public void setJmxSupportFactory(JmxSupportFactory jmxSupportFactory)
554 {
555 this.jmxSupportFactory = jmxSupportFactory;
556 }
557
558
559
560
561
562
563
564 public void setCredentials(final Map<String, String> newCredentials)
565 {
566 this.credentials.clear();
567 if (newCredentials != null && !newCredentials.isEmpty())
568 {
569 this.credentials.putAll(newCredentials);
570 }
571 }
572
573 protected void unregisterMBeansIfNecessary()
574 {
575 unregisterMBeansIfNecessary(false);
576 }
577
578
579
580
581 protected void unregisterMBeansIfNecessary(boolean containerMode)
582 {
583 if (mBeanServer == null)
584 {
585 return;
586 }
587
588 try
589 {
590
591
592
593 final String domain = jmxSupport.getDomainName(muleContext, false);
594 ObjectName query = jmxSupport.getObjectName(domain + ":*");
595 Set<ObjectName> mbeans = mBeanServer.queryNames(query, null);
596 while (!mbeans.isEmpty())
597 {
598 ObjectName name = mbeans.iterator().next();
599 try
600 {
601 if (!(containerMode && MuleServiceMBean.DEFAULT_JMX_NAME.equals(name.getCanonicalKeyPropertyListString())))
602 {
603 mBeanServer.unregisterMBean(name);
604 }
605 }
606 catch (Exception e)
607 {
608 logger.warn(String.format("Failed to unregister MBean: %s. Error is: %s", name, e.getMessage()));
609 }
610
611
612
613 mbeans = mBeanServer.queryNames(query, null);
614
615 if (containerMode)
616 {
617
618 mbeans.remove(jmxSupport.getObjectName(String.format("%s:%s", domain, MuleServiceMBean.DEFAULT_JMX_NAME)));
619 }
620 }
621 }
622 catch (MalformedObjectNameException e)
623 {
624 logger.warn("Failed to create ObjectName query", e);
625 }
626 }
627
628 public Registry getRmiRegistry()
629 {
630 return rmiRegistry;
631 }
632
633 public void setRmiRegistry(Registry rmiRegistry)
634 {
635 this.rmiRegistry = rmiRegistry;
636 }
637
638 public boolean isCreateRmiRegistry()
639 {
640 return createRmiRegistry;
641 }
642
643 public void setCreateRmiRegistry(boolean createRmiRegistry)
644 {
645 this.createRmiRegistry = createRmiRegistry;
646 }
647
648 protected class MuleContextStartedListener implements MuleContextNotificationListener<MuleContextNotification>
649 {
650
651 public void onNotification(MuleContextNotification notification)
652 {
653 if (notification.getAction() == MuleContextNotification.CONTEXT_STARTED)
654 {
655 try
656 {
657 registerWrapperService();
658 registerStatisticsService();
659 registerMuleService();
660 registerConfigurationService();
661 registerModelServices();
662 registerServiceServices();
663 registerEndpointServices();
664 registerConnectorServices();
665 }
666 catch (Exception e)
667 {
668 throw new MuleRuntimeException(CoreMessages.objectFailedToInitialise("MBeans"), e);
669 }
670 }
671 }
672 }
673
674 protected class MuleContextStoppedListener implements MuleContextNotificationListener<MuleContextNotification>
675 {
676
677 public void onNotification(MuleContextNotification notification)
678 {
679 if (notification.getAction() == MuleContextNotification.CONTEXT_STOPPED)
680 {
681 boolean containerMode = notification.getMuleContext().getConfiguration().isContainerMode();
682 unregisterMBeansIfNecessary(containerMode);
683 }
684 }
685 }
686 }