/** * A singleton that contains JMX MBean proxies that can allow a JMX service to invoke local EJBs inside * an application's class loading context. * <br>User: Joshua Davis * Date: Aug 29, 2007 * Time: 5:55:18 AM */ package org.yajul.jmx; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; /** * Provides an in-JVM bridge between a JMX service and a POJO implementation * that lives inside an EAR or a WAR deployment. The bridge is a singleton * that contains a set of proxy objects associated with the implementation class * name. The proxy objects are initialized by a startup Servlet (or any * object that has access to the EAR/WAR class loader). The lifecycle calls * from the JMX bean are routed through the proxy to the implementation POJO. * <h2>What you need to use it:</h2> * <ol><li>A JMX Management Bean, deployed as a JMX service * (a '.sar' in JBoss). The MBean code will not load the implementation class * directly, but it will know the name of the class.</li> * <li>An implementation POJO, deployed in a WAR or EAR. This must implement * <tt>org.yajul.ee5.jmx.Lifecycle</tt>.</li> * <li>A startup Servlet, or equivalent code that gets executed when the EAR/WAR * is deployed. This is what will instantiate and register the POJO * implementation with the bridge singleton.</li> * </ol> * <h2>Usage:</h2> * <ol><li>Create the implementation POJO in your WAR/EAR module. Implement * the Lifecycle interface. <i>NOTE: Do not package the YAJUL jars inside the * EAR or WAR.</i></li> * <li>Create or modify an existing startup Servlet. Call the register() method * in the JmxBridge singleton for each implementation POJO.</li> * <li>Create the JMX MBean. In the JMX MBean methods, get the proxy using * the implementation class name (<i>Don't link directly with the class, that * would defeat the purpose!</i>). Delegate the start() and stop() methods to * the proxy.</li> * <li>Package the JMX MBean appropriately for your container. <i>Make sure the * jar with the JmxBridge code is in the 'root' classloader, and not deployed * with the JMX MBean, otherwise the bridge will not function.</i> In JBoss * you can do this by simply adding the YAJUL jar to the server <tt>lib</tt> * directory.</li> * <li>Deploy the YAJUL jar, the JMX MBean, and the EAR or WAR. When the * server starts you should see the Proxy being registered and the POJO * implementation being created when the startup Servlet runs.</li> * </ol> * <br>User: Joshua Davis * Date: Aug 29, 2007 * Time: 5:58:11 AM */ public class JmxBridge { private static final Logger log = LoggerFactory.getLogger(JmxBridge.class); private Map<String, Proxy> proxiesByImplementationClassName; private ImplementationProvider implementationProvider; private static JmxBridge INSTANCE = new JmxBridge(); public static JmxBridge getInstance() { // NOTE: This it's own singleton to decouple it from SingletonManager. return INSTANCE; } public JmxBridge() { proxiesByImplementationClassName = new HashMap<String, Proxy>(); implementationProvider = new DefaultImplementationProvider(); log.info("created."); } /** * @return the implementation provider */ public ImplementationProvider getImplementationProvider() { synchronized (this) { return implementationProvider; } } /** * Sets the implementation provider (e.g. a microcontainer for the JMX MBean implementations) * * @param implementationProvider the implementation provider */ public void setImplementationProvider(ImplementationProvider implementationProvider) { synchronized (this) { this.implementationProvider = implementationProvider; } } /** * Stop all MBeans. Clear everything. */ public void reset() { synchronized (this) { for (Proxy proxy : proxiesByImplementationClassName.values()) { proxy.stop(); } proxiesByImplementationClassName.clear(); } } /** * Return the proxy for the given MBean implementation. * * @param implementationClassName the implementation class * @return the proxy */ public Proxy getProxy(String implementationClassName) { synchronized (this) { return doGetProxy(implementationClassName); } } /** * Register a specific implementation class with the bridge. Invoke this from a suitable class loading context. For example, from * a startup Servlet. * * @param implClass the implementation class. * @throws Exception if the proxies created by the MBeans could not be initialized. */ public void register(Class implClass) throws Exception { synchronized (this) { // Get or create the proxy. Proxy proxy = doGetProxy(implClass.getName()); // Initialize it now. proxy.initialize(); log.info(implClass.getName() + " registered."); } } /** * Initializes all the proxies. Invoke this from a suitable class loading context. For example, from * a startup Servlet. * * @param provider class lookup / MBean factory * @throws Exception if the proxies created by the MBeans could not be initialized. */ public void initializeProxies(ImplementationProvider provider) throws Exception { synchronized (this) { if (provider != null) this.implementationProvider = provider; for (Proxy proxy : proxiesByImplementationClassName.values()) { proxy.initialize(); } } log.info("initializeProxies() : completed."); } /** * Initializes all the proxies. Invoke this from a suitable class loading context. For example, from * a startup Servlet. * * @throws Exception if the proxies created by the MBeans could not be initialized. */ public void initializeProxies() throws Exception { initializeProxies(null); } private Proxy doGetProxy(String implementationClassName) { Proxy proxy = proxiesByImplementationClassName.get(implementationClassName); if (proxy == null) { proxy = new Proxy(implementationClassName, this); proxiesByImplementationClassName.put(implementationClassName, proxy); } return proxy; } Class<?> getImplementationClass(String className) throws ClassNotFoundException { return implementationProvider.getImplementationClass(className); } <T> T getImplementation(Class<T> implementationClass) { return implementationProvider.getImplementation(implementationClass); } }