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 ≤ 0 or port > 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 }