aboutsummaryrefslogtreecommitdiff
path: root/src/share/classes/sun/rmi/transport/DGCAckHandler.java
blob: 37769fc5e9237bb5392138ea60f0c343b0319c6e (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
/*
 * 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.server.UID;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import sun.rmi.runtime.RuntimeUtil;
import sun.security.action.GetLongAction;

/**
 * Holds strong references to a set of remote objects, or live remote
 * references to remote objects, after they have been marshalled (as
 * remote references) as parts of the arguments or the result of a
 * remote invocation.  The purpose is to prevent remote objects or
 * live remote references that might otherwise be determined to be
 * unreachable in this VM from being locally garbage collected before
 * the receiver has had an opportunity to register the unmarshalled
 * remote references for DGC.
 *
 * The references are held strongly until an acknowledgment has been
 * received that the receiver has had an opportunity to process the
 * remote references or until a timeout has expired.  For remote
 * references sent as parts of the arguments of a remote invocation,
 * the acknowledgment is the beginning of the response indicating
 * completion of the remote invocation.  For remote references sent as
 * parts of the result of a remote invocation, a UID is included as
 * part of the result, and the acknowledgment is a transport-level
 * "DGCAck" message containing that UID.
 *
 * @author      Ann Wollrath
 * @author      Peter Jones
 **/
public class DGCAckHandler {

    /** timeout for holding references without receiving an acknowledgment */
    private static final long dgcAckTimeout =           // default 5 minutes
        AccessController.doPrivileged(
            new GetLongAction("sun.rmi.dgc.ackTimeout", 300000));

    /** thread pool for scheduling delayed tasks */
    private static final ScheduledExecutorService scheduler =
        AccessController.doPrivileged(
            new RuntimeUtil.GetInstanceAction()).getScheduler();

    /** table mapping ack ID to handler */
    private static final Map<UID,DGCAckHandler> idTable =
        Collections.synchronizedMap(new HashMap<UID,DGCAckHandler>());

    private final UID id;
    private List<Object> objList = new ArrayList<Object>(); // null if released
    private Future<?> task = null;

    /**
     * Creates a new DGCAckHandler, associated with the specified UID
     * if the argument is not null.
     *
     * References added to this DGCAckHandler will be held strongly
     * until its "release" method is invoked or (after the
     * "startTimer" method has been invoked) the timeout has expired.
     * If the argument is not null, then invoking the static
     * "received" method with the specified UID is equivalent to
     * invoking this instance's "release" method.
     **/
    DGCAckHandler(UID id) {
        this.id = id;
        if (id != null) {
            assert !idTable.containsKey(id);
            idTable.put(id, this);
        }
    }

    /**
     * Adds the specified reference to this DGCAckHandler.
     **/
    synchronized void add(Object obj) {
        if (objList != null) {
            objList.add(obj);
        }
    }

    /**
     * Starts the timer for this DGCAckHandler.  After the timeout has
     * expired, the references are released even if the acknowledgment
     * has not been received.
     **/
    synchronized void startTimer() {
        if (objList != null && task == null) {
            task = scheduler.schedule(new Runnable() {
                public void run() {
                    release();
                }
            }, dgcAckTimeout, TimeUnit.MILLISECONDS);
        }
    }

    /**
     * Releases the references held by this DGCAckHandler.
     **/
    synchronized void release() {
        if (task != null) {
            task.cancel(false);
            task = null;
        }
        objList = null;
    }

    /**
     * Causes the DGCAckHandler associated with the specified UID to
     * release its references.
     **/
    public static void received(UID id) {
        DGCAckHandler h = idTable.remove(id);
        if (h != null) {
            h.release();
        }
    }
}