package org.rzo.yajsw.wrapper;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.lang.reflect.Method;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import javax.management.remote.JMXAuthenticator;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXPrincipal;
import javax.management.remote.JMXServiceURL;
import javax.security.auth.Subject;
import org.apache.commons.collections.MultiMap;
import org.apache.commons.collections.map.MultiValueMap;
import org.apache.commons.configuration.BaseConfiguration;
import org.apache.commons.configuration.Configuration;
import org.rzo.yajsw.Constants;
import org.rzo.yajsw.action.Action;
import org.rzo.yajsw.action.ActionFactory;
import org.rzo.yajsw.cache.Cache;
import org.rzo.yajsw.condition.Condition;
import org.rzo.yajsw.config.YajswConfigurationImpl;
import org.rzo.yajsw.controller.Message;
import org.rzo.yajsw.controller.jvm.Controller;
import org.rzo.yajsw.io.CircularBuffer;
import org.rzo.yajsw.log.DateFileHandler;
import org.rzo.yajsw.log.MyLogger;
import org.rzo.yajsw.log.PatternFormatter;
import org.rzo.yajsw.os.OperatingSystem;
import org.rzo.yajsw.os.Process;
import org.rzo.yajsw.os.StopableService;
import org.rzo.yajsw.os.ms.win.w32.ClusterNodeChangeListener;
import org.rzo.yajsw.script.Script;
import org.rzo.yajsw.script.ScriptFactory;
import org.rzo.yajsw.timer.Timer;
import org.rzo.yajsw.timer.TimerFactory;
import org.rzo.yajsw.tray.WrapperTrayIconFactory;
import org.rzo.yajsw.tray.ahessian.server.AHessianJmxServer;
import org.rzo.yajsw.util.DaemonThreadFactory;
import org.rzo.yajsw.util.SimpleThreadFactory;
import org.rzo.yajsw.util.Utils;
import com.sun.jna.Platform;
public abstract class AbstractWrappedProcess implements WrappedProcess, Constants, AbstractWrappedProcessMBean
{
/** The _os process. */
volatile public Process _osProcess;
/** The _controller. */
protected Controller _controller;
/** The _debug. */
protected boolean _debug = false;
/** The _config. */
protected YajswConfigurationImpl _config;
/** The _restarts. */
// protected int _restarts;
/** The _gobler_err. */
volatile protected Gobler _gobler_err;
/** The _gobler_in. */
volatile protected Gobler _gobler_in;
/** The Constant executor. */
protected static final Executor executor = Executors.newCachedThreadPool(new DaemonThreadFactory("wrappedProcess"));
protected static final Executor scriptExecutor = Executors.newCachedThreadPool(new SimpleThreadFactory("scriptExecutor"));
/** The _first restart time. */
protected long _firstRestartTime;
/** The _state. */
protected volatile int _state = STATE_IDLE;
/** The _startup exit codes. */
Set _startupExitCodes = new HashSet();
/** The _shutdown exit codes. */
Set _shutdownExitCodes = new HashSet();
/** The _exit code default restart. */
boolean _exitCodeDefaultRestart = false;
/** The _local configuration. */
protected Configuration _localConfiguration = new BaseConfiguration();
/** The _app logger. */
Logger _appLogger;
/** The _tmp path. */
protected String _tmpPath;
/** The _wrapper logger. */
Logger _wrapperLogger;
/** The _wrapper logger name. */
String _wrapperLoggerName = "wrapper";
/** The _app logger name. */
String _appLoggerName;
String _appLoggerPid;
/** The _use system properties. */
boolean _useSystemProperties = true;
/** The Constant PATHSEP. */
protected static final String PATHSEP = System.getProperty("path.separator");
/** The _restart count. */
int _restartCount;
int _totalRestartCount = 0;
/** The _timer. */
Timer _timer;
/** The _lock. */
FileLock _lock;
/** The _lock file. */
File _lockFile;
/** The _lock file channel. */
FileChannel _lockFileChannel;
/** The _pid file. */
File _pidFile;
/** The _successful invocation time. */
long _successfulInvocationTime;
MultiMap _listeners = MultiValueMap.decorate(new HashMap(), HashSet.class);
String _triggerLine;
Condition _condition;
Process _trayIconProcess;
volatile Date _appStarted;
volatile Date _appStopped;
Date _wrapperStarted;
volatile boolean _drainActive = false;
volatile int _exitCode = -99;
boolean _haltWrapperOnApp = false;
boolean _haltAppOnWrapper = false;
Object _cluster = null;
ClusterNodeChangeListener _clusterListener;
boolean _clusterTriggered = false;
Cache _cache = null;
boolean _exiting = false;
volatile TrayIconProxy _trayIconMessages = null;
Script _restartDelayScript = null;
Object _service = null;
boolean _stopRequested = false;
boolean _startRequested = false;
MBeanServer _mbeanServer = null;
AHessianJmxServer _ahessianServer = null;
boolean _reconnecting = false;
boolean _init = false;
public static final int MIN_PROCESS_LINES_TO_LOG = 40;
List<Thread> _shutdownHooks = new ArrayList<Thread>();
/**
* Inits the.
*/
@Override
public void init()
{
Map utils = new HashMap();
utils.put("util", new Utils(this));
_config = new YajswConfigurationImpl(_localConfiguration, _useSystemProperties, utils);
getWrapperLogger().info("init ");
if (!_config.isLocalFile())
if (_cache == null)
{
_cache = new Cache();
_cache.load(_config);
}
String dbg = _config.getString("wrapper.debug");
_debug = dbg == null ? false : dbg.equals("true");
_successfulInvocationTime = _config.getLong("wrapper.successful_invocation_time", DEFAULT_SUCCESSFUL_INVOCATION_TIME) * 1000;
String control = _config.getString("wrapper.control", DEFAULT_CONTROL);
if ("TIGHT".equals(control) || "WRAPPER".equals(control))
_haltWrapperOnApp = true;
if ("TIGHT".equals(control) || "APPLICATION".equals(control))
_haltAppOnWrapper = true;
for (Iterator it = _config.getKeys("wrapper.on_exit"); it.hasNext();)
{
String key = (String) it.next();
String value = _config.getString(key);
if ("RESTART".equals(value))
{
String postfix = key.substring(key.lastIndexOf(".") + 1);
if ("default".equals(postfix))
_exitCodeDefaultRestart = true;
else
try
{
_startupExitCodes.add(Integer.parseInt(postfix));
}
catch (Exception ex)
{
getWrapperLogger().info("error evaluating " + key + " " + ex.getMessage());
}
}
if ("SHUTDOWN".equals(value))
{
String postfix = key.substring(key.lastIndexOf(".") + 1);
if ("default".equals(postfix))
// do nothing
{
}
else
try
{
_shutdownExitCodes.add(Integer.parseInt(postfix));
}
catch (Exception ex)
{
getWrapperLogger().info("error evaluating " + key + " " + ex.getMessage());
}
}
}
if (_timer == null)
_timer = TimerFactory.createTimer(_config, this);
_timer.init();
if (_condition == null)
_condition = new Condition(_config, this, getWrapperLogger());
_condition.init();
_restartCount = 0;
// in case of active triggers control == LOOSE
if (_timer.isHasTrigger() || _condition.isHasTrigger())
{
_haltWrapperOnApp = false;
// do not halt app on wrapper -> service stop takes too long
// _haltAppOnWrapper = false;
}
// if we need the tray or jmx -> create and register a wrapper mbean
if (_config.getBoolean("wrapper.tray", false) || _config.getBoolean("wrapper.jmx", false))
registerMBean();
// if we need a try -> start asynch hessian jmx remote service & create
// a tray proxy for displaying messages
if (_config.getBoolean("wrapper.tray", false))
{
startAhessianService();
_trayIconMessages = new TrayIconProxy();
}
// if we are not running as a sevice -> spawn the tray icon as a
// separate process
if ((!_reconnecting) && _config.getBoolean("wrapper.tray", false) && _trayIconProcess == null && !isService())
{
_trayIconProcess = WrapperTrayIconFactory.startTrayIconProcess(_config);
}
// if jmx is required -> start jmx rmi remote service
if (_config.getBoolean("wrapper.jmx", false))
startJMXRmiService();
configStateChangeListeners();
configShutdownHook();
String clusterScript = _config.getString("wrapper.windows.cluster.script", null);
configClusterScript(clusterScript);
_init = true;
}
private void configClusterScript(String clusterScript)
{
if (clusterScript != null && !"".equals(clusterScript))
{
List args = _config.getList("wrapper.windows.cluster.script.args", new ArrayList());
int timeout = _config.getInt("wrapper.windows.cluster.script.timeout", 0);
final Script script = ScriptFactory.createScript(clusterScript, "", this, args, getWrapperLogger(), timeout);
if (script == null)
return;
try
{
Class clazz = this.getClass().getClassLoader().loadClass("org.rzo.yajsw.os.ms.win.w32.Cluster");
_cluster = clazz.newInstance();
_clusterListener = new ClusterNodeChangeListener()
{
@Override
public void nodeChanged()
{
script.execute();
}
};
Method m = clazz.getMethod("addNodeChangeListener", ClusterNodeChangeListener.class);
m.invoke(_cluster, _clusterListener);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
private void startCluster()
{
try
{
Class clazz = this.getClass().getClassLoader().loadClass("org.rzo.yajsw.os.ms.win.w32.Cluster");
_clusterTriggered = true;
_clusterListener.nodeChanged();
Method m = clazz.getMethod("start");
m.invoke(_cluster);
}
catch (Exception e)
{
e.printStackTrace();
}
}
private void configShutdownHook()
{
// on windows services: shutdown is handeled by the onStop method not by
// the hook
if (OperatingSystem.instance().getOperatingSystemName().toLowerCase().startsWith("windows") && isService())
return;
if (_haltAppOnWrapper)
{
Thread hook = new Thread()
{
@Override
public void run()
{
getWrapperLogger().info("Shutting down Wrapper");
if (!_exiting)
{
setExiting();
AbstractWrappedProcess.this.stop();
AbstractWrappedProcess.this.shutdown();
}
// give eventually running scripts time to terminate
try
{
Thread.sleep(5000);
}
catch (InterruptedException e)
{
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
};
Runtime.getRuntime().addShutdownHook(hook);
// remember the hook so that we can remove it to avoid mem leaks
_shutdownHooks.add(hook);
}
}
private void configStateChangeListeners()
{
Iterator listeners = _config.getKeys("wrapper.script");
for (Iterator it = listeners; it.hasNext();)
{
String key = (String) it.next();
if (!key.endsWith(".args"))
{
String value = _config.getString(key);
List args = _config.getList(key + ".args", new ArrayList());
int timeout = _config.getInt(key + ".timeout", 0);
String state = key.substring(key.lastIndexOf(".") + 1);
final Script script = ScriptFactory.createScript(value, state, this, args, getWrapperLogger(), timeout);
int iState = toIntState(state);
if (iState >= 0 && script != null)
addStateChangeListener(iState, new StateChangeListener()
{
@Override
public void stateChange(int newState, int oldState)
{
script.executeWithTimeout();
}
});
}
}
if (_haltWrapperOnApp)
addStateChangeListener(STATE_IDLE, new StateChangeListener()
{
@Override
public void stateChange(int newState, int oldState)
{
if (_exiting)
return;
_exiting = true;
executor.execute(new Runnable()
{
@Override
public void run()
{
// if this is a service: do not exit here so that we
// can inform the service controller
if (!isService())
System.exit(0);
else
{
Object service = getService();
if (service != null)
{
// windows service
getWrapperLogger().info("calling onStop");
((StopableService) service).onStop();
((StopableService) service).waitOnStop();
Runtime.getRuntime().halt(0);
}
else if (isService() && _haltWrapperOnApp)
{
// posix service
try
{
Thread.sleep(5000);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
Runtime.getRuntime().halt(0);
}
}
}
});
}
});
}
private int toIntState(String state)
{
if ("START".equals(state))
return STATE_STARTING;
else if ("RUN".equals(state))
return STATE_RUNNING;
else if ("RESTART".equals(state))
return STATE_RESTART;
else if ("STOP".equals(state))
return STATE_USER_STOP;
else if ("ABORT".equals(state))
return STATE_ABORT;
else if ("SHUTDOWN".equals(state))
return STATE_SHUTDOWN;
else if ("IDLE".equals(state))
return STATE_IDLE;
else
return -1;
}
private void startJMXRmiService()
{
try
{
int port = _config.getInt("wrapper.jmx.rmi.port", Constants.DEFAULT_RMI_PORT);
if (port > 0)
{
Registry rmiRegistry = LocateRegistry.createRegistry(port);
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + port + "/server");
Map environment = null;
if (_config.getString("wrapper.jmx.rmi.user", null) != null)
{
final String myUser = _config.getString("wrapper.jmx.rmi.user");
final String myPassword = _config.getString("wrapper.jmx.rmi.password", "");
environment = new HashMap();
JMXAuthenticator authenticator = new JMXAuthenticator()
{
@Override
public Subject authenticate(Object credentials)
{
if (!(credentials instanceof String[]))
throw new SecurityException("Bad credentials");
String[] creds = (String[]) credentials;
if (creds.length != 2)
throw new SecurityException("Bad credentials");
String user = creds[0];
String password = creds[1];
if (password == null)
password = "";
if (!myUser.equals(user))
throw new SecurityException("Unknown user " + user);
if (!myPassword.equals(password))
throw new SecurityException("Bad password");
Set principals = new HashSet();
principals.add(new JMXPrincipal(user));
return new Subject(true, principals, Collections.EMPTY_SET, Collections.EMPTY_SET);
}
};
environment.put(JMXConnectorServer.AUTHENTICATOR, authenticator);
}
JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(url, environment, _mbeanServer);
cs.start();
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
private void startAhessianService()
{
if (_ahessianServer != null)
return;
String canonName;
try
{
canonName = new File(_config.getString("wrapper.config")).getCanonicalPath();
_ahessianServer = new AHessianJmxServer(_mbeanServer, "+n:localhost, -n:*", canonName, _config.getInt("wrapper.tray.port", 0), getWrapperLogger());
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void registerMBean()
{
ArrayList servers = MBeanServerFactory.findMBeanServer(null);
try
{
if (servers != null && servers.size() > 0)
_mbeanServer = (MBeanServer) servers.get(0);
if (_mbeanServer == null)
_mbeanServer = MBeanServerFactory.createMBeanServer();
if (_mbeanServer != null)
{
String name = _config.getString("wrapper.console.title");
if (name == null)
name = _config.getString("wrapper.ntservice.name");
if (name == null)
name = "yajsw.noname";
ObjectName oName = new ObjectName("org.rzo.yajsw", "name", name);
_mbeanServer.registerMBean(this, oName);
}
else
getWrapperLogger().severe("ERROR: no mbean server found ");
}
catch (Exception e)
{
e.printStackTrace();
}
}
/**
* Gets the priority.
*
* @param priority
* the priority
*
* @return the priority
*/
protected int getPriority(String priority)
{
if ("LOW".equals(priority))
return Process.PRIORITY_LOW;
else if ("BELOW_NORMAL".equals(priority))
return Process.PRIORITY_BELOW_NORMAL;
else if ("NORMAL".equals(priority))
return Process.PRIORITY_NORMAL;
else if ("ABOVE_NORMAL".equals(priority))
return Process.PRIORITY_ABOVE_NORMAL;
else if ("HIGH".equals(priority))
return Process.PRIORITY_HIGH;
return Process.PRIORITY_UNDEFINED;
}
/**
* Gets the affinity.
*
* @param affinity
* the affinity
*
* @return the affinity
*/
protected int getAffinity(String affinity)
{
try
{
return Integer.parseInt(affinity);
}
catch (Exception ex)
{
ex.printStackTrace();
return 0;
}
}
/**
* Exit code restart.
*
* @return true, if successful
*/
protected boolean exitCodeRestart()
{
if (_state == STATE_USER_STOP || _state == STATE_SHUTDOWN)
return false;
if (_startupExitCodes.contains(_osProcess.getExitCode()))
{
getWrapperLogger().info("restart process due to exit code rule");
return true;
}
else if (_exitCodeDefaultRestart)
{
getWrapperLogger().info("restart process due to default exit code rule");
return true;
}
return false;
}
/**
* Exit code shutdown.
*
* @return true, if successful
*/
protected boolean exitCodeShutdown()
{
if (_shutdownExitCodes.contains(_osProcess.getExitCode()))
{
getWrapperLogger().info("shutdown process due to exit code rule");
return true;
}
return false;
}
/**
* Sets the state.
*
* @param state
* the new state
*/
protected void setState(int state)
{
int oldState = _state;
if (_state != state)
{
getWrapperLogger().info("set state " + getStringState(_state) + "->" + getStringState(state));
_state = state;
if (state == STATE_IDLE)
{
removeLockFile();
}
Collection listeners = (Collection) _listeners.get(_state);
Collection listeners999 = (Collection) _listeners.get(999);
Collection allListeners = new HashSet();
if (listeners != null)
allListeners.addAll(listeners);
if (listeners999 != null)
allListeners.addAll(listeners999);
for (Iterator it = allListeners.iterator(); it.hasNext();)
{
StateChangeListener listener = (StateChangeListener) it.next();
if (listener != null)
listener.stateChange(state, oldState);
}
}
}
/**
* String state.
*
* @param state
* the state
*
* @return the string
*/
static public String getStringState(int state)
{
switch (state)
{
case STATE_IDLE:
return "IDLE";
case STATE_RESTART:
return "RESTART";
case STATE_RESTART_START:
return "RESTART_START";
case STATE_RESTART_STOP:
return "RESTART_STOP";
case STATE_RESTART_WAIT:
return "RESTART_WAIT";
case STATE_RUNNING:
return "RUNNING";
case STATE_STARTING:
return "STARTING";
case STATE_STOP:
return "STOP";
case STATE_USER_STOP:
return "STATE_USER_STOP";
case STATE_ABORT:
return "STATE_ABORT";
case STATE_SHUTDOWN:
return "STATE_SHUTDOWN";
default:
return "?";
}
}
/** The _start by timer. */
boolean _startByTimer = false;
/**
* Start by timer.
*/
@Override
public synchronized void startByTimer()
{
_startByTimer = true;
start();
_startByTimer = false;
}
@Override
public synchronized void start()
{
if (!_init)
init();
_startRequested = true;
_stopRequested = false;
startInternal();
}
/**
* Start.
*/
public synchronized void startInternal()
{
if (!saveLockFile())
return;
savePidFile();
if (_timer.isHasTrigger() && !_timer.isTriggered())
_timer.start();
if (_condition.isHasTrigger() && !_condition.isTriggered())
{
_condition.start();
return;
}
if (!_timer.isStartImmediate() && !_startByTimer)
return;
if (_cluster != null && !_clusterTriggered)
{
startCluster();
return;
}
if (_state == STATE_IDLE)
setState(STATE_STARTING);
else if (_state == STATE_RESTART_WAIT)
setState(STATE_RESTART_START);
else
return;
if (_shutdownHooks.isEmpty())
configShutdownHook();
long startTimeout = _config.getLong("wrapper.startup.delay", 0);
if (_state == STATE_STARTING && startTimeout > 0)
{
try
{
getWrapperLogger().info("startup delay " + startTimeout + "sec");
Thread.sleep(startTimeout * 1000);
if (_stopRequested)
{
setState(STATE_IDLE);
return;
}
}
catch (InterruptedException e)
{
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
getWrapperLogger().info("starting Process");
if (_wrapperStarted == null)
_wrapperStarted = new Date();
if (_config.getBoolean("wrapper.restart.reload_configuration", DEFAULT_RELOAD_CONFIGURATION))
_config.reload();
if (_debug)
getWrapperLogger().info("starting controller");
if (_controller != null)
{
// release resources if controller was running before this call to
// start
// otherwise resources will be released by gc -> finalize
_controller.reset();
_controller.setDebug(isDebug());
_controller.setLogger(getWrapperLogger());
configController();
if (!_controller.start())
{
getWrapperLogger().info("could not start controller -> abort");
setState(STATE_ABORT);
setState(STATE_IDLE);
return;
}
else if (_debug)
getWrapperLogger().info("controller started");
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
if (_osProcess == null)
{
_osProcess = OperatingSystem.instance().processManagerInstance().createProcess();
}
else
{
// release resources if _osProcess was running before this call to
// start
// otherwise resources will be released by gc -> finalize
if (_debug)
getWrapperLogger().info("_osProcess destroyed");
_osProcess.destroy();
}
configProcess();
_firstRestartTime = System.currentTimeMillis();
// _restartCount++;
Map triggerActions = getTriggerActions();
Map regexTriggerActions = getRegexTriggerActions();
Map missingTriggerActions = getMissingTriggerActions();
Map missingRegexTriggerActions = getMissingRegexTriggerActions();
_osProcess.setLogger(getWrapperLogger());
_exitCode = -3;
getWrapperLogger().info("spawning wrapped process");
if (_osProcess.start())
{
_controller.processStarted();
_totalRestartCount++;
postStart();
getWrapperLogger().info("started process with pid " + _osProcess.getPid());
if (pipeStreams())
{
_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);
}
if (getState() != STATE_IDLE)
{
_appStarted = new Date();
setState(STATE_RUNNING);
updateAppLoggerName();
}
writeToFile( getJavaProcessFileName(), "STARTED" );
}
else
{
getWrapperLogger().severe("failed to spawn wrapped process");
_controller.processFailed();
}
// win 64 test
// WindowsXPProcess.getProcess(getPid());
}
private void writeToFile( File file, String string )
{
FileWriter writer = null;
try
{
writer = new FileWriter( file );
writer.write( string );
}
catch ( IOException e )
{
throw new RuntimeException( e );
}
finally
{
if ( writer != null )
{
try
{
writer.close();
}
catch ( IOException e )
{
e.printStackTrace();
}
}
}
}
abstract void configController();
private File getJavaProcessFileName()
{
String pidFileName = _config.getString( "wrapper.pidfile" );
File pidFile = new File( pidFileName );
return new File( pidFile.getParentFile(), fileNameWithoutExtension( pidFile.getName() ) + ".java.status" );
}
private String fileNameWithoutExtension( String fileName )
{
int dotIndex = fileName.lastIndexOf( '.' );
return dotIndex == -1 ? fileName : fileName.substring( 0, dotIndex );
}
abstract void postStart();
void configProcess()
{
String priority = _config.getString("wrapper.priority");
if (priority != null)
_osProcess.setPriority(getPriority(priority));
String affinity = _config.getString("wrapper.affinity");
if (affinity != null)
_osProcess.setCpuAffinity(getAffinity(affinity));
String title = _config.getString("wrapper.console.title");
if (title != null)
_osProcess.setTitle(title);
_osProcess.setVisible(_config.getBoolean("wrapper.console.visible", Constants.DEFAULT_CONSOLE_VISIBLE)
&& !_config.getBoolean("wrapper.service", false));
_osProcess.setUser(_config.getString("wrapper.app.account"));
_osProcess.setPassword(_config.getString("wrapper.app.password"));
if (_debug)
_osProcess.setDebug(true);
String workingDir = _config.getString("wrapper.working.dir", ".");
if (workingDir != null)
{
File wd = new File(workingDir);
if (!wd.exists() || !wd.isDirectory())
getWrapperLogger().warning("working directory " + workingDir + " not found");
else
_osProcess.setWorkingDir(wd.getAbsolutePath());
getWrapperLogger().info("working dir " + wd.getAbsolutePath());
}
_osProcess.setEnvironment(getProcessEnvironment(_config));
}
private List<String[]> getProcessEnvironment(YajswConfigurationImpl config)
{
if (!config.getKeys("wrapper.app.env").hasNext())
{
//getWrapperLogger().info("env: no yajsw env");
return null;
}
List<String[]> env = OperatingSystem.instance().processManagerInstance().getProcess(
OperatingSystem.instance().processManagerInstance().currentProcessId()).getEnvironment();
for (Iterator keys = config.getKeys("wrapper.app.env"); keys.hasNext();)
{
String key = (String) keys.next();
String value = config.getString(key);
String envKey = key.substring("wrapper.app.env.".length());
//getWrapperLogger().info("env: "+envKey+"="+value);
updateEnvKey(envKey, value, env);
}
return env;
}
private void updateEnvKey(String envKey, String value, List<String[]> env)
{
String[] entry = findEnvEntry(envKey, env);
if (entry != null)
entry[1] = value;
else
env.add(new String[]
{ envKey, value });
}
private String[] findEnvEntry(String envKey, List<String[]> env)
{
for (String[] entry : env)
if (envKeyEqual(envKey, entry[0]))
return entry;
return null;
}
private boolean envKeyEqual(String envKey1, String envKey2)
{
if (Platform.isWindows())
return envKey1.toLowerCase().equals(envKey2.toLowerCase());
else
return envKey1.equals(envKey2);
}
/**
* Pipe streams.
*
* @return true, if successful
*/
protected boolean pipeStreams()
{
return true;// getAppLogger() != null;
}
/** The _file handler. */
Handler _fileHandler;
/**
* Restart log file.
*/
void restartLogFile()
{
if (_fileHandler == null)
return;
String rollMode = _config.getString("wrapper.logfile.rollmode", "");
boolean append = !(rollMode.contains("WRAPPER") || rollMode.contains("JVM"));
if (!append && _appLogger != null)
{
_fileHandler.close();
getFileHandler();
}
}
private boolean isMacPlatform() {
return System.getProperty("os.name").toLowerCase().contains("mac");
}
/**
* Gets the console handler.
*
* @return the console handler
*/
public Handler getConsoleHandler()
{
Handler handler;
String consoleLogLevel = _config.getString("wrapper.console.loglevel", "INFO");
if (consoleLogLevel.equals("NONE")) {
if (isMacPlatform()) consoleLogLevel = "STATUS";
else return null;
}
// per default java console handler uses err -> use out instead
handler = new ConsoleHandler()
{
@Override
protected synchronized void setOutputStream(OutputStream out) throws SecurityException
{
super.setOutputStream(System.out);
}
};
handler.setFormatter( getConsoleFormatter() );
handler.setLevel( getLogLevel( consoleLogLevel ) );
return handler;
}
/**
* Gets the file handler.
*
* @return the file handler
*/
public Handler getFileHandler()
{
if (_fileHandler != null)
return _fileHandler;
String fileName = getLogFile();
String fileLogLevel = _config.getString("wrapper.logfile.loglevel", "INFO");
if ((fileName.equals("") || fileLogLevel.equals("NONE")))
return null;
File f = new File(fileName).getParentFile();
if (!f.exists())
try
{
f.mkdirs();
}
catch (Exception ex)
{
ex.printStackTrace();
}
try
{
String rollMode = _config.getString("wrapper.logfile.rollmode", "");
boolean append = !(rollMode.contains("WRAPPER") || rollMode.contains("JVM"));
int count = _config.getInt("wrapper.logfile.maxfiles", 1);
int limit = getLogLimit();
_fileHandler = fileName.contains("%d") ? new DateFileHandler(fileName, limit, count, append) : new FileHandler(fileName, limit, count,
append);
_fileHandler.setFormatter(getFileFormatter());
_fileHandler.setLevel(getLogLevel(fileLogLevel));
}
catch (Exception e)
{
e.printStackTrace();
}
return _fileHandler;
}
/**
* Update app logger name.
*/
private void updateAppLoggerName()
{
if (_appLogger == null)
return;
_appLoggerPid = getAppPid() + "/" + _restartCount;
((MyLogger) _appLogger).setPID(_appLoggerPid);
_appLoggerName = getName() == null ? "" : getName();
((MyLogger) _appLogger).setName(_appLoggerName);
}
/**
* Gets the app logger.
*
* @return the app logger
*/
protected Logger getAppLogger()
{
if (_appLogger != null)
return _appLogger;
if (_appLogger == null)
{
_appLogger = new MyLogger();
updateAppLoggerName();
_appLogger.setUseParentHandlers(false);
}
if (getFileHandler() != null)
_appLogger.addHandler(getFileHandler());
if ( getConsoleHandler() != null )
_appLogger.addHandler( getConsoleHandler() );
return _appLogger;
}
/**
* Gets the log level.
*
* @param logLevel
* the log level
*
* @return the log level
*/
private Level getLogLevel(String logLevel)
{
if ( "INFO".equals( logLevel ) )
return Level.ALL;
else if ( "FATAL".equals( logLevel ) )
return Level.SEVERE;
else if ( "ERROR".equals( logLevel ) )
return Level.WARNING;
else
return Level.INFO;
}
/**
* Gets the log limit.
*
* @return the log limit
*/
private int getLogLimit()
{
String res = _config.getString("wrapper.logfile.maxsize");
String units = "";
if (res == null)
return 0;
res = res.toLowerCase();
if (res.endsWith("m"))
{
res = res.substring(0, res.length() - 1);
units = "m";
}
else if (res.endsWith("k"))
{
res = res.substring(0, res.length() - 1);
units = "k";
}
int result = Integer.parseInt(res);
if (units.equals("m"))
result = result * 1024 * 1024;
else if (units.equals("k"))
result = result * 1024;
return result;
}
/**
* Gets the file formatter.
*
* @return the file formatter
*/
private PatternFormatter getFileFormatter()
{
String wFormat = _config.getString("wrapper.logfile.format", Constants.DEFAULT_LOG_FORMAT);
return getFormatter(wFormat);
}
/**
* Gets the console formatter.
*
* @return the console formatter
*/
private java.util.logging.Formatter getConsoleFormatter()
{
String wFormat = _config.getString("wrapper.console.format", Constants.DEFAULT_LOG_FORMAT);
return getFormatter(wFormat);
}
/**
* Gets the formatter.
*
* @param wFormat
* the w format
*
* @return the formatter
*/
private PatternFormatter getFormatter(String wFormat)
{
PatternFormatter formatter = new PatternFormatter();
String pattern = "";
if (wFormat.contains("Z"))
{
formatter.setTimeFormat("yy-MM-dd HH:mm:ss.SS");
}
else
{
formatter.setTimeFormat("yy-MM-dd HH:mm:ss");
}
for (int i = 0; i < wFormat.length(); i++)
{
if (i > 0)
pattern += "|";
char c = wFormat.charAt(i);
switch (c)
{
case 'L':
pattern += "%LEVEL%";
break;
case 'P':
pattern += "%PARAM0%";
break;
case 'N':
pattern += "%PARAM1%";
break;
case 'T':
case 'Z':
pattern += "%TIME%";
break;
case 'M':
pattern += "%MESSAGE%";
break;
default:
;
}
}
pattern += System.getProperty( "line.separator" );
formatter.setLogPattern(pattern);
return formatter;
}
/**
* Gets the log file.
*
* @return the log file
*/
private String getLogFile()
{
String result = _config.getString("wrapper.logfile", "wrapper.log");
File r = new File(result);
File f = null;
if (!r.isAbsolute())
{
String wDir = _config.getString("wrapper.working.dir", ".");
f = new File(wDir, result);
}
else
f = new File(result);
result = f.getAbsolutePath();
if (result.contains("ROLLNUM"))
result = result.replace("ROLLNUM", "%g");
else
result = result + ".%g";
if (result.contains("YYYYMMDD"))
result = result.replace("YYYYMMDD", "%d");
return result;
}
protected Map getMissingTriggerActions()
{
Map result = new HashMap();
for (Iterator it = _config.getKeys("wrapper.filter.missing.trigger"); it.hasNext();)
{
String tKey = (String) it.next();
List lValue = _config.getList(tKey);
if (lValue == null || lValue.size() != 3)
{
getWrapperLogger().info("check parameters for " + tKey);
continue;
}
String tValue = (String) lValue.get(0);
// commons configuration does no accept <space>, 1, 1 as valid list
// -> using * instead of space
if ("*".equals(tValue))
tValue = "";
int countValue = -1;
try
{
countValue = Integer.parseInt((String) lValue.get(1));
}
catch (Exception ex)
{
getWrapperLogger().log(Level.SEVERE, "check parameters for " + tKey, ex);
}
long periodValue = -1;
try
{
periodValue = Long.parseLong((String) lValue.get(2)) * 1000;
}
catch (Exception ex)
{
getWrapperLogger().log(Level.SEVERE, "check parameters for " + tKey, ex);
}
String tName = tKey.substring("wrapper.filter.missing.trigger.".length());
String aKey = "wrapper.filter.missing.action." + tName;
String aValue = _config.getString(aKey, "");
Object action = getTriggerAction(aValue);
String sKey = "wrapper.filter.missing.script." + tName;
String sValue = _config.getString(sKey, "");
List args = _config.getList(sKey + ".args", null);
int timeout = _config.getInt(sKey + ".timeout", 0);
String[] strArgs = null;
if (args != null && args.size() > 0)
{
strArgs = new String[args.size()];
for (int i = 0; i < strArgs.length; i++)
strArgs[i] = args.get(i).toString();
}
Object script = getTriggerScript(sValue, tKey.substring(tKey.lastIndexOf('.')), strArgs, timeout);
if (action != null || script != null)
{
result.put(tValue, new MissingTriggerAction(executor, periodValue, countValue, new TriggerAction[]
{ (TriggerAction) script, (TriggerAction) action }));
}
}
return result;
}
/**
* Gets the regex trigger actions.
*
* @return the regex trigger actions
*/
protected Map getMissingRegexTriggerActions()
{
Map result = new HashMap();
for (Iterator it = _config.getKeys("wrapper.filter.missing.trigger-regex"); it.hasNext();)
{
String tKey = (String) it.next();
List lValue = _config.getList(tKey);
if (lValue == null || lValue.size() != 3)
{
getWrapperLogger().info("check parameters for " + tKey);
continue;
}
String tValue = (String) lValue.get(0);
int countValue = -1;
try
{
countValue = Integer.parseInt((String) lValue.get(1));
}
catch (Exception ex)
{
getWrapperLogger().log(Level.SEVERE, "check parameters for " + tKey, ex);
}
long periodValue = -1;
try
{
getWrapperLogger().info("check parameters for " + tKey);
periodValue = Long.parseLong((String) lValue.get(2)) * 1000;
}
catch (Exception ex)
{
getWrapperLogger().log(Level.SEVERE, "check parameters for " + tKey, ex);
}
String tName = tKey.substring("wrapper.filter.missing.trigger.".length());
String aKey = "wrapper.filter.missing.action." + tName;
String aValue = _config.getString(aKey, "");
Object action = getTriggerAction(aValue);
String sKey = "wrapper.filter.missing.script." + tName;
String sValue = _config.getString(sKey, "");
List args = _config.getList(sKey + ".args", null);
int timeout = _config.getInt(sKey + ".timeout", 0);
String[] strArgs = null;
if (args != null && args.size() > 0)
{
strArgs = new String[args.size()];
for (int i = 0; i < strArgs.length; i++)
strArgs[i] = args.get(i).toString();
}
Object script = getTriggerScript(sValue, tKey.substring(tKey.lastIndexOf('.')), strArgs, timeout);
if (action != null || script != null)
{
result.put(tValue, new MissingTriggerAction(executor, periodValue, countValue, new TriggerAction[]
{ (TriggerAction) script, (TriggerAction) action }));
}
}
return result;
}
/**
* Gets the trigger actions.
*
* @return the trigger actions
*/
protected Map getTriggerActions()
{
Map result = new HashMap();
List configList = new ArrayList();
for (Iterator it = _config.getKeys("wrapper.filter.trigger"); it.hasNext();)
{
configList.add(it.next());
}
Collections.sort(configList, new AlphanumComparator());
for (Iterator it = configList.listIterator(); it.hasNext();)
{
String tKey = (String) it.next();
String tValue = _config.getString(tKey);
if (tValue == null || tValue.length() == 0)
continue;
String tName = tKey.substring("wrapper.filter.trigger.".length());
String aKey = "wrapper.filter.action." + tName;
String aValue = _config.getString(aKey, "");
Object action = getTriggerAction(aValue);
String sKey = "wrapper.filter.script." + tName;
String sValue = _config.getString(sKey, "");
List args = _config.getList(sKey + ".args", null);
int timeout = _config.getInt(sKey + ".timeout", 0);
String[] strArgs = null;
if (args != null && args.size() > 0)
{
strArgs = new String[args.size()];
for (int i = 0; i < strArgs.length; i++)
strArgs[i] = args.get(i).toString();
}
Object script = getTriggerScript(sValue, tKey.substring(tKey.lastIndexOf('.')), strArgs, timeout);
if (action != null && script != null)
{
addToActionMap(result, tValue, Arrays.asList(new Object[]
{ script, action }));
}
else if (action != null)
addToActionMap(result, tValue, action);
else if (script != null)
addToActionMap(result, tValue, script);
else
addToActionMap(result, aKey, "RESTART");
}
return result;
}
private void addToActionMap(Map actionsMap, String key, Object value)
{
Object c = actionsMap.get(key);
if (c == null)
{
actionsMap.put(key, value);
}
else if (c instanceof Collection && value instanceof Collection)
{
ArrayList l = new ArrayList();
l.addAll((Collection) c);
l.addAll((Collection) value);
actionsMap.put(key, l);
}
else if (c instanceof Collection && !(value instanceof Collection))
{
ArrayList l = new ArrayList();
l.addAll((Collection) c);
l.add(value);
actionsMap.put(key, l);
}
else if (!(c instanceof Collection) && value instanceof Collection)
{
ArrayList l = new ArrayList();
l.add(c);
l.addAll((Collection) value);
actionsMap.put(key, l);
}
else
// c is not a collection && value is not a collection
{
actionsMap.put(key, Arrays.asList(new Object[]
{ c, value }));
}
}
/**
* Gets the regex trigger actions.
*
* @return the regex trigger actions
*/
protected Map getRegexTriggerActions()
{
Map result = new HashMap();
for (Iterator it = _config.getKeys("wrapper.filter.trigger-regex"); it.hasNext();)
{
String tKey = (String) it.next();
String tValue = _config.getString(tKey);
if (tValue == null || tValue.length() == 0)
continue;
String tName = tKey.substring("wrapper.filter.trigger-regex.".length());
String aKey = "wrapper.filter.action." + tName;
String aValue = _config.getString(aKey, "");
Object action = getTriggerAction(aValue);
String sKey = "wrapper.filter.script." + tName;
String sValue = _config.getString(sKey, "");
List args = _config.getList(sKey + ".args", null);
int timeout = _config.getInt(sKey + ".timeout", 0);
String[] strArgs = null;
if (args != null && args.size() > 0)
{
strArgs = new String[args.size()];
for (int i = 0; i < strArgs.length; i++)
strArgs[i] = args.get(i).toString();
}
Object script = getTriggerScript(sValue, tKey.substring(tKey.lastIndexOf('.')), strArgs, timeout);
if (action != null && script != null)
{
addToActionMap(result, tValue, Arrays.asList(new Object[]
{ script, action }));
}
else if (action != null)
addToActionMap(result, tValue, action);
else if (script != null)
addToActionMap(result, tValue, script);
else
addToActionMap(result, aKey, "RESTART");
}
return result;
}
/**
* Gets the trigger script.
*
* @param script
* the script
* @param key
* the key
*
* @return the trigger script
*/
private Object getTriggerScript(String script, String key, String[] args, int timeout)
{
final Script s = ScriptFactory.createScript(script, key, this, args, getWrapperLogger(), timeout);
if (s == null)
{
this.getWrapperLogger().info("error initializing script " + script);
return null;
}
this.getWrapperLogger().info("found script " + s.getScript());
// final String id = key;
return new TriggerAction()
{
@Override
public Object execute(final String line)
{
scriptExecutor.execute(new Runnable()
{
@Override
public void run()
{
AbstractWrappedProcess.this.getWrapperLogger().info("start script " + s.getScript());
s.executeWithTimeout(new String(line));
AbstractWrappedProcess.this.getWrapperLogger().info("end script " + s.getScript());
}
});
return null;
}
};
}
/**
* Gets the trigger action.
*
* @param value
* the value
*
* @return the trigger action
*/
private Object getTriggerAction(String value)
{
if ("RESTART".equals(value))
return new TriggerAction()
{
@Override
public Object execute(String line)
{
if (allowRestart())
restartInternal();
else
stop();
return null;
}
};
else if ("SHUTDOWN".equals(value))
return new TriggerAction()
{
@Override
public Object execute(String line)
{
stop();
return null;
}
};
return null;
}
/**
* Stop timer.
*/
@Override
public synchronized void stopTimer()
{
_timer.stop();
}
public synchronized void stopCondition()
{
_condition.stop();
}
@Override
public synchronized void stop()
{
_stopRequested = true;
_startRequested = false;
stopInternal();
}
/**
* Stop.
*/
public synchronized void stopInternal()
{
if (_state == STATE_RESTART)
{
_appStopped = new Date();
setState(STATE_RESTART_STOP);
}
else if (_state == STATE_RUNNING)
{
_appStopped = new Date();
setState(STATE_USER_STOP);
}
else
return;
// if (_debug)
int shutdownWaitTime = _config.getInt("wrapper.shutdown.timeout", Constants.DEFAULT_SHUTDOWN_TIMEOUT) * 1000;
shutdownWaitTime += _config.getInt("wrapper.jvm_exit.timeout", Constants.DEFAULT_JVM_EXIT_TIMEOUT) * 1000;
getWrapperLogger().info("stopping process with pid/timeout " + _osProcess.getPid() + " " + shutdownWaitTime);
stopController(shutdownWaitTime);
stopOsProcess(shutdownWaitTime);
if ( !getJavaProcessFileName().delete() )
{
getJavaProcessFileName().deleteOnExit();
}
_appStopped = new Date();
if (_state == STATE_USER_STOP)
if (!_exiting)
setState(STATE_IDLE);
// removeShutdownHooks(); // ABK: do this or not? seems illadvised to remove shutdown hooks during shutdown
}
private void removeShutdownHooks()
{
for (Thread hook : _shutdownHooks)
{
try {
Runtime.getRuntime().removeShutdownHook(hook);
} catch (IllegalStateException ise) {
; // ABK: ignore for now. Removing shutdown hooks during shutdown hooks seems wrong. Evaluate
}
}
_shutdownHooks.clear();
}
private void stopOsProcess(int shutdownWaitTime)
{
boolean externalStop = false;
String stopConfigName = _config.getString("wrapper.stop.conf");
getWrapperLogger().info("stop config name " + stopConfigName);
File stopConfigFile = null;
if (stopConfigName != null)
{
stopConfigFile = new File(stopConfigName);
try
{
stopConfigName = stopConfigFile.getCanonicalPath();
}
catch (IOException e)
{
e.printStackTrace();
}
externalStop = stopConfigFile.isFile() && stopConfigFile.exists();
}
WrappedProcess stopper = null;
getWrapperLogger().info("externalStop " + externalStop);
if (externalStop)
{
getWrapperLogger().info("starting stop application");
Configuration stopLocalConf = new BaseConfiguration();
stopLocalConf.setProperty("wrapper.config", stopConfigName);
YajswConfigurationImpl stopConf = new YajswConfigurationImpl(stopLocalConf, _useSystemProperties);
stopper = WrappedProcessFactory.createProcess(stopConf);
stopper.getLocalConfiguration().setProperty("wrapper.config", stopConfigName);
stopper.setUseSystemProperties(_useSystemProperties);
// stopper.setDebug(true);
stopper.init();
stopper.start();
}
// else normally process is stopped by the controller
// _osProcess.stop(shutdownWaitTime, 999);
if (shutdownWaitTime > 0)
_osProcess.waitFor(shutdownWaitTime);
if (_osProcess.isRunning())
{
getWrapperLogger().info("process did not stop after " + shutdownWaitTime + " sec. -> hard kill");
}
_osProcess.kill(999);
if (stopper != null && stopper.getState() != STATE_IDLE)
stopper.stop();
// give the OS some time to clean up
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
e.printStackTrace();
Thread.currentThread().interrupt();
}
_osProcess.destroy();
getWrapperLogger().info("process exit code: " + _osProcess.getExitCode());
}
abstract void stopController(int timeout);
/**
* Allow restart.
*
* @return true, if successful
*/
protected boolean allowRestart()
{
if (System.currentTimeMillis() - _firstRestartTime > _successfulInvocationTime)
_restartCount = 0;
if (_state == STATE_USER_STOP || _state == STATE_RESTART || _state == STATE_RESTART_STOP || _state == STATE_RESTART_WAIT)
return false;
if (_restartCount < _config.getInt("wrapper.max_failed_invocations", DEFAULT_MAX_FAILED_INVOCATIONS))
return true;
getWrapperLogger().info("too many restarts ");
return false;
}
/**
* Restart.
*/
@Override
public void restart()
{
if (_state != STATE_RUNNING)
return;
restartInternal();
}
/**
* Restart by timer.
*/
boolean _timerRestart = false;
@Override
public void restartByTimer()
{
if (_timerRestart)
return;
_timerRestart = true;
stop();
try
{
Thread.sleep(getRestartDelay());
}
catch (InterruptedException e)
{
if (_debug)
getWrapperLogger().log(Level.SEVERE, this.getClass().getName() + " restart", e);
Thread.currentThread().interrupt();
}
startByTimer();
_timerRestart = false;
}
/**
* Checks if is debug.
*
* @return true, if is debug
*/
boolean isDebug()
{
return _debug;
}
/**
* Sets the debug.
*
* @param debug
* the new debug
*/
@Override
public void setDebug(boolean debug)
{
_debug = debug;
}
/**
* Gets the pid.
*
* @return the pid
*/
@Override
public int getAppPid()
{
if (_osProcess != null)
return _osProcess.getPid();
else
return -1;
}
/**
* Save pid file.
*/
private void savePidFile()
{
String file = _config.getString("wrapper.pidfile");
if (file != null)
{
try
{
_pidFile = new File(file);
if (!_pidFile.exists())
_pidFile.createNewFile();
FileWriter out = new FileWriter(_pidFile, false);
out.write("" + OperatingSystem.instance().processManagerInstance().currentProcessId());
out.flush();
out.close();
Thread hook = new Thread()
{
@Override
public void run()
{
removePidFile();
}
};
Runtime.getRuntime().addShutdownHook(hook);
_shutdownHooks.add(hook);
if (_debug)
getWrapperLogger().info("created pid file " + _pidFile.getAbsolutePath());
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* Removes the pid file.
*/
private void removePidFile()
{
if (_pidFile != null)
{
try
{
_pidFile.delete();
if (_config.getBoolean("wrapper.service", false))
this.stop();
if (_debug)
getWrapperLogger().info("removed pid file " + _pidFile.getAbsolutePath());
_pidFile = null;
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* Save lock file.
*
* @return true, if successful
*/
protected boolean saveLockFile()
{
if (_lock != null)
return true;
String file = _config.getString("wrapper.lockfile", null);
if (file != null && !"".equals(file))
{
try
{
_lockFile = new File(file);
// Check if the lock exist
if (_lockFile.exists())
{
// if exist try to delete it
_lockFile.delete();
}
// Try to get the lock
_lockFileChannel = new RandomAccessFile(_lockFile, "rw").getChannel();
_lock = _lockFileChannel.tryLock();
if (_lock == null)
{
// File is lock by other application
_lockFileChannel.close();
getWrapperLogger().warning("Lock file " + file + " already locked by another application -> abort");
return false;
}
if (_debug)
getWrapperLogger().info("created lock file " + _lockFile.getAbsolutePath());
}
catch (IOException e)
{
e.printStackTrace();
return false;
}
}
return true;
}
/**
* Removes the lock file.
*/
private void removeLockFile()
{
if (_lock != null)
{
// release and delete file lock
try
{
_lock.release();
if (_debug)
getWrapperLogger().info("removed lock file " + _lockFile.getAbsolutePath());
_lock = null;
_lockFileChannel.close();
_lockFileChannel = null;
_lockFile.delete();
_lockFile = null;
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
/**
* File writer.
*
* @param file
* the file
*
* @return the file writer
*/
private FileWriter FileWriter(String file)
{
// TODO Auto-generated method stub
return null;
}
/**
* Wait for.
*/
@Override
public void waitFor()
{
waitFor(Long.MAX_VALUE);
}
/**
* Wait for.
*
* @param t
* the t
*/
@Override
public void waitFor(long t)
{
if (_state == STATE_IDLE)
return;
final Lock lock = new ReentrantLock();
final java.util.concurrent.locks.Condition isIdle = lock.newCondition();
StateChangeListener listener = new StateChangeListener()
{
@Override
public void stateChange(int newState, int oldState)
{
lock.lock();
isIdle.signal();
lock.unlock();
}
};
this.addStateChangeListener(STATE_IDLE, listener);
if (_state != STATE_IDLE)
try
{
lock.lock();
isIdle.await(t, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e)
{
e.printStackTrace();
Thread.currentThread().interrupt();
}
lock.unlock();
this.removeStateChangeListener(listener);
}
/**
* Gets the local configuration.
*
* @return the local configuration
*/
@Override
public Configuration getLocalConfiguration()
{
return _localConfiguration;
}
@Override
public void setLocalConfiguration(Configuration config)
{
_localConfiguration = config;
}
/**
* Gets the exit code.
*
* @return the exit code
*/
@Override
public int getExitCode()
{
return _exitCode;
}
/**
* Gets the wrapper logger.
*
* @return the wrapper logger
*/
@Override
public Logger getWrapperLogger()
{
if (_wrapperLogger != null)
return _wrapperLogger;
_wrapperLogger = new MyLogger();
((MyLogger) _wrapperLogger).setPID(_wrapperLoggerName);
((MyLogger) _wrapperLogger).setName(getName());
_wrapperLogger.setUseParentHandlers(false);
if (_controller != null)
_controller.setLogger(_appLogger);
if (getFileHandler() != null)
_wrapperLogger.addHandler(getFileHandler());
Handler console = getConsoleHandler();
if ( console != null )
{
console.setLevel( Level.OFF );
if (isMacPlatform())
{
console.setLevel( Level.INFO );
}
_wrapperLogger.addHandler( console );
}
return _wrapperLogger;
}
/**
* Sets the use system properties.
*
* @param useSystemProperties
* the new use system properties
*/
@Override
public void setUseSystemProperties(boolean useSystemProperties)
{
_useSystemProperties = useSystemProperties;
}
/**
* Gets the tmp path.
*
* @return the tmp path
*/
public String getTmpPath()
{
return _tmpPath;
}
/**
* Start drain.
*/
@Override
public synchronized void startDrain()
{
if (_gobler_err != null)
_gobler_err.setDrain(true);
if (_gobler_in != null)
_gobler_in.setDrain(true);
_drainActive = true;
}
/**
* Stop drain.
*/
@Override
public synchronized void stopDrain()
{
if (_gobler_err != null)
_gobler_err.setDrain(false);
if (_gobler_in != null)
_gobler_in.setDrain(false);
_drainActive = false;
}
/**
* Read drain line.
*
* @return the string
*/
@Override
public String readDrainLine()
{
String result = null;
if (!_drainActive)
return null;
if (_gobler_err != null)
try
{
if (!_gobler_err.isDrain())
startDrain();
result = _gobler_err.getDrainReader().readLine();
}
catch (IOException e)
{
e.printStackTrace();
}
if (result == null && _gobler_in != null)
try
{
if (!_gobler_in.isDrain())
startDrain();
result = _gobler_in.getDrainReader().readLine();
}
catch (IOException e)
{
e.printStackTrace();
}
return result;
}
@Override
public int getState()
{
return _state;
}
/**
* Restart internal.
*/
@Override
public void restartInternal()
{
getWrapperLogger().info("restart internal " + getStringState());
if (_state == STATE_RUNNING || _state == STATE_STARTING || _state == STATE_RESTART_START)
setState(STATE_RESTART);
else
return;
_restartCount++;
if (_debug)
{
getWrapperLogger().info("restarting " + _restartCount + " time");
}
stopInternal();
if (!_stopRequested)
{
setState(STATE_RESTART_WAIT);
try
{
Thread.sleep(getRestartDelay());
}
catch (InterruptedException e)
{
if (_debug)
getWrapperLogger().log(Level.SEVERE, this.getClass().getName() + " restart", e);
Thread.currentThread().interrupt();
}
if (!_stopRequested)
startInternal();
else
{
getWrapperLogger().log(Level.SEVERE, "Process " + getName() + " stop requested, setting IDLE in restart internal");
_stopRequested = false;
if (!_osProcess.isRunning())
setState(STATE_IDLE);
}
}
else
{
if (_debug)
getWrapperLogger().info(" stop requested, setting IDLE in restart internal");
_stopRequested = false;
if (!_osProcess.isRunning())
setState(STATE_IDLE);
}
}
private long getRestartDelay()
{
Script script = getRestartDelayScript();
if (script != null)
{
Object time = script.execute();
if (time instanceof Number)
return ((Number) time).longValue() * 1000;
}
return _config.getLong("wrapper.restart.delay", DEFAULT_RESTART_DELAY) * 1000;
}
private Script getRestartDelayScript()
{
if (_restartDelayScript != null)
return _restartDelayScript;
String script = _config.getString("wrapper.restart.delay.script", null);
if (script != null)
{
List args = _config.getList("wrapper.restart.delay.script.args", null);
int timeout = _config.getInt("wrapper.restart.delay.script.timeout", 0);
_restartDelayScript = ScriptFactory.createScript(script, "", this, args, getWrapperLogger(), 0);
}
return _restartDelayScript;
}
/**
* The Class Gobler.
*/
protected class Gobler implements Runnable
{
/** The _input stream. */
InputStream _inputStream;
/** The _name. */
String _name;
/** The _gobler log. */
Logger _goblerLog;
/** The _actions regex. */
Map _actionsRegex;
Map _missingActionsRegex;
/** The _action triggers regex. */
com.karneim.util.collection.regex.Pattern[] _actionTriggersRegex;
com.karneim.util.collection.regex.Pattern[] _missingActionTriggersRegex;
/** The _actions. */
Map _actions;
Map _missingActions;
/** The _action triggers. */
String[] _actionTriggers;
String[] _missingActionTriggers;
/** The _drain. */
volatile boolean _drain;
/** The _drain buffer. */
volatile CircularBuffer _drainBuffer;
/** The _drain reader. */
volatile BufferedReader _drainReader;
volatile int _pid;
/**
* Sets the drain.
*
* @param drain
* the new drain
*/
public void setDrain(boolean drain)
{
if (drain && !_drain)
{
_drainBuffer = new CircularBuffer(16384, false);
_drainReader = new BufferedReader(_drainBuffer);
}
else if (!drain && _drain)
{
try
{
_drainReader.close();
_drainBuffer = null;
}
catch (IOException e)
{
e.printStackTrace();
}
}
_drain = drain;
}
/**
* Gets the drain reader.
*
* @return the drain reader
*/
public BufferedReader getDrainReader()
{
return _drainReader;
}
/**
* Instantiates a new gobler.
*
* @param stream
* the stream
* @param log
* the log
* @param events
* the events
* @param eventsRegex
* the events regex
* @param name
* the name
*/
public Gobler(InputStream stream, Logger log, Map events, Map eventsRegex, Map missingEvents, Map missingEventsRegex, String name, int pid)
{
_inputStream = stream;
_name = name;
_goblerLog = log;
_actions = events;
if (events != null)
{
_actionTriggers = new String[events.size()];
int i = 0;
for (Iterator it = events.keySet().iterator(); it.hasNext(); i++)
{
_actionTriggers[i] = (String) it.next();
}
}
_actionsRegex = eventsRegex;
if (eventsRegex != null)
{
_actionTriggersRegex = new com.karneim.util.collection.regex.Pattern[eventsRegex.size()];
int i = 0;
for (Iterator it = eventsRegex.keySet().iterator(); it.hasNext(); i++)
{
String s = (String) it.next();
try
{
_actionTriggersRegex[i] = new com.karneim.util.collection.regex.Pattern(s);
}
catch (Throwable ex)
{
getWrapperLogger().log(Level.SEVERE, "error in regular expression " + s, ex);
}
}
}
_missingActions = missingEvents;
if (_missingActions != null)
{
_missingActionTriggers = new String[_missingActions.size()];
int i = 0;
for (Iterator it = _missingActions.keySet().iterator(); it.hasNext(); i++)
{
_missingActionTriggers[i] = (String) it.next();
}
}
_missingActionsRegex = missingEventsRegex;
if (_missingActionsRegex != null)
{
_missingActionTriggersRegex = new com.karneim.util.collection.regex.Pattern[_missingActionsRegex.size()];
int i = 0;
for (Iterator it = missingEventsRegex.keySet().iterator(); it.hasNext(); i++)
{
String s = (String) it.next();
try
{
_missingActionTriggersRegex[i] = new com.karneim.util.collection.regex.Pattern(s);
}
catch (Throwable ex)
{
getWrapperLogger().log(Level.SEVERE, "error in regular expression " + s, ex);
}
}
}
}
/*
* (non-Javadoc)
*
* @see java.lang.Runnable#run()
*/
@Override
public void run()
{
try
{
if (_inputStream == null)
{
_goblerLog.info("cannot run stream gobler with a null stream");
return;
}
InputStreamReader isr = new InputStreamReader(_inputStream);
BufferedReader br = new BufferedReader(isr);
String line = null;
int k = 0;
if (_missingActions != null)
for (Iterator it = _missingActions.values().iterator(); it.hasNext();)
{
((MissingTriggerAction) it.next()).start();
}
if (_missingActionsRegex != null)
for (Iterator it = _missingActionsRegex.values().iterator(); it.hasNext();)
{
((MissingTriggerAction) it.next()).start();
}
while ((line = br.readLine()) != null)
{
if (_drain)
_drainBuffer.write(line);
if (k > MIN_PROCESS_LINES_TO_LOG)
_goblerLog.finest(line);
else
{
_goblerLog.info(line);
k++;
}
if (_actionTriggers != null)
for (int i = 0; i < _actionTriggers.length; i++)
{
if (line.contains(_actionTriggers[i]))
{
Object obj = _actions.get(_actionTriggers[i]);
if (obj instanceof TriggerAction)
{
TriggerAction action = (TriggerAction) obj;
getWrapperLogger().info("Trigger found: " + _actionTriggers[i]);
action.execute(new String(line));
}
else if (obj instanceof Collection)
{
Collection c = (Collection) obj;
for (Iterator it = c.iterator(); it.hasNext();)
{
TriggerAction action = (TriggerAction) it.next();
getWrapperLogger().info("Trigger found: " + _actionTriggers[i]);
action.execute(new String(line));
}
}
break;
}
}
if (_actionTriggersRegex != null)
for (int i = 0; i < _actionTriggersRegex.length; i++)
{
if (_actionTriggersRegex[i].contains(line))
{
Object obj = _actionsRegex.get(_actionTriggersRegex[i].getRegEx());
if (obj instanceof TriggerAction)
{
TriggerAction action = (TriggerAction) obj;
getWrapperLogger().info("Trigger found: " + _actionTriggersRegex[i]);
action.execute(new String(line));
}
else if (obj instanceof Collection)
{
Collection c = (Collection) obj;
for (Iterator it = c.iterator(); it.hasNext();)
{
TriggerAction action = (TriggerAction) it.next();
getWrapperLogger().info("Trigger found: " + _actionTriggersRegex[i]);
action.execute(new String(line));
}
}
break;
}
}
if (_missingActionTriggers != null)
for (int i = 0; i < _missingActionTriggers.length; i++)
{
if ("".equals(_missingActionTriggers[i]) || line.contains(_missingActionTriggers[i]))
{
Object obj = _missingActions.get(_missingActionTriggers[i]);
if (obj instanceof TriggerAction)
{
TriggerAction action = (TriggerAction) obj;
// getWrapperLogger().info("Trigger found: "
// + _missingActionTriggers[i]);
action.execute(new String(line));
}
// break;
}
}
if (_missingActionTriggersRegex != null)
for (int i = 0; i < _missingActionTriggersRegex.length; i++)
{
if (_missingActionTriggersRegex[i].contains(line))
{
Object obj = _actionsRegex.get(_missingActionTriggersRegex[i].getRegEx());
if (obj instanceof TriggerAction)
{
TriggerAction action = (TriggerAction) obj;
// getWrapperLogger().info("Trigger found: "
// + _missingActionTriggersRegex[i]);
action.execute(new String(line));
}
// break;
}
}
Thread.yield();
}
}
catch (Exception ioe)
{
// ioe.printStackTrace();
// _goblerLog.info("gobler execption " + _name + " " +
// ioe.getMessage());
if (_debug)
_goblerLog.log(Level.INFO, " gobler exeception " + _name, ioe);
}
if (AbstractWrappedProcess.this._osProcess != null && _pid == AbstractWrappedProcess.this._osProcess.getPid()
&& AbstractWrappedProcess.this._osProcess.isRunning())
AbstractWrappedProcess.this.executor.execute(new Runnable()
{
@Override
public void run()
{
if (AbstractWrappedProcess.this._state != STATE_RESTART_START && AbstractWrappedProcess.this._state != STATE_RESTART
&& AbstractWrappedProcess.this._state != STATE_RESTART_STOP
&& AbstractWrappedProcess.this._state != STATE_RESTART_WAIT)
{
_goblerLog.warning("yajsw panicking: gobler stopped but process is still running -> restart process");
AbstractWrappedProcess.this.restartInternal();
}
}
});
if (_debug)
_goblerLog.info("gobler terminated " + _name);
if (_missingActions != null)
for (Iterator it = _missingActions.values().iterator(); it.hasNext();)
{
((MissingTriggerAction) it.next()).stop();
}
if (_missingActionsRegex != null)
for (Iterator it = _missingActionsRegex.values().iterator(); it.hasNext();)
{
((MissingTriggerAction) it.next()).stop();
}
}
public boolean isDrain()
{
return _drain;
}
}
@Override
public void addStateChangeListener(int state, StateChangeListener listener)
{
_listeners.put(state, listener);
}
@Override
public void addStateChangeListener(StateChangeListener listener)
{
_listeners.put(999, listener);
}
@Override
public void removeStateChangeListener(StateChangeListener listener)
{
}
@Override
public void removeStateChangeListener(int state)
{
_listeners.remove(state);
}
@Override
public int getRestartCount()
{
return _restartCount;
}
@Override
public String getStringState()
{
return getStringState(_state);
}
@Override
public String getName()
{
String result = "";
if (_config == null)
return result;
if (_config.getBoolean("wrapper.service", false))
result += "Service ";
String name = _config.getString("wrapper.console.title");
if (name == null)
name = _config.getString("wrapper.ntservice.name");
if (name == null)
name = _config.getString("wrapper.image");
if (name == null)
name = _config.getString("wrapper.groovy");
if (name == null)
name = _config.getString("wrapper.java.app.mainclass");
if (name == null)
name = _config.getString("wrapper.java.app.jar");
if (name == null)
name = "";
result += name;
return result;
}
@Override
public OutputStream getOutputStream()
{
if (_osProcess != null)
return _osProcess.getOutputStream();
return null;
}
@Override
public Date getAppStarted()
{
return _appStarted;
}
@Override
public Date getAppStopped()
{
return _appStopped;
}
@Override
public Date getWrapperStarted()
{
return _wrapperStarted;
}
@Override
public int getAppThreads()
{
if (_osProcess != null)
return _osProcess.getCurrentThreads();
else
return -1;
}
@Override
public long getAppMemory()
{
if (_osProcess != null)
return _osProcess.getCurrentVirtualMemory();
else
return -1;
}
@Override
public int getAppCpu()
{
if (_osProcess != null)
return _osProcess.getCurrentCpu();
else
return -1;
}
@Override
public int getAppHandles()
{
if (_osProcess != null)
return _osProcess.getCurrentHandles();
else
return -1;
}
@Override
public void addTriggerListener(TriggerListener listener)
{
// TODO ;
}
@Override
public int getWrapperPid()
{
return OperatingSystem.instance().processManagerInstance().currentProcessId();
}
@Override
public boolean isTimerActive()
{
return (_timer != null) && _timer.isTriggered();
}
@Override
public boolean isConditionActive()
{
return (_condition != null) && _condition.isTriggered();
}
@Override
public void threadDump()
{
if (_osProcess != null && this instanceof WrappedJavaProcess && _osProcess.isRunning())
((WrappedJavaProcess) this).requestThreadDump();
}
@Override
public void wrapperThreadDump()
{
Message m = new Message(Constants.WRAPPER_MSG_THREAD_DUMP, null);
Action a = ActionFactory.getAction(m);
try
{
ByteArrayOutputStream str = new ByteArrayOutputStream();
PrintStream pr = new PrintStream(str);
a.execute(m, null, pr);
pr.flush();
getWrapperLogger().info(str.toString());
}
catch (IOException e)
{
e.printStackTrace();
}
}
@Override
public void stopTimerCondition()
{
stopTimer();
stopCondition();
}
@Override
public boolean isOSProcessRunning()
{
if (_osProcess == null)
return false;
else
return _osProcess.isRunning();
}
@Override
public int getTotalRestartCount()
{
return _totalRestartCount;
}
public boolean isService()
{
return _config.getBoolean("wrapper.service", false);
}
@Override
public String getType()
{
if (_config.getBoolean("wrapper.service", false))
return "Service";
else if (_config.getBoolean("wrapper.console.visible", false))
return "Console";
else
return "Process";
}
@Override
public void shutdown()
{
setState(STATE_SHUTDOWN);
}
@Override
public void osProcessTerminated()
{
Process process = _osProcess;
if (process != null)
_exitCode = process.getExitCode();
}
@Override
public boolean isHaltWrapperOnApp()
{
return _haltWrapperOnApp;
}
@Override
public boolean isHaltAppOnWrapper()
{
return _haltAppOnWrapper;
}
@Override
public void setExiting()
{
_exiting = true;
}
@Override
public boolean isExiting()
{
return _exiting;
}
@Override
public TrayIconProxy getTrayIcon()
{
return _trayIconMessages;
}
@Override
public String[][] getTrayIconMessages()
{
if (_trayIconMessages == null)
return null;
return _trayIconMessages.toArrayAndClear();
}
@Override
public void stopWrapper()
{
if (_haltAppOnWrapper)
stop();
shutdown();
try
{
Thread.sleep(5000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.exit(0);
}
@Override
public boolean hasOutput()
{
return getOutputStream() != null;
}
@Override
public void writeOutput(String txt)
{
((PrintStream) getOutputStream()).println(txt);
}
@Override
public void setInquireResponse(String s)
{
if (_trayIconMessages != null)
{
_trayIconMessages._inquireResponse = s;
_trayIconMessages._inquireMessage = null;
}
}
@Override
public String getInquireMessage()
{
if (_trayIconMessages != null)
return _trayIconMessages._inquireMessage;
else
return null;
}
@Override
public void setService(Object service)
{
_service = service;
}
public Object getService()
{
return _service;
}
@Override
public void setProperty(String key, String value)
{
getWrapperLogger().info("set property " + key + " " + value);
_localConfiguration.setProperty(key, value);
}
@Override
public void resetCache()
{
getWrapperLogger().info("reset cache ");
_cache = null;
}
@Override
public long getMaxStartTime()
{
if (_config == null)
return 0;
long startDelay = _config.getLong("wrapper.startup.delay", 0);
long startupTimeout = _config.getInt("wrapper.startup.timeout", DEFAULT_STARTUP_TIMEOUT) * 1000;
if (startDelay < Integer.MAX_VALUE && startupTimeout < Integer.MAX_VALUE)
return startDelay + startupTimeout;
else
return Integer.MAX_VALUE;
}
public boolean isReconnecting()
{
return _reconnecting;
}
void setReconnecting(boolean reconnecting)
{
_reconnecting = reconnecting;
}
}