/*******************************************************************************
* Copyright (c) 2009, 2010 Innovation Gate GmbH.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Innovation Gate GmbH - initial API and implementation
******************************************************************************/
package de.innovationgate.eclipse.wgadesigner.tomcat;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
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.Timer;
import java.util.TimerTask;
import javax.management.AttributeNotFoundException;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jface.preference.IPreferenceStore;
import de.innovationgate.eclipse.utils.FileUtils;
import de.innovationgate.eclipse.wgadesigner.WGADesignerPlugin;
import de.innovationgate.eclipse.wgadesigner.editors.helpers.WGADeployment;
import de.innovationgate.eclipse.wgadesigner.models.WGAExecutionEnvironment;
import de.innovationgate.eclipse.wgadesigner.natures.WGARuntime;
import de.innovationgate.eclipse.wgadesigner.preferences.PreferenceConstants;
import de.innovationgate.utils.WGUtils;
public class TomcatUtils {
// variable name for substitution in tomcat config files for e.g. context.xml
//public static final String VAR_WORKDIR = "de.innovationgate.eclipse.wgadesigner.tomcat.WorkDir";
//public static final String VAR_APPBASE = "de.innovationgate.eclipse.wgadesigner.tomcat.AppBase";
public static final String VAR_PREFIX = "${";
public static final String VAR_SUFFIX= "}";
private static final String MAIN_CLASS = "org.apache.catalina.startup.Bootstrap";
private static final String START_COMMAND = "start";
private ILaunch _tomcatInstance;
private WGARuntime _currentRuntime;
private static TomcatUtils _instance;
private Set<TomcatServerStateListener> _listeners = new HashSet<TomcatServerStateListener>();
private Timer _tomcatWatchdog;
private TimerTask _tomcatWatchdogTask = new TimerTask() {
private int _currentState = TomcatServerStateListener.STATE_TERMINATED;
@Override
public void run() {
int state = -1;
if (isRunning()) {
int coreStatus = retrieveWGACoreStatus();
if (coreStatus == -1) {
state = TomcatServerStateListener.STATE_TERMINATED;
} else if (coreStatus == 50) {
state = TomcatServerStateListener.STATE_RUNNING;
} else if (coreStatus < 50 && coreStatus >= 0) {
state = TomcatServerStateListener.STATE_STARTING;
} else if (coreStatus > 50 && coreStatus <= 100) {
state = TomcatServerStateListener.STATE_STOPPING;
}
} else {
state = TomcatServerStateListener.STATE_TERMINATED;
if(_currentRuntime!=null){
try {
_currentRuntime.getWGABase().refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
}
catch (CoreException e) {
WGADesignerPlugin.getDefault().logInfo("Unable to refresh folder : " + _currentRuntime.getWGABase().getLocation().toString(), e);
}
}
_currentRuntime = null;
}
if (state != _currentState) {
// state changed
notifyListeners(state);
_currentState = state;
}
// if (_currentState == TomcatServerStateListener.STATE_TERMINATED) {
// _currentRuntime = null;
// }
// if (isRunning() && (_currentState == TomcatServerStateListener.STATE_TERMINATED || _currentState == TomcatServerStateListener.STATE_STARTING)) {
// // state changed
// if ()
// notifyListeners(TomcatServerStateListener.STATE_RUNNING);
// _currentState = TomcatServerStateListener.STATE_RUNNING;
// } else if (!isRunning() && _currentState == TomcatServerStateListener.STATE_RUNNING) {
// // state changed
// notifyListeners(TomcatServerStateListener.STATE_TERMINATED);
// _currentRuntime = null;
// _currentState = TomcatServerStateListener.STATE_TERMINATED;
// }
}
};
private File _catalinaHome;
private File _confResourcesDir;
private boolean _stopRequested;
public static TomcatUtils getInstance() {
if (_instance == null) {
_instance = new TomcatUtils();
}
return _instance;
}
private TomcatUtils() {
_tomcatWatchdog = new Timer("Tomcat Watchdog");
_tomcatWatchdog.schedule(_tomcatWatchdogTask, 0, 500 * 1);
}
public void init(File catalinaHome, File confResourcesDir) {
_catalinaHome = catalinaHome;
_confResourcesDir = confResourcesDir;
}
public void initConfDir(File confTargetDir, Map<String,String> variables) throws IOException {
File contextXML = new File(_confResourcesDir, "context.xml");
FileUtils.copy(contextXML, new File(confTargetDir, "context.xml"));
File serverXML = new File(_confResourcesDir, "server.xml");
FileUtils.copy(serverXML, new File(confTargetDir, "server.xml"));
File webXML = new File(_confResourcesDir, "web.xml.template");
FileUtils.copyWithVariableSubstitution(webXML, new File(confTargetDir, "web.xml"), variables, VAR_PREFIX, VAR_SUFFIX);
File catalinaProperties = new File(_confResourcesDir, "catalina.properties.template");
FileUtils.copyWithVariableSubstitution(catalinaProperties, new File(confTargetDir, "catalina.properties"), variables, VAR_PREFIX, VAR_SUFFIX);
File catalinaPolicy = new File(_confResourcesDir, "catalina.policy");
FileUtils.copy(catalinaPolicy, new File(confTargetDir, "catalina.policy"));
File loggingProperties = new File(_confResourcesDir, "logging.properties");
FileUtils.copy(loggingProperties, new File(confTargetDir, "logging.properties"));
}
public void start(WGARuntime runtime, IProgressMonitor monitor) throws IOException, CoreException {
if (monitor == null) {
monitor = new NullProgressMonitor();
}
if (!isRunning()) {
notifyListeners(TomcatServerStateListener.STATE_STARTING);
try {
Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_BUILD, new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN));
} catch (OperationCanceledException e) {
} catch (InterruptedException e) {
}
// get or create WGADeployment
String wgaDistributionName = runtime.getWGADistributionName();
WGADeployment wgaDeployment = null;
if (wgaDistributionName == null) {
throw new IOException("No WGA distribution configured for runtime '" + runtime.getName() + "'.");
} else {
wgaDeployment = WGADesignerPlugin.getDefault().getWGADeploymentManager().getDeployment(wgaDistributionName);
}
if (wgaDeployment == null) {
throw new IOException("WGADeployment '" + wgaDistributionName + "' for runtime '" + runtime.getName() + "' not found.");
}
Map<String,String> variables = new HashMap<String,String>();
variables.put("workspace_loc", runtime.getProject().getWorkspace().getRoot().getLocation().toFile().getAbsolutePath());
variables.put("project_loc", runtime.getProject().getLocation().toFile().getAbsolutePath());
variables.put("wga_appbase", wgaDeployment.getWebappDir().getAbsolutePath());
variables.put("wga_workdir", wgaDeployment.getWorkDir().getAbsolutePath());
IPreferenceStore store = WGADesignerPlugin.getDefault().getPreferenceStore();
variables.put("tomcat_server_port", Integer.toString(store.getInt(PreferenceConstants.TOMCAT_SERVER_PORT)));
variables.put("tomcat_http_port", Integer.toString(store.getInt(PreferenceConstants.TOMCAT_HTTP_PORT)));
variables.put("tomcat_redirect_port", Integer.toString(store.getInt(PreferenceConstants.TOMCAT_REDIRECT_PORT)));
File orgCatalinaBase = runtime.getCatalinaBase().getLocation().toFile();
// create new temp catalinabase directory
File catalinaBase = new File(WGADesignerPlugin.getDefault().getStateLocation().toFile(), "tomcat_current");
if (!catalinaBase.exists()) {
catalinaBase.mkdir();
}
WGUtils.delTree(catalinaBase, false);
// copy current conf from workspace
File tmpConf = new File(catalinaBase, "conf");
WGUtils.copyDirContent(new File(orgCatalinaBase, "conf"), tmpConf);
// copy current lib from workspace
File tmpLib = new File(catalinaBase, "lib");
WGUtils.copyDirContent(new File(orgCatalinaBase, "lib"), tmpLib);
File orgServerXML = new File(orgCatalinaBase, "conf/server.xml");
File serverXML = new File(tmpConf, "server.xml");
FileUtils.copyWithVariableSubstitution(orgServerXML, serverXML, variables, VAR_PREFIX, VAR_SUFFIX);
StringBuffer programArguments = new StringBuffer();
Iterator<String> prgArgs = getPrgArgs(START_COMMAND, serverXML).iterator();
while (prgArgs.hasNext()) {
programArguments.append(" " + prgArgs.next());
}
List<String> vmArgs = getVmArgs(_catalinaHome, catalinaBase, true);
// B00005D9E
vmArgs.add("-Dorg.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES=false");
// add wga startup arguments
vmArgs.addAll(runtime.getWGALaunchVMArguments());
Iterator<String> it = vmArgs.iterator();
StringBuffer jvmArguments = new StringBuffer();
while (it.hasNext()) {
jvmArguments.append(" " + it.next());
}
IVMInstall vmInstall = WGAExecutionEnvironment.findVMInstallById(runtime.getConfiguration().getExecutionEnvironmentId());
if (vmInstall == null) {
vmInstall = JavaRuntime.getDefaultVMInstall();
}
List<IPath> classPath = getClasspath(_catalinaHome, vmInstall.getInstallLocation());
String[] bootClasspath = new String[0];
_tomcatInstance = VMRuntime.runVM("WGA Runtime", vmInstall, MAIN_CLASS, classPath, bootClasspath, jvmArguments.toString(), programArguments.toString(), store.getBoolean(PreferenceConstants.JAVA_DEBUG), store.getBoolean(PreferenceConstants.JAVA_DEBUG), false);
_currentRuntime = runtime;
//notifyListeners(TomcatServerStateListener.STATE_RUNNING);
}
}
/**
* checks if the tomcat socket is used by another process
* @return
*/
public boolean isSocketInUse() {
if (!isRunning()) {
// no instance of myself is running - check socket
try {
Socket socket = new Socket();
int port = WGADesignerPlugin.getDefault().getPreferenceStore().getInt(PreferenceConstants.TOMCAT_SERVER_PORT);
socket.connect(new InetSocketAddress("127.0.0.1", port), 1000);
socket.close();
return true;
} catch (IOException e) {
}
}
return false;
}
public boolean acceptRequests() {
if (retrieveWGACoreStatus() == 50) {
return true;
} else {
return false;
}
}
private int retrieveWGACoreStatus() {
JMXConnector jmxc = null;
try {
int jmxPort = WGADesignerPlugin.getDefault().getPreferenceStore().getInt(PreferenceConstants.JMX_PORT);
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:" + jmxPort + "/jmxrmi");
jmxc = JMXConnectorFactory.connect(url, null);
MBeanServerConnection mbean = jmxc.getMBeanServerConnection();
Object attribute = mbean.getAttribute(new ObjectName("de.innovationgate.WGAMonitor:context=WGAPublisher,name=Information"), "CoreStatus");
if (attribute != null && attribute instanceof Integer) {
return (Integer) attribute;
} else {
return -1;
}
} catch (AttributeNotFoundException e) {
// this might be an wga version < 5.0 - we assume running state here
return 50;
} catch (Exception e) {
// something wrong with jmx
return -1;
} finally {
if (jmxc != null) {
try {
jmxc.close();
} catch (IOException e) {
}
}
}
}
public void stop(IProgressMonitor monitor) throws IOException, DocumentException {
if (monitor == null) {
monitor = new NullProgressMonitor();
}
if (_currentRuntime != null) {
try {
_stopRequested = true;
monitor.beginTask("Stopping wga runtime '" + _currentRuntime.getName() + "'", 2);
if (_tomcatInstance != null && _currentRuntime != null) {
notifyListeners(TomcatServerStateListener.STATE_STOPPING);
// determine serverport and shutdown command from tomcat config
File catalinaBase = new File(WGADesignerPlugin.getDefault().getStateLocation().toFile(), "tomcat_current");
File conf = new File(catalinaBase, "conf");
File serverXML = new File(conf, "server.xml");
SAXReader reader = new SAXReader();
Document doc = reader.read(serverXML);
Element serverElement = (Element) doc.selectSingleNode("/Server");
String serverPort = serverElement.attributeValue("port", "8005");
String shutdownCommand = serverElement.attributeValue("shutdown", "SHUTDOWN");
// send shutdown command to specified port
Socket socket = new Socket();
socket.connect(new InetSocketAddress("127.0.0.1", Integer.parseInt(serverPort)), 1000);
socket.setSoTimeout(5000);
OutputStream stream = socket.getOutputStream();
for (int i = 0; i < shutdownCommand.length(); i++) {
stream.write(shutdownCommand.charAt(i));
}
stream.flush();
stream.close();
socket.close();
monitor.worked(1);
monitor.setTaskName("Waiting for wga shutdown ...");
// wait for tomcat shutdown
while (_tomcatInstance != null && !_tomcatInstance.isTerminated()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {};
}
monitor.worked(1);
notifyListeners(TomcatServerStateListener.STATE_TERMINATED);
}
} finally {
monitor.done();
_stopRequested = false;
}
}
}
private List<String> getPrgArgs(String command, File serverXML) {
ArrayList<String> prgArgs = new ArrayList<String>();
if (command.equals(START_COMMAND)) {
prgArgs.add("-config");
prgArgs.add("\"" + serverXML.getAbsolutePath() + "\"");
prgArgs.add(command);
return prgArgs;
} else {
return new ArrayList<String>();
}
}
private List<String> getVmArgs(File catalinaHome, File catalinaBase, boolean startup) {
ArrayList<String> vmArgs = new ArrayList<String>();
vmArgs.add("-Dcatalina.home=\"" + catalinaHome.getAbsolutePath() + "\"");
vmArgs.add("-Dcatalina.base=\"/" + catalinaBase.getAbsolutePath() + "\"");
File tmpDir = new File(catalinaHome, "/temp");
vmArgs.add("-Djava.io.tmpdir=\"" + tmpDir.getAbsolutePath() + "\"");
if (startup) {
int jmxPort = WGADesignerPlugin.getDefault().getPreferenceStore().getInt(PreferenceConstants.JMX_PORT);
vmArgs.add("-Dcom.sun.management.jmxremote.port=\"" + jmxPort + "\"");
vmArgs.add("-Dcom.sun.management.jmxremote.ssl=\"false\"");
vmArgs.add("-Dcom.sun.management.jmxremote.authenticate=\"false\"");
}
/*
if(TomcatLauncherPlugin.getDefault().isSecurityManagerEnabled()) {
vmArgs.add("-Djava.security.manager");
String securityPolicyFile = catalinaBase + File.separator + "conf" + File.separator + "catalina.policy";
vmArgs.add("-Djava.security.policy=\"" + securityPolicyFile + "\"");
}*/
return vmArgs;
}
private List<IPath> getClasspath(File catalinaHome, File javaHome) {
ArrayList<IPath> classpath = new ArrayList<IPath>();
classpath.add(new Path(catalinaHome.getAbsolutePath()).append("bin").append("bootstrap.jar"));
//classpath.add(new Path(javaHome.getAbsolutePath()).append("lib").append("tools.jar"));
return classpath;
}
public boolean isRunning() {
if (_tomcatInstance != null) {
return !_tomcatInstance.isTerminated();
} else {
return false;
}
}
public void terminateTomcat() throws Exception {
if (_tomcatInstance != null) {
_tomcatInstance.terminate();
_tomcatInstance = null;
}
}
public boolean isRunning(WGARuntime runtime) {
if (_currentRuntime != null && runtime != null && _currentRuntime.getProject().getName().equals(runtime.getProject().getName())) {
return isRunning();
} else {
return false;
}
}
public WGARuntime getCurrentRuntime() {
return _currentRuntime;
}
@Override
protected void finalize() throws Throwable {
if (_tomcatWatchdog != null) {
_tomcatWatchdog.cancel();
_tomcatWatchdog = null;
}
super.finalize();
}
public void addListener(TomcatServerStateListener listener) {
_listeners.add(listener);
if (isRunning()) {
try {
listener.stateChanged(TomcatServerStateListener.STATE_RUNNING);
} catch (Throwable e) {
}
} else {
try {
listener.stateChanged(TomcatServerStateListener.STATE_TERMINATED);
} catch (Throwable e) {
}
}
}
public void removeListener(TomcatServerStateListener listener) {
_listeners.remove(listener);
}
private void notifyListeners(int state) {
Iterator<TomcatServerStateListener> listeners = _listeners.iterator();
while (listeners.hasNext()) {
TomcatServerStateListener listener = listeners.next();
try {
listener.stateChanged(state);
} catch (Throwable e) {
WGADesignerPlugin.getDefault().logError("Exception in TomcatServerStateListener '" + listener.getClass().getName() + "'.", e);
}
}
}
public boolean isTomcatProcess(IProcess process) {
if (_tomcatInstance != null) {
for (IProcess p : _tomcatInstance.getProcesses()) {
if (p.equals(process)) {
return true;
}
}
}
return false;
}
}