blob: 04338dff56a7f9944abb422460273e2302907eb6 [file] [log] [blame]
package org.eclipse.jst.server.tomcat.core.internal;
/**********************************************************************
* Copyright (c) 2003 IBM Corporation and others.
* All rights reserved.   This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
 *
* Contributors:
* IBM - Initial API and implementation
**********************************************************************/
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.*;
import org.eclipse.debug.core.*;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jst.server.j2ee.IWebModule;
import org.eclipse.jst.server.tomcat.core.ITomcatConfiguration;
import org.eclipse.jst.server.tomcat.core.ITomcatRuntime;
import org.eclipse.jst.server.tomcat.core.ITomcatServer;
import org.eclipse.jst.server.tomcat.core.WebModule;
import org.eclipse.jst.server.tomcat.core.internal.command.RemoveWebModuleTask;
import org.eclipse.jst.server.tomcat.core.internal.command.SetWebModulePathTask;
import org.eclipse.wst.server.core.*;
import org.eclipse.wst.server.core.model.*;
import org.eclipse.wst.server.core.resources.IModuleResourceDelta;
import org.eclipse.wst.server.core.util.SocketUtil;
/**
* Generic Tomcat server.
*/
public class TomcatServer implements ITomcatServer, IStartableServer, IMonitorableServer {
private static final String ATTR_STOP = "stop-server";
protected transient IPath tempDirectory;
protected IServerState server;
// the thread used to ping the server to check for startup
protected transient PingThread ping = null;
protected transient IProcess process;
protected transient IDebugEventSetListener processListener;
/**
* TomcatServer.
*/
public TomcatServer() {
super();
}
public void initialize(IServerState server2) {
this.server = server2;
}
public void dispose() { }
public TomcatRuntime getTomcatRuntime() {
if (server.getRuntime() == null)
return null;
else
return (TomcatRuntime) server.getRuntime().getDelegate();
}
public ITomcatVersionHandler getTomcatVersionHandler() {
if (server.getRuntime() == null)
return null;
TomcatRuntime runtime = (TomcatRuntime) server.getRuntime().getDelegate();
return runtime.getVersionHandler();
}
public TomcatConfiguration getTomcatConfiguration() {
IServerConfiguration configuration = server.getServerConfiguration();
if (configuration == null)
return null;
else
return (TomcatConfiguration) configuration.getDelegate();
}
/**
* Returns the project publisher that can be used to
* publish the given project.
*
* @param project org.eclipse.core.resources.IProject
* @return org.eclipse.wst.server.core.model.IProjectPublisher
*/
public IPublisher getPublisher(List parents, IModule module) {
if (isTestEnvironment())
return null;
else
return new TomcatWebModulePublisher((IWebModule) module, server.getRuntime().getLocation());
}
/**
* Return the root URL of this module.
* @param module org.eclipse.wst.server.core.model.IModule
* @return java.net.URL
*/
public URL getModuleRootURL(IModule module) {
try {
if (module == null || !(module instanceof IWebModule))
return null;
IServerConfiguration serverConfig = server.getServerConfiguration();
if (serverConfig == null)
return null;
TomcatConfiguration config = (TomcatConfiguration) serverConfig.getDelegate();
if (config == null)
return null;
String url = "http://localhost";
int port = config.getMainPort().getPort();
port = ServerCore.getServerMonitorManager().getMonitoredPort(server, port, "web");
if (port != 80)
url += ":" + port;
IWebModule module2 = (IWebModule) module;
url += config.getWebModuleURL(module2);
if (!url.endsWith("/"))
url += "/";
return new URL(url);
} catch (Exception e) {
Trace.trace("Could not get root URL", e);
return null;
}
}
/**
* Return the runtime class name.
*
* @return java.lang.String
*/
public String getRuntimeClass() {
return getTomcatVersionHandler().getRuntimeClass();
}
/**
* Return the program's runtime arguments to start or stop.
*
* @param boolean starting
* @return java.lang.String
*/
protected String[] getRuntimeProgramArguments(boolean starting) {
IPath configPath = null;
if (isTestEnvironment())
configPath = getTempDirectory();
return getTomcatVersionHandler().getRuntimeProgramArguments(configPath, isDebug(), starting);
}
/**
* Return the runtime (VM) arguments.
*
* @return java.lang.String
*/
protected String[] getRuntimeVMArguments() {
IPath configPath = null;
if (isTestEnvironment())
configPath = getTempDirectory();
return getTomcatVersionHandler().getRuntimeVMArguments(server.getRuntime().getLocation(), configPath, isSecure());
}
/**
* Obtain a temporary directory if this server doesn't
* already have one. Otherwise, return the existing one.
* @return java.io.File
*/
public IPath getTempDirectory() {
if (tempDirectory == null)
tempDirectory = server.getTempDirectory();
return tempDirectory;
}
/**
* Returns true if the process is set to run in debug mode.
* This feature only works with Tomcat v4.0.
*
* @return boolean
*/
public boolean isDebug() {
return server.getAttribute(PROPERTY_DEBUG, false);
}
/**
* Returns true if this is a test (run code out of the workbench) server.
*
* @return boolean
*/
public boolean isTestEnvironment() {
return server.getAttribute(PROPERTY_TEST_ENVIRONMENT, false);
}
/**
* Returns true if the process is set to run in secure mode.
*
* @return boolean
*/
public boolean isSecure() {
return server.getAttribute(PROPERTY_SECURE, false);
}
protected static String renderCommandLine(String[] commandLine, String separator) {
if (commandLine == null || commandLine.length < 1)
return "";
StringBuffer buf= new StringBuffer(commandLine[0]);
for (int i = 1; i < commandLine.length; i++) {
buf.append(separator);
buf.append(commandLine[i]);
}
return buf.toString();
}
public void setProcess(final IProcess newProcess) {
if (process != null)
return;
process = newProcess;
processListener = new IDebugEventSetListener() {
public void handleDebugEvents(DebugEvent[] events) {
if (events != null) {
int size = events.length;
for (int i = 0; i < size; i++) {
if (process.equals(events[i].getSource()) && events[i].getKind() == DebugEvent.TERMINATE) {
DebugPlugin.getDefault().removeDebugEventListener(this);
stopImpl();
}
}
}
}
};
DebugPlugin.getDefault().addDebugEventListener(processListener);
}
protected void stopImpl() {
if (ping != null) {
ping.stopPinging();
ping = null;
}
if (process != null) {
process = null;
DebugPlugin.getDefault().removeDebugEventListener(processListener);
processListener = null;
}
server.setServerState(IServer.SERVER_STOPPED);
}
/**
* Methods called to notify that publishing is about to begin.
* This allows the server to open a connection to the server
* or get any global information ready.
*
* @param monitor org.eclipse.core.runtime.IProgressMonitor
*/
public IStatus publishStart(IProgressMonitor monitor) {
return new Status(IStatus.OK, TomcatPlugin.PLUGIN_ID, 0, TomcatPlugin.getResource("%publishingStarted"), null);
}
public IStatus publishConfiguration(IProgressMonitor monitor) {
IPath confDir = null;
if (isTestEnvironment()) {
confDir = getTempDirectory();
File temp = confDir.append("conf").toFile();
if (!temp.exists())
temp.mkdirs();
} else
confDir = server.getRuntime().getLocation();
return getTomcatConfiguration().backupAndPublish(confDir, !isTestEnvironment(), monitor);
}
/**
* Methods called to notify that publishing has finished.
* The server can close any open connections to the server
* and do any cleanup operations.
*
* @param monitor org.eclipse.core.runtime.IProgressMonitor
*/
public IStatus publishStop(IProgressMonitor monitor) {
server.setConfigurationSyncState(IServer.SYNC_STATE_IN_SYNC);
return new Status(IStatus.OK, TomcatPlugin.PLUGIN_ID, 0, TomcatPlugin.getResource("%publishingStopped"), null);
}
/**
* Return true if the server should be terminated before the workbench
* shutdown and false if not. If the server is not terminated when
* workbench shutdown, then the server should get reconnected
* in the server load when the workbench startsup.
*
* @return boolean
**/
public boolean isTerminateOnShutdown() {
return true;
}
/**
* Setup for starting the server.
*
* @param launch ILaunch
* @param launchMode String
* @param monitor IProgressMonitor
*/
public void setupLaunch(ILaunch launch, String launchMode, IProgressMonitor monitor) throws CoreException {
if ("true".equals(launch.getLaunchConfiguration().getAttribute(ATTR_STOP, "false")))
return;
IStatus status = getTomcatRuntime().validate();
if (status != null && !status.isOK())
throw new CoreException(status);
//setRestartNeeded(false);
TomcatConfiguration configuration = getTomcatConfiguration();
// check that ports are free
Iterator iterator = configuration.getServerPorts().iterator();
while (iterator.hasNext()) {
IServerPort sp = (IServerPort) iterator.next();
if (SocketUtil.isPortInUse(sp.getPort(), 5))
throw new CoreException(new Status(IStatus.ERROR, TomcatPlugin.PLUGIN_ID, 0, TomcatPlugin.getResource("%errorPortInUse", new String[] {sp.getPort() + "", sp.getName()}), null));
}
server.setServerState(IServer.SERVER_STARTING);
// ping server to check for startup
try {
String url = "http://localhost";
int port = configuration.getMainPort().getPort();
if (port != 80)
url += ":" + port;
ping = new PingThread(this, server, url, launchMode);
ping.start();
} catch (Exception e) {
Trace.trace(Trace.SEVERE, "Can't ping for Tomcat startup.");
}
}
/**
* Cleanly shuts down and terminates the server.
*/
public void stop() {
byte state = server.getServerState();
if (state == IServer.SERVER_STOPPED)
return;
else if (state == IServer.SERVER_STARTING || state == IServer.SERVER_STOPPING) {
terminate();
return;
}
try {
Trace.trace(Trace.FINER, "Stopping Tomcat");
if (state != IServer.SERVER_STOPPED)
server.setServerState(IServer.SERVER_STOPPING);
ILaunchConfiguration launchConfig = server.getLaunchConfiguration(true);
ILaunchConfigurationWorkingCopy wc = launchConfig.getWorkingCopy();
String args = renderCommandLine(getRuntimeProgramArguments(false), " ");
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, args);
wc.setAttribute(ATTR_STOP, "true");
wc.launch(ILaunchManager.RUN_MODE, new NullProgressMonitor());
} catch (Exception e) {
Trace.trace(Trace.SEVERE, "Error stopping Tomcat", e);
}
}
/**
* Terminates the server.
*/
public void terminate() {
if (server.getServerState() == IServer.SERVER_STOPPED)
return;
try {
server.setServerState(IServer.SERVER_STOPPING);
Trace.trace(Trace.FINER, "Killing the Tomcat process");
if (process != null && !process.isTerminated()) {
process.terminate();
stopImpl();
}
} catch (Exception e) {
Trace.trace(Trace.SEVERE, "Error killing the process", e);
}
}
public int getStartTimeout() {
return 45000;
}
public int getStopTimeout() {
return 10000;
}
/**
* Return a string representation of this object.
* @return java.lang.String
*/
public String toString() {
return "TomcatServer";
}
/**
* Update the given configuration in the server.
* (i.e. publish any changes to the server, and restart if necessary)
* @param config org.eclipse.wst.server.core.model.IServerConfiguration
*/
public void updateConfiguration() {
Trace.trace(Trace.FINEST, "Configuration updated " + this);
//setConfigurationSyncState(SYNC_STATE_DIRTY);
//setRestartNeeded(true);
}
/**
* Respond to updates within the project tree.
*/
public void updateModule(final IModule module, IModuleResourceDelta delta) { }
public void setLaunchDefaults(ILaunchConfigurationWorkingCopy workingCopy) {
ITomcatRuntime runtime = getTomcatRuntime();
workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_INSTALL_TYPE, runtime.getVMInstallTypeId());
workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_INSTALL_NAME, runtime.getVMInstall().getName());
String[] args = getRuntimeProgramArguments(true);
String args2 = renderCommandLine(args, " ");
workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, args2);
args = getRuntimeVMArguments();
args2 = renderCommandLine(args, " ");
workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, args2);
List cp = runtime.getRuntimeClasspath();
// add tools.jar to the path
IVMInstall vmInstall = runtime.getVMInstall();
if (vmInstall != null) {
try {
cp.add(JavaRuntime.newRuntimeContainerClasspathEntry(new Path(JavaRuntime.JRE_CONTAINER).append("org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType").append(vmInstall.getName()), IRuntimeClasspathEntry.BOOTSTRAP_CLASSES));
} catch (Exception e) { }
IPath jrePath = new Path(vmInstall.getInstallLocation().getAbsolutePath());
if (jrePath != null) {
IPath toolsPath = jrePath.append("lib").append("tools.jar");
if (toolsPath.toFile().exists()) {
cp.add(JavaRuntime.newArchiveRuntimeClasspathEntry(toolsPath));
}
}
}
Iterator cpi = cp.iterator();
List list = new ArrayList();
while (cpi.hasNext()) {
IRuntimeClasspathEntry entry = (IRuntimeClasspathEntry) cpi.next();
try {
list.add(entry.getMemento());
} catch (Exception e) {
Trace.trace(Trace.SEVERE, "Could not resolve classpath entry: " + entry, e);
}
}
workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, list);
workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, false);
}
/**
* Returns the child project(s) of this project. If this
* project contains other projects, it should list those
* projects. If not, it should return an empty list.
*
* @param project org.eclipse.core.resources.IProject
* @return java.util.List
*/
public List getChildModules(IModule project) {
return new ArrayList(0);
}
/**
* Returns the parent project(s) of this project. When
* determining if a given project can run on a server
* configuration, this method will be used to find the
* actual project that will be run on the server. For
* instance, a Web project may return a list of Ear projects
* that it is contained in if the server only supports Ear
* projects.
*
* <p>If the given project will directly run on the server,
* it should just be returned.</p>
*
* @param project org.eclipse.core.resources.IProject
* @return java.util.List
*/
public List getParentModules(IModule module) throws CoreException {
if (module instanceof IWebModule) {
IWebModule webModule = (IWebModule) module;
IStatus status = canModifyModules(new IModule[] { module }, null);
if (status == null || !status.isOK())
throw new CoreException(status);
ArrayList l = new ArrayList();
l.add(webModule);
return l;
} else
return null;
}
/**
* Returns the project references for projects that are in
* this configuration.
*
* @return java.lang.String[]
*/
public IModule[] getModules() {
List list = new ArrayList();
ITomcatConfiguration config = getTomcatConfiguration();
if (config != null) {
List modules = config.getWebModules();
int size = modules.size();
for (int i = 0; i < size; i++) {
WebModule module = (WebModule) modules.get(i);
String memento = module.getMemento();
if (memento != null) {
int index = memento.indexOf(":");
if (index > 0) {
String factoryId = memento.substring(0, index);
String mem = memento.substring(index + 1);
IModule module2 = ServerUtil.getModule(factoryId, mem);
if (module2 != null)
list.add(module2);
}
}
}
}
IModule[] s = new IModule[list.size()];
list.toArray(s);
return s;
}
public byte getModuleState(IModule module) {
return IServer.MODULE_STATE_STARTED;
}
/**
* Returns true if the given project is supported by this
* server, and false otherwise.
*
* @param project org.eclipse.core.resources.IProject
* @return boolean
*/
public IStatus canModifyModules(IModule[] add, IModule[] remove) {
if (add != null) {
int size = add.length;
for (int i = 0; i < size; i++) {
IModule module = add[i];
if (!(module instanceof IWebModule))
return new Status(IStatus.ERROR, TomcatPlugin.PLUGIN_ID, 0, TomcatPlugin.getResource("%errorWebModulesOnly"), null);
IStatus status = getTomcatVersionHandler().canAddModule((IWebModule) module);
if (status != null && !status.isOK())
return status;
}
}
return new Status(IStatus.OK, TomcatPlugin.PLUGIN_ID, 0, "%canModifyModules", null);
}
/**
* Method called when changes to the modules or module factories
* within this configuration occur. Return any necessary commands to repair
* or modify the server configuration in response to these changes.
*
* @param org.eclipse.wst.server.core.model.IModuleFactoryEvent[]
* @param org.eclipse.wst.server.core.model.IModuleEvent[]
* @return org.eclipse.wst.server.core.model.ITask[]
*/
public ITask[] getRepairCommands(IModuleFactoryEvent[] factoryEvent, IModuleEvent[] moduleEvent) {
List list = new ArrayList();
// check for Web modules being removed
if (factoryEvent != null) {
List modules = getTomcatConfiguration().getWebModules();
int size = modules.size();
for (int i = 0; i < size; i++) {
WebModule module = (WebModule) modules.get(i);
String memento = module.getMemento();
if (memento != null) {
boolean found = false;
int index = memento.indexOf(":");
String factoryId = memento.substring(0, index);
String mem = memento.substring(index + 1);
int size2 = factoryEvent.length;
for (int j = 0; !found && j < size2; j++) {
IModule[] removed = factoryEvent[j].getRemovedModules();
if (removed != null) {
int size3 = removed.length;
for (int k = 0; !found && k < size3; k++) {
if (removed[k] != null && removed[k].getFactoryId().equals(factoryId) &&
removed[k].getId().equals(mem)) {
list.add(new RemoveWebModuleTask(i));
found = true;
}
}
}
}
}
}
}
// check for changing context roots
if (moduleEvent != null) {
int size2 = moduleEvent.length;
for (int j = 0; j < size2; j++) {
if (moduleEvent[j].getModule() instanceof IWebModule && moduleEvent[j].isChanged()) {
IWebModule webModule = (IWebModule) moduleEvent[j].getModule();
String contextRoot = webModule.getContextRoot();
if (contextRoot != null && !contextRoot.startsWith("/"))
contextRoot = "/" + contextRoot;
List modules = getTomcatConfiguration().getWebModules();
int size = modules.size();
boolean found = false;
for (int i = 0; !found && i < size; i++) {
WebModule module = (WebModule) modules.get(i);
String memento = module.getMemento();
if (memento != null) {
int index = memento.indexOf(":");
String factoryId = memento.substring(0, index);
String mem = memento.substring(index + 1);
if (webModule.getFactoryId().equals(factoryId) && webModule.getId().equals(mem)) {
if (!module.getPath().equals(contextRoot)) {
list.add(new SetWebModulePathTask(i, contextRoot));
found = true;
}
}
}
}
}
}
}
ITask[] commands = new ITask[list.size()];
list.toArray(commands);
return commands;
}
public List getServerPorts() {
if (server.getServerConfiguration() == null)
return new ArrayList();
return getTomcatConfiguration().getServerPorts();
}
}