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 {
  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 }