blob: 0751e0ad5e905d1ce125bb1d6aa8337573873be1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2017 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.jsdt.js.node.internal.launch;
import java.io.File;
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.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.LaunchConfigurationDelegate;
import org.eclipse.osgi.util.NLS;
import org.eclipse.wst.jsdt.chromium.debug.core.ChromiumDebugPlugin;
import org.eclipse.wst.jsdt.core.runtime.IJSRunner;
import org.eclipse.wst.jsdt.core.runtime.IJSRuntimeInstall;
import org.eclipse.wst.jsdt.core.runtime.JSRunnerConfiguration;
import org.eclipse.wst.jsdt.core.runtime.JSRuntimeManager;
import org.eclipse.wst.jsdt.js.node.NodePlugin;
import org.eclipse.wst.jsdt.js.node.internal.Messages;
import org.eclipse.wst.jsdt.js.node.internal.NodeConstants;
import org.eclipse.wst.jsdt.js.node.internal.util.LaunchConfigurationUtil;
import org.eclipse.wst.jsdt.js.node.runtime.NodeJsRuntimeType;
import org.eclipse.wst.jsdt.launching.ExecutionArguments;
/**
* Launch configuration delegate for node application
*
* @author "Adalberto Lopez Venegas (adalbert)"
* @author "Ilya Buziuk (ibuziuk)"
* @author "Gorkem Ercan (gercan)"
*/
public class NodeLaunchConfigurationDelegate extends LaunchConfigurationDelegate {
@Override
public void launch(ILaunchConfiguration configuration, final String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException {
if (mode.equals(ILaunchManager.DEBUG_MODE) && !LaunchConfigurationUtil.isChromiumAvailable()) {
throw new CoreException(new Status(IStatus.ERROR, NodePlugin.PLUGIN_ID,
Messages.LAUNCH_CONFIGURATION_CHROMIUM_IS_NOT_AVAILABLE_ERROR));
}
if (monitor == null) {
monitor = new NullProgressMonitor();
}
monitor.beginTask(NLS.bind("{0}...", new String[]{configuration.getName()}), 3); //$NON-NLS-1$
// check for cancellation
if (monitor.isCanceled()) {
return;
}
try {
String mainTypeName = configuration.getAttribute(NodeConstants.ATTR_APP_PATH, NodeConstants.EMPTY);
// Resolve possible ${workspace_loc} variable
mainTypeName = LaunchConfigurationUtil.resolveValue(mainTypeName);
IJSRunner runner = getJSRunner(configuration, mode);
if (runner == null) {
throw new CoreException(new Status (IStatus.ERROR, NodePlugin.PLUGIN_ID,
Messages.LAUNCH_CONFIGURATION_NO_RUNNER_FOUND_ERROR));
}
File workingDir = verifyWorkingDirectory(configuration);
String workingDirName = null;
if (workingDir != null) {
workingDirName = workingDir.getAbsolutePath();
}
// Environment variables
String[] envp= getEnvironment(configuration);
// Program & VM arguments
String pgmArgs = getProgramArguments(configuration);
String nodeArgs = getNodeArguments(configuration);
if (mode.equals(ILaunchManager.DEBUG_MODE)) {
String debugPort = configuration.getAttribute(NodeConstants.ATTR_PORT_FIELD,
String.valueOf(NodeConstants.DEFAULT_PORT));
Boolean isBreakEnable = configuration.getAttribute(NodeConstants.ATTR_BREAK_FIELD, true);
// Adding the "debug" flag first
if (isBreakEnable) {
nodeArgs = "--debug-brk=" + debugPort + " " + nodeArgs; //$NON-NLS-1$ //$NON-NLS-2$
} else {
nodeArgs = "--debug=" + debugPort + " " + nodeArgs; //$NON-NLS-1$ //$NON-NLS-2$
}
}
ExecutionArguments execArgs = new ExecutionArguments(nodeArgs, pgmArgs);
// Replace with js file if mainTypeName is a TypeScript file
mainTypeName = getJsFile(mainTypeName);
// Create VM config
JSRunnerConfiguration runConfig = new JSRunnerConfiguration(mainTypeName);
runConfig.setProgramArguments(execArgs.getProgramArgumentsArray());
runConfig.setEnvironment(envp);
runConfig.setJSRuntimeArguments(execArgs.getVMArgumentsArray());
runConfig.setWorkingDirectory(workingDirName);
// check for cancellation
if (monitor.isCanceled()) {
return;
}
// done the verification phase
monitor.worked(1);
// Launch the configuration - 1 unit of work
IProcess process = runner.run(runConfig, launch, monitor);
if (process == null) return;
// Attaching V8 debugger process
if (mode.equals(ILaunchManager.DEBUG_MODE)) {
DebuggerConnectRunnable runnable = new DebuggerConnectRunnable();
runnable.connector = new NodeDebugConnector(configuration, launch);
Thread thread = new Thread(runnable, "Connect to node.js debugger thread"); //$NON-NLS-1$
thread.setDaemon(true);
thread.start();
while (thread.isAlive()) {
if (monitor.isCanceled()) {
thread.interrupt();
if (process.canTerminate()) {
process.terminate();
}
}
try {
Thread.sleep(100);
} catch (Exception e) {
}
}
if (runnable.exception != null) {
if (runnable.exception instanceof CoreException) {
throw (CoreException) runnable.exception;
}
throw new CoreException(new Status(IStatus.ERROR, NodePlugin.PLUGIN_ID,
runnable.exception.getMessage(), runnable.exception));
}
}
// check for cancellation
if (monitor.isCanceled()) {
return;
}
} finally {
monitor.done();
}
}
private String getJsFile(String mainTypeName) throws CoreException {
IPath file = new Path(mainTypeName);
String jsFile = ChromiumDebugPlugin.getSourceMapManager().getJsFile(file);
return jsFile != null ? jsFile : mainTypeName;
}
class DebuggerConnectRunnable implements Runnable {
private static final int TIMEOUT = 15000;
Exception exception = null;
NodeDebugConnector connector;
@Override
public void run() {
long start = System.currentTimeMillis();
boolean attached = false;
Exception e = null;
do {
try {
attached = connector.attach();
} catch (Exception ex) {
e = ex;
}
} while (!attached && System.currentTimeMillis() < start + TIMEOUT);
if (!attached) {
exception = e;
}
}
}
public IJSRunner getJSRunner(ILaunchConfiguration configuration, String mode) throws CoreException {
IJSRuntimeInstall runtimeInstall = verifyJSRuntimeInstall(configuration);
if (runtimeInstall != null) {
return runtimeInstall.getJSRunner(mode);
}
return null;
}
public IJSRuntimeInstall verifyJSRuntimeInstall(ILaunchConfiguration configuration) throws CoreException {
return getJSRuntimeInstall(configuration);
}
public IJSRuntimeInstall getJSRuntimeInstall(ILaunchConfiguration configuration) throws CoreException {
// As for now, always run using the default runtime install for Node.js
return JSRuntimeManager.getDefaultRuntimeInstall(NodeJsRuntimeType.NODE_JS_RUNTIME_TYPE_ID);
}
public File verifyWorkingDirectory(ILaunchConfiguration configuration)
throws CoreException {
File workingPath = null;
String workingDirectory = configuration.getAttribute(NodeConstants.ATTR_WORKING_DIRECTORY, NodeConstants.EMPTY);
if (workingDirectory.equals(NodeConstants.EMPTY)) {
String projectName = configuration.getAttribute(NodeConstants.ATTR_APP_PROJECT, NodeConstants.EMPTY);
if (!projectName.equals(NodeConstants.EMPTY)) {
IPath location = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName).getLocation();
if (location != null) {
workingDirectory = location.toOSString();
}
}
}
if (workingDirectory.length() > 0){
workingDirectory = LaunchConfigurationUtil.resolveValue(workingDirectory);
if(workingDirectory != null){
workingPath = new File(workingDirectory);
}
}
return workingPath;
}
public String[] getEnvironment(ILaunchConfiguration configuration) throws CoreException {
return DebugPlugin.getDefault().getLaunchManager().getEnvironment(configuration);
}
public String getProgramArguments(ILaunchConfiguration configuration)
throws CoreException {
String appArguments = configuration.getAttribute(NodeConstants.ATTR_APP_ARGUMENTS, NodeConstants.EMPTY);
return VariablesPlugin.getDefault().getStringVariableManager()
.performStringSubstitution(appArguments);
}
public String getNodeArguments(ILaunchConfiguration configuration) throws CoreException {
String nodeArguments = configuration.getAttribute(NodeConstants.ATTR_NODE_ARGUMENTS, NodeConstants.EMPTY);
String args = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(nodeArguments);
return args;
}
}