package org.nodeclipse.debug.launch;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.chromium.debug.core.ChromiumDebugPlugin;
import org.eclipse.core.resources.ResourcesPlugin;
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.DebugException;
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.ILaunchConfigurationDelegate;
import org.eclipse.debug.core.model.RuntimeProcess;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.nodeclipse.debug.util.Constants;
import org.nodeclipse.debug.util.NodeDebugUtil;
import org.nodeclipse.debug.util.VariablesUtil;
import org.nodeclipse.ui.Activator;
import org.nodeclipse.ui.preferences.Dialogs;
import org.nodeclipse.ui.preferences.PreferenceConstants;
import org.nodeclipse.ui.util.NodeclipseConsole;
/**
* launch() implements starting Node and passing all parameters.
* Node is launched as node, coffee, coffee -c, tsc or node-dev(or other monitors)
*
* @author Lamb, Tomoyuki, Pushkar, Paul Verest
*/
public class LaunchConfigurationDelegate implements
ILaunchConfigurationDelegate {
private static RuntimeProcess nodeProcess = null; //since 0.7 it should be debuggable instance
//@since 0.7. contain all running Node thread, including under debug. Non Thread-safe, as it should be only in GUI thread
//private static List<RuntimeProcess> nodeRunningProcesses = new LinkedList<RuntimeProcess>();
private boolean warned = false;
/*
* (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)
*/
@Override
public void launch(ILaunchConfiguration configuration, String mode,
ILaunch launch, IProgressMonitor monitor) throws CoreException {
IPreferenceStore preferenceStore = Activator.getDefault().getPreferenceStore();
boolean allowedMany = preferenceStore.getBoolean(PreferenceConstants.NODE_ALLOW_MANY);//@since 0.7
boolean isDebugMode = mode.equals(ILaunchManager.DEBUG_MODE);
if (allowedMany){//@since 0.7
if ( isDebugMode
&& (nodeProcess != null && !nodeProcess.isTerminated()) ) {
showErrorDialog("Only 1 node process can be debugged in 1 Eclipse instance!\n\n"+
"Open other Eclipse/Enide Studio with different node debug port configurred. ");
return;
}
}else{
if(nodeProcess != null && !nodeProcess.isTerminated()) {
//throw new CoreException(new Status(IStatus.OK, ChromiumDebugPlugin.PLUGIN_ID, null, null));
showErrorDialog("Other node process is running!");
return;
//TODO suggest to terminate and start new
}
}
// Using configuration to build command line
List<String> cmdLine = new ArrayList<String>();
if (preferenceStore.getBoolean(PreferenceConstants.NODE_JUST_NODE)){
cmdLine.add("node");
}else{
// old way: Application path should be stored in preference.
String nodePath= preferenceStore.getString(PreferenceConstants.NODE_PATH);
// Check if the node location is correctly configured
File nodeFile = new File(nodePath);
if(!nodeFile.exists()){
// If the location is not valid than show a dialog which prompts the user to goto the preferences page
Dialogs.showPreferencesDialog("Node.js runtime is not correctly configured.\n\n"
+ "Please goto Window -> Prefrences -> Nodeclipse and configure the correct location");
return;
}
cmdLine.add(nodePath);
}
if (isDebugMode) {
// -brk says to Node runtime wait until Chromium Debugger starts and connects
// that is causing "stop on first line" behavior,
// otherwise small apps or first line can be undebuggable.
String brk = "-brk" ; //default "-brk"
if (preferenceStore.getBoolean(PreferenceConstants.NODE_DEBUG_NO_BREAK)) //default false
brk = "";
// done: flexible debugging port, instead of hard-coded 5858
// #61 https://github.com/Nodeclipse/nodeclipse-1/issues/61
int nodeDebugPort = preferenceStore.getInt(PreferenceConstants.NODE_DEBUG_PORT);
if (nodeDebugPort==0) { nodeDebugPort=5858;};
cmdLine.add("--debug"+brk+"="+nodeDebugPort); //--debug-brk=5858
}
//@since 0.9 from Preferences
String nodeOptions= preferenceStore.getString(PreferenceConstants.NODE_OPTIONS);
if(!nodeOptions.equals("")) {
String[] sa = nodeOptions.split(" ");
for(String s : sa) {
cmdLine.add(s);
}
}
String nodeArgs = configuration.getAttribute(Constants.ATTR_NODE_ARGUMENTS, "");
if(!nodeArgs.equals("")) {
String[] sa = nodeArgs.split(" ");
for(String s : sa) {
cmdLine.add(s);
}
}
String file = configuration.getAttribute(Constants.KEY_FILE_PATH, Constants.BLANK_STRING);
String extension = null;
int i = file.lastIndexOf('.');
if(i > 0) {
extension = file.substring(i+1);
} else {
// throw new CoreException(new Status(IStatus.OK, ChromiumDebugPlugin.PLUGIN_ID,
// "Target file does not have extension: " + file, null));
// by default assume
extension = "js";
}
// #57 running app.js with node-dev, forever, supervisor, nodemon etc
// https://github.com/Nodeclipse/nodeclipse-1/issues/57
String nodeMonitor = configuration.getAttribute(Constants.ATTR_NODE_MONITOR, "");
if(!nodeMonitor.equals("")) { // any value
//TODO support selection, now only one
String nodeMonitorPath= preferenceStore.getString(PreferenceConstants.NODE_MONITOR_PATH);
// Check if the node monitor location is correctly configured
File nodeMonitorFile = new File(nodeMonitorPath);
if(!nodeMonitorFile.exists()){
// If the location is not valid than show a dialog which prompts the user to goto the preferences page
Dialogs.showPreferencesDialog("Node.js monitor is not correctly configured.\n"
+ "Select path to installed util: forever, node-dev, nodemon or superviser.\n\n"
+ "Please goto Window -> Prefrences -> Nodeclipse and configure the correct location");
return;
}
cmdLine.add(nodeMonitorPath);
} else if ( ("coffee".equals(extension))||("litcoffee".equals(extension))||("md".equals(extension)) ) {
//if (preferenceStore.getBoolean(PreferenceConstants.COFFEE_JUST_COFFEE)){
// cmdLine.add("coffee"); //TODO should be instead of node above
//}else{
cmdLine.add(preferenceStore.getString(PreferenceConstants.COFFEE_PATH));
//}
// coffee -c
String coffeeCompile = configuration.getAttribute(Constants.ATTR_COFFEE_COMPILE, "");
if(!coffeeCompile.equals("")) { // any value
cmdLine.add("-c");
String coffeeCompileOptions = preferenceStore.getString(PreferenceConstants.COFFEE_COMPILE_OPTIONS);
if(!coffeeCompileOptions.equals("")) {
cmdLine.add(coffeeCompileOptions);
}
}
} else if ("ts".equals(extension)) {
// the only thing we can do now with .ts is to compile, so no need to check if it was launched as tsc
//String typescriptCompiler = configuration.getAttribute(Constants.ATTR_TYPESCRIPT_COMPILER, "");
cmdLine.add(preferenceStore.getString(PreferenceConstants.TYPESCRIPT_COMPILER_PATH));
}
String filePath = ResourcesPlugin.getWorkspace().getRoot().findMember(file).getLocation().toOSString();
// path is relative, so can not found it.
cmdLine.add(filePath);
//@since 0.9 from Preferences
String nodeApplicationArguments = preferenceStore.getString(PreferenceConstants.NODE_APPLICATION_ARGUMENTS);
if(!nodeApplicationArguments.equals("")) {
String[] sa = nodeApplicationArguments.split(" ");
for(String s : sa) {
cmdLine.add(s);
}
}
String programArgs = configuration.getAttribute(Constants.ATTR_PROGRAM_ARGUMENTS, "");
if(!programArgs.equals("")) {
String[] sa = programArgs.split(" ");
for(String s : sa) {
cmdLine.add(s);
}
}
//TODO rename workingPath to workingDirectory, workingDirectory to workingDirectoryConfig
//DONE propagate changes to .phantom,.mongo ...
File workingPath = null;
String workingDirectory = configuration.getAttribute(Constants.ATTR_WORKING_DIRECTORY, "");
if(workingDirectory.length() > 0) {
workingDirectory = VariablesUtil.resolveValue(workingDirectory);
if(workingDirectory != null) {
workingPath = new File(workingDirectory);
}
}
if (workingPath == null){
workingPath = (new File(filePath)).getParentFile();
}
//env
String[] envp = getEnvironmentVariables(configuration);
for(String s : cmdLine) NodeclipseConsole.write(s+" ");
NodeclipseConsole.write("\n");
String[] cmds = {};
cmds = cmdLine.toArray(cmds);
// Launch a process to run/debug. See also #71 (output is less or no output)
Process p = DebugPlugin.exec(cmds, workingPath, envp);
// no way to get private p.handle from java.lang.ProcessImpl
RuntimeProcess process = (RuntimeProcess)DebugPlugin.newProcess(launch, p, Constants.PROCESS_MESSAGE);
if (isDebugMode) {
if(!process.isTerminated()) {
int nodeDebugPort = preferenceStore.getInt(PreferenceConstants.NODE_DEBUG_PORT);
NodeDebugUtil.launch(mode, launch, monitor, nodeDebugPort);
}
}
if (allowedMany){ //@since 0.7
if (isDebugMode){
nodeProcess = process;
}
//nodeRunningProcesses.add(process);
}else{
nodeProcess = process;
}
}
private String[] getEnvironmentVariables(ILaunchConfiguration configuration) throws CoreException {
Map<String, String> envm = new HashMap<String, String>();
envm = configuration.getAttribute(Constants.ATTR_ENVIRONMENT_VARIABLES, envm);
int envmSizeDelta = 4;
Map<String,String> all = null;
IPreferenceStore preferenceStore = Activator.getDefault().getPreferenceStore();
boolean passAllEnvVars = preferenceStore.getBoolean(PreferenceConstants.NODE_PASS_ALL_ENVIRONMENT_VARIABLES);//@since 0.12
if (passAllEnvVars){
all = System.getenv();
envmSizeDelta = all.size();
}
String[] envp = new String[envm.size()+envmSizeDelta]; // see below
int idx = 0;
for(String key : envm.keySet()) {
String value = envm.get(key);
envp[idx++] = key + "=" + value;
}
if (passAllEnvVars){
for (Map.Entry<String, String> entry : all.entrySet())
{
//System.out.println(entry.getKey() + "/" + entry.getValue());
envp[idx++] = entry.getKey() + "=" + entry.getValue();
}
}else{
//+ #81
envp[idx++] = getEnvVariableEqualsString("PATH");
envp[idx++] = getEnvVariableEqualsString("TEMP");
envp[idx++] = getEnvVariableEqualsString("TMP");
envp[idx++] = getEnvVariableEqualsString("SystemDrive");
}
if (!warned ){
NodeclipseConsole.write(" These environment variables will be applied automatically to every `node` launch.\n");
StringBuilder sb = new StringBuilder(100);
for(int i=0; i<envp.length; i++){
sb.append(" ").append(envp[i]).append('\n');
}
NodeclipseConsole.write(sb.toString());
warned = true;
}
return envp;
}
protected String getEnvVariableEqualsString(String envvarName){
String envvarValue = System.getenv(envvarName);
if (envvarValue==null) envvarValue = "";
return envvarName + "=" + envvarValue;
}
private void showErrorDialog(final String message) {
Display.getDefault().syncExec(new Runnable() {
public void run() {
Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
MessageDialog dialog = new MessageDialog(shell, "Nodeclipse", null, message,
MessageDialog.ERROR, new String[] { "OK" }, 0);
dialog.open();
}
});
}
public static void terminateNodeProcess() {
if(nodeProcess != null) {
try {
nodeProcess.terminate();
} catch (DebugException e) {
//e.printStackTrace();
NodeclipseConsole.write(e.getLocalizedMessage()+"\n");
}
nodeProcess = null;
}
}
}