aboutsummaryrefslogtreecommitdiff
path: root/src/share/classes/sun/rmi/transport/Target.java
blob: 6abf092d725d0a65623edc92ff5df42a3a24695d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
/*
 * Copyright 1996-2005 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
package sun.rmi.transport;

import java.rmi.Remote;
import java.rmi.NoSuchObjectException;
import java.rmi.dgc.VMID;
import java.rmi.server.ObjID;
import java.rmi.server.Unreferenced;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.util.*;
import sun.rmi.runtime.Log;
import sun.rmi.runtime.NewThreadAction;
import sun.rmi.server.Dispatcher;

/**
 * A target contains information pertaining to a remote object that
 * resides in this address space.  Targets are located via the
 * ObjectTable.
 */
public final class Target {
    /** object id for target */
    private final ObjID id;
    /** flag indicating whether target is subject to collection */
    private final boolean permanent;
    /** weak reference to remote object implementation */
    private final WeakRef weakImpl;
    /** dispatcher for remote object */
    private volatile Dispatcher disp;
    /** stub for remote object */
    private final Remote stub;
    /** set of clients that hold references to this target */
    private final Vector refSet = new Vector();
    /** table that maps client endpoints to sequence numbers */
    private final Hashtable sequenceTable = new Hashtable(5);
    /** access control context in which target was created */
    private final AccessControlContext acc;
    /** context class loader in which target was created */
    private final ClassLoader ccl;
    /** number of pending/executing calls */
    private int callCount = 0;
    /** true if this target has been removed from the object table */
    private boolean removed = false;
    /**
     * the transport through which this target was exported and
     * through which remote calls will be allowed
     */
    private volatile Transport exportedTransport = null;

    /** number to identify next callback thread created here */
    private static int nextThreadNum = 0;

    /**
     * Construct a Target for a remote object "impl" with
     * a specific object id.
     *
     * If "permanent" is true, then the impl is pinned permanently
     * (the impl will not be collected via distributed and/or local
     * GC).  If "on" is false, than the impl is subject to
     * collection. Permanent objects do not keep a server from
     * exiting.
     */
    public Target(Remote impl, Dispatcher disp, Remote stub, ObjID id,
                  boolean permanent)
    {
        this.weakImpl = new WeakRef(impl, ObjectTable.reapQueue);
        this.disp = disp;
        this.stub = stub;
        this.id = id;
        this.acc = AccessController.getContext();

        /*
         * Fix for 4149366: so that downloaded parameter types unmarshalled
         * for this impl will be compatible with types known only to the
         * impl class's class loader (when it's not identical to the
         * exporting thread's context class loader), mark the impl's class
         * loader as the loader to use as the context class loader in the
         * server's dispatch thread while a call to this impl is being
         * processed (unless this exporting thread's context class loader is
         * a child of the impl's class loader, such as when a registry is
         * exported by an application, in which case this thread's context
         * class loader is preferred).
         */
        ClassLoader threadContextLoader =
            Thread.currentThread().getContextClassLoader();
        ClassLoader serverLoader = impl.getClass().getClassLoader();
        if (checkLoaderAncestry(threadContextLoader, serverLoader)) {
            this.ccl = threadContextLoader;
        } else {
            this.ccl = serverLoader;
        }

        this.permanent = permanent;
        if (permanent) {
            pinImpl();
        }
    }

    /**
     * Return true if the first class loader is a child of (or identical
     * to) the second class loader.  Either loader may be "null", which is
     * considered to be the parent of any non-null class loader.
     *
     * (utility method added for the 1.2beta4 fix for 4149366)
     */
    private static boolean checkLoaderAncestry(ClassLoader child,
                                               ClassLoader ancestor)
    {
        if (ancestor == null) {
            return true;
        } else if (child == null) {
            return false;
        } else {
            for (ClassLoader parent = child;
                 parent != null;
                 parent = parent.getParent())
            {
                if (parent == ancestor) {
                    return true;
                }
            }
            return false;
        }
    }

    /** Get the stub (proxy) object for this target
     */
    public Remote getStub() {
        return stub;
    }

    /**
     * Returns the object endpoint for the target.
     */
    ObjectEndpoint getObjectEndpoint() {
        return new ObjectEndpoint(id, exportedTransport);
    }

