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; } } public static String[] NODE_ENV_VAR_SET = new String[]{"APPDATA","PATH","TEMP","TMP","SystemDrive"}; // #81, #197 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 = NODE_ENV_VAR_SET.length; 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{ for (String envVarName : NODE_ENV_VAR_SET){ envp[idx++] = getEnvVariableEqualsString(envVarName); } } 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; } } }