/* * Copyright (c) 2008-2009 "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.remote.transports; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URI; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.RMIClientSocketFactory; import java.rmi.server.RMIServerSocketFactory; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.remote.BasicGraphDatabaseServer; import org.neo4j.remote.ConnectionTarget; import org.neo4j.remote.Transport; /** * A {@link Transport} that communicates with a remote graph database using RMI. * * @author Tobias Ivarsson */ public final class RmiTransport extends Transport { /** * Create a new {@link Transport} for the rmi:// protocol. */ public RmiTransport() { super( "rmi" ); } @Override protected boolean handlesUri( URI resourceUri ) { return "rmi".equals( resourceUri.getScheme() ); } @Override protected ConnectionTarget create( URI resourceUri ) { return new RmiTarget( resourceUri ); } /** * Registers a {@link GraphDatabaseService} as an RMI service with a given name. If * a resource is already registered with the specified name, that resources * is replaced. Use {@link Naming#unbind(String)} to unregister the * {@link GraphDatabaseService}. * @param server * the graph database server to register as an RMI service. * @param resourceUri * the name in URL form to register the exported graph database server as. * @throws RemoteException * if the RMI registry could not be contacted. * @throws MalformedURLException * if the <code>resourceUri</code> is not properly formatted. */ public static void register( BasicGraphDatabaseServer server, String resourceUri ) throws RemoteException, MalformedURLException { Naming.rebind( resourceUri, RmiConnectionServer.setup( server ) ); } /** * Registers a {@link GraphDatabaseService} as an RMI service with a given name on * a given port. If a resource is already registered with the specified * name, that resources is replaced. Use {@link Naming#unbind(String)} to * unregister the {@link GraphDatabaseService}. * @param server * the graph database server to register as an RMI service. * @param resourceUri * the name in URL form to register the exported graph database server as. * @param port * the port number on which the remote object receives calls (if * port is zero, an anonymous port is chosen). * @throws RemoteException * if the RMI registry could not be contacted. * @throws MalformedURLException * if the <code>resourceUri</code> is not properly formatted. */ public static void register( BasicGraphDatabaseServer server, String resourceUri, int port ) throws RemoteException, MalformedURLException { Naming.rebind( resourceUri, RmiConnectionServer.setup( server, port ) ); } /** * Registers a {@link GraphDatabaseService} as an RMI service with a given name on * a given port. Uses the specified socket factories to get the sockets for * the connections. If a resource is already registered with the specified * name, that resources is replaced. Use {@link Naming#unbind(String)} to * unregister the {@link GraphDatabaseService}. * @param server * the graph database server to register as an RMI service. * @param resourceUri * the name in URL form to register the exported graph database server as. * @param port * the port number on which the remote object receives calls (if * port is zero, an anonymous port is chosen). * @param csf * the client-side socket factory for making calls to the remote * object. * @param ssf * the server-side socket factory for receiving remote calls. * @throws RemoteException * if the RMI registry could not be contacted. * @throws MalformedURLException * if the <code>resourceUri</code> is not properly formatted. */ public static void register( BasicGraphDatabaseServer server, String resourceUri, int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf ) throws RemoteException, MalformedURLException { Naming.rebind( resourceUri, RmiConnectionServer.setup( server, port, csf, ssf ) ); } /** * Start a stand alone Remote graph database / RMI server. * <p /> * Usage: * * <pre> * java -cp kernel.jar:jta.jar:remote.jar org.neo4j.remote.sites.RmiSite PATH RESOURCE_URI * </pre> * <p /> * If the host in the <code>RESOURCE_URI</code> resolves to the local host a * registry will be started if none is running. * <p /> * Any further arguments will be used to register index services. These take * the form of: * * <pre> * class.name.for.the.IndexServiceImplementation:index-service-identifier * </pre> * * @param args * The arguments passed on the command line. * @throws RemoteException * when the registration of the server fails. * @throws IllegalArgumentException * when an error was found in the command line arguments. */ public static void main( String[] args ) throws RemoteException, IllegalArgumentException { String usage = "Usage: " + RmiTarget.class.getName() + " <GraphDB dir> <rmi resource uri>"; if ( args.length < 2 ) { throw new IllegalArgumentException( usage ); } // Instantiate the Neo4j server final LocalGraphDatabase server; try { server = new LocalGraphDatabase( args[ 0 ] ); } catch ( RuntimeException ex ) { throw new IllegalArgumentException( usage, ex ); } System.out.println( "Created Neo4j server. Store directory: " + args[ 0 ] ); // The rest of the parameters define indexes // Loosely couple the indexes, we don't need them unless we use them Class<?> indexService; Method registerIndexMethod; try { indexService = Class.forName( "org.neo4j.index.IndexService" ); registerIndexMethod = BasicGraphDatabaseServer.class.getDeclaredMethod( "registerIndexService", String.class, indexService ); } catch ( Exception ex ) { indexService = null; registerIndexMethod = null; } // Start each index service for ( int i = 2; i < args.length; i++ ) { // ... but not if we don't have the index component if ( indexService == null ) { System.err.println( "Could not instantiate index \"" + args[ i ] + "\"\n The neo-index component is not loaded." ); continue; } // Separate the index class name from the index service identifier String argument[] = args[ i ].split( ":", 2 ); String className = argument[ 0 ]; String indexName = argument.length == 2 ? argument[ 1 ] : argument[ 0 ]; // Instantiate and register the index service try { Class<?> cls = Class.forName( className ); Constructor<?> ctor = cls.getConstructor( GraphDatabaseService.class ); Object index = ctor.newInstance( server.neo.service ); registerIndexMethod.invoke( server, indexName, index ); System.out.println( "Registered index service: " + indexName ); } catch ( Exception ex ) { System.err .println( "Could not instantiate index \"" + args[ i ] ); ex.printStackTrace( System.err ); } } // Check if the resource uri host is localhost final URI uri; final InetAddress addr; final InetAddress localhost; final InetAddress[] localhosts; try { uri = new URI( args[ 1 ] ); addr = InetAddress.getByName( uri.getHost() ); localhost = InetAddress.getLocalHost(); localhosts = InetAddress.getAllByName( "localhost" ); } catch ( Exception ex ) { throw new IllegalArgumentException( usage, ex ); } boolean addrIsLocalHost = addr.equals( localhost ); for ( InetAddress local : localhosts ) { if ( addrIsLocalHost ) break; addrIsLocalHost = addr.equals( local ); } // if it is localhost - try to start a registry if ( addrIsLocalHost ) { int port = uri.getPort(); if ( port == -1 ) port = Registry.REGISTRY_PORT; try { LocateRegistry.createRegistry( port ); System.out.println( "Created RMI registry on localhost." ); } catch ( Exception ex ) { // it could have failed because it is already started } } // Register the server at the specified mounting point in the registry try { register( server, args[ 1 ] ); } catch ( MalformedURLException ex ) { throw new IllegalArgumentException( usage, ex ); } System.out.println( "Neo4j RMI server registered at: " + args[ 1 ] ); System.out.println( "Press Ctrl+C to stop serving." ); } }