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.relacs.registry;
20  
21  import java.net.UnknownHostException;
22  import java.rmi.NotBoundException;
23  import java.rmi.RemoteException;
24  import java.rmi.registry.Registry;
25  import java.rmi.server.RMIClientSocketFactory;
26  import java.rmi.server.RMIServerSocketFactory;
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.Iterator;
30  import java.util.List;
31  
32  import jgroup.core.ConfigManager;
33  import jgroup.core.ConfigurationException;
34  import jgroup.core.JgroupException;
35  import jgroup.core.ExternalGMIService.BootstrapInvocationHandler;
36  import jgroup.core.registry.BootstrapRegistry;
37  import jgroup.core.registry.DependableRegistry;
38  import jgroup.core.registry.LookupRegistry;
39  import jgroup.core.registry.RegistryLocator;
40  import jgroup.relacs.config.AppConfig;
41  import jgroup.relacs.config.Host;
42  import jgroup.relacs.config.HostSet;
43  import jgroup.relacs.rmi.Exporter;
44  import jgroup.util.Util;
45  
46  import org.apache.log4j.Logger;
47  
48  /**
49   *  <code>RelacsRegistryLocator</code> is used to obtain a bootstrap
50   *  reference to the Jgroup dependable registry, or to create a Jgroup
51   *  dependable registry instance on the local machine.  Every instance
52   *  of the Jgroup registry service is based on two remote registries:
53   *  the standard local registry and the replicated one.  The standard
54   *  local registry is used to retrieve a reference for the replicated
55   *  remote registry.  The access port for these registries are
56   *  configured in the XML configuration file.
57   *
58   *  @see <a href="http://java.sun.com/j2se/1.4.1/docs/api/java/rmi/registry/LocateRegistry.html">java.rmi.LocateRegistry</a>
59   *  @author Alberto Montresor
60   *  @author Hein Meling
61   *  @since Jgroup 0.3
62   */
63  public final class RelacsRegistryLocator
64    implements RegistryLocator
65  {
66  
67    ////////////////////////////////////////////////////////////////////////////////////////////
68    // Logger
69    ////////////////////////////////////////////////////////////////////////////////////////////
70  
71    /** Obtain logger for this class */
72    private static final Logger log = Logger.getLogger(RelacsRegistryLocator.class);
73  
74  
75    ////////////////////////////////////////////////////////////////////////////////////////////
76    // Fields
77    ////////////////////////////////////////////////////////////////////////////////////////////
78  
79    /**
80     *  Variable for keeping the current group proxy for the dependable
81     *  registry instances.  It is returned when multiple instances query
82     *  for a dependable registry within the same JVM.
83     */
84    private DependableRegistry drProxy = null;
85  
86    /**
87     *  A list of threads used for polling (checking) for dependable
88     *  registry instances within the current distributed system.
89     */
90    private List<RegistryPollingThread> pollingThreads =
91      Collections.synchronizedList(new ArrayList<RegistryPollingThread>(16));
92  
93    /**
94     *  The set of group proxy handlers for the dependable registry.
95     */
96    private List<BootstrapInvocationHandler> handlers =
97      new ArrayList<BootstrapInvocationHandler>(5);
98  
99  
100   ////////////////////////////////////////////////////////////////////////////////////////////
101   // Constructors
102   ////////////////////////////////////////////////////////////////////////////////////////////
103 
104   /**
105    *  Will be used to obtain an instance of RegistryLocator.
106    */
107   public RelacsRegistryLocator() { }
108 
109 
110   ////////////////////////////////////////////////////////////////////////////////////////////
111   // Public methods
112   ////////////////////////////////////////////////////////////////////////////////////////////
113 
114   /**
115    *  Returns a proxy for the dependable registry service.
116    *
117    *  @return 
118    *    A proxy for the dependable registry service.
119    *
120    *  @exception RemoteException 
121    *    Raised if the proxy could not be obtained.
122    *  @exception ConfigurationException
123    *    Raised if there is a problem with the distributed system
124    *    configuration data.
125    */
126   public DependableRegistry getRegistry()
127     throws JgroupException, RemoteException
128   {
129     return getRegistry(null);
130   }
131 
132 
133   /**
134    *  Returns a proxy for the dependable registry service formed by all
135    *  registry replica instances found within the distributed system
136    *  configuration. <p>
137    *
138    *  Communication with this remote registry will use the supplied
139    *  <t>RMIClientSocketFactory csf</t> to create socket connections to
140    *  the dependable registry on the distributed system and port. <p>
141    *
142    *  Note that the method returns the same group reference (variable
143    *  <code>drProxy</code>) for the dependable registry upon multiple
144    *  invocations of this method.  However, the threads continue to run
145    *  also after the first invocation, and thus the group proxy may be
146    *  augmented with further dependable registry replicas.  This means
147    *  that, although the first (few) use(s) of the returned group proxy
148    *  may have accessed only a subset of the available DR replicas,
149    *  future invocations on the group proxy will include all available
150    *  DR replicas.
151    *
152    *  @param csf
153    *    Client-side socket factory used to make connections to the
154    *    dependable registry.  If <code>csf</code> is null, then the
155    *    default client-side socket factory will be used.
156    *
157    *  @return 
158    *    A proxy for the dependable registry service
159    *
160    *  @exception RemoteException 
161    *    If a proxy could not be obtained; probably due to unavailability
162    *    of any registry replica instances.
163    *  @exception ConfigurationException
164    *    Raised if there is a problem with the distributed system
165    *    configuration data.
166    */
167   public DependableRegistry getRegistry(RMIClientSocketFactory csf)
168     throws JgroupException, RemoteException
169   {
170     if (drProxy != null) 
171       return drProxy;
172 
173     /* 
174      * Obtains remote references for the dependable registry and inserts
175      * them in the handler.  This is achieved by probing each host in
176      * the distributed system, using a separate thread for each host.
177      * This is to avoid blocking when a host is not responsive.
178      */
179     HostSet allHosts = ConfigManager.getDistributedSystem().getAllHosts();
180     for (Iterator hostIter = allHosts.iterator(); hostIter.hasNext(); ) {
181       Host host = (Host) hostIter.next();
182       String fqdn = host.getCanonicalHostName();
183       /* Create a polling thread for each host in the distributed system. */
184       RegistryPollingThread regPoller = new RegistryPollingThread(fqdn, csf);
185       Thread t = new Thread(regPoller);
186       t.setDaemon(true);
187       t.start();
188       pollingThreads.add(regPoller);
189     }
190 
191     /*
192      * Here we sleep a bit, to allow (all) registry instances to return
193      * their <code>Remote</code> reference.
194      */
195     AppConfig app = AppConfig.getApplication(RegistryImpl.class);
196     int locatorProbeTimeout = app.getIntParam("LocatorProbeTimeout", 1000);
197     Util.sleep(locatorProbeTimeout);
198     if (handlers.size() < 1) {
199       Util.sleep(2*locatorProbeTimeout);
200       if (handlers.size() < 1)
201         throw new RemoteException("Giving up: No registry instances found.");
202     }
203     if (log.isDebugEnabled()) {
204       log.debug("Number of hosts not responded yet: " + pollingThreads.size()
205                 + ", registries found thus far: " + handlers.size());
206     }
207 
208     BootstrapInvocationHandler handler = handlers.get(0);
209     for (int i = 1; i < handlers.size(); ++i) {
210       handler.merge(handlers.get(i));
211     }
212     /* Returns the proxy for the registry, using the handler */
213     drProxy = (DependableRegistry) Exporter.getProxy(RegistryImpl.class, handler);
214     return drProxy;
215   }
216 
217 
218   /**
219    *  Creates and exports a dependable registry replica on the local
220    *  host listening for incoming requests on an the port specified in
221    *  the system configuration file. <p>
222    *
223    *  @return 
224    *    The newly created dependable registry replica.
225    *
226    *  @exception RemoteException
227    *    Raised if the registry could not be exported.
228    *  @exception JgroupException
229    *    Raised if a registry instance could not be created.
230    *  @exception ConfigurationException
231    *    Raised if there was a problem parsing the distributed system
232    *    configuration file.
233    */
234   public DependableRegistry createRegistry()
235     throws JgroupException, RemoteException
236   {
237     return new RegistryImpl();
238   }
239 
240 
241   /**
242    *  Creates and exports a dependable registry replica on the local
243    *  host listening for incoming requests on an the port specified in
244    *  the system configuration file. <p>
245    *
246    *  The new instance of the dependable registry listens to incoming
247    *  requests using a <code>ServerSocket</code> created from the
248    *  supplied <code>RMIServerSocketFactory</code>.  A client that
249    *  receives a reference to this registry will then use a
250    *  <code>Socket</code> created from the supplied
251    *  <code>RMIClientSocketFactory</code>.
252    *
253    *  @param csf
254    *    Client-side <code>Socket</code> factory used to make connections
255    *    to the registry.
256    *  @param ssf
257    *    Server-side <code>ServerSocket</code> factory used to accept
258    *    connections to the registry.
259    *
260    *  @return 
261    *    The newly created dependable registry replica.
262    *
263    *  @exception RemoteException
264    *    Raised if the registry could not be exported.
265    *  @exception JgroupException
266    *    Raised if a registry instance could not be created.
267    *  @exception ConfigurationException
268    *    Raised if there was a problem parsing the distributed system
269    *    configuration file.
270    */
271   public DependableRegistry createRegistry(RMIClientSocketFactory csf, RMIServerSocketFactory ssf)
272     throws RemoteException, JgroupException
273   {
274     return new RegistryImpl(csf, ssf);
275   }
276 
277 
278   /* (non-Javadoc)
279    * @see jgroup.core.registry.RegistryLocator#getLookupRegistry()
280    */
281   public LookupRegistry getLookupRegistry() 
282     throws JgroupException, RemoteException
283   {
284     return (LookupRegistry) getRegistry();
285   }
286 
287   
288   ////////////////////////////////////////////////////////////////////////////////////////////
289   // Private methods
290   ////////////////////////////////////////////////////////////////////////////////////////////
291 
292   /**
293    *  Probe the specified host for a dependable registry replica, and
294    *  returns a remote reference to the dependable registry replica if
295    *  such replica exists on the given host; otherwise an exception is
296    *  thrown. <p>
297    *
298    *  Note that the <code>lookup</code> method used within this method
299    *  may block if there is a communication problem.
300    *
301    *  @param hostname
302    *    The host to probe for a dependable registry replica.
303    *  @param csf
304    *    The <code>RMIClientSocketFactory</code> to use.
305    *
306    *  @return 
307    *    The <code>Remote</code> object reference to the registry replica.
308    *
309    *  @exception RemoteException
310    *    Is raised if there was communication problems, such as the host
311    *    is not running any bootstrap registry, or other communication
312    *    problems.
313    *  @exception NotBoundException
314    *    Is raised if the bootstrap registry did not contain a reference
315    *    to a dependable registry replica.
316    */
317   private static BootstrapInvocationHandler getSingleRegistry(String hostname, RMIClientSocketFactory csf)
318     throws UnknownHostException, RemoteException, NotBoundException
319   {
320     Registry registry = BootstrapRegistry.getRemoteRegistry(hostname, csf, false);
321     return (BootstrapInvocationHandler) registry.lookup(DependableRegistry.DEPENDABLE_REGISTRY_NAME);
322   }
323 
324 
325   ////////////////////////////////////////////////////////////////////////////////////////////
326   // Private internal class
327   ////////////////////////////////////////////////////////////////////////////////////////////
328 
329   /**
330    *  A <code>RegistryPollingThread</code> class is created for each
331    *  host on which there may be running a dependable registry instance.
332    *  It will query the specified host for its local registry instance
333    *  and if there is a communication problem (long delay), the thread
334    *  will simply block.  Since each host is being queried by a separate
335    *  thread, those threads without communication problems will return
336    *  quickly, allowing the system to avoid problems with blocking in
337    *  the event of communication problems (due to the lookup problem
338    *  with standard Java RMI.) <p>
339    *
340    *  @author Hein Meling
341    *  @since Jgroup 1.2
342    */
343   private class RegistryPollingThread
344     implements Runnable
345   {
346     private String fqdn;
347     private RMIClientSocketFactory csf;
348 
349     RegistryPollingThread(String fqdn, RMIClientSocketFactory csf)
350     {
351       this.fqdn = fqdn;
352       this.csf = csf;
353     }
354 
355     /**
356      *  Get the <code>Remote</code> reference for a registry running on
357      *  the host associated with this thread instance, and if there was
358      *  a registry, insert that <code>Remote</code> reference into the
359      *  <code>handler</code> object.  Given that the method
360      *  getSingleRegistry does not block, the run method will simply
361      *  finish by removing this thread from the polling list and exit.
362      */
363     public void run()
364     {
365       try {
366         handlers.add(getSingleRegistry(fqdn, csf));
367         if (log.isDebugEnabled())
368           log.debug(fqdn + ": dependable registry found.");
369       } catch (Exception e) {
370         if (log.isDebugEnabled())
371           log.debug(fqdn + ": dependable registry not found.");
372       } finally {
373         pollingThreads.remove(this);
374       }
375     }
376   }
377 
378 } // END RelacsRegistryLocator