/** * Copyright (c) 2002-2011 "Neo Technology," * Network Engine for Objects in Lund AB [http://neotechnology.com] * * This file is part of Neo4j. * * Neo4j is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.neo4j.management.impl; import static java.lang.management.ManagementFactory.getPlatformMBeanServer; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.management.MBeanServer; import javax.management.ObjectName; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; import javax.management.remote.rmi.RMIConnectorServer; import javax.rmi.ssl.SslRMIClientSocketFactory; import javax.rmi.ssl.SslRMIServerSocketFactory; import org.neo4j.helpers.Service; import org.neo4j.kernel.KernelExtension; @Service.Implementation( KernelExtension.class ) public final class JmxExtension extends KernelExtension { private static final Logger log = Logger.getLogger( JmxExtension.class.getName() ); public JmxExtension() { super( "kernel jmx" ); } @Override protected void load( KernelData kernel ) { kernel.setState( this, loadBeans( kernel ) ); } @Override protected void unload( KernelData kernel ) { ( (JmxData) kernel.getState( this ) ).shutdown(); } private JmxData loadBeans( KernelData kernel ) { MBeanServer mbs = getPlatformMBeanServer(); List<Neo4jMBean> beans = new ArrayList<Neo4jMBean>(); for ( ManagementBeanProvider provider : Service.load( ManagementBeanProvider.class ) ) { try { Neo4jMBean bean = provider.loadBeen( kernel ); if ( bean != null ) { mbs.registerMBean( bean, bean.objectName ); beans.add( bean ); } } catch ( Exception e ) { log.info( "Failed to register JMX Bean " + provider ); } } try { Neo4jMBean bean = new KernelBean( kernel ); mbs.registerMBean( bean, bean.objectName ); beans.add( bean ); } catch ( Exception e ) { log.info( "Failed to register Kernel JMX Bean" ); } return new JmxData( kernel, beans.toArray( new Neo4jMBean[beans.size()] ) ); } private static final class JmxData { private final Neo4jMBean[] beans; private final JMXServiceURL url; JmxData( KernelData kernel, Neo4jMBean[] beans ) { this.beans = beans; @SuppressWarnings( "hiding" ) JMXServiceURL url = null; try { Class<?> cal = Class.forName( "sun.management.ConnectorAddressLink" ); try { Method importRemoteFrom = cal.getMethod( "importRemoteFrom", int.class ); @SuppressWarnings( "unchecked" ) Map<String, String> remote = (Map<String, String>) importRemoteFrom.invoke( null, Integer.valueOf( 0 ) ); url = getUrlFrom( remote ); } catch ( NoSuchMethodException ex ) { } if ( url == null ) { Method importFrom = cal.getMethod( "importFrom", int.class ); url = getUrlFrom( (String) importFrom.invoke( null, Integer.valueOf( 0 ) ) ); } } catch ( InvocationTargetException e ) { log.log( Level.CONFIG, "Failed to load local JMX connection URL.", e.getTargetException() ); } catch ( LinkageError e ) { log.log( Level.CONFIG, "Failed to load local JMX connection URL.", e ); } catch ( Exception e ) { log.log( Level.CONFIG, "Failed to load local JMX connection URL.", e ); } // No previous connection server -- create one! if ( url == null ) { Object portObj = kernel.getParam( "jmx.port" ); int port = 0; if ( portObj instanceof Integer ) { port = ( (Integer) portObj ).intValue(); } else if ( portObj instanceof String ) { try { port = Integer.parseInt( (String) portObj ); } catch ( NumberFormatException ok ) { } } if ( port > 0 ) { Object useSslObj = kernel.getParam( "jmx.use_ssl" ); boolean useSSL = false; if ( useSslObj instanceof Boolean ) { useSSL = ( (Boolean) useSslObj ).booleanValue(); } else if ( useSslObj instanceof String ) { useSSL = Boolean.parseBoolean( (String) useSslObj ); } log.log( Level.CONFIG, "Creating new MBean server on port %s%s", new Object[] { port, useSSL ? " using ssl" : "" } ); JMXConnectorServer server = createServer( port, useSSL ); if ( server != null ) { try { server.start(); } catch ( IOException e ) { log.log( Level.CONFIG, "Failed to start MBean server", e ); server = null; } if ( server != null ) { try { server.getMBeanServer().registerMBean( server, getObjectName( kernel, null, "JMX Server" ) ); } catch ( Exception e ) { log.log( Level.CONFIG, "Failed to register MBean server as JMX bean", e ); } url = server.getAddress(); } } } } this.url = url; } private static JMXServiceURL getUrlFrom( String url ) { if ( url == null ) return null; JMXServiceURL jmxUrl; try { jmxUrl = new JMXServiceURL( url ); } catch ( MalformedURLException e1 ) { return null; } String host = null; try { host = InetAddress.getLocalHost().getHostAddress(); } catch ( UnknownHostException ok ) { } if ( host == null ) { host = jmxUrl.getHost(); } try { return new JMXServiceURL( jmxUrl.getProtocol(), host, jmxUrl.getPort(), jmxUrl.getURLPath() ); } catch ( MalformedURLException e ) { return null; } } private static JMXServiceURL getUrlFrom( Map<String, String> remote ) { Set<Integer> instances = new HashSet<Integer>(); for ( String key : remote.keySet() ) { if ( key.startsWith( "sun.management.JMXConnectorServer" ) ) { int end = key.lastIndexOf( '.' ); if ( end < 0 ) continue; int start = key.lastIndexOf( '.', end ); if ( start < 0 ) continue; final int id; try { id = Integer.parseInt( key.substring( start, end ) ); } catch ( NumberFormatException e ) { continue; } instances.add( Integer.valueOf( id ) ); } } if ( !instances.isEmpty() ) { String prefix = "sun.management.JMXConnectorServer."; if ( instances.size() > 1 ) { for ( Object key : instances.toArray() ) { if ( !remote.containsKey( "sun.management.JMXConnectorServer." + key + ".remoteAddress" ) ) { instances.remove( key ); } } if ( instances.contains( Integer.valueOf( 0 ) ) ) { prefix = prefix + "0."; } } if ( instances.size() == 1 ) { String remoteAddress = remote.get( prefix + instances.iterator().next() + "remoteAddress" ); try { return new JMXServiceURL( remoteAddress ); } catch ( MalformedURLException e ) { return null; } } else if ( !instances.isEmpty() ) { // TODO: find the appropriate one } } return null; } void shutdown() { MBeanServer mbs = getPlatformMBeanServer(); for ( Neo4jMBean bean : beans ) { try { mbs.unregisterMBean( bean.objectName ); } catch ( Exception e ) { log.log( Level.WARNING, "Failed to unregister JMX Bean " + bean, e ); } } } } JMXServiceURL getConnectionURL( KernelData kernel ) { return ( (JmxData) kernel.getState( this ) ).url; } public static JMXConnectorServer createServer( int port, boolean useSSL ) { MBeanServer server = getPlatformMBeanServer(); final JMXServiceURL url; try { url = new JMXServiceURL( "rmi", null, port ); } catch ( MalformedURLException e ) { log.log( Level.WARNING, "Failed to start JMX Server", e ); return null; } Map<String, Object> env = new HashMap<String, Object>(); if ( useSSL ) { env.put( RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, new SslRMIClientSocketFactory() ); env.put( RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, new SslRMIServerSocketFactory() ); } try { return JMXConnectorServerFactory.newJMXConnectorServer( url, env, server ); } catch ( IOException e ) { log.log( Level.WARNING, "Failed to start JMX Server", e ); return null; } } public <T> T getBean( KernelData kernel, Class<T> beanInterface ) { if ( !isLoaded( kernel ) ) throw new IllegalStateException( "Not Loaded!" ); ObjectName name = getObjectName( kernel, beanInterface, null ); if ( name == null ) { throw new IllegalArgumentException( beanInterface + " is not a Neo4j Management Bean interface" ); } return BeanProxy.load( getPlatformMBeanServer(), beanInterface, name ); } static ObjectName getObjectName( KernelData kernel, Class<?> beanInterface, String beanName ) { return BeanNaming.getObjectName( kernel.instanceId(), beanInterface, beanName ); } }