/******************************************************************************* * 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.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())); workingCopy.setAttribute(ExternalLaunchConfigurationDelegate.HOST, getServer().getHost()); // 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(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; } }