    /**
     * Get the weak reference for the Impl of this target.
     */
    WeakRef getWeakImpl() {
        return weakImpl;
    }

    /**
     * Returns the dispatcher for this remote object target.
     */
    Dispatcher getDispatcher() {
        return disp;
    }

    AccessControlContext getAccessControlContext() {
        return acc;
    }

    ClassLoader getContextClassLoader() {
        return ccl;
    }

    /**
     * Get the impl for this target.
     * Note: this may return null if the impl has been garbage collected.
     * (currently, there is no need to make this method public)
     */
    Remote getImpl() {
        return (Remote)weakImpl.get();
    }

    /**
     * Returns true if the target is permanent.
     */
    boolean isPermanent() {
        return permanent;
    }

    /**
     * Pin impl in target. Pin the WeakRef object so it holds a strong
     * reference to the object to it will not be garbage collected locally.
     * This way there is a single object responsible for the weak ref
     * mechanism.
     */
    synchronized void pinImpl() {
        weakImpl.pin();
    }

    /**
     * Unpin impl in target.  Weaken the reference to impl so that it
     * can be garbage collected locally. But only if there the refSet
     * is empty.  All of the weak/strong handling is in WeakRef
     */
    synchronized void unpinImpl() {
        /* only unpin if:
         * a) impl is not permanent, and
         * b) impl is not already unpinned, and
         * c) there are no external references (outside this
         *    address space) for the impl
         */
        if (!permanent && refSet.isEmpty()) {
            weakImpl.unpin();
        }
    }

    /**
     * Enable the transport through which remote calls to this target
     * are allowed to be set if it has not already been set.
     */
    void setExportedTransport(Transport exportedTransport) {
        if (this.exportedTransport == null) {
            this.exportedTransport = exportedTransport;
        }
    }

