/* 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.
* <p/>
* 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.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
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;
// 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;
public void init()
{
super.init();
_key = "" + new Random(System.currentTimeMillis()).nextLong();
_config.setProperty("wrapper.key", _key);
if (_controller == null)
{
_controller = new JVMController(this);
configController();
}
}
protected void setState(int state)
{
super.setState(state);
if (state == STATE_IDLE)
{
removeJavaPidFile();
}
}
/**
* Config process.
*/
void configProcess()
{
// _osProcess.destroy();
if (pipeStreams())
{
// _osProcess.setPipeStreams(true, false);
_teeName = _key + "$" + System.currentTimeMillis();
_config.setProperty("wrapper.teeName", _teeName);
_osProcess.setTeeName(_teeName);
_tmpPath = _config.getString("wrapper.tmp.path");
if (_tmpPath == null)
_tmpPath = System.getProperty("java.io.tmpdir");
if (_tmpPath == null)
_tmpPath = "/tmp";
File t = new File(_tmpPath);
if (!t.exists())
t.mkdirs();
_tmpPath = t.getAbsolutePath();
_osProcess.setTmpPath(_tmpPath);
}
JavaHome javaHome = OperatingSystem.instance().getJavaHome(_config);
String java = javaHome.findJava();
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);
_osProcess.setPipeStreams(false, false);
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;
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("-D" + key + "=" + _config.getCachedPath()));
}
else
result.add(checkValue("-D" + key + "=" + _config.getProperty(key).toString()));
}
result.add("-Dwrapper.port=" + controller.getPort());
result.add("-Dwrapper.key=" + controller.getKey());
result.add("-Dwrapper.teeName=" + _teeName);
result.add("-Dwrapper.tmpPath=" + _tmpPath);
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("-Dwrapper.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);
}
result.add(checkValue(sb.toString()));
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);
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())
{
sb.append("-Djava.library.path=");
for (Iterator it = _config.getKeys("wrapper.java.library.path"); it.hasNext();)
{
String key = (String) it.next();
sb.append(_config.getString(key));
if (it.hasNext())
sb.append(PATHSEP);
}
result.add(checkValue(sb.toString()));
}
if (_config.getBoolean("wrapper.service", false) && !hasXrs)
{
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");
long xmx = 0;
long xmxr = 0;
long xms = 0;
long xmsr = 0;
OperatingSystem.instance().systemInformation().setLogger(this.getWrapperLogger());
long totalRAM = OperatingSystem.instance().systemInformation().totalRAM();
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 > 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)
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);
}
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)
{
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)
{
files.addAll(FileUtils.getFiles(workingDir, jar));
}
for (Iterator it = configList.listIterator(); it.hasNext();)
{
String file = _config.getString((String) it.next());
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;
}
/**
* Config controller.
*/
void configController()
{
JVMController controller = (JVMController) _controller;
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())
{
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);
}
}
};
controller.addListener(JVMController.STATE_STARTUP_TIMEOUT, restartHandler);
controller.addListener(JVMController.STATE_PING_TIMEOUT, restartHandler);
controller.addListener(JVMController.STATE_PROCESS_KILLED, restartHandler);
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);
_localConfiguration.setProperty("wrapper.key", key);
_localConfiguration.setProperty("wrapper.port", port);
_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.
*/
public void requestThreadDump()
{
if (_controller != null)
{
JVMController controller = (JVMController) _controller;
controller.requestThreadDump();
}
}
void stopController(int timeout)
{
JVMController controller = (JVMController) _controller;
controller.stop(JVMController.STATE_USER_STOP);
}
public String getType()
{
return "Java-" + super.getType();
}
public void setServiceStartupListener(Runnable serviceStartupListener)
{
_serviceStartupListener = serviceStartupListener;
}
}