/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.server.resin; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; import java.util.logging.Level; import java.util.logging.Logger; import com.caucho.env.service.ResinSystem; import com.caucho.env.shutdown.ExitCode; import com.caucho.env.shutdown.ShutdownSystem; import com.caucho.env.thread.ThreadPool; import com.caucho.env.warning.WarningService; import com.caucho.util.L10N; import com.caucho.vfs.ReadStream; /** * The wait-for-exit service waits for Resin to exit. */ class ResinWaitForExitService { private static final Logger log = Logger.getLogger(ResinWaitForExitService.class.getSimpleName()); private static final L10N L = new L10N(ResinWaitForExitService.class); private Resin _resin; private ResinSystem _resinSystem; private InputStream _waitIn; private Socket _pingSocket; private ResinActor _resinActor; /** * Creates a new resin server. */ ResinWaitForExitService(Resin resin, ResinSystem resinSystem, InputStream waitIn, Socket pingSocket) { _resin = resin; _resinSystem = resinSystem; _waitIn = waitIn; _pingSocket = pingSocket; } ResinActor getResinActor() { return _resinActor; } /** * Thread to wait until Resin should be stopped. */ void waitForExit() { int socketExceptionCount = 0; Runtime runtime = Runtime.getRuntime(); ShutdownSystem shutdown = _resinSystem.getService(ShutdownSystem.class); if (shutdown == null) { throw new IllegalStateException(L.l("'{0}' requires an active {1}", this, ShutdownSystem.class.getSimpleName())); } /* * If the server has a parent process watching over us, close * gracefully when the parent dies. */ while (! _resin.isClosing()) { try { Thread.sleep(10); if (! checkMemory(runtime)) { shutdown.shutdown(ExitCode.MEMORY, "Resin shutdown from out of memory"); // dumpHeapOnExit(); return; } if (! checkFileDescriptor()) { shutdown.shutdown(ExitCode.MEMORY, "Resin shutdown from out of file descriptors"); //dumpHeapOnExit(); return; } if (_waitIn != null) { if (_waitIn.read() >= 0) { socketExceptionCount = 0; } else { shutdown.shutdown(ExitCode.WATCHDOG_EXIT, "Stopping due to watchdog or user."); return; } } else { synchronized (this) { wait(10000); } } } catch (SocketTimeoutException e) { socketExceptionCount = 0; } catch (InterruptedIOException e) { socketExceptionCount = 0; } catch (InterruptedException e) { socketExceptionCount = 0; } catch (SocketException e) { // The Solaris JVM will throw SocketException periodically // instead of interrupted exception, so those exceptions need to // be ignored. // However, the Windows JVMs will throw connection reset by peer // instead of returning an end of file in the read. So those // need to be trapped to close the socket. if (socketExceptionCount++ == 0) { log.log(Level.FINE, e.toString(), e); } else if (socketExceptionCount > 100) return; } catch (OutOfMemoryError e) { String msg = "Resin shutdown from out of memory"; ShutdownSystem.shutdownOutOfMemory(msg); } catch (Throwable e) { log.log(Level.WARNING, e.toString(), e); return; } } } private boolean checkMemory(Runtime runtime) throws InterruptedException { long minFreeMemory = 0;//_resinConfig.getMinFreeMemory(); if (minFreeMemory <= 0) { // memory check disabled return true; } else if (2 * minFreeMemory < getFreeMemory(runtime)) { // plenty of free memory return true; } else { if (log.isLoggable(Level.FINER)) { log.finer(L.l("free memory {0} max:{1} total:{2} free:{3}", "" + getFreeMemory(runtime), "" + runtime.maxMemory(), "" + runtime.totalMemory(), "" + runtime.freeMemory())); } log.info(L.l("Forcing GC due to low memory. {0} free bytes.", getFreeMemory(runtime))); runtime.gc(); Thread.sleep(1000); runtime.gc(); if (getFreeMemory(runtime) < minFreeMemory) { log.severe(L.l("Restarting due to low free memory. {0} free bytes", getFreeMemory(runtime))); return false; } } // second memory check allocateMemory(); return true; } private Object allocateMemory() { return new Object(); } private boolean checkFileDescriptor() { try { ReadStream is = _resin.getResinConf().openRead(); is.close(); return true; } catch (IOException e) { log.severe(L.l("Restarting due to file descriptor failure:\n{0}", e)); return false; } } private static long getFreeMemory(Runtime runtime) { long maxMemory = runtime.maxMemory(); long totalMemory = runtime.totalMemory(); long freeMemory = runtime.freeMemory(); // Some JDKs (JRocket) return 0 for the maxMemory if (maxMemory < totalMemory) return freeMemory; else return maxMemory - totalMemory + freeMemory; } void startResinActor() throws IOException { Thread thread = Thread.currentThread(); ClassLoader loader = thread.getContextClassLoader(); try { thread.setContextClassLoader(_resinSystem.getClassLoader()); WarningService warning = WarningService.getCurrent(); if (_pingSocket != null) { _resinActor = new ResinActor(_resin); warning.addHandler(new ResinWarningHandler(_resinActor)); InputStream is = _pingSocket.getInputStream(); OutputStream os = _pingSocket.getOutputStream(); ResinLink link = new ResinLink(_resinActor, is, os); ThreadPool.getThreadPool().schedule(link); } } finally { thread.setContextClassLoader(loader); } } @Override public String toString() { return getClass().getSimpleName() + "[id=" + _resinSystem.getId() + "]"; } }