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