Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/rmi/transport/Target.java
38831 views
/*1* Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package sun.rmi.transport;2526import java.rmi.Remote;27import java.rmi.NoSuchObjectException;28import java.rmi.dgc.VMID;29import java.rmi.server.ObjID;30import java.rmi.server.Unreferenced;31import java.security.AccessControlContext;32import java.security.AccessController;33import java.security.PrivilegedAction;34import java.util.*;35import sun.rmi.runtime.Log;36import sun.rmi.runtime.NewThreadAction;37import sun.rmi.server.Dispatcher;3839/**40* A target contains information pertaining to a remote object that41* resides in this address space. Targets are located via the42* ObjectTable.43*/44public final class Target {45/** object id for target */46private final ObjID id;47/** flag indicating whether target is subject to collection */48private final boolean permanent;49/** weak reference to remote object implementation */50private final WeakRef weakImpl;51/** dispatcher for remote object */52private volatile Dispatcher disp;53/** stub for remote object */54private final Remote stub;55/** set of clients that hold references to this target */56private final Vector<VMID> refSet = new Vector<>();57/** table that maps client endpoints to sequence numbers */58private final Hashtable<VMID, SequenceEntry> sequenceTable =59new Hashtable<>(5);60/** access control context in which target was created */61private final AccessControlContext acc;62/** context class loader in which target was created */63private final ClassLoader ccl;64/** number of pending/executing calls */65private int callCount = 0;66/** true if this target has been removed from the object table */67private boolean removed = false;68/**69* the transport through which this target was exported and70* through which remote calls will be allowed71*/72private volatile Transport exportedTransport = null;7374/** number to identify next callback thread created here */75private static int nextThreadNum = 0;7677/**78* Construct a Target for a remote object "impl" with79* a specific object id.80*81* If "permanent" is true, then the impl is pinned permanently82* (the impl will not be collected via distributed and/or local83* GC). If "on" is false, than the impl is subject to84* collection. Permanent objects do not keep a server from85* exiting.86*/87public Target(Remote impl, Dispatcher disp, Remote stub, ObjID id,88boolean permanent)89{90this.weakImpl = new WeakRef(impl, ObjectTable.reapQueue);91this.disp = disp;92this.stub = stub;93this.id = id;94this.acc = AccessController.getContext();9596/*97* Fix for 4149366: so that downloaded parameter types unmarshalled98* for this impl will be compatible with types known only to the99* impl class's class loader (when it's not identical to the100* exporting thread's context class loader), mark the impl's class101* loader as the loader to use as the context class loader in the102* server's dispatch thread while a call to this impl is being103* processed (unless this exporting thread's context class loader is104* a child of the impl's class loader, such as when a registry is105* exported by an application, in which case this thread's context106* class loader is preferred).107*/108ClassLoader threadContextLoader =109Thread.currentThread().getContextClassLoader();110ClassLoader serverLoader = impl.getClass().getClassLoader();111if (checkLoaderAncestry(threadContextLoader, serverLoader)) {112this.ccl = threadContextLoader;113} else {114this.ccl = serverLoader;115}116117this.permanent = permanent;118if (permanent) {119pinImpl();120}121}122123/**124* Return true if the first class loader is a child of (or identical125* to) the second class loader. Either loader may be "null", which is126* considered to be the parent of any non-null class loader.127*128* (utility method added for the 1.2beta4 fix for 4149366)129*/130private static boolean checkLoaderAncestry(ClassLoader child,131ClassLoader ancestor)132{133if (ancestor == null) {134return true;135} else if (child == null) {136return false;137} else {138for (ClassLoader parent = child;139parent != null;140parent = parent.getParent())141{142if (parent == ancestor) {143return true;144}145}146return false;147}148}149150/** Get the stub (proxy) object for this target151*/152public Remote getStub() {153return stub;154}155156/**157* Returns the object endpoint for the target.158*/159ObjectEndpoint getObjectEndpoint() {160return new ObjectEndpoint(id, exportedTransport);161}162163/**164* Get the weak reference for the Impl of this target.165*/166WeakRef getWeakImpl() {167return weakImpl;168}169170/**171* Returns the dispatcher for this remote object target.172*/173Dispatcher getDispatcher() {174return disp;175}176177AccessControlContext getAccessControlContext() {178return acc;179}180181ClassLoader getContextClassLoader() {182return ccl;183}184185/**186* Get the impl for this target.187* Note: this may return null if the impl has been garbage collected.188* (currently, there is no need to make this method public)189*/190Remote getImpl() {191return (Remote)weakImpl.get();192}193194/**195* Returns true if the target is permanent.196*/197boolean isPermanent() {198return permanent;199}200201/**202* Pin impl in target. Pin the WeakRef object so it holds a strong203* reference to the object to it will not be garbage collected locally.204* This way there is a single object responsible for the weak ref205* mechanism.206*/207synchronized void pinImpl() {208weakImpl.pin();209}210211/**212* Unpin impl in target. Weaken the reference to impl so that it213* can be garbage collected locally. But only if there the refSet214* is empty. All of the weak/strong handling is in WeakRef215*/216synchronized void unpinImpl() {217/* only unpin if:218* a) impl is not permanent, and219* b) impl is not already unpinned, and220* c) there are no external references (outside this221* address space) for the impl222*/223if (!permanent && refSet.isEmpty()) {224weakImpl.unpin();225}226}227228/**229* Enable the transport through which remote calls to this target230* are allowed to be set if it has not already been set.231*/232void setExportedTransport(Transport exportedTransport) {233if (this.exportedTransport == null) {234this.exportedTransport = exportedTransport;235}236}237238/**239* Add an endpoint to the remembered set. Also adds a notifier240* to call back if the address space associated with the endpoint241* dies.242*/243synchronized void referenced(long sequenceNum, VMID vmid) {244// check sequence number for vmid245SequenceEntry entry = sequenceTable.get(vmid);246if (entry == null) {247sequenceTable.put(vmid, new SequenceEntry(sequenceNum));248} else if (entry.sequenceNum < sequenceNum) {249entry.update(sequenceNum);250} else {251// late dirty call; ignore.252return;253}254255if (!refSet.contains(vmid)) {256/*257* A Target must be pinned while its refSet is not empty. It may258* have become unpinned if external LiveRefs only existed in259* serialized form for some period of time, or if a client failed260* to renew its lease due to a transient network failure. So,261* make sure that it is pinned here; this fixes bugid 4069644.262*/263pinImpl();264if (getImpl() == null) // too late if impl was collected265return;266267if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {268DGCImpl.dgcLog.log(Log.VERBOSE, "add to dirty set: " + vmid);269}270271refSet.addElement(vmid);272273DGCImpl.getDGCImpl().registerTarget(vmid, this);274}275}276277/**278* Remove endpoint from remembered set. If set becomes empty,279* remove server from Transport's object table.280*/281synchronized void unreferenced(long sequenceNum, VMID vmid, boolean strong)282{283// check sequence number for vmid284SequenceEntry entry = sequenceTable.get(vmid);285if (entry == null || entry.sequenceNum > sequenceNum) {286// late clean call; ignore287return;288} else if (strong) {289// strong clean call; retain sequenceNum290entry.retain(sequenceNum);291} else if (entry.keep == false) {292// get rid of sequence number293sequenceTable.remove(vmid);294}295296if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {297DGCImpl.dgcLog.log(Log.VERBOSE, "remove from dirty set: " + vmid);298}299300refSetRemove(vmid);301}302303/**304* Remove endpoint from the reference set.305*/306synchronized private void refSetRemove(VMID vmid) {307// remove notification request308DGCImpl.getDGCImpl().unregisterTarget(vmid, this);309310if (refSet.removeElement(vmid) && refSet.isEmpty()) {311// reference set is empty, so server can be garbage collected.312// remove object from table.313if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {314DGCImpl.dgcLog.log(Log.VERBOSE,315"reference set is empty: target = " + this);316}317318/*319* If the remote object implements the Unreferenced interface,320* invoke its unreferenced callback in a separate thread.321*/322Remote obj = getImpl();323if (obj instanceof Unreferenced) {324final Unreferenced unrefObj = (Unreferenced) obj;325AccessController.doPrivileged(326new NewThreadAction(() -> {327Thread.currentThread().setContextClassLoader(ccl);328AccessController.doPrivileged((PrivilegedAction<Void>) () -> {329unrefObj.unreferenced();330return null;331}, acc);332}, "Unreferenced-" + nextThreadNum++, false, true)).start();333// REMIND: access to nextThreadNum not synchronized; you care?334}335336unpinImpl();337}338}339340/**341* Mark this target as not accepting new calls if any of the342* following conditions exist: a) the force parameter is true,343* b) the target's call count is zero, or c) the object is already344* not accepting calls. Returns true if target is marked as not345* accepting new calls; returns false otherwise.346*/347synchronized boolean unexport(boolean force) {348349if ((force == true) || (callCount == 0) || (disp == null)) {350disp = null;351/*352* Fix for 4331349: unpin object so that it may be gc'd.353* Also, unregister all vmids referencing this target354* so target can be gc'd.355*/356unpinImpl();357DGCImpl dgc = DGCImpl.getDGCImpl();358Enumeration<VMID> enum_ = refSet.elements();359while (enum_.hasMoreElements()) {360VMID vmid = enum_.nextElement();361dgc.unregisterTarget(vmid, this);362}363return true;364} else {365return false;366}367}368369/**370* Mark this target as having been removed from the object table.371*/372synchronized void markRemoved() {373if (!(!removed)) { throw new AssertionError(); }374375removed = true;376if (!permanent && callCount == 0) {377ObjectTable.decrementKeepAliveCount();378}379380if (exportedTransport != null) {381exportedTransport.targetUnexported();382}383}384385/**386* Increment call count.387*/388synchronized void incrementCallCount() throws NoSuchObjectException {389390if (disp != null) {391callCount ++;392} else {393throw new NoSuchObjectException("object not accepting new calls");394}395}396397/**398* Decrement call count.399*/400synchronized void decrementCallCount() {401402if (--callCount < 0) {403throw new Error("internal error: call count less than zero");404}405406/*407* The "keep-alive count" is the number of non-permanent remote408* objects that are either in the object table or still have calls409* in progress. Therefore, this state change may affect the410* keep-alive count: if this target is for a non-permanent remote411* object that has been removed from the object table and now has a412* call count of zero, it needs to be decremented.413*/414if (!permanent && removed && callCount == 0) {415ObjectTable.decrementKeepAliveCount();416}417}418419/**420* Returns true if remembered set is empty; otherwise returns421* false422*/423boolean isEmpty() {424return refSet.isEmpty();425}426427/**428* This method is called if the address space associated with the429* vmid dies. In that case, the vmid should be removed430* from the reference set.431*/432synchronized public void vmidDead(VMID vmid) {433if (DGCImpl.dgcLog.isLoggable(Log.BRIEF)) {434DGCImpl.dgcLog.log(Log.BRIEF, "removing endpoint " +435vmid + " from reference set");436}437438sequenceTable.remove(vmid);439refSetRemove(vmid);440}441}442443class SequenceEntry {444long sequenceNum;445boolean keep;446447SequenceEntry(long sequenceNum) {448this.sequenceNum = sequenceNum;449keep = false;450}451452void retain(long sequenceNum) {453this.sequenceNum = sequenceNum;454keep = true;455}456457void update(long sequenceNum) {458this.sequenceNum = sequenceNum;459}460}461462463