/*
 * Copyright (c) 1996, 2017, 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.security.PrivilegedAction;
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.
/** * 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
/** object id for target */
private final ObjID id;
flag indicating whether target is subject to collection
/** flag indicating whether target is subject to collection */
private final boolean permanent;
weak reference to remote object implementation
/** weak reference to remote object implementation */
private final WeakRef weakImpl;
dispatcher for remote object
/** dispatcher for remote object */
private volatile Dispatcher disp;
stub for remote object
/** stub for remote object */
private final Remote stub;
set of clients that hold references to this target
/** set of clients that hold references to this target */
private final Vector refSet = new Vector();
table that maps client endpoints to sequence numbers
/** table that maps client endpoints to sequence numbers */
private final Hashtable sequenceTable = new Hashtable(5);
access control context in which target was created
/** access control context in which target was created */
private final AccessControlContext acc;
context class loader in which target was created
/** context class loader in which target was created */
private final ClassLoader ccl;
number of pending/executing calls
/** number of pending/executing calls */
private int callCount = 0;
true if this target has been removed from the object table
/** 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
/** * 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
/** 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.
/** * 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)
/** * 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
/** Get the stub (proxy) object for this target */
public Remote getStub() { return stub; }
Returns the object endpoint for the target.
/** * Returns the object endpoint for the target. */
ObjectEndpoint getObjectEndpoint() { return new ObjectEndpoint(id, exportedTransport); }
Get the weak reference for the Impl of this target.
/** * Get the weak reference for the Impl of this target. */
WeakRef getWeakImpl() { return weakImpl; }
Returns the dispatcher for this remote object target.
/** * 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)
/** * 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.
/** * 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.
/** * 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
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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; AccessController.doPrivileged( new NewThreadAction(new Runnable() { @Override public void run() { Thread.currentThread().setContextClassLoader(ccl); AccessController.doPrivileged(new PrivilegedAction<Void>() { @Override public Void run() { unrefObj.unreferenced(); return null; } }, acc); } }, "Unreferenced-" + nextThreadNum++, false, true)).start(); // REMIND: access to nextThreadNum not synchronized; you care? } 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.
/** * 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.
/** * 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.
/** * Increment call count. */
synchronized void incrementCallCount() throws NoSuchObjectException { if (disp != null) { callCount ++; } else { throw new NoSuchObjectException("object not accepting new calls"); } }
Decrement call count.
/** * 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
/** * 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.
/** * 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; } }