    /**
     * Add an endpoint to the remembered set.  Also adds a notifier
     * to call back if the address space associated with the endpoint
     * dies.
     */
    synchronized void referenced(long sequenceNum, VMID vmid) {
        // check sequence number for vmid
        SequenceEntry entry = (SequenceEntry) sequenceTable.get(vmid);
        if (entry == null) {
            sequenceTable.put(vmid, new SequenceEntry(sequenceNum));
        } else if (entry.sequenceNum < sequenceNum) {
            entry.update(sequenceNum);
        } else  {
            // late dirty call; ignore.
            return;
        }

        if (!refSet.contains(vmid)) {
            /*
             * A Target must be pinned while its refSet is not empty.  It may
             * have become unpinned if external LiveRefs only existed in
             * serialized form for some period of time, or if a client failed
             * to renew its lease due to a transient network failure.  So,
             * make sure that it is pinned here; this fixes bugid 4069644.
             */
            pinImpl();
            if (getImpl() == null)      // too late if impl was collected
                return;

            if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
                DGCImpl.dgcLog.log(Log.VERBOSE, "add to dirty set: " + vmid);
            }

            refSet.addElement(vmid);

            DGCImpl.getDGCImpl().registerTarget(vmid, this);
        }
    }

    /**
     * Remove endpoint from remembered set.  If set becomes empty,
     * remove server from Transport's object table.
     */
    synchronized void unreferenced(long sequenceNum, VMID vmid, boolean strong)
    {
        // check sequence number for vmid
        SequenceEntry entry = (SequenceEntry) sequenceTable.get(vmid);
        if (entry == null || entry.sequenceNum > sequenceNum) {
            // late clean call; ignore
            return;
        } else if (strong) {
            // strong clean call; retain sequenceNum
            entry.retain(sequenceNum);
        } else if (entry.keep == false) {
            // get rid of sequence number
            sequenceTable.remove(vmid);
        }

        if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
            DGCImpl.dgcLog.log(Log.VERBOSE, "remove from dirty set: " + vmid);
        }

        refSetRemove(vmid);
    }

    /**
     * Remove endpoint from the reference set.
     */
    synchronized private void refSetRemove(VMID vmid) {
        // remove notification request
        DGCImpl.getDGCImpl().unregisterTarget(vmid, this);

        if (refSet.removeElement(vmid) && refSet.isEmpty()) {
            // reference set is empty, so server can be garbage collected.
            // remove object from table.
            if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
                DGCImpl.dgcLog.log(Log.VERBOSE,
                    "reference set is empty: target = " + this);
            }

            /*
             * If the remote object implements the Unreferenced interface,
             * invoke its unreferenced callback in a separate thread.
             */
            Remote obj = getImpl();
            if (obj instanceof Unreferenced) {
                final Unreferenced unrefObj = (Unreferenced) obj;
                final Thread t =
                    java.security.AccessController.doPrivileged(
                        new NewThreadAction(new Runnable() {
                            public void run() {
                                unrefObj.unreferenced();
                            }
                        }, "Unreferenced-" + nextThreadNum++, false, true));
                // REMIND: access to nextThreadNum not synchronized; you care?
                /*
                 * We must manually set the context class loader appropriately
                 * for threads that may invoke user code (see bugid 4171278).
                 */
                java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedAction<Void>() {
                        public Void run() {
                        t.setContextClassLoader(ccl);
                        return null;
                    }
                });

                t.start();
            }

            unpinImpl();
        }
    }

    /**
     * Mark this target as not accepting new calls if any of the
     * following conditions exist: a) the force parameter is true,
     * b) the target's call count is zero, or c) the object is already
     * not accepting calls. Returns true if target is marked as not
     * accepting new calls; returns false otherwise.
     */
    synchronized boolean unexport(boolean force) {

        if ((force == true) || (callCount == 0) || (disp == null)) {
            disp = null;
            /*
             * Fix for 4331349: unpin object so that it may be gc'd.
             * Also, unregister all vmids referencing this target
             * so target can be gc'd.
             */
            unpinImpl();
            DGCImpl dgc = DGCImpl.getDGCImpl();
            Enumeration enum_ = refSet.elements();
            while (enum_.hasMoreElements()) {
                VMID vmid = (VMID) enum_.nextElement();
                dgc.unregisterTarget(vmid, this);
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * Mark this target as having been removed from the object table.
     */
    synchronized void markRemoved() {
        if (!(!removed)) { throw new AssertionError(); }

        removed = true;
        if (!permanent && callCount == 0) {
            ObjectTable.decrementKeepAliveCount();
        }

        if (exportedTransport != null) {
            exportedTransport.targetUnexported();
        }
    }

    /**
     * Increment call count.
     */
    synchronized void incrementCallCount() throws NoSuchObjectException {

        if (disp != null) {
            callCount ++;
        } else {
            throw new NoSuchObjectException("object not accepting new calls");
        }
    }

    /**
     * Decrement call count.
     */
    synchronized void decrementCallCount() {

        if (--callCount < 0) {
            throw new Error("internal error: call count less than zero");
        }

        /*
         * The "keep-alive count" is the number of non-permanent remote
         * objects that are either in the object table or still have calls
         * in progress.  Therefore, this state change may affect the
         * keep-alive count: if this target is for a non-permanent remote
         * object that has been removed from the object table and now has a
         * call count of zero, it needs to be decremented.
         */
        if (!permanent && removed && callCount == 0) {
            ObjectTable.decrementKeepAliveCount();
        }
    }

    /**
     * Returns true if remembered set is empty; otherwise returns
     * false
     */
    boolean isEmpty() {
        return refSet.isEmpty();
    }

    /**
     * This method is called if the address space associated with the
     * vmid dies.  In that case, the vmid should be removed
     * from the reference set.
     */
    synchronized public void vmidDead(VMID vmid) {
        if (DGCImpl.dgcLog.isLoggable(Log.BRIEF)) {
            DGCImpl.dgcLog.log(Log.BRIEF, "removing endpoint " +
                            vmid + " from reference set");
        }

        sequenceTable.remove(vmid);
        refSetRemove(vmid);
    }
}

class SequenceEntry {
    long sequenceNum;
    boolean keep;

    SequenceEntry(long sequenceNum) {
        this.sequenceNum = sequenceNum;
        keep = false;
    }

    void retain(long sequenceNum) {
        this.sequenceNum = sequenceNum;
        keep = true;
    }

    void update(long sequenceNum) {
        this.sequenceNum = sequenceNum;
    }
}