/* * 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 Sam */ package com.caucho.netbeans; import org.openide.execution.NbProcessDescriptor; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.net.InetAddress; import java.net.ServerSocket; import java.util.logging.Level; import java.util.logging.Logger; public class ResinProcess { private static final PluginL10N L = new PluginL10N(ResinProcess.class); private static final Logger log = Logger.getLogger(ResinProcess.class.getName()); public static final int LIFECYCLE_NEW = 0; public static final int LIFECYCLE_INITIALIZING = 1; public static final int LIFECYCLE_INIT = 2; public static final int LIFECYCLE_STARTING = 3; public static final int LIFECYCLE_STANDBY = 4; public static final int LIFECYCLE_WARMUP = 5; public static final int LIFECYCLE_ACTIVE = 6; public static final int LIFECYCLE_FAILED = 7; public static final int LIFECYCLE_STOPPING = 8; public static final int LIFECYCLE_STOPPED = 9; public static final int LIFECYCLE_DESTROYING = 10; public static final int LIFECYCLE_DESTROYED = 11; private int _lifecycle = LIFECYCLE_NEW; private static final int TIMEOUT_TICK = 250; private final ResinConfiguration _resinConfiguration; private boolean _isDebug; private int _activeServerPort; private int _activeDebugPort; private Process _process; private Console _console; private final Object _lock = new Object(); private File _javaExe; private File _resinJar; public ResinProcess(ResinConfiguration resinConfiguration) { _resinConfiguration = resinConfiguration; } public ResinConfiguration getResinConfiguration() { return _resinConfiguration; } private boolean isInit() { return _lifecycle >= LIFECYCLE_INIT; } public boolean isActive() { return _lifecycle == LIFECYCLE_ACTIVE; } public void init() throws IllegalStateException { synchronized (_lock) { if (isInit()) { return; } _lifecycle = LIFECYCLE_INITIALIZING; _resinConfiguration.validate(); File javaExe; File javaHome = _resinConfiguration.calculateJavaHome(); javaExe = new File(javaHome, "bin/java"); if (!javaExe.exists()) { javaExe = new File(javaHome, "bin/java.exe"); } if (!javaExe.exists()) { throw new IllegalStateException(L.l("Cannot find java exe in ''{0}''", javaHome)); } _javaExe = javaExe; File resinHome = _resinConfiguration.getResinHome(); File resinJar = new File(resinHome, "lib/resin.jar"); if (!resinJar.exists()) { throw new IllegalStateException(L.l( "Cannot find lib/resin.jar in ''{0}''", resinHome)); } _resinJar = resinJar; _lifecycle = LIFECYCLE_INIT; } } public void start() throws IllegalStateException, IOException { init(); synchronized (_lock) { if (isActive()) { stopImpl(false); } _isDebug = false; startImpl(); } } public void startDebug() throws IllegalStateException, IOException { init(); synchronized (_lock) { if (isActive()) { stopImpl(false); } _isDebug = true; startImpl(); } } public boolean isDebug() { return _isDebug; } private void startImpl() throws IllegalStateException, IOException { _lifecycle = LIFECYCLE_STARTING; int serverPort = _resinConfiguration.getServerPort(); int debugPort = _resinConfiguration.getDebugPort(); File resinHome = _resinConfiguration.getResinHome(); //File resinConf = _resinConfiguration.getResinConf(); String serverId = _resinConfiguration.getServerId(); String address = _resinConfiguration.getResinInstance().getAddress(); //String user = _resinConfiguration.getResinInstance().getUser(); //String password = _resinConfiguration.getResinInstance().getPassword(); int port = _resinConfiguration.getResinInstance().getPort(); log.info("Resin starting on " + serverPort); if (!isPortFree(serverPort)) { throw new IllegalStateException(L.l( "Cannot start Resin, server-port {0} is already in use", serverPort)); } if (_isDebug) { if (debugPort == 0) { ServerSocket ss = new ServerSocket(0, 5, InetAddress.getByName("127.0.0.1")); debugPort = ss.getLocalPort(); try { Thread.sleep(100); } catch (InterruptedException e) { } ss.close(); } if (!isPortFree(debugPort)) { throw new IllegalStateException(L.l( "Cannot start Resin, debug-port {0} is already in use", debugPort)); } } StringBuilder args = new StringBuilder(); //args.append("-Ddeploy.username=").append(user); //args.append(" -Ddeploy.password=").append(password); //args.append(" -Dserver.address=").append(address); //args.append(" -Dserver.port=").append(port); args.append(" -jar "); args.append('"').append(resinHome.getPath()).append("/lib/resin.jar\" "); args.append(" -resin-home \"" + resinHome + "\""); //args.append(" -conf \"" + resinConf.getPath() + "\""); args.append(" console"); if (_isDebug) { throw new IllegalStateException("debug mode not implemented"); } final String uri = _resinConfiguration.getResinInstance().getUrl(); // open the ServerLog synchronized (this) { if (_console != null) { _console.takeFocus(); } else { _console = new Console(uri); } } String classpath = null; String displayName = _resinConfiguration.getDisplayName(); NbProcessDescriptor processDescriptor = new NbProcessDescriptor(_javaExe.getAbsolutePath(), args.toString(), displayName); _console.println(L.l("Starting Resin process {0} {1}", processDescriptor.getProcessName(), processDescriptor.getArguments())); _console.flush(); _process = processDescriptor.exec(null, new String[]{}, true, resinHome); _console.println(); _console.start(new InputStreamReader(_process.getInputStream()), new InputStreamReader(_process.getErrorStream())); new Thread("resin-" + uri + "-process-monitor") { public void run() { try { _process.waitFor(); Thread.sleep(2000); } catch (InterruptedException e) { } finally { handleProcessDied(); } } }.start(); // wait for server port to become active _activeServerPort = serverPort; _activeDebugPort = debugPort; int startTimeout = _resinConfiguration.getStartTimeout(); boolean isResponding = false; for (int i = startTimeout; i > 0; i -= TIMEOUT_TICK) { if (isResponding()) { isResponding = true; break; } try { Thread.sleep(TIMEOUT_TICK); } catch (InterruptedException ex) { log.log(Level.WARNING, ex.toString(), ex); } } if (!isResponding) { String msg = L.l("Resin process failed to respond on server-port {0}", serverPort); log.log(Level.WARNING, msg); try { stopImpl(false); } catch (Exception ex) { log.log(Level.WARNING, ex.toString(), ex); } throw new IOException(msg); } _lifecycle = LIFECYCLE_ACTIVE; } public Console getConsole() { return _console; } private static boolean isPortFree(int port) { ServerSocket ss = null; try { ss = new ServerSocket(port); return true; } catch (IOException ioe) { return false; } finally { if (ss != null) { try { ss.close(); } catch (IOException ex) { } } } } public String getHttpUrl() { return null; } /** * Return true if the process is running */ public boolean isProcessRunning() { Process process = _process; if (process != null) { try { process.exitValue(); return false; } catch (IllegalThreadStateException e) { return true; } } return false; } /** * Return true if the server is responding */ public boolean isResponding() { // XXX: could be more robust, contact the server and make sure there is a response return _activeServerPort > 0 && !isPortFree(_activeServerPort); } public Process getJavaProcess() { return _process; } private void handleProcessDied() { stopImpl(false); } public void stop() { synchronized (_lock) { if (_lifecycle != LIFECYCLE_ACTIVE) { return; } stopImpl(true); } } private void stopImpl(boolean isGraceful) { _lifecycle = LIFECYCLE_STOPPING; Process process = _process; _process = null; Console console = _console; _console = null; int activeServerPort = _activeServerPort; _activeServerPort = 0; int activeDebugPort = _activeDebugPort; _activeDebugPort = 0; _isDebug = false; try { // XXX: graceful shutdown, send message to server, // then use isPortFree in a while loop that times out /* if (isGraceful) { try { printConsoleLine(L.l("Stopping Resin process ...")); } catch (Exception ex) { // no-op } for (int i = STOP_TIMEOUT; !isPortFree(activeServerPort) && i > 0; i-= TICK) { try { Thread.sleep(TICK); } catch (InterruptedException ex) { if (log.isLoggable(Level.WARNING)) log.log(Level.WARNING, e), ex); } } } */ } finally { try { if (process != null) { process.destroy(); } } finally { try { console.println(L.l("Resin process destroyed")); console.flush(); } catch (Exception ex) { // no-op } if (console != null) { console.destroy(); } } } _lifecycle = LIFECYCLE_STOPPED; } public void destroy() { synchronized (_lock) { _lifecycle = LIFECYCLE_DESTROYING; try { try { stop(); } catch (Exception ex) { log.log(Level.WARNING, ex.toString(), ex); try { stopImpl(false); } catch (Exception ex2) { log.log(Level.WARNING, ex2.toString(), ex); } } } finally { _lifecycle = LIFECYCLE_DESTROYED; } } } }