/* * 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.boot; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Locale; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import com.caucho.VersionFactory; import com.caucho.bam.RemoteConnectionFailedException; import com.caucho.bam.actor.ActorSender; import com.caucho.config.ConfigException; import com.caucho.env.service.ResinSystem; import com.caucho.hmtp.HmtpClient; import com.caucho.server.util.CauchoSystem; import com.caucho.util.L10N; import com.caucho.vfs.Path; /** * Client to a watchdog-manager, i.e. ResinBoot code to ask the * watchdog-manager to do something. */ class WatchdogClient { private static final L10N L = new L10N(WatchdogClient.class); private static final Logger log = Logger.getLogger(WatchdogClient.class.getName()); private static final long BAM_TIMEOUT = 3 * 60 * 1000; //3 minutes public static final String WATCHDOG_ADDRESS = "watchdog@admin.resin.caucho"; private final BootResinConfig _bootManager; private String _id = ""; private ResinSystem _system; private WatchdogConfig _config; private WatchdogChild _watchdog; private ActorSender _conn; private Boot _jniBoot; private ResinGUI _ui; WatchdogClient(ResinSystem system, BootResinConfig bootManager, WatchdogConfig config) { _system = system; _bootManager = bootManager; _config = config; _id = config.getId(); } public WatchdogConfig getConfig() { return _config; } public String getId() { return _id; } public String getClusterId() { return _config.getCluster().getId(); } public int getIndex() { return _config.getIndex(); } public String getWatchdogAddress() { return _config.getWatchdogAddress(); } public int getWatchdogPort() { return _config.getWatchdogPort(); } String[] getArgv() { return _config.getArgv(); } Path getPwd() { return _config.getPwd(); } Path getResinHome() { return _bootManager.getResinHome(); } Path getRootDirectory() { return _bootManager.getRootDirectory(); } boolean hasXmx() { return _config.hasXmx(); } boolean hasXss() { return _config.hasXss(); } boolean is64bit() { return _config.is64bit(); } boolean isVerbose() { return _config.isVerbose(); } public String getGroupName() { return _config.getGroupName(); } public String getUserName() { return _config.getUserName(); } public Path getLogDirectory() { return _config.getLogDirectory(); } public Path getResinDataDirectory() { return _bootManager.getResinDataDirectory(); } public String getResinSystemAuthKey() { return _bootManager.getResinSystemAuthKey(); } public long getShutdownWaitTime() { return _config.getShutdownWaitTime(); } public int startConsole() throws IOException { if (_watchdog == null) _watchdog = new WatchdogChild(_system, _config); return _watchdog.startConsole(); } public void stopConsole() { _watchdog.stop(); } public int startGui(GuiCommand command) throws IOException { if (_ui != null && _ui.isVisible()) return 1; else if (_ui != null) return 0; _ui = new ResinGUI(command, this); _ui.setVisible(true); return 1; } // // watchdog commands // public String statusWatchdog() throws IOException { ActorSender conn = getConnection(); try { ResultStatus status = (ResultStatus) conn.query(WATCHDOG_ADDRESS, new WatchdogStatusQuery()); if (status.isSuccess()) return status.getMessage(); throw new RuntimeException(L.l("{0}: watchdog status failed because of '{1}'", this, status.getMessage())); } catch (Exception e) { Throwable e1 = e; while (e1.getCause() != null) e1 = e1.getCause(); log.log(Level.FINE, e.toString(), e); return e1.toString(); } } public Process startWatchdog(String []argv) throws ConfigException, IOException { if (getUserName() != null && ! hasBoot()) { String message = getTroubleshootMessage(); if (message == null) message = "Check the $RESIN_HOME/libexec or $RESIN_HOME/libexec64 directory for libresin_os.so."; throw new ConfigException(L.l("<user-name> requires compiled JNI.\n{0}", message)); } if (getGroupName() != null && ! hasBoot()) { String message = getTroubleshootMessage(); if (message == null) message = "Check the $RESIN_HOME/libexec or $RESIN_HOME/libexec64 directory for libresin_os.so."; throw new ConfigException(L.l("<group-name> requires compiled JNI.\n{0}", message)); } ActorSender conn = null; try { conn = getConnection(); ResultStatus status = (ResultStatus) conn.query(WATCHDOG_ADDRESS, new WatchdogStartQuery(argv), BAM_TIMEOUT); if (status.isSuccess()) return null; throw new ConfigException(L.l("{0}: watchdog start failed because of '{1}'", this, status.getMessage())); } catch (RemoteConnectionFailedException e) { log.log(Level.FINE, e.toString(), e); } catch (RuntimeException e) { throw e; } finally { if (conn != null) conn.close(); } return launchManager(argv); } public void stopWatchdog(String serverId) { ActorSender conn = getConnection(); try { ResultStatus status = (ResultStatus) conn.query(WATCHDOG_ADDRESS, new WatchdogStopQuery(serverId), BAM_TIMEOUT); if (! status.isSuccess()) throw new RuntimeException(L.l("{0}: watchdog '{1}' stop failed because of '{2}'", this, serverId, status.getMessage())); } catch (RuntimeException e) { throw e; } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } } public void killWatchdog(String serverId) throws IOException { ActorSender conn = getConnection(); try { ResultStatus status = (ResultStatus) conn.query(WATCHDOG_ADDRESS, new WatchdogKillQuery(serverId), BAM_TIMEOUT); if (! status.isSuccess()) throw new RuntimeException(L.l("{0}: watchdog kill failed because of '{1}'", this, status.getMessage())); } catch (RuntimeException e) { throw e; } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } } public void restartWatchdog(String id, String []argv) throws IOException { // cloud/1295 ActorSender conn = getConnection(); try { ResultStatus status = (ResultStatus) conn.query(WATCHDOG_ADDRESS, new WatchdogRestartQuery(id, argv), BAM_TIMEOUT); if (! status.isSuccess()) throw new RuntimeException(L.l("{0}: watchdog restartfailed because of '{1}'", this, status.getMessage())); } catch (RuntimeException e) { throw e; } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } } public boolean shutdown() throws IOException { ActorSender conn = getConnection(); try { ResultStatus status = (ResultStatus) conn.query(WATCHDOG_ADDRESS, new WatchdogShutdownQuery(), BAM_TIMEOUT); if (! status.isSuccess()) throw new RuntimeException(L.l("{0}: watchdog shutdown failed because of '{1}'", this, status.getMessage())); } catch (RuntimeException e) { throw e; } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } return true; } private ActorSender getConnection() { if (_conn == null) { String url = ("http://" + getWatchdogAddress() + ":" + getWatchdogPort() + "/hmtp"); HmtpClient client = new HmtpClient(url); client.setVirtualHost("admin.resin"); String uid = ""; client.setEncryptPassword(true); client.connect(uid, getResinSystemAuthKey()); _conn = client; } return _conn; } public Process launchManager(String []argv) throws IOException { System.out.println(L.l("Resin/{0} launching watchdog at {1}:{2}", VersionFactory.getVersion(), getWatchdogAddress(), getWatchdogPort())); log.fine(this + " starting ResinWatchdogManager"); Path resinHome = getResinHome(); Path resinRoot = getRootDirectory(); ProcessBuilder builder = new ProcessBuilder(); builder.directory(new File(resinRoot.getNativePath())); Map<String,String> env = builder.environment(); env.putAll(System.getenv()); String classPath = WatchdogArgs.calculateClassPath(resinHome); env.put("CLASSPATH", classPath); String libexecPath; if (is64bit()) { libexecPath = resinHome.lookup("libexec64").getNativePath(); appendEnvPath(env, "LD_LIBRARY_PATH", libexecPath); appendEnvPath(env, "LD_LIBRARY_PATH_64", libexecPath); appendEnvPath(env, "DYLD_LIBRARY_PATH", libexecPath); if (CauchoSystem.isWindows()) appendEnvPath(env, "Path", resinHome.lookup("win64").getNativePath()); } else { libexecPath = resinHome.lookup("libexec").getNativePath(); appendEnvPath(env, "LD_LIBRARY_PATH", libexecPath); appendEnvPath(env, "DYLD_LIBRARY_PATH", libexecPath); if (CauchoSystem.isWindows()) appendEnvPath(env, "Path", resinHome.lookup("win32").getNativePath()); } ArrayList<String> list = new ArrayList<String>(); list.add(_config.getJavaExe()); // #3759 - user args are first so they're displayed by ps list.addAll(_config.getWatchdogJvmArgs()); list.add("-Dresin.watchdog=" + _id); list.add("-Djava.util.logging.manager=com.caucho.log.LogManagerImpl"); list.add("-Djavax.management.builder.initial=com.caucho.jmx.MBeanServerBuilderImpl"); list.add("-Djava.awt.headless=true"); list.add("-Dresin.home=" + resinHome.getFullPath()); list.add("-Dresin.root=" + resinRoot.getFullPath()); for (int i = 0; i < argv.length; i++) { if (argv[i].startsWith("-Djava.class.path=")) { // IBM JDK startup issues } else if (argv[i].startsWith("-J")) { list.add(argv[i].substring(2)); } } // #2566 list.add("-Xrs"); if (! _config.hasWatchdogXss()) list.add("-Xss256k"); if (! _config.hasWatchdogXmx()) list.add("-Xmx32m"); // XXX: can this just be copied from original args? if (! list.contains("-d32") && ! list.contains("-d64") && is64bit() && ! CauchoSystem.isWindows()) { list.add("-d64"); } if (! list.contains("-server") && ! list.contains("-client") && ! CauchoSystem.isWindows()) { // #3331, windows can't add -server automatically list.add("-server"); } list.add("com.caucho.boot.WatchdogManager"); for (int i = 0; i < argv.length; i++) { if (argv[i].equals("-conf") || argv[i].equals("--conf")) { list.add(argv[i]); list.add(resinHome.lookup(argv[i + 1]).getNativePath()); i++; } else list.add(argv[i]); } list.add("--log-directory"); list.add(getLogDirectory().getFullPath()); builder = builder.command(list); // builder.redirectErrorStream(true); Process process = null; try { process = builder.start(); } catch(Exception e) { e.printStackTrace(); } InputStream stdIs = process.getInputStream(); InputStream stdErr = process.getErrorStream(); OutputStream stdOs = process.getOutputStream(); ProcessThreadReader reader = new ProcessThreadReader(stdIs); reader.setDaemon(true); reader.start(); ProcessThreadReader errorReader = new ProcessThreadReader(stdErr); errorReader.setDaemon(true); errorReader.start(); try { Thread.sleep(1000); } catch (Exception e) { } // stdIs.close(); stdOs.close(); return process; } public static void appendEnvPath(Map<String,String> env, String prop, String value) { String oldValue = env.get(prop); if (oldValue == null && CauchoSystem.isWindows()) { String winProp = prop.toUpperCase(Locale.ENGLISH); oldValue = env.get(winProp); if (oldValue != null) prop = winProp; } if (oldValue != null && ! "".equals(oldValue)) value = value + File.pathSeparator + oldValue; env.put(prop, value); } @Override public String toString() { return getClass().getSimpleName() + "[" + getId() + "]"; } Boot getJniBoot() { if (_jniBoot != null) return _jniBoot.isValid() ? _jniBoot : null; try { ClassLoader loader = Thread.currentThread().getContextClassLoader(); Class<?> cl = Class.forName("com.caucho.bootjni.JniBoot", false, loader); _jniBoot = (Boot) cl.newInstance(); } catch (ClassNotFoundException e) { log.fine(e.toString()); } catch (IllegalStateException e) { log.fine(e.toString()); } catch (Throwable e) { log.log(Level.FINE, e.toString(), e); } return _jniBoot != null && _jniBoot.isValid() ? _jniBoot : null; } private boolean hasBoot() { try { Boot boot = getJniBoot(); return boot != null && boot.isValid(); } catch (Throwable e) { log.log(Level.FINE, e.toString(), e); } return false; } private String getTroubleshootMessage() { Boot boot = getJniBoot(); if (_jniBoot != null) boot = _jniBoot; return boot != null ? boot.getValidationMessage() : null; } static class ProcessThreadReader extends Thread { private InputStream _is; ProcessThreadReader(InputStream is) { _is = is; } public void run() { try { int ch; while ((ch = _is.read()) >= 0) { System.out.print((char) ch); } } catch (Exception e) { } } } }