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