1 /*
2 * Copyright (c) 1998-2002 The Jgroup Team.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 *
17 */
18
19 package jgroup.core.registry;
20
21 import java.net.UnknownHostException;
22 import java.rmi.AccessException;
23 import java.rmi.AlreadyBoundException;
24 import java.rmi.NotBoundException;
25 import java.rmi.Remote;
26 import java.rmi.RemoteException;
27 import java.rmi.registry.LocateRegistry;
28 import java.rmi.registry.Registry;
29 import java.rmi.server.RMIClientSocketFactory;
30 import java.rmi.server.RMIServerSocketFactory;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.Map;
34
35 import jgroup.core.ConfigManager;
36 import jgroup.relacs.simulator.SocketStatusImpl;
37 import jgroup.util.Abort;
38
39 import org.apache.log4j.Logger;
40
41 /**
42 * Provides static methods for binding bootstrap references to a local
43 * RMI registry. It will create local registry if no such registry
44 * exists and bind the given name-remote object pair.
45 *
46 * Currently, used by ExecDaemon, RegistryImpl (DependableRegistry),
47 * the Jgroup Daemon and the partition simulator to bootstraping their
48 * references.
49 *
50 * @author Hein Meling
51 * @since Jgroup 1.2
52 */
53 public class BootstrapRegistry
54 {
55
56 ////////////////////////////////////////////////////////////////////////////////////////////
57 // Logger
58 ////////////////////////////////////////////////////////////////////////////////////////////
59
60 /** Obtain logger for this class */
61 private static final Logger log = Logger.getLogger(BootstrapRegistry.class);
62
63 ////////////////////////////////////////////////////////////////////////////////////////////
64 // Static fields and constants
65 ////////////////////////////////////////////////////////////////////////////////////////////
66
67 /** Bootstrap registry test string */
68 private static final String BOOTREGISTRY_TEST = "BOOTSTRAP_REGISTRY_TEST";
69
70 /** Mapping: host -> registry stub */
71 private static final Map<String,Registry> registryMap =
72 Collections.synchronizedMap(new HashMap<String,Registry>());
73
74 /** The port used by the bootstrap registry */
75 private static int port;
76
77 /** The registry stub for the local bootstrap registry*/
78 private static Registry reg;
79
80 /**
81 * An object used to obtain information about link connectivity when
82 * the partition simulator has been activated.
83 */
84 private static SocketStatusImpl status = null;
85
86
87 ////////////////////////////////////////////////////////////////////////////////////////////
88 // Static block
89 ////////////////////////////////////////////////////////////////////////////////////////////
90
91 /** Initialize the bootstrap registry. */
92 static {
93 port = ConfigManager.getBootstrapPort();
94 if (port < 1024)
95 Abort.exit("Illegal bootstrap port: " + port);
96
97 /* Check if the partition simulator should be activated. */
98 if (Boolean.getBoolean("jgroup.simulator")) {
99 status = SocketStatusImpl.instance();
100 }
101 }
102
103
104 ////////////////////////////////////////////////////////////////////////////////////////////
105 // Public static methods
106 ////////////////////////////////////////////////////////////////////////////////////////////
107
108 /**
109 * Update the bootstrap registry reference. This should be used in the
110 * case when the JVM hosting the bootstrap registry has crashed, and
111 * the bootstrap registry should be recreated within a new JVM.
112 */
113 public static void refreshRegistryStub()
114 throws RemoteException
115 {
116 reg = getRegistry();
117 }
118
119 /**
120 * Update the bootstrap registry references to the remote registries.
121 */
122 public static void refreshRemoteRegistries()
123 throws RemoteException
124 {
125 for (String hostname : registryMap.keySet()) {
126 try {
127 getRemoteRegistry(hostname, null, false);
128 } catch (UnknownHostException e) {
129 log.error("Unknown host: " + hostname, e);
130 }
131 }
132 }
133
134 /**
135 * Returns an array of the names bound in the bootstrap registry.
136 * The array will contain a snapshot of the names bound in this
137 * registry at the time of the given invocation of this method.
138 *
139 * @return an array of the names bound in this registry
140 *
141 * @exception AccessException
142 * if this operation is not permitted (if originating from a
143 * non-local host, for example)
144 * @exception RemoteException
145 * if registry could not be contacted
146 */
147 public static String[] list()
148 throws AccessException, RemoteException
149 {
150 if (reg == null)
151 reg = getRegistry();
152 return reg.list();
153 }
154
155
156 /**
157 * Obtains a remote object reference for the specified object name,
158 * from the local bootstrap registry.
159 *
160 * @param name
161 * Name of remote object to be retreived.
162 *
163 * @exception AccessException
164 * if this operation is not permitted (if originating from a
165 * non-local host, for example)
166 * @exception RemoteException
167 * if registry could not be contacted
168 * @exception NotBoundException
169 * if name is not bound in the registry
170 */
171 public static Remote lookup(String name)
172 throws AccessException, RemoteException, NotBoundException
173 {
174 if (reg == null)
175 reg = getRegistry();
176 return reg.lookup(name);
177 }
178
179
180 /**
181 * Obtains a remote object reference for the specified object name,
182 * from the bootstrap registry located on the given hostname.
183 *
184 * @param hostname
185 * name of remote host from which the object reference is obtained
186 * @param name
187 * name of remote object to be retreived
188 *
189 * @exception AccessException
190 * if this operation is not permitted (if originating from a
191 * non-local host, for example)
192 * @exception RemoteException
193 * if registry could not be contacted
194 * @exception NotBoundException
195 * if name is not bound in the registry
196 */
197 public static Remote lookup(String hostname, String name)
198 throws AccessException, UnknownHostException, RemoteException, NotBoundException
199 {
200 Registry remoteReg = getRemoteRegistry(hostname, null, true);
201 if (log.isDebugEnabled())
202 log.debug("lookup(" + hostname + ", " + name + ")");
203 return remoteReg.lookup(name);
204 }
205
206
207 /**
208 * Binds the specified name to a new remote object. This bind
209 * operation works on the existing local BootstrapRegistry
210 * instance.
211 *
212 * @param name
213 * Name to be associated with the remote object
214 * @param obj
215 * Remote object to associate with the name
216 *
217 * @exception AccessException
218 * if this operation is not permitted (if originating from a
219 * non-local host, for example)
220 * @exception RemoteException
221 * if registry could not be contacted
222 * @exception AlreadyBoundException
223 * if name is already bound
224 */
225 public static void bind(String name, Remote obj)
226 throws AccessException, RemoteException, AlreadyBoundException
227 {
228 if (log.isDebugEnabled())
229 log.debug("Binding object with name: " + name + ", remoteRef: " + obj);
230 if (reg == null)
231 reg = getRegistry();
232 reg.bind(name, obj);
233 }
234
235
236 /**
237 * Rebinds the specified name to a new remote object. Any
238 * existing binding for the name is replaced. This rebind
239 * operation works on the existing local BootstrapRegistry
240 * instance.
241 *
242 * @param name
243 * name to be associated with the remote object
244 * @param obj
245 * new remote object to associate with the name
246 *
247 * @exception AccessException
248 * if this operation is not permitted (if originating from a
249 * non-local host, for example)
250 * @exception RemoteException
251 * if registry could not be contacted
252 */
253 public static void rebind(String name, Remote obj)
254 throws AccessException, RemoteException
255 {
256 if (log.isDebugEnabled())
257 log.debug("Rebinding object with name: " + name + ", remoteRef: " + obj);
258 if (reg == null)
259 reg = getRegistry();
260 reg.rebind(name, obj);
261 }
262
263 /**
264 * Removes the binding for the specified name in the bootstrap
265 * registry.
266 *
267 * @param name
268 * The name of the binding to remove
269 *
270 * @exception AccessException
271 * if this operation is not permitted (if originating from a
272 * non-local host, for example)
273 * @exception RemoteException
274 * if registry could not be contacted
275 * @exception NotBoundException
276 * if name is not bound in the registry
277 */
278 public static void unbind(String name)
279 throws AccessException, RemoteException, NotBoundException
280 {
281 if (reg == null)
282 reg = getRegistry();
283 reg.unbind(name);
284 }
285
286
287 /**
288 * Get a reference to a local RMI registry associated with the
289 * bootstrap port specified in the configuration files. The socket
290 * factories used by the registry will be the default once.
291 *
292 * @return
293 * a <code>Registry</code> object
294 *
295 * @exception AccessException
296 * if this operation is not permitted (if originating from a
297 * non-local host, for example)
298 * @exception RemoteException
299 * if registry could not be contacted
300 */
301 public static Registry getRegistry()
302 throws RemoteException
303 {
304 return getLocalRegistry(null, null);
305 }
306
307
308 /**
309 * Get a reference to a local RMI registry associated with the
310 * bootstrap port specified in the configuration files. The socket
311 * factories may be specified.
312 *
313 * @param csf
314 * client-side <code>Socket</code> factory used to make
315 * connections to the registry
316 * @param ssf
317 * server-side <code>ServerSocket</code> factory used to accept
318 * connections to the registry
319 * @return
320 * a <code>Registry</code> object
321 *
322 * @exception AccessException
323 * if this operation is not permitted (if originating from a
324 * non-local host, for example)
325 * @exception RemoteException
326 * if registry could not be contacted
327 */
328 public static Registry getLocalRegistry(RMIClientSocketFactory csf, RMIServerSocketFactory ssf)
329 throws AccessException, RemoteException
330 {
331 try {
332 reg = LocateRegistry.getRegistry(null, port, csf);
333 /*
334 * The <code>LocateRegistry.getRegistry()</code> method above simply
335 * returns a stub for the bootstrap registry, even in the case when
336 * no registry exists. To circumvent this problem and to ensure that
337 * this method always returns a valid registry, we invoke a dummy
338 * rebind() method. If this throws a <code>RemoteException</code> we
339 * know that the stub returned above was not a valid registry, and we
340 * can safely create a registry in this JVM instead.
341 *
342 * Further, note that the rebind method cannot take <code>null</code>
343 * as its second argument, therefore we simply pass the stub of the
344 * registry returned from the above <code>getRegistry()</code> method,
345 * since that is a remote object instance.
346 */
347 reg.rebind(BOOTREGISTRY_TEST, reg);
348 if (log.isDebugEnabled()) {
349 log.debug("Found bootstrap registry on localhost at port " + port);
350 }
351 } catch (RemoteException e) {
352 if (log.isDebugEnabled())
353 log.debug("No bootstrap registry found on localhost");
354 /*
355 * If createRegistry() below throws a RemoteException we leave
356 * it to the invoking class to deal with the problem; it should
357 * probably abort.
358 */
359 try {
360 reg = LocateRegistry.createRegistry(port, csf, ssf);
361 } catch (RemoteException ex) {
362 log.error("Failed to create bootstrap registry on localhost at port " + port);
363 throw ex;
364 }
365 if (log.isDebugEnabled())
366 log.debug("Bootstrap registry successfully created on localhost at port " + port);
367 }
368 /* Return an existing or newly created registry stub. */
369 return reg;
370 }
371
372
373 /**
374 * Get a reference to a local RMI registry associated with the specified
375 * bootstrap port and socket factories.
376 *
377 * Note that for non-local RMI registries, the returned reference (stub)
378 * may not work since we cannot create a registry on the remote host.
379 * This case may be used to determine if the host is running a registry.
380 *
381 * @param hostname
382 * the hostname to query for bootstrap registry
383 * @param csf
384 * client-side <code>Socket</code> factory used to make
385 * connections to the registry
386 * @param checkCache
387 * use true to check the cache for the registry reference; false will
388 * not query the cache, but will update it.
389 * @return
390 * a <code>Registry</code> object
391 *
392 * @exception AccessException
393 * if this operation is not permitted (if originating from a
394 * non-local host, for example)
395 * @exception RemoteException
396 * if registry could not be contacted
397 */
398 public static Registry getRemoteRegistry(String hostname, RMIClientSocketFactory csf, boolean checkCache)
399 throws UnknownHostException, RemoteException
400 {
401 if (hostname == null)
402 throw new NullPointerException("hostname not specified");
403
404 Registry reg = null;
405 if (checkCache)
406 reg = registryMap.get(hostname);
407
408 if (status == null || status.isReliable(hostname)) {
409 if (status != null)
410 log.debug("Partition simulator: link is up: " + hostname);
411 if (reg != null)
412 return reg;
413 } else {
414 if (log.isDebugEnabled())
415 log.debug("Partition simulator: link is down: " + hostname);
416 throw new RemoteException("Partition simulator: broken link to " + hostname);
417 }
418
419 try {
420 reg = LocateRegistry.getRegistry(hostname, port, csf);
421 /*
422 * The <code>LocateRegistry.getRegistry()</code> method above simply
423 * returns a stub for the bootstrap registry, even in the case when
424 * no registry exists.
425 */
426 registryMap.put(hostname, reg);
427 if (log.isDebugEnabled())
428 log.debug("Found bootstrap registry on " + hostname + " at port " + port);
429 } catch (RemoteException e) {
430 if (log.isDebugEnabled())
431 log.debug("No bootstrap registry found on: " + hostname);
432 throw e;
433 }
434
435 /* Return the remote registry stub. */
436 return reg;
437 }
438
439
440 /**
441 * Create a local RMI registry on the default bootstrap port and
442 * socket factories.
443 *
444 * @return
445 * a <code>Registry</code> object
446 *
447 * @exception AccessException
448 * if this operation is not permitted (if originating from a
449 * non-local host, for example)
450 * @exception RemoteException
451 * if registry could not be contacted
452 */
453 public static Registry createRegistry()
454 throws AccessException, RemoteException
455 {
456 return createRegistry(null, null);
457 }
458
459
460 /**
461 * Create a local RMI registry on the specified bootstrap port and
462 * socket factories.
463 *
464 * @param csf
465 * client-side <code>Socket</code> factory used to make
466 * connections to the registry
467 * @param ssf
468 * server-side <code>ServerSocket</code> factory used to accept
469 * connections to the registry
470 * @return
471 * a <code>Registry</code> object
472 *
473 * @exception AccessException
474 * if this operation is not permitted (if originating from a
475 * non-local host, for example)
476 * @exception RemoteException
477 * if registry could not be contacted
478 */
479 public static Registry createRegistry(RMIClientSocketFactory csf, RMIServerSocketFactory ssf)
480 throws AccessException, RemoteException
481 {
482 if (reg != null)
483 return reg;
484 try {
485 reg = LocateRegistry.createRegistry(port, csf, ssf);
486 return reg;
487 } catch (RemoteException ex) {
488 log.error("Failed to create local BootstrapRegistry", ex);
489 throw ex;
490 }
491 }
492
493 } // END BootstrapRegistry