/* This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. *

* This library 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. See the GNU * Lesser General Public License for more details. */ package org.rzo.yajsw.wrapper; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicLong; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.rzo.yajsw.Constants; import org.rzo.yajsw.app.WrapperMainServiceWin; import org.rzo.yajsw.boot.WrapperLoader; import org.rzo.yajsw.controller.AbstractController.ControllerListener; import org.rzo.yajsw.controller.jvm.JVMController; import org.rzo.yajsw.os.JavaHome; import org.rzo.yajsw.os.OperatingSystem; import org.rzo.yajsw.util.Utils; import com.sun.jna.Platform; import com.sun.jna.PlatformEx; // TODO: Auto-generated Javadoc /** * The Class WrappedJavaProcess. */ public class WrappedJavaProcess extends AbstractWrappedProcess { /** The _key. */ String _key; /** The _tee name. */ String _teeName; /** The _java pid file. */ File _javaPidFile; boolean _initController = false; Runnable _serviceStartupListener = null; final private static Random RANDOM = new Random(); final private static AtomicLong FILE_COUNTER = new AtomicLong(System.currentTimeMillis()); public void init() { super.init(); _key = "" + RANDOM.nextLong(); _localConfiguration.setProperty("wrapper.key", _key); if (_controller == null) { _controller = new JVMController(this); configController(); } } protected boolean pipeStreams() { return super.pipeStreams() || _teeName != null; } protected void setState(int state) { super.setState(state); if (state == STATE_IDLE) { removeJavaPidFile(); } } /** * Config process. */ void configProcess() { // _osProcess.destroy(); _osProcess.setTmpPath(_tmpPath); if (!super.pipeStreams()) { // _osProcess.setPipeStreams(true, false); _teeName = _key + "$" + FILE_COUNTER.incrementAndGet(); _localConfiguration.setProperty("wrapper.teeName", _teeName); _osProcess.setTeeName(_teeName); _osProcess.setPipeStreams(false, false); } else { _osProcess.setPipeStreams(true, true); _osProcess.setTeeName(null); if (!_haltAppOnWrapper) getWrapperLogger().log(Level.WARNING, "WARNING: application streams are piped, but wrapper.control setting may cause zombie processes. Please set to TIGHT"); } JavaHome javaHome = OperatingSystem.instance().getJavaHome(_config); javaHome.setLogger(getInternalWrapperLogger()); String java = javaHome.findJava(_config.getString("wrapper.java.command"), _config.getString("wrapper.java.customProcName")); if (java == null) getWrapperLogger().log(Level.SEVERE, "ERROR: could not get java command"); List jvmOptions = jvmOptions(); List wrapperOptions = wrapperOptions(); String mainClass = getMainClass(); List command = new ArrayList(); command.add(java); command.addAll(jvmOptions); command.addAll(wrapperOptions); command.add(mainClass); String[] arrCmd = new String[command.size()]; for (int i = 0; i < arrCmd.length; i++) arrCmd[i] = (String) command.get(i); _osProcess.setCommand(arrCmd); super.configProcess(); } protected String getMainClass() { return _config.getString("wrapper.java.mainclass", "org.rzo.yajsw.app.WrapperJVMMain"); } /** * Wrapper options. * * @return the string */ private List wrapperOptions() { ArrayList result = new ArrayList(); JVMController controller = (JVMController) _controller; result.add(Utils.getDOption("wrapper.port", "" + controller.getPort())); result.add(Utils.getDOption("wrapper.key", controller.getKey())); if (_teeName != null) result.add(Utils.getDOption("wrapper.teeName", _teeName)); result.add(Utils.getDOption("wrapper.tmp.path", _tmpPath)); result.add(Utils.getDOption("jna_tmpdir", _tmpPath)); for (Iterator it = _config.getSystemConfiguration().getKeys("wrapper"); it.hasNext();) { String key = (String) it.next(); if (("wrapper.service".equals(key) || "wrapper.console.visible".equals(key)) && _config.getBoolean("wrapper.service", false)) continue; if ("wrapper.config".equals(key)) { result.add(checkValue(Utils.getDOption(key, _config.getCachedPath()))); } else { String opt = Utils.getDOption(key, _config.getProperty(key).toString()); if (!result.contains(opt)) result.add(checkValue(opt)); } } String gcPattern = _config.getString("wrapper.java.monitor.gc", null); if ((gcPattern != null) && (gcPattern.length() > 0)) { gcPattern = gcPattern.replaceAll(",", "\\\\,"); result.add(Utils.getDOption("wrapper.java.monitor.gc", gcPattern)); } String preScript = _config.getString("wrapper.app.pre.script", null); if (preScript != null & !"".equals(preScript)) try { File f = new File(preScript); if (!f.exists()) getWrapperLogger().warning("app.pre.script not found: " + preScript); else { preScript = checkValue(f.getCanonicalPath()); result.add(Utils.getDOption("wrapper.app.pre.script", preScript)); } } catch (Exception ex) { getWrapperLogger().log(Level.SEVERE, "WrappedJavaProcess wrapperOptions", ex); } return result; } /** * Jvm options. * * @return the string */ private List jvmOptions() { ArrayList result = new ArrayList(); result.add("-classpath"); StringBuffer sb = new StringBuffer(); sb.append(WrapperLoader.getWrapperAppJar().trim()); StringBuilder appCp = getAppClassPath(_config.getString("wrapper.working.dir", "."), _config.getKeys("wrapper.java.classpath")); if (appCp != null && appCp.length() > 0) { sb.append(PATHSEP); sb.append(appCp); } String cp = sb.toString(); if (cp.contains(" ") && Platform.isWindows()) cp = "\"" + cp + "\""; result.add(checkValue(cp)); boolean hasXrs = false; boolean hasXmx = false; boolean hasXms = false; for (Iterator it = _config.getKeys("wrapper.java.additional"); it.hasNext();) { String key = (String) it.next(); String value = _config.getString(key); if (value == null) continue; result.add(checkValue(value)); hasXrs |= value.contains("-Xrs"); hasXmx |= value.contains("-Xmx"); hasXms |= value.contains("-Xms"); } sb = new StringBuffer(); if (_config.getKeys("wrapper.java.library.path").hasNext()) { for (Iterator it = _config.getKeys("wrapper.java.library.path"); it.hasNext();) { String key = (String) it.next(); if (_config.getString(key) == null) continue; sb.append(checkValue(_config.getString(key))); if (it.hasNext()) sb.append(PATHSEP); } result.add(Utils.getDOption("java.library.path", sb.toString())); } if (_config.getBoolean("wrapper.service", false) && !hasXrs && _config.getBoolean("wrapper.ntservice.reduce_signals", true)) { result.add("-Xrs"); } if (_config.getBoolean("wrapper.service", false)) { result.add("-Dwrapper.service=true"); result.add("-Dwrapper.console.visible=false"); } else if (_config.getBoolean("wrapper.console.visible", Constants.DEFAULT_CONSOLE_VISIBLE)) result.add("-Dwrapper.console.visible=true"); if (_config.containsKey("wrapper.java.initmemory") || _config.containsKey("wrapper.java.initmemory.relative") || _config.containsKey("wrapper.java.maxmemory") || _config.containsKey("wrapper.java.maxmemory.relative")) { long xmx = 0; long xmxr = 0; long xms = 0; long xmsr = 0; OperatingSystem.instance().systemInformation().setLogger(this.getWrapperLogger()); long totalRAM = 0; if (!hasXms) { try { xms = _config.getLong("wrapper.java.initmemory", 0); xmsr = _config.getLong("wrapper.java.initmemory.relative", 0); } catch (Exception ex) { getWrapperLogger().info("error in wrapper.java.initmemory " + ex.getMessage()); } if (xmsr > 0) totalRAM = OperatingSystem.instance().systemInformation().totalRAM(); if (xmsr > 0 && totalRAM > 0) xms = (totalRAM * xmsr) / 100 / (1024 * 1024); if (xms > 0) { result.add("-Xms" + xms + "m"); } } if (!hasXmx) { try { xmx = _config.getLong("wrapper.java.maxmemory", 0); xmxr = _config.getLong("wrapper.java.maxmemory.relative", 0); } catch (Exception ex) { getWrapperLogger().info("error in wrapper.java.maxmemory " + ex.getMessage()); } if (xmxr > 0 && totalRAM == 0) totalRAM = OperatingSystem.instance().systemInformation().totalRAM(); if (xmxr > 0 && totalRAM > 0) xmx = (totalRAM * xmxr) / 100 / (1024 * 1024); if (xmx > 0) { if (xmx < xms) xmx = xms; if (xmx < 3) xmx = 3; result.add("-Xmx" + xmx + "m"); } } } int port = _config.getInt("wrapper.java.debug.port", -1); if (port != -1) { result.add("-Xdebug"); result.add("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=" + port); } String preMainScript = _config.getString("wrapper.app.pre_main.script", null); if (preMainScript != null && preMainScript.length() > 0) result.add(Utils.getDOption("wrapper.app.pre_main.script",preMainScript)); // if we are running as service "remember" the system properties and env // vars we have used if (_config.getBoolean("wrapper.service", false) || _config.getBoolean("wrapper.console.use_interpolated", true)) { for (Entry e : _config.getEnvLookupSet().entrySet()) { String opt = Utils.getDOption(e.getKey(), e.getValue()); if (opt != null && !result.contains(opt)) result.add(opt); } } // TODO ??? there seems to be a bug in windows or java socket ??? // when piping stdout/stderr streams netty client connect hangs // it does not hang when jmxremote is set, or in debug mode. // probably due to open of socket in launcher before java code is // executed // jmxremote causes problems with jboss7 loading java.util.Log /* * if (Platform.isWindows() && * _config.getBoolean("wrapper.console.pipestreams")) if * (!result.contains("-Dcom.sun.management.jmxremote")) * result.add("-Dcom.sun.management.jmxremote"); */ return result; } // call to java "-Ddir=c:\" will cause a parse exception in the java // launcher private String checkValue(String value) { value = value.trim(); if (value.endsWith("\\") && !value.endsWith("\\\\")) value += "\\"; return value; } /** * Gets the app class path. * * @param workingDir * the working dir * @param config * the config * * @return the app class path */ private StringBuilder getAppClassPath(String workingDir, Iterator keys) { workingDir = workingDir.replaceAll("\"", ""); List configList = new ArrayList(); for (Iterator it = keys; it.hasNext();) { configList.add(it.next()); } Collections.sort(configList, new AlphanumComparator()); List files = new ArrayList(); String jar = _config.getString("wrapper.java.app.jar", null); if (jar != null) { jar = jar.replaceAll("\"", ""); Collection jars = FileUtils.getFiles(workingDir, jar); files.addAll(jars); files.addAll(classpathFromJar(jars, workingDir)); } for (Iterator it = configList.listIterator(); it.hasNext();) { String file = _config.getString((String) it.next()); file = file.replaceAll("\"", ""); if (file == null) continue; files.addAll(FileUtils.getFiles(workingDir, file)); } StringBuilder sb = new StringBuilder(); for (Iterator it = files.iterator(); it.hasNext();) { try { sb.append(((File) it.next()).getCanonicalPath()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (it.hasNext()) sb.append(PATHSEP); } return sb; } private Collection classpathFromJar(Collection jars, String workingDir) { Collection result = new ArrayList(); URL url = null; for (Object jar : jars) { try { url = ((File) jar).toURI().toURL(); } catch (MalformedURLException e2) { e2.printStackTrace(); continue; } Manifest manifest; try { manifest = new JarFile((File) jar).getManifest(); } catch (IOException e1) { e1.printStackTrace(); continue; } Attributes attr = manifest.getMainAttributes(); String cl = attr.getValue("Class-Path"); ClassLoader loader = null; if (cl != null) { String[] clArr = cl.split(" "); for (int i = 0; i < clArr.length; i++) { String file = clArr[i]; Collection myFile; try { myFile = FileUtils.getFiles(workingDir, file); result.addAll(myFile); } catch (Exception e) { e.printStackTrace(); } } } } return result; } /** * Config controller. */ void configController() { JVMController controller = (JVMController) _controller; if (_config.getBoolean("wrapper.java.monitor.gc.restart", false)) { long max = _config.getLong("wrapper.java.monitor.gc.threshold", -1); controller.setMaxFullGCTimeRestart(max); } if (_config.getBoolean("wrapper.java.monitor.heap.restart", false)) { long max = _config.getLong("wrapper.java.monitor.heap.threshold.percent", -1); controller.setMaxHeapRestart(max); } controller.setLogger(getWrapperLogger()); controller.setKey(_config.getString("wrapper.key")); if (_config.containsKey("wrapper.port")) { controller.setMinPort(_config.getInt("wrapper.port")); controller.setMaxPort(_config.getInt("wrapper.port")); } else { controller.setMinPort(_config.getInt("wrapper.port.min", Constants.DEFAULT_PORT)); controller.setMaxPort(_config.getInt("wrapper.port.max", 65535)); } controller.setStartupTimeout(_config.getInt("wrapper.startup.timeout", DEFAULT_STARTUP_TIMEOUT) * 1000); controller.setPingTimeout(_config.getInt("wrapper.ping.timeout", DEFAULT_PING_TIMEOUT) * 1000); if (!_initController) { ControllerListener restartHandler = new ControllerListener() { public void fire() { if (_state == STATE_RESTART_STOP || _state == STATE_RESTART || _state == STATE_RESTART_START || _state == STATE_RESTART_WAIT) return; if (allowRestart() && exitCodeRestart() && !exitCodeShutdown() && !exitCodeStop()) { restartInternal(); } else { if (_debug) { getWrapperLogger().info("giving up after " + _restartCount + " retries"); } if (_state != STATE_USER_STOP) setState(STATE_ABORT); if (!_exiting) stop(); setState(STATE_IDLE); if (exitCodeShutdown()) stopWrapper(); } } }; ControllerListener killedRestartHandler = new ControllerListener() { public void fire() { if (_state == STATE_RESTART_STOP || _state == STATE_RESTART || _state == STATE_RESTART_WAIT) return; if (allowRestart() && exitCodeRestart() && !exitCodeShutdown() && !exitCodeStop()) { restartInternal(); } else { if (_debug) { getWrapperLogger().info("giving up after " + _restartCount + " retries"); } if (_state != STATE_USER_STOP) setState(STATE_ABORT); if (!_exiting) stop(); setState(STATE_IDLE); if (exitCodeShutdown()) stopWrapper(); } } }; controller.addListener(JVMController.STATE_STARTUP_TIMEOUT, restartHandler); controller.addListener(JVMController.STATE_THRESHOLD, restartHandler); controller.addListener(JVMController.STATE_PING_TIMEOUT, restartHandler); controller.addListener(JVMController.STATE_PROCESS_KILLED, killedRestartHandler); if (!_config.getBoolean("wrapper.ntservice.autoreport.startup", true)) if (getService() instanceof WrapperMainServiceWin) setServiceStartupListener(new Runnable() { public void run() { ((WrapperMainServiceWin) getService()).notifyStartup(); } }); controller.setServiceStartupListener(_serviceStartupListener); controller.init(); _initController = true; } } void postStart() { saveJavaPidFile(); } // test main /** * The main method. * * @param args * the arguments */ public static void main(String[] args) { WrappedProcess[] w = new WrappedProcess[20]; for (int i = 0; i < w.length; i++) { w[i] = new WrappedJavaProcess(); w[i].getLocalConfiguration().setProperty("wrapper.config", "conf/wrapper.helloworld.conf"); w[i].getLocalConfiguration().setProperty("wrapper.debug", "true"); w[i].setUseSystemProperties(false); w[i].init(); } boolean done = false; while (!done) { // done = true; for (int i = 0; i < w.length; i++) { System.out.println("starting " + i); w[i].start(); } try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } for (int i = 0; i < w.length; i++) { System.out.println("stopping " + i); w[i].stop(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } } } /** * Save java pid file. */ void saveJavaPidFile() { String file = _config.getString("wrapper.java.pidfile"); if (file != null) { try { _javaPidFile = new File(file); if (!_javaPidFile.exists()) _javaPidFile.createNewFile(); FileWriter out = new FileWriter(_javaPidFile, false); out.write("" + getAppPid()); out.flush(); out.close(); if (_debug) getWrapperLogger().info("created jva.pid file " + _javaPidFile.getAbsolutePath()); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * Removes the java pid file. */ void removeJavaPidFile() { if (_javaPidFile != null) { try { _javaPidFile.delete(); if (_debug) getWrapperLogger().info("removed java.pid file " + _javaPidFile.getAbsolutePath()); _javaPidFile = null; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * Reconnect. * * @param pid * the pid * * @return true, if successful */ public boolean reconnect(int pid) { if (_state != STATE_IDLE) return false; _osProcess = OperatingSystem.instance().processManagerInstance().getProcess(pid); if (_osProcess == null) return false; String cmd = _osProcess.getCommand(); if (cmd == null) return false; String key = getPropertyFromCommandLine("wrapper.key=[^ \"]*", cmd); if (key == null) return false; String port = getPropertyFromCommandLine("wrapper.port=[^ \"]*", cmd); if (port == null) return false; String configFile = getPropertyFromCommandLine("wrapper.config=[^ \"]*", cmd); String teeName = getPropertyFromCommandLine("wrapper.teeName=[^ \"]*", cmd); String tmpPath = getPropertyFromCommandLine("wrapper.tmpPath=[^ \"]*", cmd); if (tmpPath == null) { tmpPath = getPropertyFromCommandLine("wrapper.tmp.path=[^ \"]*", cmd); } _localConfiguration.setProperty("wrapper.key", key); _localConfiguration.setProperty("wrapper.port", port); if (teeName != null) _localConfiguration.setProperty("wrapper.teeName", teeName); _localConfiguration.setProperty("wrapper.tmpPath", tmpPath); if (configFile != null) _localConfiguration.setProperty("wrapper.config", configFile); setReconnecting(true); super.init(); _osProcess.setTeeName(teeName); _osProcess.setTmpPath(tmpPath); _osProcess.reconnectStreams(); if (_controller == null) _controller = new JVMController(this); JVMController controller = (JVMController) _controller; // controller.setDebug(true); configController(); _firstRestartTime = System.currentTimeMillis(); // controller.setDebug(true); controller.start(); controller.processStarted(); setState(STATE_RUNNING); boolean result = controller.waitFor(_config.getInt("wrapper.ping.timeout", DEFAULT_PING_TIMEOUT) * 1000); if (result) { // wait for stream to be available for (int i = 0; i < 5 && _osProcess.getInputStream() == null; i++) try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } Map triggerActions = getTriggerActions(); Map regexTriggerActions = getRegexTriggerActions(); Map missingTriggerActions = getMissingTriggerActions(); Map missingRegexTriggerActions = getMissingRegexTriggerActions(); _gobler_in = new Gobler(_osProcess.getInputStream(), getAppLogger(), triggerActions, regexTriggerActions, missingTriggerActions, missingRegexTriggerActions, "OUTPUT " + _osProcess.getPid(), _osProcess.getPid()); _gobler_err = new Gobler(_osProcess.getErrorStream(), getAppLogger(), triggerActions, regexTriggerActions, missingTriggerActions, missingRegexTriggerActions, "ERROR " + _osProcess.getPid(), _osProcess.getPid()); executor.execute(_gobler_err); executor.execute(_gobler_in); setState(STATE_RUNNING); saveJavaPidFile(); saveLockFile(); } return result; } /** * Gets the property from command line. * * @param pattern * the pattern * @param cmd * the cmd * * @return the property from command line */ private String getPropertyFromCommandLine(String pattern, String cmd) { String result = null; Pattern p = Pattern.compile(pattern); Matcher m = p.matcher(cmd); if (m.find()) result = m.group(); if (result != null && result.length() > 0) return result.substring(result.indexOf("=") + 1).replaceAll("'", ""); return null; } /** * Gets the tee name. * * @return the tee name */ public String getTeeName() { return _teeName; } /** * Request thread dump. */ /** * Request thread dump. */ public void requestThreadDump() { if (_controller != null) { JVMController controller = (JVMController) _controller; controller.requestThreadDump(); } } public void requestGc() { if (_controller != null) { JVMController controller = (JVMController) _controller; controller.requestGc(); } } public void requestDumpHeap(String fileName) { if (_controller != null) { JVMController controller = (JVMController) _controller; controller.requestDumpHeap(fileName); } } void stopController(int timeout, String reason) { JVMController controller = (JVMController) _controller; controller.stop(JVMController.STATE_USER_STOP, reason); String shutdownScript = _config.getString("wrapper.app.shutdown.script", null); if (shutdownScript != null && !"".equals(shutdownScript)) { getWrapperLogger().log(Level.FINEST, "waiting for shutdownScript to terminate process"); _osProcess.waitFor(timeout); } } public String getType() { return "Java-" + super.getType(); } public void setServiceStartupListener(Runnable serviceStartupListener) { _serviceStartupListener = serviceStartupListener; } protected void reloadConfiguration() { super.reloadConfiguration(); } public float getHeapPercent() { if (_controller == null) return -1; return ((JVMController) _controller).getHeap(); } public long getMinorGCTime() { if (_controller == null) return -1; return ((JVMController) _controller).getMinGC(); } public long getFullGCTime() { if (_controller == null) return -1; return ((JVMController) _controller).getFullGC(); } public long getHeapInBytes() { if (_controller == null) { return -1; } return ((JVMController) _controller).getHeapInBytes(); } }