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