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