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