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;
20  
21  import java.lang.reflect.Proxy;
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  
28  import jgroup.core.Layer.FinalizeLayer;
29  import jgroup.relacs.config.AppConfig;
30  import jgroup.relacs.config.DistributedSystemConfig;
31  import jgroup.relacs.config.LayerConfig;
32  import jgroup.relacs.config.ServiceConfig;
33  import jgroup.relacs.rmi.IntGroupHandler;
34  import jgroup.util.Network;
35  
36  import org.apache.log4j.Logger;
37  
38  /**
39   *  The <code>GroupManager</code> class is used to configure the Jgroup
40   *  system.  In order to make use of the Jgroup facilities, a group
41   *  manager must be obtained through the invocation of static factory
42   *  method <code>getGroupManager</code>.  The main parameter to this
43   *  method is the server object that implements a number of listener
44   *  interfaces.  That is, the server object is a listener for various
45   *  services.  A group manager is constructed by collecting and
46   *  configuring the services needed by the server object.  In order for
47   *  a server object to gain access to one of the provided services, the
48   *  method <code>getService</code> can be invoked on the group manager,
49   *  by specifying the service interface class or the shorthand service
50   *  name (as given in the <code>services.xml</code> file) of the desired
51   *  service.
52   *
53   *  @author Hein Meling
54   *  @author Alberto Montresor
55   *  @since Jgroup 0.8 (revised Jgroup 1.2)
56   */
57  public final class GroupManager
58  {
59  
60    ////////////////////////////////////////////////////////////////////////////////////////////
61    // Logger
62    ////////////////////////////////////////////////////////////////////////////////////////////
63  
64    /** Obtain logger for this class */
65    private static final Logger log = Logger.getLogger(GroupManager.class);
66  
67  
68    ////////////////////////////////////////////////////////////////////////////////////////////
69    // Fields
70    ////////////////////////////////////////////////////////////////////////////////////////////
71  
72    /** Mapping from classes to their corresponding service objects */
73    private Map map;
74  
75    /** The group identifier for this group manager instance */
76    private int groupId;
77  
78    /** The set of local group managers (in this JVM) */
79    private static final Map<Integer, GroupManager> groupManagers =
80      new HashMap<Integer, GroupManager>(5);
81  
82  
83    ////////////////////////////////////////////////////////////////////////////////////////////
84    // Constructors
85    ////////////////////////////////////////////////////////////////////////////////////////////
86  
87    private GroupManager(Map map, int groupId)
88    {
89      this.map = map;
90      this.groupId = groupId;
91      groupManagers.put(groupId, this);
92    }
93  
94  
95    ////////////////////////////////////////////////////////////////////////////////////////////
96    // Service methods
97    ////////////////////////////////////////////////////////////////////////////////////////////
98  
99    /**
100    *  Returns an handler for a Jgroup service (e.g., the membership
101    *  service) specified by the interface class <code>cl</code>.
102    *  Services provided by this instance of the group manager, must have
103    *  been initialized when invoking the static factory method
104    *  <code>getGroupManager</code>.  That is, the service must have been
105    *  specified either in the <code>LayerStack</code> entry of the
106    *  corresponding application in the <code>application.xml</code>
107    *  file, or through the <code>serviceSet</code> array provided to the
108    *  <code>getGroupManager</code> method.
109    *
110    *  @param cl
111    *    Class specifying the interface of the required service.
112    *  @exception ClassCastException
113    *    If the generated group manager is unable to provide the requested
114    *    service class.
115    */
116   public Object getService(Class cl)
117   {
118     try {
119       return getService((String) map.get(cl));
120     } catch (ClassCastException e) {
121       throw new ClassCastException("Class " + cl.getName() + " not found");
122     }
123   }
124 
125 
126   /**
127    *  Returns an handler for a Jgroup service (e.g., the membership
128    *  service) specified by the shorthand service name, as specified in
129    *  the <code>services.xml</code> file.  Services provided by this
130    *  instance of the group manager, must have been initialized when
131    *  invoking the static factory method <code>getGroupManager</code>.
132    *  That is, the service must have been specified either in the
133    *  <code>LayerStack</code> entry of the corresponding application in
134    *  the <code>application.xml</code> file, or through the
135    *  <code>serviceSet</code> array provided to the
136    *  <code>getGroupManager</code> method.
137    *
138    *  @param serviceName
139    *    Shorthand name for the required service.
140    *  @exception ClassCastException
141    *    If the generated group manager is unable to provide the requested
142    *    service class.
143    */
144   public Object getService(String serviceName)
145   {
146     Object service = map.get(serviceName);
147     if (service == null)
148       throw new ClassCastException("Service " + serviceName + " not found");
149     return service;
150   }
151 
152   /**
153    *  Returns the group identifier for this group manager instance.
154    */
155   public int getGroupId()
156   {
157     return groupId;
158   }
159 
160   /**
161    *  Returns the local member identifier associated with this group manager.
162    */
163   public MemberId getLocalMember()
164   {
165     MembershipService pgms = (MembershipService) getService("PGMS");
166     return pgms.getMyIdentifier();
167   }
168 
169 
170   ////////////////////////////////////////////////////////////////////////////////////////////
171   // Static methods
172   ////////////////////////////////////////////////////////////////////////////////////////////
173 
174   public static GroupManager getGroupManager(int gid)
175   {
176     return groupManagers.get(gid);
177   }
178 
179 
180   public static Object getService(int gid, Class service)
181   {
182     GroupManager gm = groupManagers.get(gid);
183     if (gm == null)
184       return null;
185     else
186       return gm.getService(service);
187   }
188 
189 
190   public static boolean contains(int gid)
191   {
192     return groupManagers.containsKey(gid);
193   }
194 
195 
196   public static boolean isEmpty()
197   {
198     return groupManagers.isEmpty();
199   }
200 
201 
202   ////////////////////////////////////////////////////////////////////////////////////////////
203   // Static factory methods
204   ////////////////////////////////////////////////////////////////////////////////////////////
205 
206   /**
207    *  Returns a group manager object through which handlers, for each of
208    *  the services associated with the listener interfaces implemented
209    *  by the specified object, can be accessed.
210    *
211    *  @param listener
212    *    Object implementing the listeners interfaces associated with the
213    *    the requested services.
214    *  @exception JgroupException
215    *    Raised if some configuration error occurs.
216    */
217   public static GroupManager getGroupManager(Object listener)
218     throws JgroupException
219   {
220     if (listener == null)
221       throw new JgroupException("No listener specified for the group manager");
222     Class serverClass = listener.getClass();
223 
224     ConfigManager.init();
225 
226     /* Get application info object for this server (group manager listener) */
227     AppConfig app = AppConfig.getApplication(serverClass);
228     if (app == null)
229       throw new ConfigurationException(serverClass.getName()
230         + " is not available in the application configuration.");
231 
232     /* Get the set of services (layers) used by this application. */
233     String[] serviceSet = app.getServiceSet();
234     int groupId = app.getGroupId();
235     return getGroupManager(groupId, serviceSet, listener);
236   }
237 
238 
239   /**
240    *  Returns a group manager object through which handlers, for each of
241    *  the services associated with the listener interfaces implemented
242    *  by the specified object, can be accessed.
243    *
244    *  @param serviceSet
245    *    An array of <code>String</code>s, each representing a service or
246    *    layer that should be incorporated with the returned GroupManager.
247    *  @param listener
248    *    Object implementing the listeners interfaces associated with the
249    *    the requested services.
250    *  @exception JgroupException
251    *    Raised if some configuration error occurs.
252    */
253   @SuppressWarnings("unchecked")
254   public static GroupManager getGroupManager(int groupId, String[] serviceSet, Object listener)
255     throws JgroupException
256   {
257     if (listener == null)
258       throw new JgroupException("No listener specified for the group manager");
259     Class serverClass = listener.getClass();
260     if (log.isDebugEnabled())
261       log.debug(serverClass.getName() + ": wants a group manager");
262 
263     ConfigManager.init();
264     DistributedSystemConfig dsc = ConfigManager.getDistributedSystem();
265     if (!dsc.containsLocalHost())
266       throw new JgroupException("Distributed system configuration does not contain local host: "
267         + Network.getLocalHostName() + "\nThis is required for servers, but not for clients.");
268 
269     /* This map should be immutable from outside the GroupManager class. */
270     Map layerMap = new HashMap(serviceSet.length);
271 
272     /* Construct the group manager object and add its reference to the layerMap */
273     GroupManager gm = new GroupManager(layerMap, groupId);
274     layerMap.put("GM", gm);
275 
276     /*
277      * Initialize the layers.
278      */
279     LayerConfig.initializeLayers();
280 
281     /*
282      * Create a layer object for each service specified in the
283      * <code>LayerStack<code> of the given application; that is the
284      * layers to be used by the application.  The layers are placed in a
285      * map, associated with the service that they provide as a key.
286      */
287     List<Class> internalIfaces = new ArrayList<Class>();
288     for (int layerNo = 0; layerNo < serviceSet.length; layerNo++) {
289       /*
290        * Create an instance of the next layer obtained from the service
291        * set.  For layers that share implementation class the
292        * getLayerObject() method must ensure to return the same layer
293        * instance for all layers.  Furthermore, note that we need to pass
294        * the layerMap in order to be able to check dependencies between
295        * layers, and also the internalIfaces list, so as to be able to
296        * construct a IGMI proxy for layers that use IGMI.
297        */
298       LayerConfig.constructLayerObject(serviceSet[layerNo], layerMap, internalIfaces);
299     }
300 
301     /*
302      * Iterate over the interfaces implemented by the server, and add it
303      * as a listener for all interfacs that are listener interfaces for
304      * services defined in the XML file.
305      */
306     Class serverInterfaces[] = serverClass.getInterfaces();
307     for (int i = 0; i < serverInterfaces.length; i++) {
308 
309       /*
310        * Find the internal interfaces implemented by this server.
311        */
312       if (InternalGMIListener.class.isAssignableFrom(serverInterfaces[i])) {
313         internalIfaces.add(serverInterfaces[i]);
314         /*
315          * Add also the server interface for this server to the layer
316          * map, so that the interfaces assignable from a layer
317          * interface can be reached.  This will allow using
318          * getService(jgroup.test.hello.InternalHello.class).  The
319          * actual layer implementing the IGMI service will be
320          * replaced by a proxy for the layer (see below).
321          */
322         layerMap.put(serverInterfaces[i], "IGMI");
323       }
324     }
325 
326     /*
327      * Iterate over the services to obtain the service name and its
328      * corresponding listener interface to be matched against the
329      * interfaces implemented by the server.
330      */
331     for (int layerNo = 0; layerNo < serviceSet.length; layerNo++) {
332       boolean foundIface = false;
333       ServiceConfig serviceConfig = ServiceConfig.getService(serviceSet[layerNo]);
334       Class listenerIface = serviceConfig.getListener();
335       if (listenerIface == null)
336         foundIface = true;
337 
338       for (int i = 0; !foundIface && i < serverInterfaces.length; i++) {
339         /*
340          * Check if this particular service's listener interface is
341          * implemented by the server.  If so we invoke addListener on
342          * that service's layer to allow this particular server to be a
343          * listener for its events.
344          */
345         if (listenerIface.isAssignableFrom(serverInterfaces[i])) {
346           Layer layer = (Layer) layerMap.get(serviceSet[layerNo]);
347           if (layer == null)
348             throw new ConfigurationException("The layer implementing " + serviceSet[layerNo]
349               + " is not be initialized. \nThis layer is required by the " + serverInterfaces[i]
350               + " implemented by the server class: " + serverClass.getName());
351           layer.addListener(listener);
352           if (log.isDebugEnabled()) {
353             log.debug(serverClass.getName() + ": listen to events from " + serviceSet[layerNo]);
354           }
355           foundIface = true;
356         }
357       }
358 
359       if (!foundIface && serviceConfig.isMarker()) {
360         /*
361          * If the listener interface of the current service is a marker
362          * interface that is not implemented by the server, we still
363          * need to add the server as a listener for this layer.
364          */
365         Layer layer = (Layer) layerMap.get(serviceSet[layerNo]);
366         layer.addListener(listener);
367         if (log.isDebugEnabled()) {
368           log.debug(serverClass.getName() + ": registered with " + serviceSet[layerNo]);
369         }
370       }
371     }
372 
373     /*
374      * For applications that require internal GMI, we replace the
375      * IntGMILayer instances in the layer map with the proxy instance
376      * that is required for use by the server or layer.
377      */
378     if (internalIfaces.size() > 0) {
379 
380       /*
381        * Get the interfaces to be implemented by the proxy, that is those
382        * found to implement the InternalGMIService interface and that
383        * interface itself.
384        */
385       internalIfaces.add(InternalGMIService.class);
386       Class[] interfaces = new Class[internalIfaces.size()];
387       internalIfaces.toArray(interfaces);
388 
389       /* Get the invocation handler IntGroupHandler (also known as IntGMILayer) */
390       IntGroupHandler handler = (IntGroupHandler) layerMap.get("IGMI");
391       if (handler == null)
392         throw new ConfigurationException(serverClass.getName()
393           + " requires the IGMI layer, but it is not specified for this application.");
394 
395       /* Get the class loader for the server */
396       ClassLoader classLoader = serverClass.getClassLoader();
397 
398       /* Now we can obtain the proxy object */
399       Object proxy = Proxy.newProxyInstance(classLoader, interfaces, handler);
400 
401       /*
402        * Replace the IntGMILayer instance with the proxy instance in the
403        * layer map.
404        */
405       layerMap.put("IGMI", proxy);
406     }
407 
408     /*
409      * For each of the layers in the map, check if it implements the
410      * FinalizeLayer interface.  For those that do, invoke the complete()
411      * method.
412      * 
413      * Herein, we provide special handling for the MembershipService, since
414      * the MembershipLayer may perform the join() operation automatically,
415      * and this should be done last.  This is since otherwise, we often
416      * run into bootstrap problems for a number of references that have not
417      * yet been allocated when used in the viewChange() method that is invoked
418      * during the join() operation.
419      */
420     for (Iterator iter = layerMap.values().iterator(); iter.hasNext();) {
421       Object layer = iter.next();
422       if (layer instanceof FinalizeLayer && !(layer instanceof MembershipService)) {
423         ((FinalizeLayer) layer).complete(listener);
424       }
425     }
426     /* Finalize the MembershipService (PGMS) layer construction (autojoin) */
427     FinalizeLayer pgmsLayer = (FinalizeLayer) layerMap.get("PGMS");
428     pgmsLayer.complete(listener);
429 
430     return gm;
431   }
432 
433 } // END GroupManager