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