0N/A/*
2362N/A * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.rmi.server;
0N/A
0N/Aimport java.io.IOException;
0N/Aimport java.lang.reflect.Constructor;
0N/Aimport java.lang.reflect.InvocationTargetException;
0N/Aimport java.net.ServerSocket;
0N/Aimport java.rmi.MarshalledObject;
0N/Aimport java.rmi.NoSuchObjectException;
0N/Aimport java.rmi.Remote;
0N/Aimport java.rmi.RemoteException;
0N/Aimport java.rmi.activation.Activatable;
0N/Aimport java.rmi.activation.ActivationDesc;
0N/Aimport java.rmi.activation.ActivationException;
0N/Aimport java.rmi.activation.ActivationGroup;
0N/Aimport java.rmi.activation.ActivationGroupID;
0N/Aimport java.rmi.activation.ActivationID;
0N/Aimport java.rmi.activation.UnknownObjectException;
0N/Aimport java.rmi.server.RMIClassLoader;
0N/Aimport java.rmi.server.RMIServerSocketFactory;
0N/Aimport java.rmi.server.RMISocketFactory;
0N/Aimport java.rmi.server.UnicastRemoteObject;
0N/Aimport java.security.AccessController;
0N/Aimport java.security.PrivilegedActionException;
0N/Aimport java.security.PrivilegedExceptionAction;
0N/Aimport java.util.ArrayList;
0N/Aimport java.util.Hashtable;
0N/Aimport java.util.List;
0N/Aimport sun.rmi.registry.RegistryImpl;
0N/A
0N/A/**
0N/A * The default activation group implementation.
0N/A *
0N/A * @author Ann Wollrath
0N/A * @since 1.2
0N/A * @see java.rmi.activation.ActivationGroup
0N/A */
0N/Apublic class ActivationGroupImpl extends ActivationGroup {
0N/A
0N/A // use serialVersionUID from JDK 1.2.2 for interoperability
0N/A private static final long serialVersionUID = 5758693559430427303L;
0N/A
0N/A /** maps persistent IDs to activated remote objects */
0N/A private final Hashtable<ActivationID,ActiveEntry> active =
5559N/A new Hashtable<>();
0N/A private boolean groupInactive = false;
0N/A private final ActivationGroupID groupID;
5559N/A private final List<ActivationID> lockedIDs = new ArrayList<>();
0N/A
0N/A /**
0N/A * Creates a default activation group implementation.
0N/A *
0N/A * @param id the group's identifier
0N/A * @param data ignored
0N/A */
0N/A public ActivationGroupImpl(ActivationGroupID id, MarshalledObject<?> data)
0N/A throws RemoteException
0N/A {
0N/A super(id);
0N/A groupID = id;
0N/A
0N/A /*
0N/A * Unexport activation group impl and attempt to export it on
0N/A * an unshared anonymous port. See 4692286.
0N/A */
0N/A unexportObject(this, true);
0N/A RMIServerSocketFactory ssf = new ServerSocketFactoryImpl();
0N/A UnicastRemoteObject.exportObject(this, 0, null, ssf);
0N/A
0N/A if (System.getSecurityManager() == null) {
0N/A try {
0N/A // Provide a default security manager.
0N/A System.setSecurityManager(new SecurityManager());
0N/A
0N/A } catch (Exception e) {
0N/A throw new RemoteException("unable to set security manager", e);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Trivial server socket factory used to export the activation group
0N/A * impl on an unshared port.
0N/A */
0N/A private static class ServerSocketFactoryImpl
0N/A implements RMIServerSocketFactory
0N/A {
0N/A public ServerSocket createServerSocket(int port) throws IOException
0N/A {
0N/A RMISocketFactory sf = RMISocketFactory.getSocketFactory();
0N/A if (sf == null) {
0N/A sf = RMISocketFactory.getDefaultSocketFactory();
0N/A }
0N/A return sf.createServerSocket(port);
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Obtains a lock on the ActivationID id before returning. Allows only one
0N/A * thread at a time to hold a lock on a particular id. If the lock for id
0N/A * is in use, all requests for an equivalent (in the Object.equals sense)
0N/A * id will wait for the id to be notified and use the supplied id as the
0N/A * next lock. The caller of "acquireLock" must execute the "releaseLock"
0N/A * method" to release the lock and "notifyAll" waiters for the id lock
0N/A * obtained from this method. The typical usage pattern is as follows:
0N/A *
0N/A * try {
0N/A * acquireLock(id);
0N/A * // do stuff pertaining to id...
0N/A * } finally {
0N/A * releaseLock(id);
0N/A * checkInactiveGroup();
0N/A * }
0N/A */
0N/A private void acquireLock(ActivationID id) {
0N/A
0N/A ActivationID waitForID;
0N/A
0N/A for (;;) {
0N/A
0N/A synchronized (lockedIDs) {
0N/A int index = lockedIDs.indexOf(id);
0N/A if (index < 0) {
0N/A lockedIDs.add(id);
0N/A return;
0N/A } else {
0N/A waitForID = lockedIDs.get(index);
0N/A }
0N/A }
0N/A
0N/A synchronized (waitForID) {
0N/A synchronized (lockedIDs) {
0N/A int index = lockedIDs.indexOf(waitForID);
0N/A if (index < 0) continue;
0N/A ActivationID actualID = lockedIDs.get(index);
0N/A if (actualID != waitForID)
0N/A /*
0N/A * don't wait on an id that won't be notified.
0N/A */
0N/A continue;
0N/A }
0N/A
0N/A try {
0N/A waitForID.wait();
0N/A } catch (InterruptedException ignore) {
0N/A }
0N/A }
0N/A }
0N/A
0N/A }
0N/A
0N/A /*
0N/A * Releases the id lock obtained via the "acquireLock" method and then
0N/A * notifies all threads waiting on the lock.
0N/A */
0N/A private void releaseLock(ActivationID id) {
0N/A synchronized (lockedIDs) {
0N/A id = lockedIDs.remove(lockedIDs.indexOf(id));
0N/A }
0N/A
0N/A synchronized (id) {
0N/A id.notifyAll();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Creates a new instance of an activatable remote object. The
0N/A * <code>Activator</code> calls this method to create an activatable
0N/A * object in this group. This method should be idempotent; a call to
0N/A * activate an already active object should return the previously
0N/A * activated object.
0N/A *
0N/A * Note: this method assumes that the Activator will only invoke
0N/A * newInstance for the same object in a serial fashion (i.e.,
0N/A * the activator will not allow the group to see concurrent requests
0N/A * to activate the same object.
0N/A *
0N/A * @param id the object's activation identifier
0N/A * @param desc the object's activation descriptor
0N/A * @return a marshalled object containing the activated object's stub
0N/A */
0N/A public MarshalledObject<? extends Remote>
0N/A newInstance(final ActivationID id,
0N/A final ActivationDesc desc)
0N/A throws ActivationException, RemoteException
0N/A {
0N/A RegistryImpl.checkAccess("ActivationInstantiator.newInstance");
0N/A
0N/A if (!groupID.equals(desc.getGroupID()))
0N/A throw new ActivationException("newInstance in wrong group");
0N/A
0N/A try {
0N/A acquireLock(id);
0N/A synchronized (this) {
0N/A if (groupInactive == true)
0N/A throw new InactiveGroupException("group is inactive");
0N/A }
0N/A
0N/A ActiveEntry entry = active.get(id);
0N/A if (entry != null)
0N/A return entry.mobj;
0N/A
0N/A String className = desc.getClassName();
0N/A
0N/A final Class<? extends Remote> cl =
0N/A RMIClassLoader.loadClass(desc.getLocation(), className)
0N/A .asSubclass(Remote.class);
0N/A Remote impl = null;
0N/A
0N/A final Thread t = Thread.currentThread();
0N/A final ClassLoader savedCcl = t.getContextClassLoader();
0N/A ClassLoader objcl = cl.getClassLoader();
0N/A final ClassLoader ccl = covers(objcl, savedCcl) ? objcl : savedCcl;
0N/A
0N/A /*
0N/A * Fix for 4164971: allow non-public activatable class
0N/A * and/or constructor, create the activatable object in a
0N/A * privileged block
0N/A */
0N/A try {
0N/A /*
0N/A * The code below is in a doPrivileged block to
0N/A * protect against user code which code might have set
0N/A * a global socket factory (in which case application
0N/A * code would be on the stack).
0N/A */
0N/A impl = AccessController.doPrivileged(
0N/A new PrivilegedExceptionAction<Remote>() {
0N/A public Remote run() throws InstantiationException,
0N/A NoSuchMethodException, IllegalAccessException,
0N/A InvocationTargetException
0N/A {
0N/A Constructor<? extends Remote> constructor =
0N/A cl.getDeclaredConstructor(
0N/A ActivationID.class, MarshalledObject.class);
0N/A constructor.setAccessible(true);
0N/A try {
0N/A /*
0N/A * Fix for 4289544: make sure to set the
0N/A * context class loader to be the class
0N/A * loader of the impl class before
0N/A * constructing that class.
0N/A */
0N/A t.setContextClassLoader(ccl);
0N/A return constructor.newInstance(id,
0N/A desc.getData());
0N/A } finally {
0N/A t.setContextClassLoader(savedCcl);
0N/A }
0N/A }
0N/A });
0N/A } catch (PrivilegedActionException pae) {
0N/A Throwable e = pae.getException();
0N/A
0N/A // narrow the exception's type and rethrow it
0N/A if (e instanceof InstantiationException) {
0N/A throw (InstantiationException) e;
0N/A } else if (e instanceof NoSuchMethodException) {
0N/A throw (NoSuchMethodException) e;
0N/A } else if (e instanceof IllegalAccessException) {
0N/A throw (IllegalAccessException) e;
0N/A } else if (e instanceof InvocationTargetException) {
0N/A throw (InvocationTargetException) e;
0N/A } else if (e instanceof RuntimeException) {
0N/A throw (RuntimeException) e;
0N/A } else if (e instanceof Error) {
0N/A throw (Error) e;
0N/A }
0N/A }
0N/A
0N/A entry = new ActiveEntry(impl);
0N/A active.put(id, entry);
0N/A return entry.mobj;
0N/A
5559N/A } catch (NoSuchMethodException | NoSuchMethodError e) {
5559N/A /* user forgot to provide activatable constructor?
5559N/A * or code recompiled and user forgot to provide
0N/A * activatable constructor?
0N/A */
0N/A throw new ActivationException
0N/A ("Activatable object must provide an activation"+
0N/A " constructor", e );
0N/A
0N/A } catch (InvocationTargetException e) {
0N/A throw new ActivationException("exception in object constructor",
0N/A e.getTargetException());
0N/A
0N/A } catch (Exception e) {
0N/A throw new ActivationException("unable to activate object", e);
0N/A } finally {
0N/A releaseLock(id);
0N/A checkInactiveGroup();
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * The group's <code>inactiveObject</code> method is called
0N/A * indirectly via a call to the <code>Activatable.inactive</code>
0N/A * method. A remote object implementation must call
0N/A * <code>Activatable</code>'s <code>inactive</code> method when
0N/A * that object deactivates (the object deems that it is no longer
0N/A * active). If the object does not call
0N/A * <code>Activatable.inactive</code> when it deactivates, the
0N/A * object will never be garbage collected since the group keeps
0N/A * strong references to the objects it creates. <p>
0N/A *
0N/A * The group's <code>inactiveObject</code> method
0N/A * unexports the remote object from the RMI runtime so that the
0N/A * object can no longer receive incoming RMI calls. This call will
0N/A * only succeed if the object has no pending/executing calls. If
0N/A * the object does have pending/executing RMI calls, then false
0N/A * will be returned.
0N/A *
0N/A * If the object has no pending/executing calls, the object is
0N/A * removed from the RMI runtime and the group informs its
0N/A * <code>ActivationMonitor</code> (via the monitor's
0N/A * <code>inactiveObject</code> method) that the remote object is
0N/A * not currently active so that the remote object will be
0N/A * re-activated by the activator upon a subsequent activation
0N/A * request.
0N/A *
0N/A * @param id the object's activation identifier
0N/A * @returns true if the operation succeeds (the operation will
0N/A * succeed if the object in currently known to be active and is
0N/A * either already unexported or is currently exported and has no
0N/A * pending/executing calls); false is returned if the object has
0N/A * pending/executing calls in which case it cannot be deactivated
0N/A * @exception UnknownObjectException if object is unknown (may already
0N/A * be inactive)
0N/A * @exception RemoteException if call informing monitor fails
0N/A */
0N/A public boolean inactiveObject(ActivationID id)
0N/A throws ActivationException, UnknownObjectException, RemoteException
0N/A {
0N/A
0N/A try {
0N/A acquireLock(id);
0N/A synchronized (this) {
0N/A if (groupInactive == true)
0N/A throw new ActivationException("group is inactive");
0N/A }
0N/A
0N/A ActiveEntry entry = active.get(id);
0N/A if (entry == null) {
0N/A // REMIND: should this be silent?
0N/A throw new UnknownObjectException("object not active");
0N/A }
0N/A
0N/A try {
0N/A if (Activatable.unexportObject(entry.impl, false) == false)
0N/A return false;
0N/A } catch (NoSuchObjectException allowUnexportedObjects) {
0N/A }
0N/A
0N/A try {
0N/A super.inactiveObject(id);
0N/A } catch (UnknownObjectException allowUnregisteredObjects) {
0N/A }
0N/A
0N/A active.remove(id);
0N/A
0N/A } finally {
0N/A releaseLock(id);
0N/A checkInactiveGroup();
0N/A }
0N/A
0N/A return true;
0N/A }
0N/A
0N/A /*
0N/A * Determines if the group has become inactive and
0N/A * marks it as such.
0N/A */
0N/A private void checkInactiveGroup() {
0N/A boolean groupMarkedInactive = false;
0N/A synchronized (this) {
0N/A if (active.size() == 0 && lockedIDs.size() == 0 &&
0N/A groupInactive == false)
0N/A {
0N/A groupInactive = true;
0N/A groupMarkedInactive = true;
0N/A }
0N/A }
0N/A
0N/A if (groupMarkedInactive) {
0N/A try {
0N/A super.inactiveGroup();
0N/A } catch (Exception ignoreDeactivateFailure) {
0N/A }
0N/A
0N/A try {
0N/A UnicastRemoteObject.unexportObject(this, true);
0N/A } catch (NoSuchObjectException allowUnexportedGroup) {
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * The group's <code>activeObject</code> method is called when an
0N/A * object is exported (either by <code>Activatable</code> object
0N/A * construction or an explicit call to
0N/A * <code>Activatable.exportObject</code>. The group must inform its
0N/A * <code>ActivationMonitor</code> that the object is active (via
0N/A * the monitor's <code>activeObject</code> method) if the group
0N/A * hasn't already done so.
0N/A *
0N/A * @param id the object's identifier
0N/A * @param obj the remote object implementation
0N/A * @exception UnknownObjectException if object is not registered
0N/A * @exception RemoteException if call informing monitor fails
0N/A */
0N/A public void activeObject(ActivationID id, Remote impl)
0N/A throws ActivationException, UnknownObjectException, RemoteException
0N/A {
0N/A
0N/A try {
0N/A acquireLock(id);
0N/A synchronized (this) {
0N/A if (groupInactive == true)
0N/A throw new ActivationException("group is inactive");
0N/A }
0N/A if (!active.contains(id)) {
0N/A ActiveEntry entry = new ActiveEntry(impl);
0N/A active.put(id, entry);
0N/A // created new entry, so inform monitor of active object
0N/A try {
0N/A super.activeObject(id, entry.mobj);
0N/A } catch (RemoteException e) {
0N/A // daemon can still find it by calling newInstance
0N/A }
0N/A }
0N/A } finally {
0N/A releaseLock(id);
0N/A checkInactiveGroup();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Entry in table for active object.
0N/A */
0N/A private static class ActiveEntry {
0N/A Remote impl;
0N/A MarshalledObject<Remote> mobj;
0N/A
0N/A ActiveEntry(Remote impl) throws ActivationException {
0N/A this.impl = impl;
0N/A try {
0N/A this.mobj = new MarshalledObject<Remote>(impl);
0N/A } catch (IOException e) {
0N/A throw new
0N/A ActivationException("failed to marshal remote object", e);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns true if the first argument is either equal to, or is a
0N/A * descendant of, the second argument. Null is treated as the root of
0N/A * the tree.
0N/A */
0N/A private static boolean covers(ClassLoader sub, ClassLoader sup) {
0N/A if (sup == null) {
0N/A return true;
0N/A } else if (sub == null) {
0N/A return false;
0N/A }
0N/A do {
0N/A if (sub == sup) {
0N/A return true;
0N/A }
0N/A sub = sub.getParent();
0N/A } while (sub != null);
0N/A return false;
0N/A }
0N/A}