View Javadoc

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