package org.neo4j.kernel.management; import static java.lang.management.ManagementFactory.getPlatformMBeanServer; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.logging.Logger; import javax.management.DynamicMBean; import javax.management.MBeanServer; import javax.management.MBeanServerConnection; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import org.neo4j.kernel.impl.core.NodeManager; import org.neo4j.kernel.impl.nioneo.xa.NeoStoreXaDataSource; import org.neo4j.kernel.impl.transaction.TxModule; import org.neo4j.kernel.impl.transaction.XaDataSourceManager; public abstract class Neo4jJmx { private static final Logger log = Logger.getLogger( Neo4jJmx.class.getName() ); public static Runnable initJMX( Neo4jJmx.Creator creator ) { Factory jmx = new Factory( getPlatformMBeanServer(), creator ); creator.create( jmx ); jmx.createKernelMBean( creator.kernelVersion ); return new JmxShutdown( jmx.beans ); } public static <T> T getBean( int instanceId, Class<T> beanType ) { if ( beanType.isInterface() && beanType.getName().endsWith( "Bean" ) && beanType.getPackage().equals( Neo4jJmx.class.getPackage() ) ) { if ( PROXY_MAKER == null ) { throw new UnsupportedOperationException( "Creating Management Bean proxies requires Java 1.6" ); } else { ObjectName name = getObjectName( instanceId, beanType, null ); return PROXY_MAKER.makeProxy( name, beanType ); } } throw new IllegalArgumentException( "Not a Neo4j management bean: " + beanType ); } public static abstract class Creator { private final int id; private final String kernelVersion; private final NeoStoreXaDataSource datasource; protected Creator( int instanceId, String kernelVersion, NeoStoreXaDataSource datasource ) { if ( kernelVersion == null || datasource == null ) { throw new IllegalArgumentException( "null valued argument" ); } this.id = instanceId; this.kernelVersion = kernelVersion; this.datasource = datasource; } protected abstract void create( Neo4jJmx.Factory jmx ); } public static final class Factory { private final MBeanServer mbs; private final Creator instance; private final List<Neo4jJmx> beans = new ArrayList<Neo4jJmx>(); private Factory( MBeanServer mbs, Creator creator ) { this.mbs = mbs; this.instance = creator; } private void createKernelMBean( String kernelVersion ) { if ( !register( new Kernel( instance.id, kernelVersion, instance.datasource ) ) ) failedToRegister( "KernelMBean" ); } public void createPrimitiveMBean( NodeManager nodeManager ) { if ( !register( new Primitive( instance.id, nodeManager ) ) ) failedToRegister( "PrimitiveMBean" ); } public void createCacheMBean( NodeManager nodeManager ) { if ( !register( new Cache( instance.id, nodeManager ) ) ) failedToRegister( "CacheMBean" ); } public void createDynamicConfigurationMBean( Map<Object, Object> params ) { if ( !register( new Configuration( instance.id, params ) ) ) failedToRegister( "ConfigurationMBean" ); } public void createMemoryMappingMBean( XaDataSourceManager datasourceMananger ) { NeoStoreXaDataSource datasource = (NeoStoreXaDataSource) datasourceMananger.getXaDataSource( "nioneodb" ); if ( !register( new MemoryMapping.AsMxBean( instance.id, datasource ) ) ) { if ( !register( new MemoryMapping( instance.id, datasource ) ) ) failedToRegister( "MemoryMappingMBean" ); } } public void createXaManagerMBean( XaDataSourceManager datasourceMananger ) { if ( !register( new XaManager.AsMXBean( instance.id, datasourceMananger ) ) ) { if ( !register( new XaManager( instance.id, datasourceMananger ) ) ) failedToRegister( "XaManagerMBean" ); } } public void createTransactionManagerMBean( TxModule txModule ) { if ( !register( new TransactionManager( instance.id, txModule ) ) ) failedToRegister( "TransactionManagerMBean" ); } public void createLockManagerMBean( org.neo4j.kernel.impl.transaction.LockManager lockManager ) { if ( !register( new LockManager( instance.id, lockManager ) ) ) failedToRegister( "LockManagerMBean" ); } public void createStoreFileMBean() { File storePath; try { storePath = new File( instance.datasource.getStoreDir() ).getCanonicalFile().getAbsoluteFile(); } catch ( IOException e ) { storePath = new File( instance.datasource.getStoreDir() ).getAbsoluteFile(); } if ( !register( new StoreFile( instance.id, storePath ) ) ) failedToRegister( "StoreFileMBean" ); } private boolean register( Neo4jJmx bean ) { bean = registerBean( mbs, bean ); if ( bean != null ) { beans.add( bean ); return true; } return false; } } private static final ProxyMaker PROXY_MAKER; private static class ProxyMaker { private final Method isMXBeanInterface; private final Method newMBeanProxy; private final Method newMXBeanProxy; ProxyMaker() throws Exception { Class<?> JMX = Class.forName( "javax.management.JMX" ); this.isMXBeanInterface = JMX.getMethod( "isMXBeanInterface", Class.class ); this.newMBeanProxy = JMX.getMethod( "newMBeanProxy", MBeanServerConnection.class, ObjectName.class, Class.class ); this.newMXBeanProxy = JMX.getMethod( "newMXBeanProxy", MBeanServerConnection.class, ObjectName.class, Class.class ); } <T> T makeProxy( ObjectName name, Class<T> beanType ) { try { final Method factoryMethod; if ( isMXBeanInterface( beanType ) ) { factoryMethod = newMXBeanProxy; } else { factoryMethod = newMBeanProxy; } return beanType.cast( factoryMethod.invoke( null, getPlatformMBeanServer(), name, beanType ) ); } catch ( InvocationTargetException exception ) { throw launderRuntimeException( exception.getTargetException() ); } catch ( Exception exception ) { throw new UnsupportedOperationException( "Creating Management Bean proxies requires Java 1.6", exception ); } } private boolean isMXBeanInterface( Class<?> interfaceClass ) throws Exception { return (Boolean) isMXBeanInterface.invoke( null, interfaceClass ); } static RuntimeException launderRuntimeException( Throwable exception ) { if ( exception instanceof RuntimeException ) { return (RuntimeException) exception; } else if ( exception instanceof Error ) { throw (Error) exception; } else { throw new RuntimeException( "Unexpected Exception!", exception ); } } } static { ProxyMaker proxyMaker; try { proxyMaker = new ProxyMaker(); } catch ( Throwable t ) { proxyMaker = null; } PROXY_MAKER = proxyMaker; } private static Neo4jJmx registerBean( MBeanServer mbs, Neo4jJmx bean ) { try { mbs.registerMBean( bean, bean.objectName ); return bean; } catch ( Exception e ) { return null; } } private static final class JmxShutdown implements Runnable { private final Neo4jJmx[] beans; public JmxShutdown( List<Neo4jJmx> beans ) { this.beans = beans.toArray( new Neo4jJmx[beans.size()] ); } public void run() { MBeanServer mbs = getPlatformMBeanServer(); for ( Neo4jJmx bean : beans ) { unregisterBean( mbs, bean ); } } } private static void unregisterBean( MBeanServer mbs, Neo4jJmx bean ) { try { mbs.unregisterMBean( bean.objectName ); } catch ( Exception e ) { log.warning( "Failed to unregister JMX Bean " + bean ); e.printStackTrace(); } } private static void failedToRegister( String mBean ) { log.info( "Failed to register " + mBean ); } private final ObjectName objectName; Neo4jJmx( int instanceId ) { ObjectName name = null; for ( Class<?> beanType : getClass().getInterfaces() ) { name = getObjectName( instanceId, beanType, getClass() ); } if ( name == null ) { throw new IllegalArgumentException( "" ); } objectName = name; } private static ObjectName getObjectName( int instanceId, Class<?> iface, Class<?> clazz ) { final String name; if ( iface == DynamicMBean.class ) { name = clazz.getSimpleName(); } else { try { name = (String) iface.getField( "NAME" ).get( null ); } catch ( Exception e ) { return null; } } StringBuilder identifier = new StringBuilder( "org.neo4j:" ); identifier.append( "instance=kernel#" ); identifier.append( instanceId ); identifier.append( ",name=" ); identifier.append( name ); try { return new ObjectName( identifier.toString() ); } catch ( MalformedObjectNameException e ) { return null; } } }