History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/rmi/server/ActivationGroupImpl.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/rmi/server/ActivationGroupImpl.java+505−0
@@ -0,0 +1,505 @@
1+/*
2+ * Copyright (c) 1997, 2006, 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 sun.rmi.server;
27+
28+import java.io.IOException;
29+import java.lang.reflect.Constructor;
30+import java.lang.reflect.InvocationTargetException;
31+import java.net.ServerSocket;
32+import java.rmi.MarshalledObject;
33+import java.rmi.NoSuchObjectException;
34+import java.rmi.Remote;
35+import java.rmi.RemoteException;
36+import java.rmi.activation.Activatable;
37+import java.rmi.activation.ActivationDesc;
38+import java.rmi.activation.ActivationException;
39+import java.rmi.activation.ActivationGroup;
40+import java.rmi.activation.ActivationGroupID;
41+import java.rmi.activation.ActivationID;
42+import java.rmi.activation.UnknownObjectException;
43+import java.rmi.server.RMIClassLoader;
44+import java.rmi.server.RMIServerSocketFactory;
45+import java.rmi.server.RMISocketFactory;
46+import java.rmi.server.UnicastRemoteObject;
47+import java.security.AccessController;
48+import java.security.PrivilegedActionException;
49+import java.security.PrivilegedExceptionAction;
50+import java.util.ArrayList;
51+import java.util.Hashtable;
52+import java.util.List;
53+import sun.rmi.registry.RegistryImpl;
54+
55+/**
56+ * The default activation group implementation.
57+ *
58+ * @author Ann Wollrath
59+ * @since 1.2
60+ * @see java.rmi.activation.ActivationGroup
61+ */
62+public class ActivationGroupImpl extends ActivationGroup {
63+
64+ // use serialVersionUID from JDK 1.2.2 for interoperability
65+ private static final long serialVersionUID = 5758693559430427303L;
66+
67+ /** maps persistent IDs to activated remote objects */
68+ private final Hashtable<ActivationID,ActiveEntry> active =
69+ new Hashtable<ActivationID,ActiveEntry>();
70+ private boolean groupInactive = false;
71+ private final ActivationGroupID groupID;
72+ private final List<ActivationID> lockedIDs = new ArrayList<ActivationID>();
73+
74+ /**
75+ * Creates a default activation group implementation.
76+ *
77+ * @param id the group's identifier
78+ * @param data ignored
79+ */
80+ public ActivationGroupImpl(ActivationGroupID id, MarshalledObject<?> data)
81+ throws RemoteException
82+ {
83+ super(id);
84+ groupID = id;
85+
86+ /*
87+ * Unexport activation group impl and attempt to export it on
88+ * an unshared anonymous port. See 4692286.
89+ */
90+ unexportObject(this, true);
91+ RMIServerSocketFactory ssf = new ServerSocketFactoryImpl();
92+ UnicastRemoteObject.exportObject(this, 0, null, ssf);
93+
94+ if (System.getSecurityManager() == null) {
95+ try {
96+ // Provide a default security manager.
97+ System.setSecurityManager(new SecurityManager());
98+
99+ } catch (Exception e) {
100+ throw new RemoteException("unable to set security manager", e);
101+ }
102+ }
103+ }
104+
105+ /**
106+ * Trivial server socket factory used to export the activation group
107+ * impl on an unshared port.
108+ */
109+ private static class ServerSocketFactoryImpl
110+ implements RMIServerSocketFactory
111+ {
112+ public ServerSocket createServerSocket(int port) throws IOException
113+ {
114+ RMISocketFactory sf = RMISocketFactory.getSocketFactory();
115+ if (sf == null) {
116+ sf = RMISocketFactory.getDefaultSocketFactory();
117+ }
118+ return sf.createServerSocket(port);
119+ }
120+ }
121+
122+ /*
123+ * Obtains a lock on the ActivationID id before returning. Allows only one
124+ * thread at a time to hold a lock on a particular id. If the lock for id
125+ * is in use, all requests for an equivalent (in the Object.equals sense)
126+ * id will wait for the id to be notified and use the supplied id as the
127+ * next lock. The caller of "acquireLock" must execute the "releaseLock"
128+ * method" to release the lock and "notifyAll" waiters for the id lock
129+ * obtained from this method. The typical usage pattern is as follows:
130+ *
131+ * try {
132+ * acquireLock(id);
133+ * // do stuff pertaining to id...
134+ * } finally {
135+ * releaseLock(id);
136+ * checkInactiveGroup();
137+ * }
138+ */
139+ private void acquireLock(ActivationID id) {
140+
141+ ActivationID waitForID;
142+
143+ for (;;) {
144+
145+ synchronized (lockedIDs) {
146+ int index = lockedIDs.indexOf(id);
147+ if (index < 0) {
148+ lockedIDs.add(id);
149+ return;
150+ } else {
151+ waitForID = lockedIDs.get(index);
152+ }
153+ }
154+
155+ synchronized (waitForID) {
156+ synchronized (lockedIDs) {
157+ int index = lockedIDs.indexOf(waitForID);
158+ if (index < 0) continue;
159+ ActivationID actualID = lockedIDs.get(index);
160+ if (actualID != waitForID)
161+ /*
162+ * don't wait on an id that won't be notified.
163+ */
164+ continue;
165+ }
166+
167+ try {
168+ waitForID.wait();
169+ } catch (InterruptedException ignore) {
170+ }
171+ }
172+ }
173+
174+ }
175+
176+ /*
177+ * Releases the id lock obtained via the "acquireLock" method and then
178+ * notifies all threads waiting on the lock.
179+ */
180+ private void releaseLock(ActivationID id) {
181+ synchronized (lockedIDs) {
182+ id = lockedIDs.remove(lockedIDs.indexOf(id));
183+ }
184+
185+ synchronized (id) {
186+ id.notifyAll();
187+ }
188+ }
189+
190+ /**
191+ * Creates a new instance of an activatable remote object. The
192+ * <code>Activator</code> calls this method to create an activatable
193+ * object in this group. This method should be idempotent; a call to
194+ * activate an already active object should return the previously
195+ * activated object.
196+ *
197+ * Note: this method assumes that the Activator will only invoke
198+ * newInstance for the same object in a serial fashion (i.e.,
199+ * the activator will not allow the group to see concurrent requests
200+ * to activate the same object.
201+ *
202+ * @param id the object's activation identifier
203+ * @param desc the object's activation descriptor
204+ * @return a marshalled object containing the activated object's stub
205+ */
206+ public MarshalledObject<? extends Remote>
207+ newInstance(final ActivationID id,
208+ final ActivationDesc desc)
209+ throws ActivationException, RemoteException
210+ {
211+ RegistryImpl.checkAccess("ActivationInstantiator.newInstance");
212+
213+ if (!groupID.equals(desc.getGroupID()))
214+ throw new ActivationException("newInstance in wrong group");
215+
216+ try {
217+ acquireLock(id);
218+ synchronized (this) {
219+ if (groupInactive == true)
220+ throw new InactiveGroupException("group is inactive");
221+ }
222+
223+ ActiveEntry entry = active.get(id);
224+ if (entry != null)
225+ return entry.mobj;
226+
227+ String className = desc.getClassName();
228+
229+ final Class<? extends Remote> cl =
230+ RMIClassLoader.loadClass(desc.getLocation(), className)
231+ .asSubclass(Remote.class);
232+ Remote impl = null;
233+
234+ final Thread t = Thread.currentThread();
235+ final ClassLoader savedCcl = t.getContextClassLoader();
236+ ClassLoader objcl = cl.getClassLoader();
237+ final ClassLoader ccl = covers(objcl, savedCcl) ? objcl : savedCcl;
238+
239+ /*
240+ * Fix for 4164971: allow non-public activatable class
241+ * and/or constructor, create the activatable object in a
242+ * privileged block
243+ */
244+ try {
245+ /*
246+ * The code below is in a doPrivileged block to
247+ * protect against user code which code might have set
248+ * a global socket factory (in which case application
249+ * code would be on the stack).
250+ */
251+ impl = AccessController.doPrivileged(
252+ new PrivilegedExceptionAction<Remote>() {
253+ public Remote run() throws InstantiationException,
254+ NoSuchMethodException, IllegalAccessException,
255+ InvocationTargetException
256+ {
257+ Constructor<? extends Remote> constructor =
258+ cl.getDeclaredConstructor(
259+ ActivationID.class, MarshalledObject.class);
260+ constructor.setAccessible(true);
261+ try {
262+ /*
263+ * Fix for 4289544: make sure to set the
264+ * context class loader to be the class
265+ * loader of the impl class before
266+ * constructing that class.
267+ */
268+ t.setContextClassLoader(ccl);
269+ return constructor.newInstance(id,
270+ desc.getData());
271+ } finally {
272+ t.setContextClassLoader(savedCcl);
273+ }
274+ }
275+ });
276+ } catch (PrivilegedActionException pae) {
277+ Throwable e = pae.getException();
278+
279+ // narrow the exception's type and rethrow it
280+ if (e instanceof InstantiationException) {
281+ throw (InstantiationException) e;
282+ } else if (e instanceof NoSuchMethodException) {
283+ throw (NoSuchMethodException) e;
284+ } else if (e instanceof IllegalAccessException) {
285+ throw (IllegalAccessException) e;
286+ } else if (e instanceof InvocationTargetException) {
287+ throw (InvocationTargetException) e;
288+ } else if (e instanceof RuntimeException) {
289+ throw (RuntimeException) e;
290+ } else if (e instanceof Error) {
291+ throw (Error) e;
292+ }
293+ }
294+
295+ entry = new ActiveEntry(impl);
296+ active.put(id, entry);
297+ return entry.mobj;
298+
299+ } catch (NoSuchMethodException e) {
300+ /* user forgot to provide activatable constructor? */
301+ throw new ActivationException
302+ ("Activatable object must provide an activation"+
303+ " constructor", e);
304+
305+ } catch (NoSuchMethodError e) {
306+ /* code recompiled and user forgot to provide
307+ * activatable constructor?
308+ */
309+ throw new ActivationException
310+ ("Activatable object must provide an activation"+
311+ " constructor", e );
312+
313+ } catch (InvocationTargetException e) {
314+ throw new ActivationException("exception in object constructor",
315+ e.getTargetException());
316+
317+ } catch (Exception e) {
318+ throw new ActivationException("unable to activate object", e);
319+ } finally {
320+ releaseLock(id);
321+ checkInactiveGroup();
322+ }
323+ }
324+
325+
326+ /**
327+ * The group's <code>inactiveObject</code> method is called
328+ * indirectly via a call to the <code>Activatable.inactive</code>
329+ * method. A remote object implementation must call
330+ * <code>Activatable</code>'s <code>inactive</code> method when
331+ * that object deactivates (the object deems that it is no longer
332+ * active). If the object does not call
333+ * <code>Activatable.inactive</code> when it deactivates, the
334+ * object will never be garbage collected since the group keeps
335+ * strong references to the objects it creates. <p>
336+ *
337+ * The group's <code>inactiveObject</code> method
338+ * unexports the remote object from the RMI runtime so that the
339+ * object can no longer receive incoming RMI calls. This call will
340+ * only succeed if the object has no pending/executing calls. If
341+ * the object does have pending/executing RMI calls, then false
342+ * will be returned.
343+ *
344+ * If the object has no pending/executing calls, the object is
345+ * removed from the RMI runtime and the group informs its
346+ * <code>ActivationMonitor</code> (via the monitor's
347+ * <code>inactiveObject</code> method) that the remote object is
348+ * not currently active so that the remote object will be
349+ * re-activated by the activator upon a subsequent activation
350+ * request.
351+ *
352+ * @param id the object's activation identifier
353+ * @returns true if the operation succeeds (the operation will
354+ * succeed if the object in currently known to be active and is
355+ * either already unexported or is currently exported and has no
356+ * pending/executing calls); false is returned if the object has
357+ * pending/executing calls in which case it cannot be deactivated
358+ * @exception UnknownObjectException if object is unknown (may already
359+ * be inactive)
360+ * @exception RemoteException if call informing monitor fails
361+ */
362+ public boolean inactiveObject(ActivationID id)
363+ throws ActivationException, UnknownObjectException, RemoteException
364+ {
365+
366+ try {
367+ acquireLock(id);
368+ synchronized (this) {
369+ if (groupInactive == true)
370+ throw new ActivationException("group is inactive");
371+ }
372+
373+ ActiveEntry entry = active.get(id);
374+ if (entry == null) {
375+ // REMIND: should this be silent?
376+ throw new UnknownObjectException("object not active");
377+ }
378+
379+ try {
380+ if (Activatable.unexportObject(entry.impl, false) == false)
381+ return false;
382+ } catch (NoSuchObjectException allowUnexportedObjects) {
383+ }
384+
385+ try {
386+ super.inactiveObject(id);
387+ } catch (UnknownObjectException allowUnregisteredObjects) {
388+ }
389+
390+ active.remove(id);
391+
392+ } finally {
393+ releaseLock(id);
394+ checkInactiveGroup();
395+ }
396+
397+ return true;
398+ }
399+
400+ /*
401+ * Determines if the group has become inactive and
402+ * marks it as such.
403+ */
404+ private void checkInactiveGroup() {
405+ boolean groupMarkedInactive = false;
406+ synchronized (this) {
407+ if (active.size() == 0 && lockedIDs.size() == 0 &&
408+ groupInactive == false)
409+ {
410+ groupInactive = true;
411+ groupMarkedInactive = true;
412+ }
413+ }
414+
415+ if (groupMarkedInactive) {
416+ try {
417+ super.inactiveGroup();
418+ } catch (Exception ignoreDeactivateFailure) {
419+ }
420+
421+ try {
422+ UnicastRemoteObject.unexportObject(this, true);
423+ } catch (NoSuchObjectException allowUnexportedGroup) {
424+ }
425+ }
426+ }
427+
428+ /**
429+ * The group's <code>activeObject</code> method is called when an
430+ * object is exported (either by <code>Activatable</code> object
431+ * construction or an explicit call to
432+ * <code>Activatable.exportObject</code>. The group must inform its
433+ * <code>ActivationMonitor</code> that the object is active (via
434+ * the monitor's <code>activeObject</code> method) if the group
435+ * hasn't already done so.
436+ *
437+ * @param id the object's identifier
438+ * @param obj the remote object implementation
439+ * @exception UnknownObjectException if object is not registered
440+ * @exception RemoteException if call informing monitor fails
441+ */
442+ public void activeObject(ActivationID id, Remote impl)
443+ throws ActivationException, UnknownObjectException, RemoteException
444+ {
445+
446+ try {
447+ acquireLock(id);
448+ synchronized (this) {
449+ if (groupInactive == true)
450+ throw new ActivationException("group is inactive");
451+ }
452+ if (!active.contains(id)) {
453+ ActiveEntry entry = new ActiveEntry(impl);
454+ active.put(id, entry);
455+ // created new entry, so inform monitor of active object
456+ try {
457+ super.activeObject(id, entry.mobj);
458+ } catch (RemoteException e) {
459+ // daemon can still find it by calling newInstance
460+ }
461+ }
462+ } finally {
463+ releaseLock(id);
464+ checkInactiveGroup();
465+ }
466+ }
467+
468+ /**
469+ * Entry in table for active object.
470+ */
471+ private static class ActiveEntry {
472+ Remote impl;
473+ MarshalledObject<Remote> mobj;
474+
475+ ActiveEntry(Remote impl) throws ActivationException {
476+ this.impl = impl;
477+ try {
478+ this.mobj = new MarshalledObject<Remote>(impl);
479+ } catch (IOException e) {
480+ throw new
481+ ActivationException("failed to marshal remote object", e);
482+ }
483+ }
484+ }
485+
486+ /**
487+ * Returns true if the first argument is either equal to, or is a
488+ * descendant of, the second argument. Null is treated as the root of
489+ * the tree.
490+ */
491+ private static boolean covers(ClassLoader sub, ClassLoader sup) {
492+ if (sup == null) {
493+ return true;
494+ } else if (sub == null) {
495+ return false;
496+ }
497+ do {
498+ if (sub == sup) {
499+ return true;
500+ }
501+ sub = sub.getParent();
502+ } while (sub != null);
503+ return false;
504+ }
505+}