1/*
2 * Copyright (c) 2003, 2004, 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
26package com.sun.jmx.remote.internal;
27
28
29import com.sun.jmx.remote.util.ClassLogger;
30
31public abstract class ServerCommunicatorAdmin {
32    public ServerCommunicatorAdmin(long timeout) {
33        if (logger.traceOn()) {
34            logger.trace("Constructor",
35                         "Creates a new ServerCommunicatorAdmin object "+
36                         "with the timeout "+timeout);
37        }
38
39        this.timeout = timeout;
40
41        timestamp = 0;
42        if (timeout < Long.MAX_VALUE) {
43            Runnable timeoutTask = new Timeout();
44            final Thread t = new Thread(null,
45                                        timeoutTask,
46                                        "JMX-Server-Admin-Timeout",
47                                        0,
48                                        false);
49            t.setName("JMX server connection timeout " + t.getId());
50            // If you change this name you will need to change a unit test
51            // (NoServerTimeoutTest)
52            t.setDaemon(true);
53            t.start();
54        }
55    }
56
57    /**
58     * Tells that a new request message is received.
59     * A caller of this method should always call the method
60     * <code>rspOutgoing</code> to inform that a response is sent out
61     * for the received request.
62     * @return the value of the termination flag:
63     *         true if the connection is already being terminated,
64     *         false otherwise.
65     */
66    public boolean reqIncoming() {
67        if (logger.traceOn()) {
68            logger.trace("reqIncoming", "Receive a new request.");
69        }
70
71        synchronized(lock) {
72            if (terminated) {
73                logger.warning("reqIncoming",
74                               "The server has decided to close " +
75                               "this client connection.");
76            }
77            ++currentJobs;
78
79            return terminated;
80        }
81    }
82
83    /**
84     * Tells that a response is sent out for a received request.
85     * @return the value of the termination flag:
86     *         true if the connection is already being terminated,
87     *         false otherwise.
88     */
89    public boolean rspOutgoing() {
90        if (logger.traceOn()) {
91            logger.trace("reqIncoming", "Finish a request.");
92        }
93
94        synchronized(lock) {
95            if (--currentJobs == 0) {
96                timestamp = System.currentTimeMillis();
97                logtime("Admin: Timestamp=",timestamp);
98                // tells the adminor to restart waiting with timeout
99                lock.notify();
100            }
101            return terminated;
102        }
103    }
104
105    /**
106     * Called by this class to tell an implementation to do stop.
107     */
108    protected abstract void doStop();
109
110    /**
111     * Terminates this object.
112     * Called only by outside, so do not need to call doStop
113     */
114    public void terminate() {
115        if (logger.traceOn()) {
116            logger.trace("terminate",
117                         "terminate the ServerCommunicatorAdmin object.");
118        }
119
120        synchronized(lock) {
121            if (terminated) {
122                return;
123            }
124
125            terminated = true;
126
127            // tell Timeout to terminate
128            lock.notify();
129        }
130    }
131
132// --------------------------------------------------------------
133// private classes
134// --------------------------------------------------------------
135    private class Timeout implements Runnable {
136        public void run() {
137            boolean stopping = false;
138
139            synchronized(lock) {
140                if (timestamp == 0) timestamp = System.currentTimeMillis();
141                logtime("Admin: timeout=",timeout);
142                logtime("Admin: Timestamp=",timestamp);
143
144                while(!terminated) {
145                    try {
146                        // wait until there is no more job
147                        while(!terminated && currentJobs != 0) {
148                            if (logger.traceOn()) {
149                                logger.trace("Timeout-run",
150                                             "Waiting without timeout.");
151                            }
152
153                            lock.wait();
154                        }
155
156                        if (terminated) return;
157
158                        final long remaining =
159                            timeout - (System.currentTimeMillis() - timestamp);
160
161                        logtime("Admin: remaining timeout=",remaining);
162
163                        if (remaining > 0) {
164
165                            if (logger.traceOn()) {
166                                logger.trace("Timeout-run",
167                                             "Waiting with timeout: "+
168                                             remaining + " ms remaining");
169                            }
170
171                            lock.wait(remaining);
172                        }
173
174                        if (currentJobs > 0) continue;
175
176                        final long elapsed =
177                            System.currentTimeMillis() - timestamp;
178                        logtime("Admin: elapsed=",elapsed);
179
180                        if (!terminated && elapsed > timeout) {
181                            if (logger.traceOn()) {
182                                logger.trace("Timeout-run",
183                                             "timeout elapsed");
184                            }
185                            logtime("Admin: timeout elapsed! "+
186                                    elapsed+">",timeout);
187                                // stopping
188                            terminated = true;
189
190                            stopping = true;
191                            break;
192                        }
193                    } catch (InterruptedException ire) {
194                        logger.warning("Timeout-run","Unexpected Exception: "+
195                                       ire);
196                        logger.debug("Timeout-run",ire);
197                        return;
198                    }
199                }
200            }
201
202            if (stopping) {
203                if (logger.traceOn()) {
204                    logger.trace("Timeout-run", "Call the doStop.");
205                }
206
207                doStop();
208            }
209        }
210    }
211
212    private void logtime(String desc,long time) {
213        timelogger.trace("synchro",desc+time);
214    }
215
216// --------------------------------------------------------------
217// private variables
218// --------------------------------------------------------------
219    private long    timestamp;
220
221    private final int[] lock = new int[0];
222    private int currentJobs = 0;
223
224    private long timeout;
225
226    // state issue
227    private boolean terminated = false;
228
229    private static final ClassLogger logger =
230        new ClassLogger("javax.management.remote.misc",
231                        "ServerCommunicatorAdmin");
232    private static final ClassLogger timelogger =
233        new ClassLogger("javax.management.remote.timeout",
234                        "ServerCommunicatorAdmin");
235}
236