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