RmiRegistry.java
package org.djutils.rmi;
import java.net.InetAddress;
import java.rmi.AccessException;
import java.rmi.AlreadyBoundException;
import java.rmi.ConnectException;
import java.rmi.NoSuchObjectException;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import org.djutils.exceptions.Throw;
import org.djutils.logger.CategoryLogger;
/**
* RMIUtils contains a number of utilities to help with the RMI registry.
* <p>
* Copyright (c) 2019-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
* BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>.
* </p>
* @author <a href="https://www.tudelft.nl/averbraeck" target="_blank">Alexander Verbraeck</a>
*/
public final class RmiRegistry
{
/**
* Static class; constructor should not be called.
*/
private RmiRegistry()
{
// static class; should not be called.
}
/**
* Lookup or create the RMI registry. When the RMI registry does not exist yet, it will be created, but <b>only</b> on the
* local host. Remote creation of a registry on another computer is not possible. Any attempt to do so will cause an
* AccessException to be fired.
* @param host String; the host where the RMI registry resides or will be created. Creation is only possible on localhost.
* @param port int; the port where the RMI registry can be found or will be created
* @return Registry; the located or created RMI registry
* @throws RemoteException when there is a problem with locating or creating the RMI registry
* @throws NullPointerException when host is null
* @throws IllegalArgumentException when port ≤ 0 or port > 65535
* @throws AccessException when there is an attempt to create a registry on a remote host
*/
public static Registry getRegistry(final String host, final int port) throws RemoteException
{
Throw.whenNull(host, "host cannot be null");
Throw.when(port <= 0 || port > 65535, IllegalArgumentException.class, "port <= 0 or port > 65535");
try
{
Registry registry = LocateRegistry.getRegistry(host, port);
boolean validRegistry = registry != null;
if (validRegistry)
{
try
{
// If there is no registry, registry!=null, so we have to test the registry for validity
// see https://stackoverflow.com/questions/13779624/java-rmi-check-if-registry-exists
// or https://stackoverflow.com/questions/8337215/remote-method-invocation-port-in-use
registry.list();
}
catch (ConnectException | NoSuchObjectException connectException)
{
validRegistry = false;
}
}
// create the registry when it does not exist yet, but ONLY on localhost
if (!validRegistry)
{
if (!(host.equals("localhost") || host.equals("127.0.0.1")
|| host.equals(InetAddress.getLocalHost().getHostName())
|| host.equals(InetAddress.getLocalHost().getHostAddress())))
{
throw new AccessException("Cannot create registry on remote host: " + host);
}
registry = LocateRegistry.createRegistry(port);
}
return registry;
}
catch (Exception exception)
{
CategoryLogger.always().error(exception, "RMI exception when locating or creating RMI registry");
throw new RemoteException("RMI exception when locating or creating RMI registry", exception);
}
}
/**
* Bind an object in the RMI registry.
* @param registry Registry; the RMI registry where the object will be bound using the key
* @param bindingKey String; the key under which the object will be bound in the RMI registry
* @param object Remote; the object that will be bound
* @throws RemoteException when there is a problem with the RMI registry
* @throws AlreadyBoundException when there is already another object bound to the bindingKey
* @throws NullPointerException when registry, bindingKey or object is null
* @throws IllegalArgumentException when bindingKey is the empty String
*/
public static void bind(final Registry registry, final String bindingKey, final Remote object)
throws RemoteException, AlreadyBoundException
{
Throw.whenNull(registry, "registry cannot be null");
Throw.whenNull(bindingKey, "bindingKey cannot be null");
Throw.when(bindingKey.length() == 0, IllegalArgumentException.class, "bindingKey cannot be the empty String");
Throw.whenNull(object, "null object cannot be bound");
try
{
registry.bind(bindingKey, object);
}
catch (RemoteException | AlreadyBoundException exception)
{
CategoryLogger.always().error(exception, "RMI exception when binding object to the registry");
throw exception;
}
}
/**
* Unbind an object from the RMI registry.
* @param registry Registry; the RMI registry where the object will be bound using the key
* @param bindingKey String; the key under which the object will be bound in the RMI registry
* @throws RemoteException when there is a problem with the RMI registry
* @throws NotBoundException when there is no object bound to the bindingKey
* @throws NullPointerException when registry or bindingKey is null
* @throws IllegalArgumentException when bindingKey is the empty String
*/
public static void unbind(final Registry registry, final String bindingKey) throws RemoteException, NotBoundException
{
Throw.whenNull(registry, "registry cannot be null");
Throw.whenNull(bindingKey, "bindingKey cannot be null");
Throw.when(bindingKey.length() == 0, IllegalArgumentException.class, "bindingKey cannot be the empty String");
try
{
registry.unbind(bindingKey);
}
catch (RemoteException | NotBoundException exception)
{
CategoryLogger.always().error(exception, "RMI exception when unbinding object from the registry");
throw exception;
}
}
/**
* Rebind an object to an existing string in the RMI registry.
* @param registry Registry; the RMI registry where the object will be bound using the key
* @param bindingKey String; the (existing) key under which the new object will be bound in the RMI registry
* @param newObject Remote; the new object that will be bound
* @throws RemoteException when there is a problem with the RMI registry
* @throws NullPointerException when registry, bindingKey or newObject is null
* @throws IllegalArgumentException when bindingKey is the empty String
*/
public static void rebind(final Registry registry, final String bindingKey, final Remote newObject) throws RemoteException
{
Throw.whenNull(registry, "registry cannot be null");
Throw.whenNull(bindingKey, "bindingKey cannot be null");
Throw.when(bindingKey.length() == 0, IllegalArgumentException.class, "bindingKey cannot be the empty String");
Throw.whenNull(newObject, "null object cannot be bound");
try
{
registry.rebind(bindingKey, newObject);
}
catch (RemoteException exception)
{
CategoryLogger.always().error(exception, "RMI exception when rebinding object to the registry");
throw exception;
}
}
/**
* Lookup an object in the RMI registry.
* @param registry Registry; the RMI registry in which the object will be looked up using the key
* @param bindingKey String; the key under which the object should be registered in the RMI registry
* @return Remote; the remote object that bound to the key in the RMI registry
* @throws RemoteException when there is a problem with the RMI registry
* @throws NotBoundException when there is no object bound to the bindingKey
* @throws NullPointerException when registry or bindingKey is null
* @throws IllegalArgumentException when bindingKey is the empty String
*/
public static Remote lookup(final Registry registry, final String bindingKey) throws RemoteException, NotBoundException
{
Throw.whenNull(registry, "registry cannot be null");
Throw.whenNull(bindingKey, "bindingKey cannot be null");
Throw.when(bindingKey.length() == 0, IllegalArgumentException.class, "bindingKey cannot be the empty String");
try
{
return registry.lookup(bindingKey);
}
catch (RemoteException | NotBoundException exception)
{
CategoryLogger.always().error(exception, "RMI exception when looking up key {} in the RMI registry", bindingKey);
throw exception;
}
}
/**
* Unbinds all the objects from the RMI registry and closes the registry.
* @param registry Registry; the RMI registry that will unbind all the objects and close.
* @throws RemoteException when there is a problem with the RMI registry
* @throws NullPointerException when registry is null
*/
public static void closeRegistry(final Registry registry) throws RemoteException
{
Throw.whenNull(registry, "registry cannot be null");
for (String key : registry.list())
{
try
{
unbind(registry, key);
}
catch (RemoteException | NotBoundException nbe)
{
CategoryLogger.always().error(nbe, "RMI exception when unbinding key {} from the RMI registry", key);
}
}
UnicastRemoteObject.unexportObject(registry, true);
}
}