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 
26 package com.sun.jmx.remote.internal;
27 
28 
29 import com.sun.jmx.remote.util.ClassLogger;
30 
31 public abstract class ServerCommunicatorAdmin {
ServerCommunicatorAdmin(long timeout)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      */
reqIncoming()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      */
rspOutgoing()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      */
doStop()108     protected abstract void doStop();
109 
110     /**
111      * Terminates this object.
112      * Called only by outside, so do not need to call doStop
113      */
terminate()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 {
run()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 
logtime(String desc,long time)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