471 lines · 15.6 KB · Java
RawDownload
/* * Copyright (c) 1996, 2008, 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.transport; import java.rmi.Remote;import java.rmi.NoSuchObjectException;import java.rmi.dgc.VMID;import java.rmi.server.ObjID;import java.rmi.server.Unreferenced;import java.security.AccessControlContext;import java.security.AccessController;import java.util.*;import sun.rmi.runtime.Log;import sun.rmi.runtime.NewThreadAction;import sun.rmi.server.Dispatcher; /** * A target contains information pertaining to a remote object that * resides in this address space.  Targets are located via the * ObjectTable. */public final class Target {    /** object id for target */    private final ObjID id;    /** flag indicating whether target is subject to collection */    private final boolean permanent;    /** weak reference to remote object implementation */    private final WeakRef weakImpl;    /** dispatcher for remote object */    private volatile Dispatcher disp;    /** stub for remote object */    private final Remote stub;    /** set of clients that hold references to this target */    private final Vector refSet = new Vector();    /** table that maps client endpoints to sequence numbers */    private final Hashtable sequenceTable = new Hashtable(5);    /** access control context in which target was created */    private final AccessControlContext acc;    /** context class loader in which target was created */    private final ClassLoader ccl;    /** number of pending/executing calls */    private int callCount = 0;    /** true if this target has been removed from the object table */    private boolean removed = false;    /**     * the transport through which this target was exported and     * through which remote calls will be allowed     */    private volatile Transport exportedTransport = null;     /** number to identify next callback thread created here */    private static int nextThreadNum = 0;     /**     * Construct a Target for a remote object "impl" with     * a specific object id.     *     * If "permanent" is true, then the impl is pinned permanently     * (the impl will not be collected via distributed and/or local     * GC).  If "on" is false, than the impl is subject to     * collection. Permanent objects do not keep a server from     * exiting.     */    public Target(Remote impl, Dispatcher disp, Remote stub, ObjID id,                  boolean permanent)    {        this.weakImpl = new WeakRef(impl, ObjectTable.reapQueue);        this.disp = disp;        this.stub = stub;        this.id = id;        this.acc = AccessController.getContext();         /*         * Fix for 4149366: so that downloaded parameter types unmarshalled         * for this impl will be compatible with types known only to the         * impl class's class loader (when it's not identical to the         * exporting thread's context class loader), mark the impl's class         * loader as the loader to use as the context class loader in the         * server's dispatch thread while a call to this impl is being         * processed (unless this exporting thread's context class loader is         * a child of the impl's class loader, such as when a registry is         * exported by an application, in which case this thread's context         * class loader is preferred).         */        ClassLoader threadContextLoader =            Thread.currentThread().getContextClassLoader();        ClassLoader serverLoader = impl.getClass().getClassLoader();        if (checkLoaderAncestry(threadContextLoader, serverLoader)) {            this.ccl = threadContextLoader;        } else {            this.ccl = serverLoader;        }         this.permanent = permanent;        if (permanent) {            pinImpl();        }    }     /**     * Return true if the first class loader is a child of (or identical     * to) the second class loader.  Either loader may be "null", which is     * considered to be the parent of any non-null class loader.     *     * (utility method added for the 1.2beta4 fix for 4149366)     */    private static boolean checkLoaderAncestry(ClassLoader child,                                               ClassLoader ancestor)    {        if (ancestor == null) {            return true;        } else if (child == null) {            return false;        } else {            for (ClassLoader parent = child;                 parent != null;                 parent = parent.getParent())            {                if (parent == ancestor) {                    return true;                }            }            return false;        }    }     /** Get the stub (proxy) object for this target     */    public Remote getStub() {        return stub;    }     /**     * Returns the object endpoint for the target.     */    ObjectEndpoint getObjectEndpoint() {        return new ObjectEndpoint(id, exportedTransport);    }     /**     * Get the weak reference for the Impl of this target.     */    WeakRef getWeakImpl() {        return weakImpl;    }     /**     * Returns the dispatcher for this remote object target.     */    Dispatcher getDispatcher() {        return disp;    }     AccessControlContext getAccessControlContext() {        return acc;    }     ClassLoader getContextClassLoader() {        return ccl;    }     /**     * Get the impl for this target.     * Note: this may return null if the impl has been garbage collected.     * (currently, there is no need to make this method public)     */    Remote getImpl() {        return (Remote)weakImpl.get();    }     /**     * Returns true if the target is permanent.     */    boolean isPermanent() {        return permanent;    }     /**     * Pin impl in target. Pin the WeakRef object so it holds a strong     * reference to the object to it will not be garbage collected locally.     * This way there is a single object responsible for the weak ref     * mechanism.     */    synchronized void pinImpl() {        weakImpl.pin();    }     /**     * Unpin impl in target.  Weaken the reference to impl so that it     * can be garbage collected locally. But only if there the refSet     * is empty.  All of the weak/strong handling is in WeakRef     */    synchronized void unpinImpl() {        /* only unpin if:         * a) impl is not permanent, and         * b) impl is not already unpinned, and         * c) there are no external references (outside this         *    address space) for the impl         */        if (!permanent && refSet.isEmpty()) {            weakImpl.unpin();        }    }     /**     * Enable the transport through which remote calls to this target     * are allowed to be set if it has not already been set.     */    void setExportedTransport(Transport exportedTransport) {        if (this.exportedTransport == null) {            this.exportedTransport = exportedTransport;        }    }     /**     * Add an endpoint to the remembered set.  Also adds a notifier     * to call back if the address space associated with the endpoint     * dies.     */    synchronized void referenced(long sequenceNum, VMID vmid) {        // check sequence number for vmid        SequenceEntry entry = (SequenceEntry) sequenceTable.get(vmid);        if (entry == null) {            sequenceTable.put(vmid, new SequenceEntry(sequenceNum));        } else if (entry.sequenceNum < sequenceNum) {            entry.update(sequenceNum);        } else  {            // late dirty call; ignore.            return;        }         if (!refSet.contains(vmid)) {            /*             * A Target must be pinned while its refSet is not empty.  It may             * have become unpinned if external LiveRefs only existed in             * serialized form for some period of time, or if a client failed             * to renew its lease due to a transient network failure.  So,             * make sure that it is pinned here; this fixes bugid 4069644.             */            pinImpl();            if (getImpl() == null)      // too late if impl was collected                return;             if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {                DGCImpl.dgcLog.log(Log.VERBOSE, "add to dirty set: " + vmid);            }             refSet.addElement(vmid);             DGCImpl.getDGCImpl().registerTarget(vmid, this);        }    }     /**     * Remove endpoint from remembered set.  If set becomes empty,     * remove server from Transport's object table.     */    synchronized void unreferenced(long sequenceNum, VMID vmid, boolean strong)    {        // check sequence number for vmid        SequenceEntry entry = (SequenceEntry) sequenceTable.get(vmid);        if (entry == null || entry.sequenceNum > sequenceNum) {            // late clean call; ignore            return;        } else if (strong) {            // strong clean call; retain sequenceNum            entry.retain(sequenceNum);        } else if (entry.keep == false) {            // get rid of sequence number            sequenceTable.remove(vmid);        }         if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {            DGCImpl.dgcLog.log(Log.VERBOSE, "remove from dirty set: " + vmid);        }         refSetRemove(vmid);    }     /**     * Remove endpoint from the reference set.     */    synchronized private void refSetRemove(VMID vmid) {        // remove notification request        DGCImpl.getDGCImpl().unregisterTarget(vmid, this);         if (refSet.removeElement(vmid) && refSet.isEmpty()) {            // reference set is empty, so server can be garbage collected.            // remove object from table.            if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {                DGCImpl.dgcLog.log(Log.VERBOSE,                    "reference set is empty: target = " + this);            }             /*             * If the remote object implements the Unreferenced interface,             * invoke its unreferenced callback in a separate thread.             */            Remote obj = getImpl();            if (obj instanceof Unreferenced) {                final Unreferenced unrefObj = (Unreferenced) obj;                final Thread t =                    java.security.AccessController.doPrivileged(                        new NewThreadAction(new Runnable() {                            public void run() {                                unrefObj.unreferenced();                            }                        }, "Unreferenced-" + nextThreadNum++, false, true));                // REMIND: access to nextThreadNum not synchronized; you care?                /*                 * We must manually set the context class loader appropriately                 * for threads that may invoke user code (see bugid 4171278).                 */                java.security.AccessController.doPrivileged(                    new java.security.PrivilegedAction<Void>() {                        public Void run() {                        t.setContextClassLoader(ccl);                        return null;                    }                });                 t.start();            }             unpinImpl();        }    }     /**     * Mark this target as not accepting new calls if any of the     * following conditions exist: a) the force parameter is true,     * b) the target's call count is zero, or c) the object is already     * not accepting calls. Returns true if target is marked as not     * accepting new calls; returns false otherwise.     */    synchronized boolean unexport(boolean force) {         if ((force == true) || (callCount == 0) || (disp == null)) {            disp = null;            /*             * Fix for 4331349: unpin object so that it may be gc'd.             * Also, unregister all vmids referencing this target             * so target can be gc'd.             */            unpinImpl();            DGCImpl dgc = DGCImpl.getDGCImpl();            Enumeration enum_ = refSet.elements();            while (enum_.hasMoreElements()) {                VMID vmid = (VMID) enum_.nextElement();                dgc.unregisterTarget(vmid, this);            }            return true;        } else {            return false;        }    }     /**     * Mark this target as having been removed from the object table.     */    synchronized void markRemoved() {        if (!(!removed)) { throw new AssertionError(); }         removed = true;        if (!permanent && callCount == 0) {            ObjectTable.decrementKeepAliveCount();        }         if (exportedTransport != null) {            exportedTransport.targetUnexported();        }    }     /**     * Increment call count.     */    synchronized void incrementCallCount() throws NoSuchObjectException {         if (disp != null) {            callCount ++;        } else {            throw new NoSuchObjectException("object not accepting new calls");        }    }     /**     * Decrement call count.     */    synchronized void decrementCallCount() {         if (--callCount < 0) {            throw new Error("internal error: call count less than zero");        }         /*         * The "keep-alive count" is the number of non-permanent remote         * objects that are either in the object table or still have calls         * in progress.  Therefore, this state change may affect the         * keep-alive count: if this target is for a non-permanent remote         * object that has been removed from the object table and now has a         * call count of zero, it needs to be decremented.         */        if (!permanent && removed && callCount == 0) {            ObjectTable.decrementKeepAliveCount();        }    }     /**     * Returns true if remembered set is empty; otherwise returns     * false     */    boolean isEmpty() {        return refSet.isEmpty();    }     /**     * This method is called if the address space associated with the     * vmid dies.  In that case, the vmid should be removed     * from the reference set.     */    synchronized public void vmidDead(VMID vmid) {        if (DGCImpl.dgcLog.isLoggable(Log.BRIEF)) {            DGCImpl.dgcLog.log(Log.BRIEF, "removing endpoint " +                            vmid + " from reference set");        }         sequenceTable.remove(vmid);        refSetRemove(vmid);    }} class SequenceEntry {    long sequenceNum;    boolean keep;     SequenceEntry(long sequenceNum) {        this.sequenceNum = sequenceNum;        keep = false;    }     void retain(long sequenceNum) {        this.sequenceNum = sequenceNum;        keep = true;    }     void update(long sequenceNum) {        this.sequenceNum = sequenceNum;    }}