Path: blob/master/src/java.rmi/share/classes/sun/rmi/transport/Target.java
41154 views
/*1* Copyright (c) 1996, 2021, 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 */61@SuppressWarnings("removal")62private final AccessControlContext acc;63/** context class loader in which target was created */64private final ClassLoader ccl;65/** number of pending/executing calls */66private int callCount = 0;67/** true if this target has been removed from the object table */68private boolean removed = false;69/**70* the transport through which this target was exported and71* through which remote calls will be allowed72*/73private volatile Transport exportedTransport = null;7475/** number to identify next callback thread created here */76private static int nextThreadNum = 0;7778/**79* Construct a Target for a remote object "impl" with80* a specific object id.81*82* If "permanent" is true, then the impl is pinned permanently83* (the impl will not be collected via distributed and/or local84* GC). If "on" is false, than the impl is subject to85* collection. Permanent objects do not keep a server from86* exiting.87*/88@SuppressWarnings("removal")89public Target(Remote impl, Dispatcher disp, Remote stub, ObjID id,90boolean permanent)91{92this.weakImpl = new WeakRef(impl, ObjectTable.reapQueue);93this.disp = disp;94this.stub = stub;95this.id = id;96this.acc = AccessController.getContext();9798/*99* Fix for 4149366: so that downloaded parameter types unmarshalled100* for this impl will be compatible with types known only to the101* impl class's class loader (when it's not identical to the102* exporting thread's context class loader), mark the impl's class103* loader as the loader to use as the context class loader in the104* server's dispatch thread while a call to this impl is being105* processed (unless this exporting thread's context class loader is106* a child of the impl's class loader, such as when a registry is107* exported by an application, in which case this thread's context108* class loader is preferred).109*/110ClassLoader threadContextLoader =111Thread.currentThread().getContextClassLoader();112ClassLoader serverLoader = impl.getClass().getClassLoader();113if (checkLoaderAncestry(threadContextLoader, serverLoader)) {114this.ccl = threadContextLoader;115} else {116this.ccl = serverLoader;117}118119this.permanent = permanent;120if (permanent) {121pinImpl();122}123}124125/**126* Return true if the first class loader is a child of (or identical127* to) the second class loader. Either loader may be "null", which is128* considered to be the parent of any non-null class loader.129*130* (utility method added for the 1.2beta4 fix for 4149366)131*/132private static boolean checkLoaderAncestry(ClassLoader child,133ClassLoader ancestor)134{135if (ancestor == null) {136return true;137} else if (child == null) {138return false;139} else {140for (ClassLoader parent = child;141parent != null;142parent = parent.getParent())143{144if (parent == ancestor) {145return true;146}147}148return false;149}150}151152/** Get the stub (proxy) object for this target153*/154public Remote getStub() {155return stub;156}157158/**159* Returns the object endpoint for the target.160*/161ObjectEndpoint getObjectEndpoint() {162return new ObjectEndpoint(id, exportedTransport);163}164165/**166* Get the weak reference for the Impl of this target.167*/168WeakRef getWeakImpl() {169return weakImpl;170}171172/**173* Returns the dispatcher for this remote object target.174*/175Dispatcher getDispatcher() {176return disp;177}178179@SuppressWarnings("removal")180AccessControlContext getAccessControlContext() {181return acc;182}183184ClassLoader getContextClassLoader() {185return ccl;186}187188/**189* Get the impl for this target.190* Note: this may return null if the impl has been garbage collected.191* (currently, there is no need to make this method public)192*/193Remote getImpl() {194return (Remote)weakImpl.get();195}196197/**198* Returns true if the target is permanent.199*/200boolean isPermanent() {201return permanent;202}203204/**205* Pin impl in target. Pin the WeakRef object so it holds a strong206* reference to the object to it will not be garbage collected locally.207* This way there is a single object responsible for the weak ref208* mechanism.209*/210synchronized void pinImpl() {211weakImpl.pin();212}213214/**215* Unpin impl in target. Weaken the reference to impl so that it216* can be garbage collected locally. But only if there the refSet217* is empty. All of the weak/strong handling is in WeakRef218*/219synchronized void unpinImpl() {220/* only unpin if:221* a) impl is not permanent, and222* b) impl is not already unpinned, and223* c) there are no external references (outside this224* address space) for the impl225*/226if (!permanent && refSet.isEmpty()) {227weakImpl.unpin();228}229}230231/**232* Enable the transport through which remote calls to this target233* are allowed to be set if it has not already been set.234*/235void setExportedTransport(Transport exportedTransport) {236if (this.exportedTransport == null) {237this.exportedTransport = exportedTransport;238}239}240241/**242* Add an endpoint to the remembered set. Also adds a notifier243* to call back if the address space associated with the endpoint244* dies.245*/246synchronized void referenced(long sequenceNum, VMID vmid) {247// check sequence number for vmid248SequenceEntry entry = sequenceTable.get(vmid);249if (entry == null) {250sequenceTable.put(vmid, new SequenceEntry(sequenceNum));251} else if (entry.sequenceNum < sequenceNum) {252entry.update(sequenceNum);253} else {254// late dirty call; ignore.255return;256}257258if (!refSet.contains(vmid)) {259/*260* A Target must be pinned while its refSet is not empty. It may261* have become unpinned if external LiveRefs only existed in262* serialized form for some period of time, or if a client failed263* to renew its lease due to a transient network failure. So,264* make sure that it is pinned here; this fixes bugid 4069644.265*/266pinImpl();267if (getImpl() == null) // too late if impl was collected268return;269270if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {271DGCImpl.dgcLog.log(Log.VERBOSE, "add to dirty set: " + vmid);272}273274refSet.addElement(vmid);275276DGCImpl.getDGCImpl().registerTarget(vmid, this);277}278}279280/**281* Remove endpoint from remembered set. If set becomes empty,282* remove server from Transport's object table.283*/284synchronized void unreferenced(long sequenceNum, VMID vmid, boolean strong)285{286// check sequence number for vmid287SequenceEntry entry = sequenceTable.get(vmid);288if (entry == null || entry.sequenceNum > sequenceNum) {289// late clean call; ignore290return;291} else if (strong) {292// strong clean call; retain sequenceNum293entry.retain(sequenceNum);294} else if (entry.keep == false) {295// get rid of sequence number296sequenceTable.remove(vmid);297}298299if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {300DGCImpl.dgcLog.log(Log.VERBOSE, "remove from dirty set: " + vmid);301}302303refSetRemove(vmid);304}305306/**307* Remove endpoint from the reference set.308*/309@SuppressWarnings("removal")310synchronized private void refSetRemove(VMID vmid) {311// remove notification request312DGCImpl.getDGCImpl().unregisterTarget(vmid, this);313314if (refSet.removeElement(vmid) && refSet.isEmpty()) {315// reference set is empty, so server can be garbage collected.316// remove object from table.317if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {318DGCImpl.dgcLog.log(Log.VERBOSE,319"reference set is empty: target = " + this);320}321322/*323* If the remote object implements the Unreferenced interface,324* invoke its unreferenced callback in a separate thread.325*/326Remote obj = getImpl();327if (obj instanceof Unreferenced) {328final Unreferenced unrefObj = (Unreferenced) obj;329AccessController.doPrivileged(330new NewThreadAction(() -> {331Thread.currentThread().setContextClassLoader(ccl);332AccessController.doPrivileged((PrivilegedAction<Void>) () -> {333unrefObj.unreferenced();334return null;335}, acc);336}, "Unreferenced-" + nextThreadNum++, false, true)).start();337// REMIND: access to nextThreadNum not synchronized; you care?338}339340unpinImpl();341}342}343344/**345* Mark this target as not accepting new calls if any of the346* following conditions exist: a) the force parameter is true,347* b) the target's call count is zero, or c) the object is already348* not accepting calls. Returns true if target is marked as not349* accepting new calls; returns false otherwise.350*/351synchronized boolean unexport(boolean force) {352353if ((force == true) || (callCount == 0) || (disp == null)) {354disp = null;355/*356* Fix for 4331349: unpin object so that it may be gc'd.357* Also, unregister all vmids referencing this target358* so target can be gc'd.359*/360unpinImpl();361DGCImpl dgc = DGCImpl.getDGCImpl();362Enumeration<VMID> enum_ = refSet.elements();363while (enum_.hasMoreElements()) {364VMID vmid = enum_.nextElement();365dgc.unregisterTarget(vmid, this);366}367return true;368} else {369return false;370}371}372373/**374* Mark this target as having been removed from the object table.375*/376synchronized void markRemoved() {377if (!(!removed)) { throw new AssertionError(); }378379removed = true;380if (!permanent && callCount == 0) {381ObjectTable.decrementKeepAliveCount();382}383384if (exportedTransport != null) {385exportedTransport.targetUnexported();386}387}388389/**390* Increment call count.391*/392synchronized void incrementCallCount() throws NoSuchObjectException {393394if (disp != null) {395callCount ++;396} else {397throw new NoSuchObjectException("object not accepting new calls");398}399}400401/**402* Decrement call count.403*/404synchronized void decrementCallCount() {405406if (--callCount < 0) {407throw new Error("internal error: call count less than zero");408}409410/*411* The "keep-alive count" is the number of non-permanent remote412* objects that are either in the object table or still have calls413* in progress. Therefore, this state change may affect the414* keep-alive count: if this target is for a non-permanent remote415* object that has been removed from the object table and now has a416* call count of zero, it needs to be decremented.417*/418if (!permanent && removed && callCount == 0) {419ObjectTable.decrementKeepAliveCount();420}421}422423/**424* Returns true if remembered set is empty; otherwise returns425* false426*/427boolean isEmpty() {428return refSet.isEmpty();429}430431/**432* This method is called if the address space associated with the433* vmid dies. In that case, the vmid should be removed434* from the reference set.435*/436synchronized public void vmidDead(VMID vmid) {437if (DGCImpl.dgcLog.isLoggable(Log.BRIEF)) {438DGCImpl.dgcLog.log(Log.BRIEF, "removing endpoint " +439vmid + " from reference set");440}441442sequenceTable.remove(vmid);443refSetRemove(vmid);444}445}446447class SequenceEntry {448long sequenceNum;449boolean keep;450451SequenceEntry(long sequenceNum) {452this.sequenceNum = sequenceNum;453keep = false;454}455456void retain(long sequenceNum) {457this.sequenceNum = sequenceNum;458keep = true;459}460461void update(long sequenceNum) {462this.sequenceNum = sequenceNum;463}464}465466467