blob: cad2cecf29e8142cb37bc8b3d66f2044567ce22b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 BEA Systems, Inc.
* 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:
* rfrost@bea.com - initial API and implementation
*
* Based on GenericServerBehavior by Gorkem Ercan
*******************************************************************************/
package org.eclipse.jst.server.generic.core.internal;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jst.server.generic.internal.xml.Resolver;
import org.eclipse.jst.server.generic.servertype.definition.External;
import org.eclipse.jst.server.generic.servertype.definition.ServerRuntime;
import org.eclipse.osgi.util.NLS;
import org.eclipse.wst.server.core.IServer;
import org.eclipse.wst.server.core.ServerPort;
import org.eclipse.wst.server.core.internal.Server;
import org.eclipse.wst.server.core.util.SocketUtil;
/**
* Subclass of <code>GenericServerBehavior</code> that supports
* servers which are started/stopped via external executables (e.g. scripts).
*/
public class ExternalServerBehaviour extends GenericServerBehaviour {
// config for debugging session
private ILaunchConfigurationWorkingCopy fLaunchConfigurationWC;
private String fMode;
private ILaunch fLaunch;
private IProgressMonitor fProgressMonitor;
/**
* Override to reset the status if the state was unknown
* @param force
*/
public void stop(boolean force) {
resetStatus(getServer().getServerState());
super.stop(force);
}
/**
* Override to set status to unknown if the port was in use and to reset the status if the state was
* unknown and an exception was not thrown. Will want to change logic once external generic server pings
* server process to determine state instead of maintaining handle to process.
*/
protected void setupLaunch(ILaunch launch, String launchMode, IProgressMonitor monitor) throws CoreException {
int state = getServer().getServerState();
try {
super.setupLaunch(launch, launchMode, monitor);
} catch (CoreException ce) {
ServerPort portInUse = portInUse();
if (portInUse != null) {
Trace.trace(Trace.WARNING, "Port " + portInUse.getPort() + " is currently in use"); //$NON-NLS-1$//$NON-NLS-2$
Status status = new Status(IStatus.WARNING, CorePlugin.PLUGIN_ID, IStatus.OK,
NLS.bind(GenericServerCoreMessages.errorPortInUse,Integer.toString(portInUse.getPort()),portInUse.getName()), null);
setServerStatus(status);
setServerState(IServer.STATE_UNKNOWN);
}
throw ce;
}
resetStatus(state);
}
private ServerPort portInUse() {
ServerPort[] ports = getServer().getServerPorts(null);
ServerPort sp;
for(int i=0;i<ports.length;i++){
sp = ports[i];
if (SocketUtil.isPortInUse(sp.getPort(), 5)) {
return sp;
}
}
return null;
}
/**
* Override to trigger the launch of the debugging session (if appropriate).
*/
protected synchronized void setServerStarted() {
if (fLaunchConfigurationWC != null) {
try {
setupSourceLocator( fLaunch );
ExternalLaunchConfigurationDelegate.startDebugging(fLaunchConfigurationWC, fMode, fLaunch, fProgressMonitor);
} catch (CoreException ce) {
// failed to start debugging, so set mode to run
setMode(ILaunchManager.RUN_MODE);
final Status status = new Status(IStatus.ERROR, CorePlugin.PLUGIN_ID, 1,
GenericServerCoreMessages.errorStartingExternalDebugging, ce);
CorePlugin.getDefault().getLog().log(status);
Trace.trace(Trace.SEVERE, GenericServerCoreMessages.errorStartingExternalDebugging, ce);
} finally {
clearDebuggingConfig();
}
}
setServerState(IServer.STATE_STARTED);
}
/**
* Subclasses may override this method to replace default source locator or add additional
* sourceLookupParticipant, if necessary
* @param launch the ILaunch object of the debug session
*/
protected void setupSourceLocator(ILaunch launch) {
//nothing to do
}
/*
* If the server state is unknown, reset the status to OK
*/
private void resetStatus(int state) {
if (state == IServer.STATE_UNKNOWN) {
setServerStatus(null);
}
}
/**
* Since terminate() is called during restart, need to override to
* call shutdown instead of just killing the original process.
*/
protected void terminate() {
int state = getServer().getServerState();
if (state == IServer.STATE_STOPPED)
return;
// cache a ref to the current process
IProcess currentProcess = process;
// set the process var to null so that GenericServerBehavior.setProcess()
// will grab the stop executable (and declare the server stopped when it exits)
process = null;
// execute the standard shutdown
shutdown(state);
// if the shutdown did not terminate the process, forcibly terminate it
try {
if (currentProcess != null && !currentProcess.isTerminated()) {
Trace.trace(Trace.FINER, "About to kill process: " + currentProcess); //$NON-NLS-1$
currentProcess.terminate();
currentProcess = null;
}
} catch (Exception e) {
Trace.trace(Trace.SEVERE, "Error killing the process", e); //$NON-NLS-1$
}
}
/**
* Override superclass method to correctly setup the launch configuration for starting an external
* server.
* @param workingCopy
* @param monitor
* @throws CoreException
*/
public void setupLaunchConfiguration(ILaunchConfigurationWorkingCopy workingCopy,
IProgressMonitor monitor) throws CoreException {
clearDebuggingConfig();
ServerRuntime serverDef = getServerDefinition();
Resolver resolver = serverDef.getResolver();
workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY,
resolver.resolveProperties(serverDef.getStart().getWorkingDirectory()));
String external = resolver.resolveProperties(getExternalForOS(serverDef.getStart().getExternal()));
workingCopy.setAttribute(ExternalLaunchConfigurationDelegate.COMMANDLINE, external);
workingCopy.setAttribute(ExternalLaunchConfigurationDelegate.DEBUG_PORT,
resolver.resolveProperties(serverDef.getStart().getDebugPort()));
// just use the commandline for now
workingCopy.setAttribute(ExternalLaunchConfigurationDelegate.EXECUTABLE_NAME, external);
Map environVars = getEnvironmentVariables(getServerDefinition().getStart());
if(!environVars.isEmpty()){
workingCopy.setAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES,environVars);
}
String existingProgArgs = workingCopy.getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, (String)null);
String serverProgArgs = getProgramArguments();
if(existingProgArgs==null || existingProgArgs.indexOf(serverProgArgs)<0) {
workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS,serverProgArgs);
}
}
/*
* Returns the first external whose "os" attribute matches (case insensitive) the beginning
* of the name of the current OS (as determined by the System "os.name" property). If
* no such match is found, returns the first external that does not have an OS attribute.
*/
private String getExternalForOS(List externals) {
String currentOS = System.getProperty("os.name").toLowerCase(); //$NON-NLS-1$
External external;
String matchingExternal = null;
String externalOS;
Iterator i = externals.iterator();
while (i.hasNext()) {
external= (External) i.next();
externalOS = external.getOs();
if (externalOS == null) {
if (matchingExternal == null) {
matchingExternal = external.getValue();
}
} else if (currentOS.startsWith(externalOS.toLowerCase())) {
matchingExternal = external.getValue();
break;
}
}
return matchingExternal;
}
/**
* Returns the String ID of the launch configuration type.
* @return launchTypeID
*/
protected String getConfigTypeID() {
return ExternalLaunchConfigurationDelegate.ID_EXTERNAL_LAUNCH_TYPE;
}
/**
* Returns the String name of the stop launch configuration.
* @return launcherName
*/
protected String getStopLaunchName() {
return GenericServerCoreMessages.externalStopLauncher;
}
/**
* Sets up the launch configuration for stopping the server.
*
*/
protected void setupStopLaunchConfiguration(GenericServerRuntime runtime, ILaunchConfigurationWorkingCopy wc) {
clearDebuggingConfig();
ServerRuntime serverDef = getServerDefinition();
Resolver resolver = serverDef.getResolver();
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY,
resolver.resolveProperties(serverDef.getStop().getWorkingDirectory()));
String external = resolver.resolveProperties(getExternalForOS(serverDef.getStop().getExternal()));
wc.setAttribute(ExternalLaunchConfigurationDelegate.COMMANDLINE, external);
// just use commandline for now
Map environVars = getEnvironmentVariables(getServerDefinition().getStop());
if(!environVars.isEmpty()){
wc.setAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES,environVars);
}
wc.setAttribute(
IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS,
resolver.resolveProperties(serverDef.getStop().getProgramArgumentsAsString()));
wc.setAttribute(ExternalLaunchConfigurationDelegate.EXECUTABLE_NAME, external);
wc.setAttribute(Server.ATTR_SERVER_ID, getServer().getId());
}
/**
* Sets the configuration to use for launching a debugging session
*/
protected synchronized void setDebuggingConfig(ILaunchConfigurationWorkingCopy wc,
String mode,
ILaunch launch,
IProgressMonitor monitor) {
this.fLaunchConfigurationWC = wc;
this.fMode = mode;
this.fLaunch = launch;
this.fProgressMonitor = monitor;
}
private synchronized void clearDebuggingConfig() {
this.fLaunchConfigurationWC = null;
this.fMode = null;
this.fLaunch = null;
this.fProgressMonitor = null;
}
}