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 }