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.config;
20  
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  import java.lang.reflect.Modifier;
24  import java.util.HashMap;
25  import java.util.HashSet;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  
31  import jgroup.core.ConfigurationException;
32  import jgroup.core.GroupManager;
33  import jgroup.core.InternalGMIListener;
34  import jgroup.core.JgroupException;
35  import jgroup.core.Layer;
36  import jgroup.core.MalformedLayerException;
37  
38  import org.apache.log4j.Logger;
39  import org.w3c.dom.Element;
40  import org.w3c.dom.Node;
41  import org.w3c.dom.NodeList;
42  
43  
44  
45  
46  /**
47   *  Object for holding layer information and creating instances of the
48   *  layer, and add listeners between the layers.
49   *
50   *  @author Hein Meling
51   *  @since Jgroup 1.2
52   */
53  public final class LayerConfig
54    implements ConfigurationObject
55  {
56  
57    ////////////////////////////////////////////////////////////////////////////////////////////
58    // Logger
59    ////////////////////////////////////////////////////////////////////////////////////////////
60  
61    /** Obtain logger for this class */
62    private static final Logger log = Logger.getLogger(LayerConfig.class);
63  
64  
65    ////////////////////////////////////////////////////////////////////////////////////////////
66    // Tags
67    ////////////////////////////////////////////////////////////////////////////////////////////
68  
69    private static final String DEPENDS_TAG_NAME  = "Depends";
70    private static final String REPLACES_TAG_NAME = "Replaces";
71    private static final String SERVICE_ATTRIB    = "service";
72    private static final String CLASS_ATTRIB      = "class";
73  
74  
75    ////////////////////////////////////////////////////////////////////////////////////////////
76    // Static collections
77    ////////////////////////////////////////////////////////////////////////////////////////////
78  
79    /**
80     * Map of layers (association from service name to LayerConfig object).
81     * Note that this map must be static to allow cross access between
82     * instances of the LayerConfig class.  This is because the parser
83     * creates a new LayerConfig object for each Layer entry in the XML
84     * file.
85     */
86    private static final Map<String, LayerConfig> layers =
87      new HashMap<String, LayerConfig>();
88  
89  
90    ////////////////////////////////////////////////////////////////////////////////////////////
91    // Fields
92    ////////////////////////////////////////////////////////////////////////////////////////////
93  
94    /* The service implemented by this layer. */
95    private String service;
96  
97    /* The layer class implementing the above service. */
98    private String layerClassName;
99    private Class layerClass;
100 
101   /* The getLayer method of this layer. */
102   private Method getLayerMethod;
103 
104   /* The set of interfaces implemented by this layer */
105   private Set<Class> layerIfaces;
106 
107   /* Set of services on which this layer depends */
108   private Set<String> depends;
109 
110   /* Set of services that this layer replaces. */
111   private Set<String> replaces;
112 
113   /*
114    * An instance representing this layer.  Can be obtained through the
115    * getLayerObject() method.
116    */
117   private Layer layer = null;
118 
119 
120   ////////////////////////////////////////////////////////////////////////////////////////////
121   // Constructors
122   ////////////////////////////////////////////////////////////////////////////////////////////
123 
124   /**
125    *  Constructs an empty layer config object.
126    */
127   public LayerConfig() { }
128 
129 
130   /**
131    *  Constructs a layer config object for the specified class.
132    */
133   public LayerConfig(String service, String layerClassName)
134   {
135     this.service = service;
136     this.layerClassName = layerClassName;
137   }
138 
139 
140   private void initLayer()
141     throws ConfigurationException
142   {
143     try {
144       layerClass = Class.forName(layerClassName);
145     } catch (ClassNotFoundException e) {
146       throw new ConfigurationException("Layer class not found: " + layerClassName);
147     }
148     // Ensure that the getLayer() method is nullified.
149     getLayerMethod = null;
150 
151     Method[] methods = layerClass.getMethods();
152     for (int i = 0; i < methods.length; i++) {
153       if (methods[i].getName().equals("getLayer")) {
154         if (getLayerMethod != null)
155           throw new ConfigurationException(layerClassName 
156             + " provides multiple getLayer() methods; only one is accepted.");
157         getLayerMethod = methods[i];
158         int m = getLayerMethod.getModifiers();
159         if (!Modifier.isPublic(m) || !Modifier.isStatic(m))
160           getLayerMethod = null;
161       }
162     }
163     if (getLayerMethod == null) 
164       throw new ConfigurationException("The layer (" + layerClassName
165         + ") implementing the " + service
166         + " service does not declare the required 'public static getLayer()' method.");
167 
168     Class[] ifaces = layerClass.getInterfaces();
169     layerIfaces = new HashSet<Class>(ifaces.length);
170     for (int i = 0; i < ifaces.length; i++)
171       layerIfaces.add(ifaces[i]);
172   }
173 
174 
175   ////////////////////////////////////////////////////////////////////////////////////////////
176   // Methods from ConfigurationObject
177   ////////////////////////////////////////////////////////////////////////////////////////////
178 
179   /**
180    *  Parses the specified element.
181    */
182   public void parse(Element elm)
183     throws ConfigurationException
184   {
185     String servicename = ConfigParser.getAttrib(elm, SERVICE_ATTRIB, true);
186     String clazz       = ConfigParser.getAttrib(elm, CLASS_ATTRIB,   true);
187 
188     /* Create a new service object with the parsed information */
189     LayerConfig layer = new LayerConfig(servicename, clazz);
190     layers.put(servicename, layer);
191 
192     /* Nodes within the 'Layer' tag. */
193     NodeList layerList = elm.getChildNodes();
194 
195     /* Parse the list of tags inside the Layer tag. */
196     for (int i = 1; i <= layerList.getLength(); i++) {
197       Node node = layerList.item(i);
198       if (node != null && !node.getNodeName().startsWith("#")) {
199         elm = (Element) node;
200         if (ConfigParser.checkTag(elm, DEPENDS_TAG_NAME)) 
201           layer.addDependency(ConfigParser.getAttrib(elm, SERVICE_ATTRIB, true));
202         else if (ConfigParser.checkTag(elm, REPLACES_TAG_NAME))
203           layer.addReplaces(ConfigParser.getAttrib(elm, SERVICE_ATTRIB, true));
204       }
205     }
206   }
207 
208 
209   ////////////////////////////////////////////////////////////////////////////////////////////
210   // Static Access methods
211   ////////////////////////////////////////////////////////////////////////////////////////////
212 
213   /**
214    * Check that layer dependencies are satisfied, and prepare for
215    * construction the layers given in the service set. 
216    */
217   public static void initializeLayers()
218     throws ConfigurationException
219   {
220     /*
221      * Check dependencies and reset the layers
222      */
223     for (Iterator iter = layers.entrySet().iterator(); iter.hasNext();) {
224       Map.Entry layerEntry = (Map.Entry) iter.next();
225       String serviceName = (String) layerEntry.getKey(); 
226       /* Get service interface for the specified service */
227 //      Class serviceIface = ServiceConfig.getServiceIface(serviceName);
228       /* Get the layer that implements the specified service */
229       LayerConfig layerConfig = (LayerConfig) layerEntry.getValue();
230       /*
231        * Check if the layer implements the required service interface,
232        * or an interface derived from the given service interface
233        */
234 //      if (!layerConfig.contains(serviceIface))
235 //        throw new ConfigurationException("The layer providing the " + serviceName 
236 //          + " service does not implement the required service interface: " + serviceIface);
237       /*
238        * Reset each of layers in the current group manager stack.
239        * Note that since multiple group managers may exist within
240        * the same JVM, we have to perform this reset operation before
241        * we can create the layers.
242        */
243       layerConfig.resetLayerObject();
244     }
245   }
246 
247 
248   /**
249    * Construct the layer for the given service name.
250    *
251    * @param serviceName
252    * @param layerMap
253    * @param internalIfaces
254    * @throws JgroupException
255    */
256   @SuppressWarnings("unchecked")
257   public static void constructLayerObject(String serviceName, Map layerMap, List<Class> internalIfaces)
258     throws JgroupException
259   {
260     LayerConfig layerConfig = layers.get(serviceName);
261     if (layerConfig == null)
262       throw new ConfigurationException("The layer for " + serviceName + " is not specified");
263     layerConfig.initLayer();
264     Layer layer = layerConfig.getLayerObject(layerMap, internalIfaces);
265 
266     /* Associate the service name of this layer with the layer instance. */
267     layerMap.put(serviceName, layer);
268 
269     /*
270      * Adds the *Service.class literal as the key to the service name.
271      * This is needed to check the dependency specifications.  See the
272      * LayerInfo.getLayerObject() method for details.
273      */
274     Class serviceIface = ServiceConfig.getServiceIface(serviceName);
275     layerMap.put(serviceIface, serviceName);
276   }
277 
278 
279   ////////////////////////////////////////////////////////////////////////////////////////////
280   // Private methods
281   ////////////////////////////////////////////////////////////////////////////////////////////
282 
283   private void resetLayerObject()
284   {
285     layer = null;
286   }
287 
288 
289   @SuppressWarnings("unchecked")
290   private Layer getLayerObject(Map layerMap, List<Class> internalIfaces)
291     throws JgroupException
292   {
293     /* If this layer was already constructed, just return it. */
294     if (layer != null) {
295       if (log.isDebugEnabled())
296         log.debug(layerClass + " layer already constructed");
297       return layer;
298     }
299 
300     for (Iterator<String> iter = depends(); iter.hasNext(); ) {
301       String dependsOn = iter.next();
302       if (!layerMap.containsKey(dependsOn)) {
303         if (log.isDebugEnabled())
304           log.debug("Creating: " + dependsOn + " since " + service + " depends on it");
305         constructLayerObject(dependsOn, layerMap, internalIfaces);
306       }
307     }
308 
309     /*
310      * Parse the getLayer() method to check if it contains the GroupManager
311      * in the first argument position; if so, pass the group manager reference
312      * to the layer.
313      */
314     Class[] parameters = getLayerMethod.getParameterTypes();
315     Object[] obj = new Object[parameters.length];
316     int i = 0;
317     if (parameters.length > 0 && parameters[i].equals(GroupManager.class)) {
318       /*
319        * Check that the remaining number of parameters (that is, exclusive
320        * the group manager parameter) match the number of depends.
321        */
322       if (parameters.length-1 != depends.size()) {
323         throw new MalformedLayerException("Arguments to the getLayer method of service " 
324           + service + " must correspond to the layers on which this layer depends.");
325       }
326       obj[i++] = layerMap.get("GM");
327     } else {
328       if (parameters.length != depends.size()) {
329         throw new MalformedLayerException("Arguments to the getLayer method of service " 
330           + service + " must correspond to the layers on which this layer depends.");
331       }
332     }
333 
334     /*
335      * Parse the remaining parameters for the getLayer() method
336      */
337     for ( ; i < parameters.length; i++) {
338 
339       if (!Layer.class.isAssignableFrom(parameters[i]))
340         throw new MalformedLayerException("Arguments to the getLayer method must extend jgroup.core.Layer");
341 
342       if (!layerMap.containsKey(parameters[i]))
343         throw new MalformedLayerException("The layer " + parameters[i] 
344           + " is required to initialize the " + service + " layer.");
345 
346       /*
347        * Get the service name associated with this formal parameter.
348        * The parameters array gives the service name in the form
349        * *Serivce.class, while the object returned from the layerMap
350        * will be a string specifying the service name.  This service
351        * name can be used to lookup in the depends map, to determine if
352        * it is specified also in the XML file.
353        */
354       obj[i] = layerMap.get(parameters[i]);
355       if (!depends.contains(obj[i]))
356         throw new MalformedLayerException("The layer " + parameters[i] 
357           + " is not specified in the dependencies for the " + service + " layer.");
358 
359       /*
360        * Get the layer instance associated with this service name
361        * (obtained above using the formal parameter).  The service name
362        * is in string form, while the object returned from the layerMap
363        * will be of the actual layer instance object.
364        */
365       obj[i] = layerMap.get(obj[i]);
366     }
367 
368     try {
369       /*
370        * Get an instance of this layer, instantiated using the correct
371        * formal parameters.
372        */
373       layer = (Layer) getLayerMethod.invoke(null, obj);
374     } catch (ClassCastException e) {
375       throw new JgroupException(layerClass + " or its service interface do not implement the Layer interface");
376     } catch (IllegalAccessException e) {
377       throw new JgroupException(layerClass + " cannot be accessed (check if class has package access)");
378     } catch (InvocationTargetException e) {
379       throw new JgroupException("Constructing " + layerClass + " has thrown an exception:",
380                                 e.getTargetException());
381     }
382 
383     /*
384      * Add the newly created layer as a listener to other layers; that
385      * is layers on which this layer depends.  Note that a layer may
386      * depend on a service, but still not implement the listener
387      * interface.
388      */
389     for (Iterator<String> it = depends(); it.hasNext(); ) {
390       String dependsOnService = it.next();
391 
392       /*
393        * Get the listener interface for the service on which this layer
394        * depends.  This will be used below to check if this layer
395        * implements a the listener for service on which it depends.
396        * Note that a layer may depend on a service, but still not
397        * implement the listener interface.
398        */
399       Class listenerIface = ServiceConfig.getListenerIface(dependsOnService);
400 
401       /*
402        * Get an interface associated with this layer, that is assignable from
403        * the listener interface of the service on which this layer depends.
404        */
405       Class assignableIface = getAssignableIface(listenerIface);
406       if (assignableIface != null) {
407         /*
408          * Special handling for IGMI interfaces:
409          * Find the internal interfaces implemented by this layer.
410          */
411         if (InternalGMIListener.class.isAssignableFrom(assignableIface)) {
412           internalIfaces.add(assignableIface);
413           /*
414            * Add also the layer interface to the layer map,
415            * so that the interfaces assignable from a layer
416            * interface can be reached.  This will allow using
417            * getService(jgroup.test.gm.InternalRemoteTest.class).
418            * The actual layer implementing the IGMI service will
419            * be replaced by a proxy for the layer (see getGroupManager).
420            */
421           layerMap.put(assignableIface, "IGMI");
422         }
423 
424         /*
425          * This layer implements the listener interface for the service
426          * on which it depends.  We add this layer as a listener for the
427          * layer on which it depends.
428          */
429         Layer dependOnLayer = (Layer) layerMap.get(dependsOnService);
430         dependOnLayer.addListener(layer);
431         if (log.isDebugEnabled())
432           log.debug(service + ": listen to events from " + dependsOnService);
433       } else {
434         if (log.isDebugEnabled())
435           log.debug(service + ": does not implement listener interface for: "
436             + dependsOnService + " (this is not a requirement)");
437       }
438     }
439 
440     /*
441      * Replace other layers in the layerMap with this layer, if their
442      * service is specified in the replaces tag.  Note that a layer that
443      * replaces a service, must also implement the listener interface of
444      * the service it is replacing.
445      */
446     for (Iterator<String> it = replaces(); it.hasNext(); ) {
447       String replacesService = it.next();
448 
449       /*
450        * Get the listener interface for the service that this layer
451        * replaces.  This will be used below to check if this layer
452        * implements a the listener for service that it replaces.  Note
453        * that a layer cannot replace a service without also implementing
454        * the listener interface of the service.
455        */
456       Class listenerIface = ServiceConfig.getListenerIface(replacesService);
457 
458       /* Check if this layer implements the listener interface. */
459       if (layerIfaces.contains(listenerIface)) {
460         /*
461          * This layer implements the listener interface for the service
462          * that it replaces.  We update the layerMap with this layer in
463          * the position of the service that we are replacing.
464          */
465         layerMap.put(replacesService, layer);
466         if (log.isDebugEnabled()) 
467           log.debug(service + ": replaces " + replacesService);
468       } else
469         throw new MalformedLayerException(service + " does not implement the listener " 
470           + " interface required to replace the service: " + replacesService);
471     }
472 
473     /* Return the newly created layer */
474     return layer;
475   }
476 
477 
478   /**
479    * Check wheter the layer implements the given interface or
480    * a descendand of the given layer.
481    * 
482    * @param iface Interface to implement
483    * 
484    * @return true if layer implements the given interface or one of its descendants
485    */
486   private boolean contains(Class iface)
487   {
488     return (getAssignableIface(iface) != null);
489   }
490 
491 
492   /**
493    *  Return the provided interface if it is directly implemented by
494    *  this layer.  If not, iterate over the interfaces implemented
495    *  by this layer to determine if the provided interface is
496    *  assignable from one of the interfaces implemented by this layer.
497    *  In the latter case, the interface implemented by the layer that
498    *  is assignable from the provided interface is returned.
499    *
500    *  @param iface
501    *    The interface to check if is assignable from any of the
502    *    interfaces in this layer.
503    *  @return
504    *    The interface to which the provided interface is assignable.
505    *    If no interfaces are assignable from the given interface,
506    *    <code>null</code> is returned.
507    */
508   private Class getAssignableIface(Class<?> iface)
509   {
510     if (layerIfaces.contains(iface)) {
511       return iface;
512     }
513     for (Class layerIface : layerIfaces) {
514       if (iface.isAssignableFrom(layerIface)) {
515         return layerIface;
516       }
517     }
518     return null;
519   }
520 
521 
522   private void addDependency(String dependsOn)
523     throws ConfigurationException
524   {
525     if (!layers.containsKey(dependsOn))
526       throw new ConfigurationException("Layer " + service + " depends on an unknown service " + dependsOn);
527     if (service.equals(dependsOn))
528       throw new ConfigurationException("The layer " + service + " cannot depend on itself.");
529     if (depends == null)
530       depends = new HashSet<String>(7);
531     depends.add(dependsOn);
532   }
533 
534 
535   private void addReplaces(String replacee)
536     throws ConfigurationException
537   {
538     if (!layers.containsKey(replacee))
539       throw new ConfigurationException("Layer " + service + " specifies to replace an unknown service " + replacee);
540     if (service.equals(replacee))
541       throw new ConfigurationException("The layer " + service + " cannot replace itself.");
542     if (replaces == null)
543       replaces = new HashSet<String>(4);
544     replaces.add(replacee);
545   }
546 
547 
548   private Iterator<String> depends()
549   {
550     if (depends == null)
551       depends = new HashSet<String>(0);
552     return depends.iterator();
553   }
554 
555 
556   private Iterator<String> replaces()
557   {
558     if (replaces == null)
559       replaces = new HashSet<String>(0);
560     return replaces.iterator();
561   }
562 
563 } // END LayerConfig