1 /*
   2  * Copyright (c) 2003, 2012, 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 import java.io.IOException;
  29 import java.io.InterruptedIOException;
  30 
  31 import com.sun.jmx.remote.util.ClassLogger;
  32 import com.sun.jmx.remote.util.EnvHelp;
  33 import sun.misc.ManagedLocalsThread;
  34 
  35 public abstract class ClientCommunicatorAdmin {
  36     private static volatile long threadNo = 1;
  37 
  38     public ClientCommunicatorAdmin(long period) {
  39         this.period = period;
  40 
  41         if (period > 0) {
  42             checker = new Checker();
  43 
  44             Thread t = new ManagedLocalsThread(
  45                 checker,
  46                 "JMX client heartbeat " +  (++threadNo)
  47             );
  48 
  49             t.setDaemon(true);
  50             t.start();
  51         } else
  52             checker = null;
  53     }
  54 
  55     /**
  56      * Called by a client to inform of getting an IOException.
  57      */
  58     public void gotIOException (IOException ioe) throws IOException {
  59         restart(ioe);
  60     }
  61 
  62     /**
  63      * Called by this class to check a client connection.
  64      */
  65     protected abstract void checkConnection() throws IOException;
  66 
  67     /**
  68      * Tells a client to re-start again.
  69      */
  70     protected abstract void doStart() throws IOException;
  71 
  72     /**
  73      * Tells a client to stop because failing to call checkConnection.
  74      */
  75     protected abstract void doStop();
  76 
  77     /**
  78      * Terminates this object.
  79      */
  80     public void terminate() {
  81         synchronized(lock) {
  82             if (state == TERMINATED) {
  83                 return;
  84             }
  85 
  86             state = TERMINATED;
  87 
  88             lock.notifyAll();
  89 
  90             if (checker != null)
  91                 checker.stop();
  92         }
  93     }
  94 
  95     private void restart(IOException ioe) throws IOException {
  96         // check state
  97         synchronized(lock) {
  98             if (state == TERMINATED) {
  99                 throw new IOException("The client has been closed.");
 100             } else if (state == FAILED) { // already failed to re-start by another thread
 101                 throw ioe;
 102             } else if (state == RE_CONNECTING) {
 103                 // restart process has been called by another thread
 104                 // we need to wait
 105                 while(state == RE_CONNECTING) {
 106                     try {
 107                         lock.wait();
 108                     } catch (InterruptedException ire) {
 109                         // be asked to give up
 110                         InterruptedIOException iioe = new InterruptedIOException(ire.toString());
 111                         EnvHelp.initCause(iioe, ire);
 112 
 113                         throw iioe;
 114                     }
 115                 }
 116 
 117                 if (state == TERMINATED) {
 118                     throw new IOException("The client has been closed.");
 119                 } else if (state != CONNECTED) {
 120                     // restarted is failed by another thread
 121                     throw ioe;
 122                 }
 123                 return;
 124             } else {
 125                 state = RE_CONNECTING;
 126                 lock.notifyAll();
 127             }
 128         }
 129 
 130         // re-starting
 131         try {
 132             doStart();
 133             synchronized(lock) {
 134                 if (state == TERMINATED) {
 135                     throw new IOException("The client has been closed.");
 136                 }
 137 
 138                 state = CONNECTED;
 139 
 140                 lock.notifyAll();
 141             }
 142 
 143             return;
 144         } catch (Exception e) {
 145             logger.warning("restart", "Failed to restart: " + e);
 146             logger.debug("restart",e);
 147 
 148             synchronized(lock) {
 149                 if (state == TERMINATED) {
 150                     throw new IOException("The client has been closed.");
 151                 }
 152 
 153                 state = FAILED;
 154 
 155                 lock.notifyAll();
 156             }
 157 
 158             try {
 159                 doStop();
 160             } catch (Exception eee) {
 161                 // OK.
 162                 // We know there is a problem.
 163             }
 164 
 165             terminate();
 166 
 167             throw ioe;
 168         }
 169     }
 170 
 171 // --------------------------------------------------------------
 172 // private varaibles
 173 // --------------------------------------------------------------
 174     private class Checker implements Runnable {
 175         public void run() {
 176             myThread = Thread.currentThread();
 177 
 178             while (state != TERMINATED && !myThread.isInterrupted()) {
 179                 try {
 180                     Thread.sleep(period);
 181                 } catch (InterruptedException ire) {
 182                     // OK.
 183                     // We will check the state at the following steps
 184                 }
 185 
 186                 if (state == TERMINATED || myThread.isInterrupted()) {
 187                     break;
 188                 }
 189 
 190                 try {
 191                     checkConnection();
 192                 } catch (Exception e) {
 193                     synchronized(lock) {
 194                         if (state == TERMINATED || myThread.isInterrupted()) {
 195                             break;
 196                         }
 197                     }
 198 
 199                     e = (Exception)EnvHelp.getCause(e);
 200 
 201                     if (e instanceof IOException &&
 202                         !(e instanceof InterruptedIOException)) {
 203                         try {
 204                             gotIOException((IOException)e);
 205                         } catch (Exception ee) {
 206                             logger.warning("Checker-run",
 207                                            "Failed to check connection: "+ e);
 208                             logger.warning("Checker-run", "stopping");
 209                             logger.debug("Checker-run",e);
 210 
 211                             break;
 212                         }
 213                     } else {
 214                         logger.warning("Checker-run",
 215                                      "Failed to check the connection: " + e);
 216                         logger.debug("Checker-run",e);
 217 
 218                         // XXX stop checking?
 219 
 220                         break;
 221                     }
 222                 }
 223             }
 224 
 225             if (logger.traceOn()) {
 226                 logger.trace("Checker-run", "Finished.");
 227             }
 228         }
 229 
 230         private void stop() {
 231             if (myThread != null && myThread != Thread.currentThread()) {
 232                 myThread.interrupt();
 233             }
 234         }
 235 
 236         private Thread myThread;
 237     }
 238 
 239 // --------------------------------------------------------------
 240 // private variables
 241 // --------------------------------------------------------------
 242     private final Checker checker;
 243     private long period;
 244 
 245     // state
 246     private final static int CONNECTED = 0;
 247     private final static int RE_CONNECTING = 1;
 248     private final static int FAILED = 2;
 249     private final static int TERMINATED = 3;
 250 
 251     private int state = CONNECTED;
 252 
 253     private final int[] lock = new int[0];
 254 
 255     private static final ClassLogger logger =
 256         new ClassLogger("javax.management.remote.misc",
 257                         "ClientCommunicatorAdmin");
 258 }