1 /* 2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.rmi.activation; 27 28 import java.lang.reflect.Constructor; 29 import java.lang.reflect.InvocationTargetException; 30 import java.rmi.MarshalledObject; 31 import java.rmi.Naming; 32 import java.rmi.Remote; 33 import java.rmi.RemoteException; 34 import java.rmi.activation.UnknownGroupException; 35 import java.rmi.activation.UnknownObjectException; 36 import java.rmi.server.RMIClassLoader; 37 import java.rmi.server.UnicastRemoteObject; 38 import java.security.AccessController; 39 import sun.security.action.GetIntegerAction; 40 import sun.security.action.GetPropertyAction; 41 42 /** 43 * An <code>ActivationGroup</code> is responsible for creating new 44 * instances of "activatable" objects in its group, informing its 45 * <code>ActivationMonitor</code> when either: its object's become 46 * active or inactive, or the group as a whole becomes inactive. <p> 47 * 48 * An <code>ActivationGroup</code> is <i>initially</i> created in one 49 * of several ways: <ul> 50 * <li>as a side-effect of creating an <code>ActivationDesc</code> 51 * without an explicit <code>ActivationGroupID</code> for the 52 * first activatable object in the group, or 53 * <li>via the <code>ActivationGroup.createGroup</code> method 54 * <li>as a side-effect of activating the first object in a group 55 * whose <code>ActivationGroupDesc</code> was only registered.</ul><p> 56 * 57 * Only the activator can <i>recreate</i> an 58 * <code>ActivationGroup</code>. The activator spawns, as needed, a 59 * separate VM (as a child process, for example) for each registered 60 * activation group and directs activation requests to the appropriate 61 * group. It is implementation specific how VMs are spawned. An 62 * activation group is created via the 63 * <code>ActivationGroup.createGroup</code> static method. The 64 * <code>createGroup</code> method has two requirements on the group 65 * to be created: 1) the group must be a concrete subclass of 66 * <code>ActivationGroup</code>, and 2) the group must have a 67 * constructor that takes two arguments: 68 * 69 * <ul> 70 * <li> the group's <code>ActivationGroupID</code>, and 71 * <li> the group's initialization data (in a 72 * <code>java.rmi.MarshalledObject</code>)</ul><p> 73 * 74 * When created, the default implementation of 75 * <code>ActivationGroup</code> will override the system properties 76 * with the properties requested when its 77 * <code>ActivationGroupDesc</code> was created, and will set a 78 * <code>java.rmi.RMISecurityManager</code> as the default system 79 * security manager. If your application requires specific properties 80 * to be set when objects are activated in the group, the application 81 * should create a special <code>Properties</code> object containing 82 * these properties, then create an <code>ActivationGroupDesc</code> 83 * with the <code>Properties</code> object, and use 84 * <code>ActivationGroup.createGroup</code> before creating any 85 * <code>ActivationDesc</code>s (before the default 86 * <code>ActivationGroupDesc</code> is created). If your application 87 * requires the use of a security manager other than 88 * <code>java.rmi.RMISecurityManager</code>, in the 89 * ActivativationGroupDescriptor properties list you can set 90 * <code>java.security.manager</code> property to the name of the security 91 * manager you would like to install. 92 * 93 * @author Ann Wollrath 94 * @see ActivationInstantiator 95 * @see ActivationGroupDesc 96 * @see ActivationGroupID 97 * @since 1.2 98 */ 99 public abstract class ActivationGroup 100 extends UnicastRemoteObject 101 implements ActivationInstantiator 102 { 103 /** 104 * @serial the group's identifier 105 */ 106 private ActivationGroupID groupID; 107 108 /** 109 * @serial the group's monitor 110 */ 111 private ActivationMonitor monitor; 112 113 /** 114 * @serial the group's incarnation number 115 */ 116 private long incarnation; 117 118 /** the current activation group for this VM */ 119 private static ActivationGroup currGroup; 120 /** the current group's identifier */ 121 private static ActivationGroupID currGroupID; 122 /** the current group's activation system */ 123 private static ActivationSystem currSystem; 124 /** used to control a group being created only once */ 125 private static boolean canCreate = true; 126 127 /** indicate compatibility with the Java 2 SDK v1.2 version of class */ 128 private static final long serialVersionUID = -7696947875314805420L; 129 130 /** 131 * Constructs an activation group with the given activation group 132 * identifier. The group is exported as a 133 * <code>java.rmi.server.UnicastRemoteObject</code>. 134 * 135 * @param groupID the group's identifier 136 * @throws RemoteException if this group could not be exported 137 * @since 1.2 138 */ 139 protected ActivationGroup(ActivationGroupID groupID) 140 throws RemoteException 141 { 142 // call super constructor to export the object 143 super(); 144 this.groupID = groupID; 145 } 146 147 /** 148 * The group's <code>inactiveObject</code> method is called 149 * indirectly via a call to the <code>Activatable.inactive</code> 150 * method. A remote object implementation must call 151 * <code>Activatable</code>'s <code>inactive</code> method when 152 * that object deactivates (the object deems that it is no longer 153 * active). If the object does not call 154 * <code>Activatable.inactive</code> when it deactivates, the 155 * object will never be garbage collected since the group keeps 156 * strong references to the objects it creates. <p> 157 * 158 * <p>The group's <code>inactiveObject</code> method unexports the 159 * remote object from the RMI runtime so that the object can no 160 * longer receive incoming RMI calls. An object will only be unexported 161 * if the object has no pending or executing calls. 162 * The subclass of <code>ActivationGroup</code> must override this 163 * method and unexport the object. <p> 164 * 165 * <p>After removing the object from the RMI runtime, the group 166 * must inform its <code>ActivationMonitor</code> (via the monitor's 167 * <code>inactiveObject</code> method) that the remote object is 168 * not currently active so that the remote object will be 169 * re-activated by the activator upon a subsequent activation 170 * request.<p> 171 * 172 * <p>This method simply informs the group's monitor that the object 173 * is inactive. It is up to the concrete subclass of ActivationGroup 174 * to fulfill the additional requirement of unexporting the object. <p> 175 * 176 * @param id the object's activation identifier 177 * @return true if the object was successfully deactivated; otherwise 178 * returns false. 179 * @exception UnknownObjectException if object is unknown (may already 180 * be inactive) 181 * @exception RemoteException if call informing monitor fails 182 * @exception ActivationException if group is inactive 183 * @since 1.2 184 */ 185 public boolean inactiveObject(ActivationID id) 186 throws ActivationException, UnknownObjectException, RemoteException 187 { 188 getMonitor().inactiveObject(id); 189 return true; 190 } 191 192 /** 193 * The group's <code>activeObject</code> method is called when an 194 * object is exported (either by <code>Activatable</code> object 195 * construction or an explicit call to 196 * <code>Activatable.exportObject</code>. The group must inform its 197 * <code>ActivationMonitor</code> that the object is active (via 198 * the monitor's <code>activeObject</code> method) if the group 199 * hasn't already done so. 200 * 201 * @param id the object's identifier 202 * @param obj the remote object implementation 203 * @exception UnknownObjectException if object is not registered 204 * @exception RemoteException if call informing monitor fails 205 * @exception ActivationException if group is inactive 206 * @since 1.2 207 */ 208 public abstract void activeObject(ActivationID id, Remote obj) 209 throws ActivationException, UnknownObjectException, RemoteException; 210 211 /** 212 * Create and set the activation group for the current VM. The 213 * activation group can only be set if it is not currently set. 214 * An activation group is set using the <code>createGroup</code> 215 * method when the <code>Activator</code> initiates the 216 * re-creation of an activation group in order to carry out 217 * incoming <code>activate</code> requests. A group must first be 218 * registered with the <code>ActivationSystem</code> before it can 219 * be created via this method. 220 * 221 * <p>The group class specified by the 222 * <code>ActivationGroupDesc</code> must be a concrete subclass of 223 * <code>ActivationGroup</code> and have a public constructor that 224 * takes two arguments: the <code>ActivationGroupID</code> for the 225 * group and the <code>MarshalledObject</code> containing the 226 * group's initialization data (obtained from the 227 * <code>ActivationGroupDesc</code>. 228 * 229 * <p>If the group class name specified in the 230 * <code>ActivationGroupDesc</code> is <code>null</code>, then 231 * this method will behave as if the group descriptor contained 232 * the name of the default activation group implementation class. 233 * 234 * <p>Note that if your application creates its own custom 235 * activation group, a security manager must be set for that 236 * group. Otherwise objects cannot be activated in the group. 237 * <code>java.rmi.RMISecurityManager</code> is set by default. 238 * 239 * <p>If a security manager is already set in the group VM, this 240 * method first calls the security manager's 241 * <code>checkSetFactory</code> method. This could result in a 242 * <code>SecurityException</code>. If your application needs to 243 * set a different security manager, you must ensure that the 244 * policy file specified by the group's 245 * <code>ActivationGroupDesc</code> grants the group the necessary 246 * permissions to set a new security manager. (Note: This will be 247 * necessary if your group downloads and sets a security manager). 248 * 249 * <p>After the group is created, the 250 * <code>ActivationSystem</code> is informed that the group is 251 * active by calling the <code>activeGroup</code> method which 252 * returns the <code>ActivationMonitor</code> for the group. The 253 * application need not call <code>activeGroup</code> 254 * independently since it is taken care of by this method. 255 * 256 * <p>Once a group is created, subsequent calls to the 257 * <code>currentGroupID</code> method will return the identifier 258 * for this group until the group becomes inactive. 259 * 260 * @param id the activation group's identifier 261 * @param desc the activation group's descriptor 262 * @param incarnation the group's incarnation number (zero on group's 263 * initial creation) 264 * @return the activation group for the VM 265 * @exception ActivationException if group already exists or if error 266 * occurs during group creation 267 * @exception SecurityException if permission to create group is denied. 268 * (Note: The default implementation of the security manager 269 * <code>checkSetFactory</code> 270 * method requires the RuntimePermission "setFactory") 271 * @see SecurityManager#checkSetFactory 272 * @since 1.2 273 */ 274 public static synchronized 275 ActivationGroup createGroup(ActivationGroupID id, 276 final ActivationGroupDesc desc, 277 long incarnation) 278 throws ActivationException 279 { 280 SecurityManager security = System.getSecurityManager(); 281 if (security != null) 282 security.checkSetFactory(); 283 284 if (currGroup != null) 285 throw new ActivationException("group already exists"); 286 287 if (canCreate == false) 288 throw new ActivationException("group deactivated and " + 289 "cannot be recreated"); 290 291 try { 292 // load group's class 293 String groupClassName = desc.getClassName(); 294 Class<? extends ActivationGroup> cl; 295 Class<? extends ActivationGroup> defaultGroupClass = 296 sun.rmi.server.ActivationGroupImpl.class; 297 if (groupClassName == null || // see 4252236 298 groupClassName.equals(defaultGroupClass.getName())) 299 { 300 cl = defaultGroupClass; 301 } else { 302 Class<?> cl0; 303 try { 304 cl0 = RMIClassLoader.loadClass(desc.getLocation(), 305 groupClassName); 306 } catch (Exception ex) { 307 throw new ActivationException( 308 "Could not load group implementation class", ex); 309 } 310 if (ActivationGroup.class.isAssignableFrom(cl0)) { 311 cl = cl0.asSubclass(ActivationGroup.class); 312 } else { 313 throw new ActivationException("group not correct class: " + 314 cl0.getName()); 315 } 316 } 317 318 // create group 319 Constructor<? extends ActivationGroup> constructor = 320 cl.getConstructor(ActivationGroupID.class, 321 MarshalledObject.class); 322 ActivationGroup newGroup = 323 constructor.newInstance(id, desc.getData()); 324 currSystem = id.getSystem(); 325 newGroup.incarnation = incarnation; 326 newGroup.monitor = 327 currSystem.activeGroup(id, newGroup, incarnation); 328 currGroup = newGroup; 329 currGroupID = id; 330 canCreate = false; 331 } catch (InvocationTargetException e) { 332 e.getTargetException().printStackTrace(); 333 throw new ActivationException("exception in group constructor", 334 e.getTargetException()); 335 336 } catch (ActivationException e) { 337 throw e; 338 339 } catch (Exception e) { 340 throw new ActivationException("exception creating group", e); 341 } 342 343 return currGroup; 344 } 345 346 /** 347 * Returns the current activation group's identifier. Returns null 348 * if no group is currently active for this VM. 349 * @return the activation group's identifier 350 * @since 1.2 351 */ 352 public static synchronized ActivationGroupID currentGroupID() { 353 return currGroupID; 354 } 355 356 /** 357 * Returns the activation group identifier for the VM. If an 358 * activation group does not exist for this VM, a default 359 * activation group is created. A group can be created only once, 360 * so if a group has already become active and deactivated. 361 * 362 * @return the activation group identifier 363 * @exception ActivationException if error occurs during group 364 * creation, if security manager is not set, or if the group 365 * has already been created and deactivated. 366 */ 367 static synchronized ActivationGroupID internalCurrentGroupID() 368 throws ActivationException 369 { 370 if (currGroupID == null) 371 throw new ActivationException("nonexistent group"); 372 373 return currGroupID; 374 } 375 376 /** 377 * Set the activation system for the VM. The activation system can 378 * only be set it if no group is currently active. If the activation 379 * system is not set via this call, then the <code>getSystem</code> 380 * method attempts to obtain a reference to the 381 * <code>ActivationSystem</code> by looking up the name 382 * "java.rmi.activation.ActivationSystem" in the Activator's 383 * registry. By default, the port number used to look up the 384 * activation system is defined by 385 * <code>ActivationSystem.SYSTEM_PORT</code>. This port can be overridden 386 * by setting the property <code>java.rmi.activation.port</code>. 387 * 388 * <p>If there is a security manager, this method first 389 * calls the security manager's <code>checkSetFactory</code> method. 390 * This could result in a SecurityException. 391 * 392 * @param system remote reference to the <code>ActivationSystem</code> 393 * @exception ActivationException if activation system is already set 394 * @exception SecurityException if permission to set the activation system is denied. 395 * (Note: The default implementation of the security manager 396 * <code>checkSetFactory</code> 397 * method requires the RuntimePermission "setFactory") 398 * @see #getSystem 399 * @see SecurityManager#checkSetFactory 400 * @since 1.2 401 */ 402 public static synchronized void setSystem(ActivationSystem system) 403 throws ActivationException 404 { 405 SecurityManager security = System.getSecurityManager(); 406 if (security != null) 407 security.checkSetFactory(); 408 409 if (currSystem != null) 410 throw new ActivationException("activation system already set"); 411 412 currSystem = system; 413 } 414 415 /** 416 * Returns the activation system for the VM. The activation system 417 * may be set by the <code>setSystem</code> method. If the 418 * activation system is not set via the <code>setSystem</code> 419 * method, then the <code>getSystem</code> method attempts to 420 * obtain a reference to the <code>ActivationSystem</code> by 421 * looking up the name "java.rmi.activation.ActivationSystem" in 422 * the Activator's registry. By default, the port number used to 423 * look up the activation system is defined by 424 * <code>ActivationSystem.SYSTEM_PORT</code>. This port can be 425 * overridden by setting the property 426 * <code>java.rmi.activation.port</code>. 427 * 428 * @return the activation system for the VM/group 429 * @exception ActivationException if activation system cannot be 430 * obtained or is not bound 431 * (means that it is not running) 432 * @see #setSystem 433 * @since 1.2 434 */ 435 public static synchronized ActivationSystem getSystem() 436 throws ActivationException 437 { 438 if (currSystem == null) { 439 try { 440 String host = AccessController.doPrivileged( 441 new GetPropertyAction("sun.rmi.activation.host", 442 "")); 443 int port = AccessController.doPrivileged( 444 new GetIntegerAction("java.rmi.activation.port", 445 ActivationSystem.SYSTEM_PORT)); 446 currSystem = (ActivationSystem) 447 Naming.lookup("//" + host + ":" + port + 448 "/java.rmi.activation.ActivationSystem"); 449 } catch (Exception e) { 450 throw new ActivationException( 451 "unable to obtain ActivationSystem", e); 452 } 453 } 454 return currSystem; 455 } 456 457 /** 458 * This protected method is necessary for subclasses to 459 * make the <code>activeObject</code> callback to the group's 460 * monitor. The call is simply forwarded to the group's 461 * <code>ActivationMonitor</code>. 462 * 463 * @param id the object's identifier 464 * @param mobj a marshalled object containing the remote object's stub 465 * @exception UnknownObjectException if object is not registered 466 * @exception RemoteException if call informing monitor fails 467 * @exception ActivationException if an activation error occurs 468 * @since 1.2 469 */ 470 protected void activeObject(ActivationID id, 471 MarshalledObject<? extends Remote> mobj) 472 throws ActivationException, UnknownObjectException, RemoteException 473 { 474 getMonitor().activeObject(id, mobj); 475 } 476 477 /** 478 * This protected method is necessary for subclasses to 479 * make the <code>inactiveGroup</code> callback to the group's 480 * monitor. The call is simply forwarded to the group's 481 * <code>ActivationMonitor</code>. Also, the current group 482 * for the VM is set to null. 483 * 484 * @exception UnknownGroupException if group is not registered 485 * @exception RemoteException if call informing monitor fails 486 * @since 1.2 487 */ 488 protected void inactiveGroup() 489 throws UnknownGroupException, RemoteException 490 { 491 try { 492 getMonitor().inactiveGroup(groupID, incarnation); 493 } finally { 494 destroyGroup(); 495 } 496 } 497 498 /** 499 * Returns the monitor for the activation group. 500 */ 501 private ActivationMonitor getMonitor() throws RemoteException { 502 synchronized (ActivationGroup.class) { 503 if (monitor != null) { 504 return monitor; 505 } 506 } 507 throw new RemoteException("monitor not received"); 508 } 509 510 /** 511 * Destroys the current group. 512 */ 513 private static synchronized void destroyGroup() { 514 currGroup = null; 515 currGroupID = null; 516 // NOTE: don't set currSystem to null since it may be needed 517 } 518 519 /** 520 * Returns the current group for the VM. 521 * @exception ActivationException if current group is null (not active) 522 */ 523 static synchronized ActivationGroup currentGroup() 524 throws ActivationException 525 { 526 if (currGroup == null) { 527 throw new ActivationException("group is not active"); 528 } 529 return currGroup; 530 } 531 532 }