package org.neo4j.rdf.sail.rmi;
import info.aduna.iteration.CloseableIteration;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.rmi.server.UnicastRemoteObject;
import org.neo4j.rdf.sail.GraphDatabaseSailConnection;
import org.openrdf.model.Statement;
import org.openrdf.sail.Sail;
import org.openrdf.sail.SailChangedEvent;
import org.openrdf.sail.SailChangedListener;
import org.openrdf.sail.SailConnection;
import org.openrdf.sail.SailConnectionListener;
import org.openrdf.sail.SailException;
/**
* Here you can register {@link Sail} instances so that other JVM instances
* can connect to them via RMI using {@link RmiSailClient}.
*/
public class RmiSailServer extends UnicastRemoteObject implements RmiSail
{
interface RmiSailConnectionFactory
{
RmiSailConnectionImpl connect( SailConnection connection )
throws RemoteException;
<E, X extends Exception> IterationBufferer<E, X> buffer(
CloseableIteration<E, X> iter ) throws RemoteException;
}
private final Sail sail;
private final RmiSailConnectionFactory factory;
private RmiSailServer( Sail sail ) throws RemoteException
{
super();
this.sail = sail;
this.factory = new RmiSailConnectionFactory()
{
public RmiSailConnectionImpl connect( SailConnection connection )
throws RemoteException
{
return new RmiSailConnectionImpl( connection, this );
}
public <E, X extends Exception> IterationBufferer<E, X> buffer(
CloseableIteration<E, X> iter ) throws RemoteException
{
return new IterationBufferer<E, X>( iter );
}
};
}
private RmiSailServer( Sail sail, final int port ) throws RemoteException
{
super( port );
this.sail = sail;
this.factory = new RmiSailConnectionFactory()
{
public RmiSailConnectionImpl connect( SailConnection connection )
throws RemoteException
{
return new RmiSailConnectionImpl( connection, this, port );
}
public <E, X extends Exception> IterationBufferer<E, X> buffer(
CloseableIteration<E, X> iter ) throws RemoteException
{
return new IterationBufferer<E, X>( iter, port );
}
};
}
private RmiSailServer( Sail sail, final int port,
final RMIClientSocketFactory csf, final RMIServerSocketFactory ssf )
throws RemoteException
{
super( port, csf, ssf );
this.sail = sail;
this.factory = new RmiSailConnectionFactory()
{
public RmiSailConnectionImpl connect( SailConnection connection )
throws RemoteException
{
return new RmiSailConnectionImpl( connection, this, port, csf,
ssf );
}
public <E, X extends Exception> IterationBufferer<E, X> buffer(
CloseableIteration<E, X> iter ) throws RemoteException
{
return new IterationBufferer<E, X>( iter, port, csf, ssf );
}
};
}
/**
* Registers {@code sail} at the URI given by {@code resourceUri}.
* The URI must have the "rmi" protocol. The registry for the supplied port
* (the port in the URI) must be created before calling this method.
* F.ex. with {@link LocateRegistry#createRegistry(int)}.
*
* @param sail the {@link Sail} to register at the URI.
* @param resourceUri the URI to register the sail at.
* @throws MalformedURLException if the URI is malformed.
* @throws RemoteException if an RMI problem occurs.
* @throws AlreadyBoundException if the given URI is already bound.
*/
public static void register( Sail sail, URI resourceUri )
throws MalformedURLException, RemoteException, AlreadyBoundException
{
Naming.rebind( resourceUri.toString(), new RmiSailServer( sail ) );
}
/**
* Registers {@code sail} at the URI given by {@code resourceUri}.
* The URI must have the "rmi" protocol. The registry for the supplied port
* (the port in the URI) must be created before calling this method.
* F.ex. with {@link LocateRegistry#createRegistry(int)}.
*
* @param sail the {@link Sail} to register at the URI.
* @param resourceUri the URI to register the sail at.
* @param port the port where the method invocation communication will
* occur. see {@link UnicastRemoteObject} for more details.
* @throws MalformedURLException if the URI is malformed.
* @throws RemoteException if an RMI problem occurs.
* @throws AlreadyBoundException if the given URI is already bound.
*/
public static void register( Sail sail, URI resourceUri, int port )
throws MalformedURLException, RemoteException, AlreadyBoundException
{
Naming.rebind( resourceUri.toString(),
new RmiSailServer( sail, port ) );
}
/**
* Registers {@code sail} at the URI given by {@code resourceUri}.
* The URI must have the "rmi" protocol. The registry for the supplied port
* (the port in the URI) must be created before calling this method.
* F.ex. with {@link LocateRegistry#createRegistry(int)}.
*
* @param sail the {@link Sail} to register at the URI.
* @param resourceUri the URI to register the sail at.
* @param port the port where the method invocation communication will
* occur. see {@link UnicastRemoteObject} for more details.
* @param csf the {@link RMIClientSocketFactory} to use.
* See {@link UnicastRemoteObject} for more details.
* @param ssf the {@link RMIServerSocketFactory} to use.
* See {@link UnicastRemoteObject} for more details.
* @throws MalformedURLException if the URI is malformed.
* @throws RemoteException if an RMI problem occurs.
* @throws AlreadyBoundException if the given URI is already bound.
*/
public static void register( Sail sail, URI resourceUri, int port,
RMIClientSocketFactory csf, RMIServerSocketFactory ssf )
throws MalformedURLException, RemoteException, AlreadyBoundException
{
Naming.rebind( resourceUri.toString(), new RmiSailServer( sail, port,
csf, ssf ) );
}
/**
* Used by the client to get a connection to the sail.
*
* @param callback acts as {@link SailConnectionListener} over RMI.
* @throws RemoteException if an RMI problem occurs.
* @throws SailException if a connection couldn't be retrieved.
* @return an RMI version of a {@link GraphDatabaseSailConnection}.
*/
public RmiSailConnection connect(
final RmiSailConnectionListenerCallback callback )
throws RemoteException, SailException
{
final SailConnection connection = sail.getConnection();
final RmiSailConnectionImpl remote = factory.connect(connection);
connection.addConnectionListener( new SailConnectionListener()
{
public void statementAdded( Statement statement )
{
try
{
callback.statementAdded( statement );
}
catch ( RemoteException e )
{
connection.removeConnectionListener( this );
remote.callbackConnectionLost();
}
}
public void statementRemoved( Statement statement )
{
try
{
callback.statementRemoved( statement );
}
catch ( RemoteException e )
{
connection.removeConnectionListener( this );
remote.callbackConnectionLost();
}
}
} );
return remote;
}
/**
* @return the underlying sail's {@link Sail#getDataDir()}.
*/
public File getDataDir()
{
return sail.getDataDir();
}
/**
* Calls the underlying sail's {@link Sail#initialize()}.
*/
public void initialize() throws SailException
{
sail.initialize();
}
/**
* @return the underlying sail's {@link Sail#isWritable()}.
*/
public boolean isWritable() throws SailException
{
return sail.isWritable();
}
/**
* Calls the underlying sail's {@link Sail#setDataDir(File)}.
* @param file the data directory.
*/
public void setDataDir( File file )
{
sail.setDataDir( file );
}
/**
* Calls the underlying sail's
* {@link Sail#addSailChangedListener(SailChangedListener)}. TODO.
*
* @param callback the listener which receives "change" events.
*/
public void addCallback( final RmiSailChangedListenerCallback callback )
{
sail.addSailChangedListener( new SailChangedListener()
{
public void sailChanged( SailChangedEvent event )
{
boolean alive = true;
try
{
alive = callback.sailChanged( event.statementsAdded(),
event.statementsRemoved() );
}
catch ( RemoteException ex )
{
alive = false;
}
if ( !alive )
{
sail.removeSailChangedListener( this );
}
}
} );
}
}