View Javadoc
1   package org.djutils.rmi;
2   
3   import java.net.InetAddress;
4   import java.rmi.AccessException;
5   import java.rmi.AlreadyBoundException;
6   import java.rmi.ConnectException;
7   import java.rmi.NoSuchObjectException;
8   import java.rmi.NotBoundException;
9   import java.rmi.Remote;
10  import java.rmi.RemoteException;
11  import java.rmi.registry.LocateRegistry;
12  import java.rmi.registry.Registry;
13  import java.rmi.server.UnicastRemoteObject;
14  
15  import org.djutils.exceptions.Throw;
16  import org.djutils.logger.CategoryLogger;
17  
18  /**
19   * RMIUtils contains a number of utilities to help with the RMI registry.
20   * <p>
21   * Copyright (c) 2019-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
22   * BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>.
23   * <p>
24   * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank">Alexander Verbraeck</a>
25   */
26  public final class RMIUtils
27  {
28      /**
29       * Static class; constructor should not be called.
30       */
31      private RMIUtils()
32      {
33          // static class; should not be called.
34      }
35  
36      /**
37       * Lookup or create the RMI registry. When the RMI registry does not exist yet, it will be created, but <b>only</b> on the
38       * local host. Remote creation of a registry on another computer is not possible. Any attempt to do so will cause an
39       * AccessException to be fired.
40       * @param host String; the host where the RMI registry resides or will be created. Creation is only possible on localhost.
41       * @param port int; the port where the RMI registry can be found or will be created
42       * @return Registry; the located or created RMI registry
43       * @throws RemoteException when there is a problem with locating or creating the RMI registry
44       * @throws NullPointerException when host is null
45       * @throws IllegalArgumentException when port &le; 0 or port &gt; 65535
46       * @throws AccessException when there is an attempt to create a registry on a remote host
47       */
48      public static Registry getRegistry(final String host, final int port) throws RemoteException
49      {
50          Throw.whenNull(host, "host cannot be null");
51          Throw.when(port <= 0 || port > 65535, IllegalArgumentException.class, "port <= 0 or port > 65535");
52          try
53          {
54              Registry registry = LocateRegistry.getRegistry(host, port);
55              boolean validRegistry = registry != null;
56              if (validRegistry)
57              {
58                  try
59                  {
60                      // If there is no registry, registry!=null, so we have to test the registry for validity
61                      // see https://stackoverflow.com/questions/13779624/java-rmi-check-if-registry-exists
62                      // or https://stackoverflow.com/questions/8337215/remote-method-invocation-port-in-use
63                      registry.list();
64                  }
65                  catch (ConnectException | NoSuchObjectException connectException)
66                  {
67                      validRegistry = false;
68                  }
69              }
70  
71              // create the registry when it does not exist yet, but ONLY on localhost
72              if (!validRegistry)
73              {
74                  if (!(host.equals("localhost") || host.equals("127.0.0.1")
75                          || host.equals(InetAddress.getLocalHost().getHostName())
76                          || host.equals(InetAddress.getLocalHost().getHostAddress())))
77                  {
78                      throw new AccessException("Cannot create registry on remote host: " + host);
79                  }
80                  registry = LocateRegistry.createRegistry(port);
81              }
82              return registry;
83          }
84          catch (Exception exception)
85          {
86              CategoryLogger.always().error(exception, "RMI exception when locating or creating RMI registry");
87              throw new RemoteException("RMI exception when locating or creating RMI registry", exception);
88          }
89      }
90  
91      /**
92       * Bind an object in the RMI registry.
93       * @param registry Registry; the RMI registry where the object will be bound using the key
94   * @param bindingKey String; the key under which the object will be bound in the RMI registry
95  
96       * @param object Remote; the object that will be bound
97       * @throws RemoteException when there is a problem with the RMI registry
98       * @throws AlreadyBoundException when there is already another object bound to the bindingKey
99       * @throws NullPointerException when registry, bindingKey or object is null
100      * @throws IllegalArgumentException when bindingKey is the empty String
101      */
102     public static void bind(final Registry registry, final String bindingKey, final Remote object)
103             throws RemoteException, AlreadyBoundException
104     {
105         Throw.whenNull(registry, "registry cannot be null");
106         Throw.whenNull(bindingKey, "bindingKey cannot be null");
107         Throw.when(bindingKey.length() == 0, IllegalArgumentException.class, "bindingKey cannot be the empty String");
108         Throw.whenNull(object, "null object cannot be bound");
109         try
110         {
111             registry.bind(bindingKey, object);
112         }
113         catch (RemoteException | AlreadyBoundException exception)
114         {
115             CategoryLogger.always().error(exception, "RMI exception when binding object to the registry");
116             throw exception;
117         }
118     }
119 
120     /**
121      * Unbind an object from the RMI registry.
122      * @param registry Registry; the RMI registry where the object will be bound using the key
123  * @param bindingKey String; the key under which the object will be bound in the RMI registry
124 
125      * @throws RemoteException when there is a problem with the RMI registry
126      * @throws NotBoundException when there is no object bound to the bindingKey
127      * @throws NullPointerException when registry or bindingKey is null
128      * @throws IllegalArgumentException when bindingKey is the empty String
129      */
130     public static void unbind(final Registry registry, final String bindingKey) throws RemoteException, NotBoundException
131     {
132         Throw.whenNull(registry, "registry cannot be null");
133         Throw.whenNull(bindingKey, "bindingKey cannot be null");
134         Throw.when(bindingKey.length() == 0, IllegalArgumentException.class, "bindingKey cannot be the empty String");
135         try
136         {
137             registry.unbind(bindingKey);
138         }
139         catch (RemoteException | NotBoundException exception)
140         {
141             CategoryLogger.always().error(exception, "RMI exception when unbinding object from the registry");
142             throw exception;
143         }
144     }
145 
146     /**
147      * Rebind an object to an existing string in the RMI registry.
148      * @param registry Registry; the RMI registry where the object will be bound using the key
149  * @param bindingKey String; the (existing) key under which the new object will be bound in the RMI registry
150 
151      * @param newObject Remote; the new object that will be bound
152      * @throws RemoteException when there is a problem with the RMI registry
153      * @throws NullPointerException when registry, bindingKey or newObject is null
154      * @throws IllegalArgumentException when bindingKey is the empty String
155      */
156     public static void rebind(final Registry registry, final String bindingKey, final Remote newObject) throws RemoteException
157     {
158         Throw.whenNull(registry, "registry cannot be null");
159         Throw.whenNull(bindingKey, "bindingKey cannot be null");
160         Throw.when(bindingKey.length() == 0, IllegalArgumentException.class, "bindingKey cannot be the empty String");
161         Throw.whenNull(newObject, "null object cannot be bound");
162         try
163         {
164             registry.rebind(bindingKey, newObject);
165         }
166         catch (RemoteException exception)
167         {
168             CategoryLogger.always().error(exception, "RMI exception when rebinding object to the registry");
169             throw exception;
170         }
171     }
172 
173     /**
174      * Lookup an object in the RMI registry.
175      * @param registry Registry; the RMI registry in which the object will be looked up using the key
176  * @param bindingKey String; the key under which the object should be registered in the RMI registry
177 
178      * @return Remote; the remote object that bound to the key in the RMI registry
179      * @throws RemoteException when there is a problem with the RMI registry
180      * @throws NotBoundException when there is no object bound to the bindingKey
181      * @throws NullPointerException when registry or bindingKey is null
182      * @throws IllegalArgumentException when bindingKey is the empty String
183      */
184     public static Remote lookup(final Registry registry, final String bindingKey) throws RemoteException, NotBoundException
185     {
186         Throw.whenNull(registry, "registry cannot be null");
187         Throw.whenNull(bindingKey, "bindingKey cannot be null");
188         Throw.when(bindingKey.length() == 0, IllegalArgumentException.class, "bindingKey cannot be the empty String");
189         try
190         {
191             return registry.lookup(bindingKey);
192         }
193         catch (RemoteException | NotBoundException exception)
194         {
195             CategoryLogger.always().error(exception, "RMI exception when looking up key {} in the RMI registry", bindingKey);
196             throw exception;
197         }
198     }
199 
200     /**
201      * Unbinds all the objects from the RMI registry and closes the registry.
202      * @param registry Registry; the RMI registry that will unbind all the objects and close.
203      * @throws RemoteException when there is a problem with the RMI registry
204      * @throws NullPointerException when registry is null
205      */
206     public static void closeRegistry(final Registry registry) throws RemoteException
207     {
208         Throw.whenNull(registry, "registry cannot be null");
209         for (String key : registry.list())
210         {
211             try
212             {
213                 unbind(registry, key);
214             }
215             catch (RemoteException | NotBoundException nbe)
216             {
217                 CategoryLogger.always().error(nbe, "RMI exception when unbinding key {} from the RMI registry", key);
218             }
219         }
220         UnicastRemoteObject.unexportObject(registry, true);
221     }
222 }