/******************************************************************************* * 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 * tyip@bea.com * * Based on GenericServerLaunchConfigurationDelegate by Gorkem Ercan *******************************************************************************/ package org.eclipse.jst.server.generic.core.internal; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.apache.tools.ant.taskdefs.Execute; 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.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.core.model.RuntimeProcess; import org.eclipse.jdt.launching.AbstractJavaLaunchConfigurationDelegate; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; import org.eclipse.wst.server.core.IServer; import org.eclipse.wst.server.core.ServerUtil; import org.eclipse.wst.server.core.model.ServerBehaviourDelegate; /** * <p>Extension of <code>AbstractJavaLaunchConfigurationDelegate</code> that supports * servers which are started/stopped via external executables (e.g. scripts).</p> * * <p>Note: <code>AbstractJavaLaunchConfigurationDelegate</code> is extended simply to take advantage * of a set of useful code that is not directly related to launching a JVM-based app.</p> */ public class ExternalLaunchConfigurationDelegate extends AbstractJavaLaunchConfigurationDelegate { /** * Identifier for the executable server configuration type * (value <code>"org.eclipse.jst.server.generic.core.ExternalLaunchConfigurationType"</code>). */ public static final String ID_EXTERNAL_LAUNCH_TYPE = CorePlugin.PLUGIN_ID + ".ExternalLaunchConfigurationType"; //$NON-NLS-1$ /** * Name of the launch configuration attribute that holds the external executable commandline. */ public static final String COMMANDLINE = CorePlugin.PLUGIN_ID + ".COMMANDLINE"; //$NON-NLS-1$ /** * Name of the launch configuration attribute that holds a descriptive name for the external executable. */ public static final String EXECUTABLE_NAME = CorePlugin.PLUGIN_ID + ".EXECUTABLE_NAME"; //$NON-NLS-1$ /** * Name of the launch configuration attribute that holds the debug port. */ public static final String DEBUG_PORT = CorePlugin.PLUGIN_ID + ".DEBUG_PORT"; //$NON-NLS-1$ /** * Name of the launch configuration attribute that holds the host for remote debugging. */ public static final String HOST = CorePlugin.PLUGIN_ID + ".HOST"; //$NON-NLS-1$ /** * Default value for the descriptive name for the external executable. */ public static final String DEFAULT_EXECUTABLE_NAME = "External Generic Server"; //$NON-NLS-1$ /** * Debugging launch configuration delegate. */ private static ExternalDebugLaunchConfigurationDelegate debuggingDelegate = new ExternalDebugLaunchConfigurationDelegate(); /* (non-Javadoc) * @see org.eclipse.debug.core.model.ILaunchConfigurationDelegate#launch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String, org.eclipse.debug.core.ILaunch, org.eclipse.core.runtime.IProgressMonitor) */ public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException { IServer server = ServerUtil.getServer(configuration); if (server == null) { abort(GenericServerCoreMessages.missingServer, null, IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR); } @SuppressWarnings("null") ExternalServerBehaviour serverBehavior = (ExternalServerBehaviour) server.loadAdapter(ServerBehaviourDelegate.class, null); // initialize the server, check the ports and start the PingThread that will check // server state serverBehavior.setupLaunch(launch, mode, monitor); // get the "external" command String commandline = configuration.getAttribute(COMMANDLINE, (String) null); if (commandline == null || commandline.length() == 0) { abort(GenericServerCoreMessages.commandlineUnspecified, null, IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR); } // parse the "external" command into multiple args String[] cmdArgs = DebugPlugin.parseArguments(commandline); // get the "programArguments", parsed into multiple args String[] pgmArgs = DebugPlugin.parseArguments(getProgramArguments(configuration)); // Create the full array of cmds String[] cmds = new String[cmdArgs.length + pgmArgs.length]; System.arraycopy(cmdArgs, 0, cmds, 0, cmdArgs.length); System.arraycopy(pgmArgs, 0, cmds, cmdArgs.length, pgmArgs.length); // get a descriptive name for the executable String executableName = configuration.getAttribute(EXECUTABLE_NAME, DEFAULT_EXECUTABLE_NAME); // get the executable environment ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager(); String[] env = manager.getEnvironment(configuration); // get the working directory File workingDir = verifyWorkingDirectory(configuration); if (workingDir == null) { abort(GenericServerCoreMessages.workingdirUnspecified, null, IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR); } // Launch the executable for the configuration using the Ant Execute class try { Process process = Execute.launch(null, cmds, env, workingDir, true); serverBehavior.startPingThread(); IProcess runtimeProcess = new RuntimeProcess(launch, process, executableName, null); launch.addProcess(runtimeProcess); serverBehavior.setProcess(runtimeProcess); } catch (IOException ioe) { abort(GenericServerCoreMessages.errorLaunchingExecutable, ioe, IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR); } if (mode.equals("debug")) { //$NON-NLS-1$ ILaunchConfigurationWorkingCopy wc = createDebuggingConfig(configuration); // if we're launching the debugging we need to wait for the config to start // before launching the debugging session serverBehavior.setDebuggingConfig(wc, mode, launch, monitor); } } private ILaunchConfigurationWorkingCopy createDebuggingConfig(ILaunchConfiguration configuration) throws CoreException { ILaunchConfigurationWorkingCopy wc = configuration.getWorkingCopy(); String port = configuration.getAttribute(DEBUG_PORT, (String) null); if (port==null || port.length()==0) { abort(GenericServerCoreMessages.debugPortUnspecified, null, IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR); } setDebugArgument(wc, IJavaLaunchConfigurationConstants.ATTR_CONNECT_MAP, "port", port); //$NON-NLS-1$ setDebugArgument(wc, IJavaLaunchConfigurationConstants.ATTR_CONNECT_MAP, "hostname", //$NON-NLS-1$ configuration.getAttribute(HOST, "localhost")); //$NON-NLS-1$ return wc; } /** * Starts the debugging session */ protected static void startDebugging(ILaunchConfigurationWorkingCopy wc, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException { Trace.trace(Trace.FINEST, "Starting debugging"); //$NON-NLS-1$ debuggingDelegate.launch(wc, mode, launch, monitor); } @SuppressWarnings("unchecked") private void setDebugArgument(ILaunchConfigurationWorkingCopy config, String attribKey, String key, String arg) { try { Map args = config.getAttribute(attribKey, (Map)null); if (args!=null) { args = new HashMap(args); } else { args = new HashMap(); } args.put(key, String.valueOf(arg)); config.setAttribute(attribKey, args); } catch (CoreException ce) { // ignore } } /** * Throws a core exception with the given message and optional * exception. The exception's status code will indicate an error. * * @param message error message * @param exception cause of the error, or <code>null</code> * @exception CoreException with the given message and underlying * exception */ protected void abort(String message, Throwable exception, int code) throws CoreException { throw new CoreException(new Status(IStatus.ERROR, CorePlugin.getDefault().getBundle().getSymbolicName(), code, message, exception)); } }