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