1 /*
2  * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 package sun.rmi.transport;
26 
27 import java.rmi.Remote;
28 import java.rmi.NoSuchObjectException;
29 import java.rmi.dgc.VMID;
30 import java.rmi.server.ObjID;
31 import java.rmi.server.Unreferenced;
32 import java.security.AccessControlContext;
33 import java.security.AccessController;
34 import java.security.PrivilegedAction;
35 import java.util.*;
36 import sun.rmi.runtime.Log;
37 import sun.rmi.runtime.NewThreadAction;
38 import sun.rmi.server.Dispatcher;
39 
40 /**
41  * A target contains information pertaining to a remote object that
42  * resides in this address space.  Targets are located via the
43  * ObjectTable.
44  */
45 public final class Target {
46     /** object id for target */
47     private final ObjID id;
48     /** flag indicating whether target is subject to collection */
49     private final boolean permanent;
50     /** weak reference to remote object implementation */
51     private final WeakRef weakImpl;
52     /** dispatcher for remote object */
53     private volatile Dispatcher disp;
54     /** stub for remote object */
55     private final Remote stub;
56     /** set of clients that hold references to this target */
57     private final Vector<VMID> refSet = new Vector<>();
58     /** table that maps client endpoints to sequence numbers */
59     private final Hashtable<VMID, SequenceEntry> sequenceTable =
60         new Hashtable<>(5);
61     /** access control context in which target was created */
62     private final AccessControlContext acc;
63     /** context class loader in which target was created */
64     private final ClassLoader ccl;
65     /** number of pending/executing calls */
66     private int callCount = 0;
67     /** true if this target has been removed from the object table */
68     private boolean removed = false;
69     /**
70      * the transport through which this target was exported and
71      * through which remote calls will be allowed
72      */
73     private volatile Transport exportedTransport = null;
74 
75     /** number to identify next callback thread created here */
76     private static int nextThreadNum = 0;
77 
78     /**
79      * Construct a Target for a remote object "impl" with
80      * a specific object id.
81      *
82      * If "permanent" is true, then the impl is pinned permanently
83      * (the impl will not be collected via distributed and/or local
84      * GC).  If "on" is false, than the impl is subject to
85      * collection. Permanent objects do not keep a server from
86      * exiting.
87      */
Target(Remote impl, Dispatcher disp, Remote stub, ObjID id, boolean permanent)88     public Target(Remote impl, Dispatcher disp, Remote stub, ObjID id,
89                   boolean permanent)
90     {
91         this.weakImpl = new WeakRef(impl, ObjectTable.reapQueue);
92         this.disp = disp;
93         this.stub = stub;
94         this.id = id;
95         this.acc = AccessController.getContext();
96 
97         /*
98          * Fix for 4149366: so that downloaded parameter types unmarshalled
99          * for this impl will be compatible with types known only to the
100          * impl class's class loader (when it's not identical to the
101          * exporting thread's context class loader), mark the impl's class
102          * loader as the loader to use as the context class loader in the
103          * server's dispatch thread while a call to this impl is being
104          * processed (unless this exporting thread's context class loader is
105          * a child of the impl's class loader, such as when a registry is
106          * exported by an application, in which case this thread's context
107          * class loader is preferred).
108          */
109         ClassLoader threadContextLoader =
110             Thread.currentThread().getContextClassLoader();
111         ClassLoader serverLoader = impl.getClass().getClassLoader();
112         if (checkLoaderAncestry(threadContextLoader, serverLoader)) {
113             this.ccl = threadContextLoader;
114         } else {
115             this.ccl = serverLoader;
116         }
117 
118         this.permanent = permanent;
119         if (permanent) {
120             pinImpl();
121         }
122     }
123 
124     /**
125      * Return true if the first class loader is a child of (or identical
126      * to) the second class loader.  Either loader may be "null", which is
127      * considered to be the parent of any non-null class loader.
128      *
129      * (utility method added for the 1.2beta4 fix for 4149366)
130      */
checkLoaderAncestry(ClassLoader child, ClassLoader ancestor)131     private static boolean checkLoaderAncestry(ClassLoader child,
132                                                ClassLoader ancestor)
133     {
134         if (ancestor == null) {
135             return true;
136         } else if (child == null) {
137             return false;
138         } else {
139             for (ClassLoader parent = child;
140                  parent != null;
141                  parent = parent.getParent())
142             {
143                 if (parent == ancestor) {
144                     return true;
145                 }
146             }
147             return false;
148         }
149     }
150 
151     /** Get the stub (proxy) object for this target
152      */
getStub()153     public Remote getStub() {
154         return stub;
155     }
156 
157     /**
158      * Returns the object endpoint for the target.
159      */
getObjectEndpoint()160     ObjectEndpoint getObjectEndpoint() {
161         return new ObjectEndpoint(id, exportedTransport);
162     }
163 
164     /**
165      * Get the weak reference for the Impl of this target.
166      */
getWeakImpl()167     WeakRef getWeakImpl() {
168         return weakImpl;
169     }
170 
171     /**
172      * Returns the dispatcher for this remote object target.
173      */
getDispatcher()174     Dispatcher getDispatcher() {
175         return disp;
176     }
177 
getAccessControlContext()178     AccessControlContext getAccessControlContext() {
179         return acc;
180     }
181 
getContextClassLoader()182     ClassLoader getContextClassLoader() {
183         return ccl;
184     }
185 
186     /**
187      * Get the impl for this target.
188      * Note: this may return null if the impl has been garbage collected.
189      * (currently, there is no need to make this method public)
190      */
getImpl()191     Remote getImpl() {
192         return (Remote)weakImpl.get();
193     }
194 
195     /**
196      * Returns true if the target is permanent.
197      */
isPermanent()198     boolean isPermanent() {
199         return permanent;
200     }
201 
202     /**
203      * Pin impl in target. Pin the WeakRef object so it holds a strong
204      * reference to the object to it will not be garbage collected locally.
205      * This way there is a single object responsible for the weak ref
206      * mechanism.
207      */
pinImpl()208     synchronized void pinImpl() {
209         weakImpl.pin();
210     }
211 
212     /**
213      * Unpin impl in target.  Weaken the reference to impl so that it
214      * can be garbage collected locally. But only if there the refSet
215      * is empty.  All of the weak/strong handling is in WeakRef
216      */
unpinImpl()217     synchronized void unpinImpl() {
218         /* only unpin if:
219          * a) impl is not permanent, and
220          * b) impl is not already unpinned, and
221          * c) there are no external references (outside this
222          *    address space) for the impl
223          */
224         if (!permanent && refSet.isEmpty()) {
225             weakImpl.unpin();
226         }
227     }
228 
229     /**
230      * Enable the transport through which remote calls to this target
231      * are allowed to be set if it has not already been set.
232      */
setExportedTransport(Transport exportedTransport)233     void setExportedTransport(Transport exportedTransport) {
234         if (this.exportedTransport == null) {
235             this.exportedTransport = exportedTransport;
236         }
237     }
238 
239     /**
240      * Add an endpoint to the remembered set.  Also adds a notifier
241      * to call back if the address space associated with the endpoint
242      * dies.
243      */
referenced(long sequenceNum, VMID vmid)244     synchronized void referenced(long sequenceNum, VMID vmid) {
245         // check sequence number for vmid
246         SequenceEntry entry = sequenceTable.get(vmid);
247         if (entry == null) {
248             sequenceTable.put(vmid, new SequenceEntry(sequenceNum));
249         } else if (entry.sequenceNum < sequenceNum) {
250             entry.update(sequenceNum);
251         } else  {
252             // late dirty call; ignore.
253             return;
254         }
255 
256         if (!refSet.contains(vmid)) {
257             /*
258              * A Target must be pinned while its refSet is not empty.  It may
259              * have become unpinned if external LiveRefs only existed in
260              * serialized form for some period of time, or if a client failed
261              * to renew its lease due to a transient network failure.  So,
262              * make sure that it is pinned here; this fixes bugid 4069644.
263              */
264             pinImpl();
265             if (getImpl() == null)      // too late if impl was collected
266                 return;
267 
268             if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
269                 DGCImpl.dgcLog.log(Log.VERBOSE, "add to dirty set: " + vmid);
270             }
271 
272             refSet.addElement(vmid);
273 
274             DGCImpl.getDGCImpl().registerTarget(vmid, this);
275         }
276     }
277 
278     /**
279      * Remove endpoint from remembered set.  If set becomes empty,
280      * remove server from Transport's object table.
281      */
unreferenced(long sequenceNum, VMID vmid, boolean strong)282     synchronized void unreferenced(long sequenceNum, VMID vmid, boolean strong)
283     {
284         // check sequence number for vmid
285         SequenceEntry entry = sequenceTable.get(vmid);
286         if (entry == null || entry.sequenceNum > sequenceNum) {
287             // late clean call; ignore
288             return;
289         } else if (strong) {
290             // strong clean call; retain sequenceNum
291             entry.retain(sequenceNum);
292         } else if (entry.keep == false) {
293             // get rid of sequence number
294             sequenceTable.remove(vmid);
295         }
296 
297         if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
298             DGCImpl.dgcLog.log(Log.VERBOSE, "remove from dirty set: " + vmid);
299         }
300 
301         refSetRemove(vmid);
302     }
303 
304     /**
305      * Remove endpoint from the reference set.
306      */
refSetRemove(VMID vmid)307     synchronized private void refSetRemove(VMID vmid) {
308         // remove notification request
309         DGCImpl.getDGCImpl().unregisterTarget(vmid, this);
310 
311         if (refSet.removeElement(vmid) && refSet.isEmpty()) {
312             // reference set is empty, so server can be garbage collected.
313             // remove object from table.
314             if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
315                 DGCImpl.dgcLog.log(Log.VERBOSE,
316                     "reference set is empty: target = " + this);
317             }
318 
319             /*
320              * If the remote object implements the Unreferenced interface,
321              * invoke its unreferenced callback in a separate thread.
322              */
323             Remote obj = getImpl();
324             if (obj instanceof Unreferenced) {
325                 final Unreferenced unrefObj = (Unreferenced) obj;
326                 AccessController.doPrivileged(
327                     new NewThreadAction(() -> {
328                         Thread.currentThread().setContextClassLoader(ccl);
329                         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
330                             unrefObj.unreferenced();
331                             return null;
332                         }, acc);
333                     }, "Unreferenced-" + nextThreadNum++, false, true)).start();
334                     // REMIND: access to nextThreadNum not synchronized; you care?
335             }
336 
337             unpinImpl();
338         }
339     }
340 
341     /**
342      * Mark this target as not accepting new calls if any of the
343      * following conditions exist: a) the force parameter is true,
344      * b) the target's call count is zero, or c) the object is already
345      * not accepting calls. Returns true if target is marked as not
346      * accepting new calls; returns false otherwise.
347      */
unexport(boolean force)348     synchronized boolean unexport(boolean force) {
349 
350         if ((force == true) || (callCount == 0) || (disp == null)) {
351             disp = null;
352             /*
353              * Fix for 4331349: unpin object so that it may be gc'd.
354              * Also, unregister all vmids referencing this target
355              * so target can be gc'd.
356              */
357             unpinImpl();
358             DGCImpl dgc = DGCImpl.getDGCImpl();
359             Enumeration<VMID> enum_ = refSet.elements();
360             while (enum_.hasMoreElements()) {
361                 VMID vmid = enum_.nextElement();
362                 dgc.unregisterTarget(vmid, this);
363             }
364             return true;
365         } else {
366             return false;
367         }
368     }
369 
370     /**
371      * Mark this target as having been removed from the object table.
372      */
markRemoved()373     synchronized void markRemoved() {
374         if (!(!removed)) { throw new AssertionError(); }
375 
376         removed = true;
377         if (!permanent && callCount == 0) {
378             ObjectTable.decrementKeepAliveCount();
379         }
380 
381         if (exportedTransport != null) {
382             exportedTransport.targetUnexported();
383         }
384     }
385 
386     /**
387      * Increment call count.
388      */
incrementCallCount()389     synchronized void incrementCallCount() throws NoSuchObjectException {
390 
391         if (disp != null) {
392             callCount ++;
393         } else {
394             throw new NoSuchObjectException("object not accepting new calls");
395         }
396     }
397 
398     /**
399      * Decrement call count.
400      */
decrementCallCount()401     synchronized void decrementCallCount() {
402 
403         if (--callCount < 0) {
404             throw new Error("internal error: call count less than zero");
405         }
406 
407         /*
408          * The "keep-alive count" is the number of non-permanent remote
409          * objects that are either in the object table or still have calls
410          * in progress.  Therefore, this state change may affect the
411          * keep-alive count: if this target is for a non-permanent remote
412          * object that has been removed from the object table and now has a
413          * call count of zero, it needs to be decremented.
414          */
415         if (!permanent && removed && callCount == 0) {
416             ObjectTable.decrementKeepAliveCount();
417         }
418     }
419 
420     /**
421      * Returns true if remembered set is empty; otherwise returns
422      * false
423      */
isEmpty()424     boolean isEmpty() {
425         return refSet.isEmpty();
426     }
427 
428     /**
429      * This method is called if the address space associated with the
430      * vmid dies.  In that case, the vmid should be removed
431      * from the reference set.
432      */
vmidDead(VMID vmid)433     synchronized public void vmidDead(VMID vmid) {
434         if (DGCImpl.dgcLog.isLoggable(Log.BRIEF)) {
435             DGCImpl.dgcLog.log(Log.BRIEF, "removing endpoint " +
436                             vmid + " from reference set");
437         }
438 
439         sequenceTable.remove(vmid);
440         refSetRemove(vmid);
441     }
442 }
443 
444 class SequenceEntry {
445     long sequenceNum;
446     boolean keep;
447 
SequenceEntry(long sequenceNum)448     SequenceEntry(long sequenceNum) {
449         this.sequenceNum = sequenceNum;
450         keep = false;
451     }
452 
retain(long sequenceNum)453     void retain(long sequenceNum) {
454         this.sequenceNum = sequenceNum;
455         keep = true;
456     }
457 
update(long sequenceNum)458     void update(long sequenceNum) {
459         this.sequenceNum = sequenceNum;
460     }
461 }
462