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

Lines 1–500 of 505.