diff --git a/assembly/assembly-ide-war/pom.xml b/assembly/assembly-ide-war/pom.xml index 464529a8d02..aa03387bb7a 100644 --- a/assembly/assembly-ide-war/pom.xml +++ b/assembly/assembly-ide-war/pom.xml @@ -180,6 +180,10 @@ org.eclipse.che.plugin che-plugin-maven-shared + + org.eclipse.che.plugin + che-plugin-nodejs-debugger-ide + org.eclipse.che.plugin che-plugin-nodejs-lang-ide diff --git a/assembly/assembly-ide-war/src/main/resources/org/eclipse/che/ide/IDE.gwt.xml b/assembly/assembly-ide-war/src/main/resources/org/eclipse/che/ide/IDE.gwt.xml index 2677e9551c6..e951b39f2a0 100644 --- a/assembly/assembly-ide-war/src/main/resources/org/eclipse/che/ide/IDE.gwt.xml +++ b/assembly/assembly-ide-war/src/main/resources/org/eclipse/che/ide/IDE.gwt.xml @@ -44,6 +44,7 @@ + diff --git a/assembly/assembly-wsagent-war/pom.xml b/assembly/assembly-wsagent-war/pom.xml index 1344208bc11..b03d2844460 100644 --- a/assembly/assembly-wsagent-war/pom.xml +++ b/assembly/assembly-wsagent-war/pom.xml @@ -160,6 +160,10 @@ org.eclipse.che.plugin che-plugin-maven-server + + org.eclipse.che.plugin + che-plugin-nodejs-debugger-server + org.eclipse.che.plugin che-plugin-nodejs-lang-server diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/debug/DebuggerServiceClientImpl.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/debug/DebuggerServiceClientImpl.java index 1db032ab6dc..7a46ac99507 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/debug/DebuggerServiceClientImpl.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/debug/DebuggerServiceClientImpl.java @@ -96,7 +96,6 @@ public Promise start(String id, StartActionDto action) { public Promise addBreakpoint(String id, BreakpointDto breakpointDto) { final String requestUrl = getBaseUrl(id) + "/breakpoint"; return asyncRequestFactory.createPostRequest(requestUrl, breakpointDto) - .loader(loaderFactory.newLoader()) .send(); } @@ -104,7 +103,6 @@ public Promise addBreakpoint(String id, BreakpointDto breakpointDto) { public Promise> getAllBreakpoints(String id) { final String requestUrl = getBaseUrl(id) + "/breakpoint"; return asyncRequestFactory.createGetRequest(requestUrl) - .loader(loaderFactory.newLoader()) .send(dtoUnmarshallerFactory.newListUnmarshaller(BreakpointDto.class)); } @@ -148,7 +146,6 @@ public Promise getValue(String id, VariableDto variableDto) { } return asyncRequestFactory.createGetRequest(requestUrl + params) - .loader(loaderFactory.newLoader()) .send(dtoUnmarshallerFactory.newUnmarshaller(SimpleValueDto.class)); } @@ -156,7 +153,6 @@ public Promise getValue(String id, VariableDto variableDto) { public Promise setValue(String id, VariableDto variableDto) { final String requestUrl = getBaseUrl(id) + "/value"; return asyncRequestFactory.createPutRequest(requestUrl, variableDto) - .loader(loaderFactory.newLoader()) .send(); } @@ -195,7 +191,6 @@ private String getBaseUrl(String id) { protected Promise performAction(String id, ActionDto actionDto) { final String requestUrl = getBaseUrl(id); return asyncRequestFactory.createPostRequest(requestUrl, actionDto) - .loader(loaderFactory.newLoader()) .send(); } } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/editor/EditorAgentImpl.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/editor/EditorAgentImpl.java index 9c1abb6d619..04b0bb3c49c 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/editor/EditorAgentImpl.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/editor/EditorAgentImpl.java @@ -318,7 +318,7 @@ public List getOpenedEditorsBasedOn(EditorPartPresenter edi @Override public EditorPartPresenter getOpenedEditor(Path path) { EditorPartStack editorPartStack = editorMultiPartStack.getPartStackByPart(activeEditor); - return (EditorPartPresenter)editorPartStack.getPartByPath(path); + return editorPartStack == null ? null : (EditorPartPresenter)editorPartStack.getPartByPath(path); } /** {@inheritDoc} */ diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/impl/ResourceManager.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/impl/ResourceManager.java index 9154b14661b..e8cd77ae4bc 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/impl/ResourceManager.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/impl/ResourceManager.java @@ -1131,7 +1131,9 @@ public Void apply(Optional resource) throws FunctionException { protected Promise search(final Container container, String fileMask, String contentMask) { QueryExpression queryExpression = new QueryExpression(); - queryExpression.setText(contentMask + '*'); + if (!isNullOrEmpty(contentMask)) { + queryExpression.setText(contentMask + '*'); + } if (!isNullOrEmpty(fileMask)) { queryExpression.setName(fileMask); } diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/pom.xml b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/pom.xml new file mode 100644 index 00000000000..f8aa51f6650 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/pom.xml @@ -0,0 +1,154 @@ + + + + 4.0.0 + + che-plugin-nodejs-debugger-parent + org.eclipse.che.plugin + 5.0.0-M5-SNAPSHOT + + che-plugin-nodejs-debugger-ide + jar + Che Plugin :: NodeJs Debugger :: IDE + + + com.google.guava + guava + + + com.google.gwt + gwt-elemental + + + com.google.gwt.inject + gin + + + com.google.inject + guice + + + javax.validation + validation-api + + + org.eclipse.che.core + che-core-api-project + + + org.eclipse.che.core + che-core-api-workspace + + + org.eclipse.che.core + che-core-commons-annotations + + + org.eclipse.che.core + che-core-commons-gwt + + + org.eclipse.che.core + che-core-ide-api + + + org.eclipse.che.core + che-core-ide-app + + + org.eclipse.che.core + che-core-ide-ui + + + org.eclipse.che.plugin + che-plugin-debugger-ide + + + org.vectomatic + lib-gwt-svg + + + com.google.gwt + gwt-user + provided + + + com.google.gwt + gwt-dev + test + + + com.google.gwt.gwtmockito + gwtmockito + test + + + com.googlecode.gwt-test-utils + gwt-test-utils + test + + + junit + junit + test + + + org.hamcrest + hamcrest-core + test + + + org.mockito + mockito-core + test + + + + + + src/main/java + + + src/main/resources + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + analyze + + true + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + com.mycila + license-maven-plugin + + + **/*.png + + + + + + diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebugger.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebugger.java new file mode 100644 index 00000000000..77065fe4bed --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebugger.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.ide; + +import com.google.inject.Inject; +import com.google.web.bindery.event.shared.EventBus; + +import org.eclipse.che.api.debug.shared.model.Location; +import org.eclipse.che.ide.api.debug.BreakpointManager; +import org.eclipse.che.ide.api.debug.DebuggerServiceClient; +import org.eclipse.che.ide.api.resources.VirtualFile; +import org.eclipse.che.ide.debug.DebuggerDescriptor; +import org.eclipse.che.ide.debug.DebuggerManager; +import org.eclipse.che.ide.dto.DtoFactory; +import org.eclipse.che.ide.util.storage.LocalStorageProvider; +import org.eclipse.che.ide.websocket.MessageBusProvider; +import org.eclipse.che.plugin.debugger.ide.debug.AbstractDebugger; + +import javax.validation.constraints.NotNull; +import java.util.Map; + +/** + * The NodeJs Debugger Client. + * + * @author Anatoliy Bazko + */ +public class NodeJsDebugger extends AbstractDebugger { + + public static final String ID = "nodejsdbg"; + + @Inject + public NodeJsDebugger(DebuggerServiceClient service, + DtoFactory dtoFactory, + LocalStorageProvider localStorageProvider, + MessageBusProvider messageBusProvider, + EventBus eventBus, + NodeJsDebuggerFileHandler activeFileHandler, + DebuggerManager debuggerManager, + BreakpointManager breakpointManager) { + + super(service, + dtoFactory, + localStorageProvider, + messageBusProvider, + eventBus, + activeFileHandler, + debuggerManager, + breakpointManager, + ID); + } + + @Override + protected String fqnToPath(@NotNull Location location) { + return location.getResourcePath() == null ? location.getTarget() : location.getResourcePath(); + } + + @Override + protected String pathToFqn(VirtualFile file) { + return file.getLocation().toString(); + } + + @Override + protected DebuggerDescriptor toDescriptor(Map connectionProperties) { + StringBuilder sb = new StringBuilder(); + + for (String propName : connectionProperties.keySet()) { + try { + ConnectionProperties prop = ConnectionProperties.valueOf(propName.toUpperCase()); + String connectionInfo = prop.getConnectionInfo(connectionProperties.get(propName)); + if (!connectionInfo.isEmpty()) { + if (sb.length() > 0) { + sb.append(','); + } + sb.append(connectionInfo); + } + } catch (IllegalArgumentException ignored) { + // unrecognized connection property + } + } + + return new DebuggerDescriptor("", "{ " + sb.toString() + " }"); + } + + public enum ConnectionProperties { + SCRIPT { + @Override + public String getConnectionInfo(String value) { + return value; + } + }; + + public abstract String getConnectionInfo(String value); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerExtension.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerExtension.java new file mode 100644 index 00000000000..38fb9d92e86 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerExtension.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.ide; + +import com.google.inject.Inject; +import com.google.inject.Singleton; + +import org.eclipse.che.ide.api.extension.Extension; +import org.eclipse.che.ide.debug.DebuggerManager; + +/** + * Extension allows to debug NodeJs applications. + * + * @author Anatoliy Bazko + */ +@Singleton +@Extension(title = "NodeJs Debugger", version = "5.0.0") +public class NodeJsDebuggerExtension { + + @Inject + public NodeJsDebuggerExtension(DebuggerManager debuggerManager, NodeJsDebugger nodeJsDebugger) { + debuggerManager.registeredDebugger(NodeJsDebugger.ID, nodeJsDebugger); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerFileHandler.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerFileHandler.java new file mode 100644 index 00000000000..c42aad489fc --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerFileHandler.java @@ -0,0 +1,193 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.ide; + +import com.google.common.base.Optional; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.inject.Inject; +import com.google.web.bindery.event.shared.EventBus; + +import org.eclipse.che.api.debug.shared.model.Location; +import org.eclipse.che.api.promises.client.Operation; +import org.eclipse.che.api.promises.client.OperationException; +import org.eclipse.che.api.promises.client.PromiseError; +import org.eclipse.che.ide.api.app.AppContext; +import org.eclipse.che.ide.api.editor.EditorAgent; +import org.eclipse.che.ide.api.editor.EditorPartPresenter; +import org.eclipse.che.ide.api.editor.document.Document; +import org.eclipse.che.ide.api.editor.text.TextPosition; +import org.eclipse.che.ide.api.editor.texteditor.TextEditor; +import org.eclipse.che.ide.api.event.FileEvent; +import org.eclipse.che.ide.api.resources.File; +import org.eclipse.che.ide.api.resources.Project; +import org.eclipse.che.ide.api.resources.Resource; +import org.eclipse.che.ide.api.resources.VirtualFile; +import org.eclipse.che.plugin.debugger.ide.debug.ActiveFileHandler; + +/** + * Responsible to open files in editor when debugger stopped at breakpoint. + * + * @author Anatoliy Bazko + */ +public class NodeJsDebuggerFileHandler implements ActiveFileHandler { + + private final EditorAgent editorAgent; + private final EventBus eventBus; + private final AppContext appContext; + + @Inject + public NodeJsDebuggerFileHandler(EditorAgent editorAgent, + EventBus eventBus, + AppContext appContext) { + this.editorAgent = editorAgent; + this.eventBus = eventBus; + this.appContext = appContext; + } + + @Override + public void openFile(final Location location, final AsyncCallback callback) { + final Resource resource = appContext.getResource(); + if (resource == null) { + callback.onFailure(new IllegalStateException("Resource is undefined")); + return; + } + final Optional project = resource.getRelatedProject(); + if (!project.isPresent()) { + callback.onFailure(new IllegalStateException("Project is undefined")); + return; + } + + findFileInProject(project.get(), location, callback); + } + + private void findFileInProject(final Project project, + final Location location, + final AsyncCallback callback) { + project.getFile(location.getTarget()).then(new Operation>() { + @Override + public void apply(Optional file) throws OperationException { + if (file.isPresent()) { + openFileInEditorAndScrollToLine(file.get(), callback, location.getLineNumber()); + callback.onSuccess(file.get()); + } else { + findFileInWorkspace(location, callback); + } + } + }).catchError(new Operation() { + @Override + public void apply(PromiseError arg) throws OperationException { + callback.onFailure(new IllegalArgumentException("File not found." + arg.getMessage())); + } + }); + } + + private void findFileInWorkspace(final Location location, + final AsyncCallback callback) { + try { + appContext.getWorkspaceRoot().getFile(location.getTarget()).then(new Operation>() { + @Override + public void apply(Optional file) throws OperationException { + if (file.isPresent()) { + openFileInEditorAndScrollToLine(file.get(), callback, location.getLineNumber()); + callback.onSuccess(file.get()); + } else { + searchSource(location, callback); + } + } + }).catchError(new Operation() { + @Override + public void apply(PromiseError arg) throws OperationException { + callback.onFailure(new IllegalArgumentException("File not found." + arg.getMessage())); + } + }); + } catch (IllegalStateException ignored) { + searchSource(location, callback); + } + } + + private void searchSource(final Location location, final AsyncCallback callback) { + appContext.getWorkspaceRoot().search(location.getTarget(), "").then(new Operation() { + @Override + public void apply(Resource[] resources) throws OperationException { + if (resources.length == 0) { + callback.onFailure(new IllegalArgumentException("File not found.")); + return; + } + + appContext.getWorkspaceRoot().getFile(resources[0].getLocation()).then(new Operation>() { + @Override + public void apply(Optional file) throws OperationException { + if (file.isPresent()) { + openFileInEditorAndScrollToLine(file.get(), callback, location.getLineNumber()); + callback.onSuccess(file.get()); + } else { + callback.onFailure(new IllegalArgumentException("File not found.")); + } + } + }).catchError(new Operation() { + @Override + public void apply(PromiseError arg) throws OperationException { + callback.onFailure(new IllegalArgumentException("File not found. " + arg.getMessage())); + } + }); + } + }).catchError(new Operation() { + @Override + public void apply(PromiseError arg) throws OperationException { + callback.onFailure(new IllegalArgumentException("File not found. " + arg.getMessage())); + } + }); + } + + private void openFileInEditorAndScrollToLine(final VirtualFile virtualFile, + final AsyncCallback callback, + final int scrollToLine) { + editorAgent.openEditor(virtualFile, new EditorAgent.OpenEditorCallback() { + @Override + public void onEditorOpened(EditorPartPresenter editor) { + new Timer() { + @Override + public void run() { + scrollEditorToExecutionPoint((TextEditor)editorAgent.getActiveEditor(), scrollToLine); + callback.onSuccess(virtualFile); + } + }.schedule(300); + } + + @Override + public void onEditorActivated(EditorPartPresenter editor) { + new Timer() { + @Override + public void run() { + scrollEditorToExecutionPoint((TextEditor)editorAgent.getActiveEditor(), scrollToLine); + callback.onSuccess(virtualFile); + } + }.schedule(300); + } + + @Override + public void onInitializationFailed() { + callback.onFailure(null); + } + }); + + eventBus.fireEvent(FileEvent.createOpenFileEvent(virtualFile)); + } + + private void scrollEditorToExecutionPoint(TextEditor editor, int lineNumber) { + Document document = editor.getDocument(); + if (document != null) { + TextPosition newPosition = new TextPosition(lineNumber, 0); + document.setCursorPosition(newPosition); + } + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerGinModule.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerGinModule.java new file mode 100644 index 00000000000..aff1d402319 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerGinModule.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.ide; + +import com.google.gwt.inject.client.AbstractGinModule; +import com.google.gwt.inject.client.multibindings.GinMultibinder; +import com.google.inject.Singleton; + +import org.eclipse.che.ide.api.debug.DebugConfigurationType; +import org.eclipse.che.ide.api.extension.ExtensionGinModule; +import org.eclipse.che.plugin.nodejsdbg.ide.configuration.NodeJsDebuggerConfigurationPageView; +import org.eclipse.che.plugin.nodejsdbg.ide.configuration.NodeJsDebuggerConfigurationPageViewImpl; +import org.eclipse.che.plugin.nodejsdbg.ide.configuration.NodeJsDebuggerConfigurationType; + +/** + * @author Anatolii Bazko + */ +@ExtensionGinModule +public class NodeJsDebuggerGinModule extends AbstractGinModule { + @Override + protected void configure() { + GinMultibinder.newSetBinder(binder(), DebugConfigurationType.class).addBinding().to(NodeJsDebuggerConfigurationType.class); + bind(NodeJsDebuggerConfigurationPageView.class).to(NodeJsDebuggerConfigurationPageViewImpl.class).in(Singleton.class); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerLocalizationConstant.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerLocalizationConstant.java new file mode 100644 index 00000000000..2602a012a51 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerLocalizationConstant.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.ide; + +import com.google.gwt.i18n.client.Messages; + +/** + * I18n constants for the Debugger extension. + * + * @author Anatolii Bazko + */ +public interface NodeJsDebuggerLocalizationConstant extends Messages { + + @Key("view.nodeJsDebuggerConfigurationPage.hostLabel") + String nodeJsDebuggerConfigurationPageViewHostLabel(); + + @Key("view.nodeJsDebuggerConfigurationPage.portLabel") + String nodeJsDebuggerConfigurationPageViewPortLabel(); + + @Key("view.nodeJsDebuggerConfigurationPage.scriptLabel") + String nodeJsDebuggerConfigurationPageViewScriptLabel(); + + @Key("view.nodeJsDebuggerConfigurationPage.pidLabel") + String nodeJsDebuggerConfigurationPageViewPidLable(); +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerResources.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerResources.java new file mode 100644 index 00000000000..f4bb9f81a9c --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerResources.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.ide; + +import com.google.gwt.resources.client.ClientBundle; + +import org.vectomatic.dom.svg.ui.SVGResource; + +/** + * @author Anatolii Bazko + */ +public interface NodeJsDebuggerResources extends ClientBundle { + + /** Returns the icon for NodeJs debugger configurations. */ + @Source("configuration/nodejs-debugger-configuration-type.svg") + SVGResource nodeJsDebugConfigurationType(); +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationPagePresenter.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationPagePresenter.java new file mode 100644 index 00000000000..20e2a02c3a6 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationPagePresenter.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.ide.configuration; + +import com.google.gwt.user.client.ui.AcceptsOneWidget; +import com.google.inject.Inject; +import com.google.inject.Singleton; + +import org.eclipse.che.ide.api.debug.DebugConfiguration; +import org.eclipse.che.ide.api.debug.DebugConfigurationPage; +import org.eclipse.che.ide.extension.machine.client.command.valueproviders.CurrentProjectPathProvider; + +import java.util.Map; + +import static org.eclipse.che.plugin.nodejsdbg.ide.NodeJsDebugger.ConnectionProperties.SCRIPT; + +/** + * Page allows to edit NodeJs debug configuration. + * + * @author Anatolii Bazko + */ +@Singleton +public class NodeJsDebuggerConfigurationPagePresenter implements NodeJsDebuggerConfigurationPageView.ActionDelegate, + DebugConfigurationPage { + + private static final String DEFAULT_SCRIPT_NAME = "app.js"; + + private final NodeJsDebuggerConfigurationPageView view; + private final CurrentProjectPathProvider currentProjectPathProvider; + + private DebugConfiguration editedConfiguration; + private String originScriptPath; + private DirtyStateListener listener; + + @Inject + public NodeJsDebuggerConfigurationPagePresenter(NodeJsDebuggerConfigurationPageView view, + CurrentProjectPathProvider currentProjectPathProvider) { + this.view = view; + this.currentProjectPathProvider = currentProjectPathProvider; + + view.setDelegate(this); + } + + @Override + public void resetFrom(DebugConfiguration configuration) { + editedConfiguration = configuration; + originScriptPath = getScriptPath(configuration); + + if (originScriptPath == null) { + String defaultBinaryPath = getDefaultBinaryPath(); + editedConfiguration.getConnectionProperties().put(SCRIPT.toString(), defaultBinaryPath); + originScriptPath = defaultBinaryPath; + } + } + + private String getScriptPath(DebugConfiguration debugConfiguration) { + Map connectionProperties = debugConfiguration.getConnectionProperties(); + return connectionProperties.get(SCRIPT.toString()); + } + + private String getDefaultBinaryPath() { + return currentProjectPathProvider.getKey() + "/" + DEFAULT_SCRIPT_NAME; + } + + @Override + public void go(AcceptsOneWidget container) { + container.setWidget(view); + view.setScriptPath(getScriptPath(editedConfiguration)); + } + + @Override + public boolean isDirty() { + return !originScriptPath.equals(getScriptPath(editedConfiguration)); + } + + @Override + public void setDirtyStateListener(DirtyStateListener listener) { + this.listener = listener; + } + + @Override + public void onScriptPathChanged() { + final Map connectionProperties = editedConfiguration.getConnectionProperties(); + connectionProperties.put(SCRIPT.toString(), view.getScriptPath()); + + editedConfiguration.setConnectionProperties(connectionProperties); + listener.onDirtyStateChanged(); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationPageView.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationPageView.java new file mode 100644 index 00000000000..4cd89cfce7a --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationPageView.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.ide.configuration; + +import org.eclipse.che.ide.api.mvp.View; + +/** + * The view of {@link NodeJsDebuggerConfigurationPagePresenter}. + * + * @author Anatolii Bazko + */ +public interface NodeJsDebuggerConfigurationPageView extends View { + + /** Returns path to the binary. */ + String getScriptPath(); + + /** Sets path to the binary. */ + void setScriptPath(String path); + + /** Action handler for the view's controls. */ + interface ActionDelegate { + + /** Called when 'Binary Path' has been changed. */ + void onScriptPathChanged(); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationPageViewImpl.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationPageViewImpl.java new file mode 100644 index 00000000000..e92360e6b35 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationPageViewImpl.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.ide.configuration; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.event.dom.client.KeyUpEvent; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.uibinder.client.UiHandler; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.TextBox; +import com.google.gwt.user.client.ui.Widget; + +/** + * The implementation of {@link NodeJsDebuggerConfigurationPageView}. + * + * @author Anatolii Bazko + */ +public class NodeJsDebuggerConfigurationPageViewImpl implements NodeJsDebuggerConfigurationPageView { + + private static final NodeJsDebugConfigurationPageViewImplUiBinder UI_BINDER = + GWT.create(NodeJsDebugConfigurationPageViewImplUiBinder.class); + + private final FlowPanel rootElement; + + @UiField + TextBox scriptPath; + + private ActionDelegate delegate; + + public NodeJsDebuggerConfigurationPageViewImpl() { + rootElement = UI_BINDER.createAndBindUi(this); + } + + @Override + public void setDelegate(ActionDelegate delegate) { + this.delegate = delegate; + } + + @Override + public Widget asWidget() { + return rootElement; + } + + @Override + public String getScriptPath() { + return scriptPath.getValue(); + } + + @Override + public void setScriptPath(String path) { + this.scriptPath.setValue(path); + } + + @UiHandler({"scriptPath"}) + void onScriptPathKeyUp(KeyUpEvent event) { + delegate.onScriptPathChanged(); + } + + interface NodeJsDebugConfigurationPageViewImplUiBinder extends UiBinder { + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationPageViewImpl.ui.xml b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationPageViewImpl.ui.xml new file mode 100644 index 00000000000..96e69e705f6 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationPageViewImpl.ui.xml @@ -0,0 +1,21 @@ + + + + + + + + + diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationType.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationType.java new file mode 100644 index 00000000000..14a12579f65 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationType.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.ide.configuration; + +import com.google.inject.Inject; +import com.google.inject.Singleton; + +import org.eclipse.che.ide.api.debug.DebugConfiguration; +import org.eclipse.che.ide.api.debug.DebugConfigurationPage; +import org.eclipse.che.ide.api.debug.DebugConfigurationType; +import org.eclipse.che.ide.api.icon.Icon; +import org.eclipse.che.ide.api.icon.IconRegistry; +import org.eclipse.che.plugin.nodejsdbg.ide.NodeJsDebugger; +import org.eclipse.che.plugin.nodejsdbg.ide.NodeJsDebuggerResources; + +/** + * NodeJs debug configuration type. + * + * @author Anatolii Bazko + */ +@Singleton +public class NodeJsDebuggerConfigurationType implements DebugConfigurationType { + + public static final String DISPLAY_NAME = "NodeJs"; + + private final NodeJsDebuggerConfigurationPagePresenter page; + + @Inject + public NodeJsDebuggerConfigurationType(NodeJsDebuggerConfigurationPagePresenter page, + IconRegistry iconRegistry, + NodeJsDebuggerResources resources) { + this.page = page; + iconRegistry.registerIcon(new Icon(NodeJsDebugger.ID + ".debug.configuration.type.icon", resources.nodeJsDebugConfigurationType())); + } + + @Override + public String getId() { + return NodeJsDebugger.ID; + } + + @Override + public String getDisplayName() { + return DISPLAY_NAME; + } + + @Override + public DebugConfigurationPage getConfigurationPage() { + return page; + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/resources/org/eclipse/che/plugin/nodejsdbg/NodeJsDebugger.gwt.xml b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/resources/org/eclipse/che/plugin/nodejsdbg/NodeJsDebugger.gwt.xml new file mode 100644 index 00000000000..8b761f90ffa --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/resources/org/eclipse/che/plugin/nodejsdbg/NodeJsDebugger.gwt.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/resources/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerLocalizationConstant.properties b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/resources/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerLocalizationConstant.properties new file mode 100644 index 00000000000..c80fa2b8c6c --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/resources/org/eclipse/che/plugin/nodejsdbg/ide/NodeJsDebuggerLocalizationConstant.properties @@ -0,0 +1,16 @@ +# +# Copyright (c) 2012-2016 Codenvy, S.A. +# 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: +# Codenvy, S.A. - initial API and implementation +# + +# NodeJsDebuggerConfigurationPage +view.nodeJsDebuggerConfigurationPage.hostLabel=Host +view.nodeJsDebuggerConfigurationPage.portLabel=Port +view.nodeJsDebuggerConfigurationPage.pidLabel=PID +view.nodeJsDebuggerConfigurationPage.scriptLabel=Script diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/resources/org/eclipse/che/plugin/nodejsdbg/ide/configuration/nodejs-debugger-configuration-type.svg b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/resources/org/eclipse/che/plugin/nodejsdbg/ide/configuration/nodejs-debugger-configuration-type.svg new file mode 100644 index 00000000000..41ad226705f --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/main/resources/org/eclipse/che/plugin/nodejsdbg/ide/configuration/nodejs-debugger-configuration-type.svg @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/test/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationPagePresenterTest.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/test/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationPagePresenterTest.java new file mode 100644 index 00000000000..7733ee64288 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/test/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationPagePresenterTest.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.ide.configuration; + +import com.google.gwt.user.client.ui.AcceptsOneWidget; + +import org.eclipse.che.api.promises.client.Promise; +import org.eclipse.che.ide.api.app.AppContext; +import org.eclipse.che.ide.api.debug.DebugConfiguration; +import org.eclipse.che.ide.api.debug.DebugConfigurationPage; +import org.eclipse.che.ide.api.machine.MachineServiceClient; +import org.eclipse.che.ide.api.machine.RecipeServiceClient; +import org.eclipse.che.ide.extension.machine.client.command.valueproviders.CurrentProjectPathProvider; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.Map; + +import static org.eclipse.che.plugin.nodejsdbg.ide.NodeJsDebugger.ConnectionProperties.SCRIPT; +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** @author Artem Zatsarynnyi */ +@RunWith(MockitoJUnitRunner.class) +public class NodeJsDebuggerConfigurationPagePresenterTest { + + private static final String HOST = "localhost"; + private static final int PORT = 8000; + + @Mock + private NodeJsDebuggerConfigurationPageView pageView; + @Mock + private DebugConfiguration configuration; + @Mock + private CurrentProjectPathProvider currentProjectPathProvider; + @Mock + private AppContext appContext; + @Mock + private RecipeServiceClient recipeServiceClient; + @Mock + private MachineServiceClient machineServiceClient; + + @InjectMocks + private NodeJsDebuggerConfigurationPagePresenter pagePresenter; + + @Before + public void setUp() { + when(configuration.getHost()).thenReturn(HOST); + when(configuration.getPort()).thenReturn(PORT); + + pagePresenter.resetFrom(configuration); + } + + @Test + public void testResetting() throws Exception { + verify(configuration, atLeastOnce()).getConnectionProperties(); + verify(currentProjectPathProvider).getKey(); + } + + @Test + public void testGo() throws Exception { + AcceptsOneWidget container = Mockito.mock(AcceptsOneWidget.class); + when(machineServiceClient.getMachines(appContext.getWorkspaceId())).thenReturn(mock(Promise.class)); + + pagePresenter.go(container); + + verify(container).setWidget(eq(pageView)); + verify(configuration, atLeastOnce()).getConnectionProperties(); + verify(pageView).setScriptPath(anyString()); + } + + @Test + public void testOnBinaryPathChanged() throws Exception { + String binPath = "/path"; + when(pageView.getScriptPath()).thenReturn(binPath); + + final DebugConfigurationPage.DirtyStateListener listener = mock(DebugConfigurationPage.DirtyStateListener.class); + pagePresenter.setDirtyStateListener(listener); + + pagePresenter.onScriptPathChanged(); + + verify(pageView).getScriptPath(); + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Map.class); + + verify(configuration).setConnectionProperties(argumentCaptor.capture()); + Map argumentCaptorValue = argumentCaptor.getValue(); + assertEquals(binPath, argumentCaptorValue.get(SCRIPT.toString())); + + verify(listener).onDirtyStateChanged(); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/test/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationTypeTest.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/test/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationTypeTest.java new file mode 100644 index 00000000000..72e71ba271b --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/test/java/org/eclipse/che/plugin/nodejsdbg/ide/configuration/NodeJsDebuggerConfigurationTypeTest.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.ide.configuration; + +import com.google.gwtmockito.GwtMockitoTestRunner; + +import org.eclipse.che.ide.api.debug.DebugConfiguration; +import org.eclipse.che.ide.api.debug.DebugConfigurationPage; +import org.eclipse.che.ide.api.icon.IconRegistry; +import org.eclipse.che.plugin.nodejsdbg.ide.NodeJsDebugger; +import org.eclipse.che.plugin.nodejsdbg.ide.NodeJsDebuggerResources; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import static org.junit.Assert.assertEquals; + +/** @author Anatolii Bazko */ +@RunWith(GwtMockitoTestRunner.class) +public class NodeJsDebuggerConfigurationTypeTest { + + @Mock + private NodeJsDebuggerResources resources; + @Mock + private NodeJsDebuggerConfigurationPagePresenter nodeJsDebuggerConfigurationPagePresenter; + @Mock + private IconRegistry iconRegistry; + + @InjectMocks + private NodeJsDebuggerConfigurationType nodeJsDebuggerConfigurationType; + + @Test + public void testGetId() throws Exception { + final String id = nodeJsDebuggerConfigurationType.getId(); + + assertEquals(NodeJsDebugger.ID, id); + } + + @Test + public void testGetDisplayName() throws Exception { + final String displayName = nodeJsDebuggerConfigurationType.getDisplayName(); + + assertEquals(NodeJsDebuggerConfigurationType.DISPLAY_NAME, displayName); + } + + @Test + public void testGetConfigurationPage() throws Exception { + final DebugConfigurationPage page = nodeJsDebuggerConfigurationType.getConfigurationPage(); + + assertEquals(nodeJsDebuggerConfigurationPagePresenter, page); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/test/resources/logback-test.xml b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..e8898502279 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-ide/src/test/resources/logback-test.xml @@ -0,0 +1,26 @@ + + + + + + + %-41(%date[%.15thread]) %-45([%-5level] [%.30logger{30} %L]) - %msg%n + + + + + + + + diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/pom.xml b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/pom.xml new file mode 100644 index 00000000000..77112c8d713 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/pom.xml @@ -0,0 +1,117 @@ + + + + 4.0.0 + + che-plugin-nodejs-debugger-parent + org.eclipse.che.plugin + 5.0.0-M5-SNAPSHOT + + che-plugin-nodejs-debugger-server + jar + Che Plugin :: NodeJs Debugger :: Server + + + com.google.code.gson + gson + + + com.google.guava + guava + + + com.google.inject + guice + + + com.google.inject.extensions + guice-multibindings + + + org.eclipse.che.core + che-core-api-debug + + + org.eclipse.che.core + che-core-api-debug-shared + + + org.eclipse.che.core + che-core-commons-annotations + + + org.eclipse.che.core + che-core-commons-inject + + + org.eclipse.che.core + che-core-commons-lang + + + org.slf4j + slf4j-api + + + ch.qos.logback + logback-classic + test + + + org.mockito + mockito-all + test + + + org.testng + testng + test + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + com.mycila + license-maven-plugin + + + **/*.js + + + + + + + + nodejs-debugger-tests + + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + + + + diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsDebugProcess.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsDebugProcess.java new file mode 100644 index 00000000000..7e6e56a2674 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsDebugProcess.java @@ -0,0 +1,248 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; + +import org.eclipse.che.commons.lang.IoUtil; +import org.eclipse.che.plugin.nodejsdbg.server.exception.NodeJsDebuggerException; +import org.eclipse.che.plugin.nodejsdbg.server.exception.NodeJsDebuggerTerminatedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import static java.lang.Math.min; + +/** + * Wrapper over NodeJs process is being run. + * Communication is performed through standard input/output streams. + * + * @author Anatoliy Bazko + */ +public class NodeJsDebugProcess implements NodeJsProcessObservable { + private static final Logger LOG = LoggerFactory.getLogger(NodeJsDebugProcess.class); + private static final int MAX_OUTPUT = 4096; + + private static final String NODEJS_COMMAND = detectNodeJsCommand(); + + private final Process process; + private final String outputSeparator; + private final ScheduledExecutorService executor; + private final BufferedWriter processWriter; + + private final List observers; + + private NodeJsDebugProcess(String outputSeparator, String... options) throws NodeJsDebuggerException { + this.observers = new CopyOnWriteArrayList<>(); + this.outputSeparator = outputSeparator; + + List commands = new ArrayList<>(1 + options.length); + commands.add(NODEJS_COMMAND); + commands.addAll(Arrays.asList(options)); + + ProcessBuilder processBuilder = new ProcessBuilder(commands); + try { + process = processBuilder.start(); + } catch (IOException e) { + throw new NodeJsDebuggerException("NodeJs process failed.", e); + } + + processWriter = new BufferedWriter(new OutputStreamWriter(process.getOutputStream())); + + executor = Executors.newScheduledThreadPool(1, new ThreadFactoryBuilder().setNameFormat("nodejs-debugger-%d") + .setDaemon(true) + .build()); + executor.scheduleWithFixedDelay(new OutputReader(), 0, 100, TimeUnit.MILLISECONDS); + } + + public static NodeJsDebugProcess start(String file) throws NodeJsDebuggerException { + return new NodeJsDebugProcess("debug> ", "debug", "--debug-brk", file); + } + + @Override + public void addObserver(NodeJsProcessObserver observer) { + observers.add(observer); + } + + @Override + public void removeObserver(NodeJsProcessObserver observer) { + observers.remove(observer); + } + + /** + * Stops process. + */ + void stop() { + try { + send("quit"); + } catch (NodeJsDebuggerException e) { + LOG.warn(e.getMessage()); + } + + executor.shutdown(); + try { + if (!executor.awaitTermination(10, TimeUnit.SECONDS)) { + executor.shutdownNow(); + if (!executor.awaitTermination(10, TimeUnit.SECONDS)) { + LOG.warn("Unable to terminate main pool"); + } + } + } catch (InterruptedException e) { + LOG.warn(e.getMessage()); + } + + + process.destroy(); + try { + if (!process.waitFor(10, TimeUnit.SECONDS)) { + LOG.error("Unable to terminate NodeJs"); + } + } catch (InterruptedException e) { + LOG.warn(e.getMessage()); + } + + try { + processWriter.close(); + } catch (IOException e) { + LOG.warn("Failed to close NodeJs process output stream", e); + } + observers.clear(); + } + + public synchronized void send(String command) throws NodeJsDebuggerException { + LOG.debug("Execute: {}", command); + + if (!process.isAlive()) { + throw new NodeJsDebuggerTerminatedException("NodeJs process is terminated."); + } + + try { + processWriter.write(command); + processWriter.newLine(); + processWriter.flush(); + } catch (IOException e) { + LOG.error(String.format("Command execution <%s> failed", command), e); + } + } + + + /** + * Continuously reads process output and store in the {@code #outputs}. + */ + private class OutputReader implements Runnable { + private final StringBuffer outputBuffer; + + public OutputReader() { + this.outputBuffer = new StringBuffer(); + } + + @Override + public void run() { + try { + InputStream in = getInput(); + if (in != null) { + String outputData = read(in); + if (!outputData.isEmpty()) { + outputBuffer.append(outputData); + if (outputBuffer.length() > MAX_OUTPUT) { + outputBuffer.delete(0, outputBuffer.length() - MAX_OUTPUT); + } + + extractOutputs(); + } + } + } catch (IOException e) { + LOG.error(e.getMessage(), e); + } + } + + private InputStream getInput() throws IOException { + return hasError() ? process.getErrorStream() + : (hasInput() ? (hasError() ? process.getErrorStream() + : process.getInputStream()) + : null); + } + + private void extractOutputs() { + int indexOf; + while ((indexOf = outputBuffer.indexOf(outputSeparator)) >= 0) { + NodeJsOutput nodeJsOutput = NodeJsOutput.of(outputBuffer.substring(0, indexOf)); + outputBuffer.delete(0, indexOf + outputSeparator.length()); + + notifyObservers(nodeJsOutput); + } + } + + private boolean hasError() throws IOException { + return process.getErrorStream().available() != 0; + } + + private boolean hasInput() throws IOException { + return process.getInputStream().available() != 0; + } + + private String read(InputStream in) throws IOException { + int available = min(in.available(), MAX_OUTPUT); + byte[] buf = new byte[available]; + int read = in.read(buf, 0, available); + + return new String(buf, 0, read, StandardCharsets.UTF_8); + } + } + + private void notifyObservers(NodeJsOutput nodeJsOutput) { + LOG.debug("{}{}", outputSeparator, nodeJsOutput.getOutput()); + + for (NodeJsProcessObserver observer : observers) { + try { + if (observer.onOutputProduced(nodeJsOutput)) { + break; + } + } catch (NodeJsDebuggerException e) { + LOG.error(e.getMessage(), e); + } + } + } + + /** + * Returns NodeJs command to run: either {@code nodejs} or {@code node}. + */ + private static String detectNodeJsCommand() { + String detectionCommand = "if command -v nodejs >/dev/null 2>&1; then echo -n 'nodejs'; else echo -n 'node'; fi"; + ProcessBuilder builder = new ProcessBuilder("sh", "-c", detectionCommand); + + try { + Process process = builder.start(); + int resultCode = process.waitFor(); + if (resultCode != 0) { + String errMsg = IoUtil.readAndCloseQuietly(process.getErrorStream()); + throw new IllegalStateException("NodeJs not found. " + errMsg); + } + + return IoUtil.readAndCloseQuietly(process.getInputStream()); + } catch (IOException | InterruptedException e) { + throw new IllegalStateException("NodeJs not found", e); + } + } + +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsDebugger.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsDebugger.java new file mode 100644 index 00000000000..960e8baeff9 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsDebugger.java @@ -0,0 +1,309 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server; + +import org.eclipse.che.api.debug.shared.model.Breakpoint; +import org.eclipse.che.api.debug.shared.model.DebuggerInfo; +import org.eclipse.che.api.debug.shared.model.Location; +import org.eclipse.che.api.debug.shared.model.SimpleValue; +import org.eclipse.che.api.debug.shared.model.StackFrameDump; +import org.eclipse.che.api.debug.shared.model.Variable; +import org.eclipse.che.api.debug.shared.model.VariablePath; +import org.eclipse.che.api.debug.shared.model.action.ResumeAction; +import org.eclipse.che.api.debug.shared.model.action.StartAction; +import org.eclipse.che.api.debug.shared.model.action.StepIntoAction; +import org.eclipse.che.api.debug.shared.model.action.StepOutAction; +import org.eclipse.che.api.debug.shared.model.action.StepOverAction; +import org.eclipse.che.api.debug.shared.model.impl.BreakpointImpl; +import org.eclipse.che.api.debug.shared.model.impl.DebuggerInfoImpl; +import org.eclipse.che.api.debug.shared.model.impl.SimpleValueImpl; +import org.eclipse.che.api.debug.shared.model.impl.StackFrameDumpImpl; +import org.eclipse.che.api.debug.shared.model.impl.event.BreakpointActivatedEventImpl; +import org.eclipse.che.api.debug.shared.model.impl.event.DisconnectEventImpl; +import org.eclipse.che.api.debug.shared.model.impl.event.SuspendEventImpl; +import org.eclipse.che.api.debugger.server.Debugger; +import org.eclipse.che.api.debugger.server.exceptions.DebuggerException; +import org.eclipse.che.commons.annotation.Nullable; +import org.eclipse.che.plugin.nodejsdbg.server.command.NodeJsDebugCommandsLibrary; +import org.eclipse.che.plugin.nodejsdbg.server.exception.NodeJsDebuggerException; +import org.eclipse.che.plugin.nodejsdbg.server.exception.NodeJsDebuggerTerminatedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.URI; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.regex.Pattern; + +/** + * Server side NodeJs debugger. + * + * @author Anatoliy Bazko + */ +public class NodeJsDebugger implements Debugger { + private static final Logger LOG = LoggerFactory.getLogger(NodeJsDebugger.class); + + private final Integer pid; + private final URI uri; + private final String name; + private final String version; + private final String script; + + private final NodeJsDebugProcess nodeJsDebugProcess; + private final DebuggerCallback debuggerCallback; + private final NodeJsDebugCommandsLibrary library; + private final Set pendingBreakpoints; + + NodeJsDebugger(@Nullable Integer pid, + @Nullable URI uri, + @Nullable String script, + NodeJsDebugProcess nodeJsDebugProcess, + DebuggerCallback debuggerCallback) throws NodeJsDebuggerException { + this.pid = pid; + this.uri = uri; + this.script = script; + this.nodeJsDebugProcess = nodeJsDebugProcess; + this.library = new NodeJsDebugCommandsLibrary(nodeJsDebugProcess); + this.name = library.getName(); + this.version = library.getVersion(); + this.debuggerCallback = debuggerCallback; + this.pendingBreakpoints = new CopyOnWriteArraySet<>(); + } + + public static NodeJsDebugger newInstance(@Nullable Integer pid, + @Nullable URI uri, + String file, + DebuggerCallback debuggerCallback) throws DebuggerException { + NodeJsDebugProcess nodeJsDebugProcess = NodeJsDebugProcess.start(file); + return new NodeJsDebugger(pid, + uri, + file, + nodeJsDebugProcess, + debuggerCallback); + } + + @Override + public DebuggerInfo getInfo() throws DebuggerException { + return new DebuggerInfoImpl(uri == null ? "" : uri.getHost(), + uri == null ? -1 : uri.getPort(), + name, + version, + pid == null ? -1 : pid, + script); + } + + @Override + public void disconnect() { + debuggerCallback.onEvent(new DisconnectEventImpl()); + nodeJsDebugProcess.stop(); + } + + @Override + public void addBreakpoint(Breakpoint breakpoint) throws DebuggerException { + try { + Location location = breakpoint.getLocation(); + pendingBreakpoints.add(location); + library.setBreakpoint(location.getTarget(), location.getLineNumber()); + checkActivatedBreakpoints(); + } catch (NodeJsDebuggerTerminatedException e) { + disconnect(); + throw e; + } catch (NodeJsDebuggerException e) { + throw new DebuggerException("Can't add breakpoint: " + breakpoint + ". " + e.getMessage(), e); + } + } + + @Override + public void deleteBreakpoint(Location location) throws DebuggerException { + try { + pendingBreakpoints.remove(location); + library.clearBreakpoint(location.getTarget(), location.getLineNumber()); + } catch (NodeJsDebuggerTerminatedException e) { + disconnect(); + throw e; + } catch (NodeJsDebuggerException e) { + throw new DebuggerException("Can't delete breakpoint: " + location + ". " + e.getMessage(), e); + } + } + + @Override + public void deleteAllBreakpoints() throws DebuggerException { + try { + for (Breakpoint breakpoint : library.getBreakpoints()) { + try { + deleteBreakpoint(breakpoint.getLocation()); + } catch (NodeJsDebuggerException e) { + LOG.error("Can't delete breakpoint: {}", breakpoint.getLocation(), e); + } + } + pendingBreakpoints.clear(); + } catch (NodeJsDebuggerTerminatedException e) { + disconnect(); + throw e; + } catch (NodeJsDebuggerException e) { + throw new DebuggerException("Can't delete all breakpoints. " + e.getMessage(), e); + } + } + + @Override + public List getAllBreakpoints() throws DebuggerException { + try { + return library.getBreakpoints(); + } catch (NodeJsDebuggerTerminatedException e) { + disconnect(); + throw e; + } catch (NodeJsDebuggerException e) { + throw new DebuggerException("Can't get all breakpoints. " + e.getMessage(), e); + } + } + + @Override + public void start(StartAction action) throws DebuggerException { + try { + for (Breakpoint breakpoint : action.getBreakpoints()) { + Location location = breakpoint.getLocation(); + library.setBreakpoint(location.getTarget(), location.getLineNumber()); + pendingBreakpoints.add(location); + } + + debuggerCallback.onEvent(new SuspendEventImpl(library.backtrace())); + checkActivatedBreakpoints(); + } catch (NodeJsDebuggerTerminatedException e) { + disconnect(); + throw e; + } catch (NodeJsDebuggerException e) { + throw new DebuggerException("Start error. " + e.getMessage(), e); + } + } + + @Override + public void stepOver(StepOverAction action) throws DebuggerException { + try { + debuggerCallback.onEvent(new SuspendEventImpl(library.next())); + checkActivatedBreakpoints(); + } catch (NodeJsDebuggerTerminatedException e) { + disconnect(); + throw e; + } catch (NodeJsDebuggerException e) { + throw new DebuggerException("Step over error. " + e.getMessage(), e); + } + } + + @Override + public void stepInto(StepIntoAction action) throws DebuggerException { + try { + debuggerCallback.onEvent(new SuspendEventImpl(library.stepIn())); + checkActivatedBreakpoints(); + } catch (NodeJsDebuggerTerminatedException e) { + disconnect(); + throw e; + } catch (NodeJsDebuggerException e) { + throw new DebuggerException("Step into error. " + e.getMessage(), e); + } + } + + @Override + public void stepOut(StepOutAction action) throws DebuggerException { + try { + debuggerCallback.onEvent(new SuspendEventImpl(library.stepOut())); + checkActivatedBreakpoints(); + } catch (NodeJsDebuggerTerminatedException e) { + disconnect(); + throw e; + } catch (NodeJsDebuggerException e) { + throw new DebuggerException("Step out error. " + e.getMessage(), e); + } + } + + @Override + public void resume(ResumeAction action) throws DebuggerException { + try { + debuggerCallback.onEvent(new SuspendEventImpl(library.cont())); + checkActivatedBreakpoints(); + } catch (NodeJsDebuggerTerminatedException e) { + disconnect(); + throw e; + } catch (NodeJsDebuggerException e) { + throw new DebuggerException("Resume error. " + e.getMessage(), e); + } + } + + @Override + public void setValue(Variable variable) throws DebuggerException { + try { + List path = variable.getVariablePath().getPath(); + if (path.isEmpty()) { + throw new DebuggerException("Variable path is empty"); + } + library.setVar(path.get(0), variable.getValue()); + } catch (NodeJsDebuggerTerminatedException e) { + disconnect(); + throw e; + } catch (NodeJsDebuggerException e) { + throw new DebuggerException("Can't set value for " + variable.getName() + ". " + e.getMessage(), e); + } + } + + @Override + public SimpleValue getValue(VariablePath variablePath) throws DebuggerException { + try { + List path = variablePath.getPath(); + if (path.isEmpty()) { + throw new DebuggerException("Variable path is empty"); + } + + return new SimpleValueImpl(Collections.emptyList(), library.getVar(path.get(0))); + } catch (NodeJsDebuggerTerminatedException e) { + disconnect(); + throw e; + } catch (NodeJsDebuggerException e) { + throw new DebuggerException("Can't get value for " + variablePath + ". " + e.getMessage(), e); + } + } + + @Override + public String evaluate(String expression) throws DebuggerException { + try { + return library.evaluate(expression); + } catch (NodeJsDebuggerTerminatedException e) { + disconnect(); + throw e; + } catch (NodeJsDebuggerException e) { + throw new DebuggerException("Can't evaluate '" + expression + "'. " + e.getMessage(), e); + } + } + + @Override + public StackFrameDump dumpStackFrame() throws DebuggerException { + return new StackFrameDumpImpl(Collections.emptyList(), Collections.emptyList()); + } + + private void checkActivatedBreakpoints() throws DebuggerException { + Set brk2Remove = new HashSet<>(); + for (Breakpoint breakpoint : library.getBreakpoints()) { + for (Location pending : pendingBreakpoints) { + Location added = breakpoint.getLocation(); + if (added.getLineNumber() == pending.getLineNumber() && + Pattern.compile(added.getTarget()).matcher(pending.getTarget()).matches()) { + + BreakpointImpl brkToSend = new BreakpointImpl(pending, breakpoint.isEnabled(), breakpoint.getCondition()); + debuggerCallback.onEvent(new BreakpointActivatedEventImpl(brkToSend)); + brk2Remove.add(pending); + } + } + } + + pendingBreakpoints.removeAll(brk2Remove); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsDebuggerFactory.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsDebuggerFactory.java new file mode 100644 index 00000000000..8445de3f7c7 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsDebuggerFactory.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server; + +import org.eclipse.che.api.debugger.server.Debugger; +import org.eclipse.che.api.debugger.server.DebuggerFactory; +import org.eclipse.che.api.debugger.server.exceptions.DebuggerException; + +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Map; + +import static com.google.common.base.Strings.isNullOrEmpty; +import static java.util.stream.Collectors.toMap; + +/** + * Factory to create nodejs debugger instance. + * Allowed the following connection properties: + * + * uri - connects to the process via the URI such as localhost:5858 + * pid - connects to the process via the pid + * script - entrypoint to start debugger + * + * @author Anatoliy Bazko + */ +public class NodeJsDebuggerFactory implements DebuggerFactory { + private static final String TYPE = "nodejsdbg"; + + @Override + public String getType() { + return TYPE; + } + + @Override + public Debugger create(Map properties, Debugger.DebuggerCallback debuggerCallback) throws DebuggerException { + Map normalizedProps = properties.entrySet() + .stream() + .collect(toMap(e -> e.getKey().toLowerCase(), Map.Entry::getValue)); + + + Integer pid = null; + URI uri = null; + + String pidStr = normalizedProps.get("pid"); + if (!isNullOrEmpty(pidStr)) { + try { + pid = Integer.valueOf(pidStr); + } catch (NumberFormatException e) { + throw new DebuggerException(String.format("Illegal 'pid' format %s. Debugger can't be started.", pidStr)); + } + } + + String uriStr = normalizedProps.get("uri"); + if (!isNullOrEmpty(uriStr)) { + try { + uri = URI.create(uriStr); + } catch (IllegalArgumentException e) { + throw new DebuggerException(String.format("Illegal 'uri' format %s. Debugger can't be started.", uriStr)); + } + } + + String script = normalizedProps.get("script"); + if (!isNullOrEmpty(script) && !Files.exists(Paths.get(script))) { + throw new DebuggerException(String.format("Script '%s' to debug not found. Debugger can't be started.", script)); + } + + if (isNullOrEmpty(pidStr) && isNullOrEmpty(uriStr) && isNullOrEmpty(script)) { + throw new DebuggerException("Unrecognized debug connection options. Allowed only: pid, uri or script."); + } + + return NodeJsDebugger.newInstance(pid, uri, script, debuggerCallback); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsDebuggerModule.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsDebuggerModule.java new file mode 100644 index 00000000000..dc1d64d1cf9 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsDebuggerModule.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server; + +import com.google.inject.AbstractModule; +import com.google.inject.multibindings.Multibinder; + +import org.eclipse.che.api.debugger.server.DebuggerFactory; +import org.eclipse.che.inject.DynaModule; + +/** + * @author Anatoliy Bazko + */ +@DynaModule +public class NodeJsDebuggerModule extends AbstractModule { + + @Override + protected void configure() { + Multibinder.newSetBinder(binder(), DebuggerFactory.class).addBinding().to(NodeJsDebuggerFactory.class); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsOutput.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsOutput.java new file mode 100644 index 00000000000..b5708187440 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsOutput.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server; + +/** + * Wrapper for NodeJs output. + * + * @author Anatoliy Bazko + */ +public class NodeJsOutput { + private final String output; + + private NodeJsOutput(String output) { + this.output = output; + } + + public static NodeJsOutput of(String output) { + return new NodeJsOutput(strip(output)); + } + + public String getOutput() { + return output; + } + + public boolean isEmpty() { + return output.isEmpty(); + } + + private static String strip(String output) { + if (output.endsWith("\n")) { + output = output.substring(0, output.length() - 1); + } + + return output.replaceAll("\\u001B\\[[0-9][0-9]m", "") + .replace("\b", ""); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsProcessObservable.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsProcessObservable.java new file mode 100644 index 00000000000..ec5af8e2da4 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsProcessObservable.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server; + +/** + * @author Anatolii Bazko + */ +public interface NodeJsProcessObservable { + + /** + * Adds observer. + */ + void addObserver(NodeJsProcessObserver observer); + + /** + * Removes observer. + */ + void removeObserver(NodeJsProcessObserver observer); +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsProcessObserver.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsProcessObserver.java new file mode 100644 index 00000000000..bdadd41dde7 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsProcessObserver.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server; + +import org.eclipse.che.plugin.nodejsdbg.server.exception.NodeJsDebuggerException; + +/** + * @author Anatolii Bazko + */ +public interface NodeJsProcessObserver { + + /** + * Is occurred when a nodejs generates a new output. + * + * Returns {@code true} if no processing requires after. + */ + boolean onOutputProduced(NodeJsOutput nodeJsOutput) throws NodeJsDebuggerException; +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/command/NodeJsDebugCommand.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/command/NodeJsDebugCommand.java new file mode 100644 index 00000000000..c349287e2c1 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/command/NodeJsDebugCommand.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server.command; + +import org.eclipse.che.plugin.nodejsdbg.server.NodeJsDebugProcess; +import org.eclipse.che.plugin.nodejsdbg.server.NodeJsProcessObserver; +import org.eclipse.che.plugin.nodejsdbg.server.exception.NodeJsDebuggerException; + +import java.util.concurrent.Future; + +/** + * Any nodejs command to execute in debug. + * + * @see NodeJsDebugProcess + * @see NodeJsDebugCommandsLibrary + * + * @author Anatolii Bazko + */ +public interface NodeJsDebugCommand extends NodeJsProcessObserver { + + /** + * Executes command. Deferred result is returned. + * + * @param process + * the target process + * @return the result of command execution + * + * @throws NodeJsDebuggerException + * if execution failed + */ + Future execute(NodeJsDebugProcess process) throws NodeJsDebuggerException; +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/command/NodeJsDebugCommandImpl.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/command/NodeJsDebugCommandImpl.java new file mode 100644 index 00000000000..aa46d74c3f4 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/command/NodeJsDebugCommandImpl.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server.command; + +import com.google.common.util.concurrent.SettableFuture; + +import org.eclipse.che.plugin.nodejsdbg.server.NodeJsDebugProcess; +import org.eclipse.che.plugin.nodejsdbg.server.NodeJsOutput; +import org.eclipse.che.plugin.nodejsdbg.server.exception.NodeJsDebuggerException; +import org.eclipse.che.plugin.nodejsdbg.server.exception.NodeJsDebuggerParseException; +import org.eclipse.che.plugin.nodejsdbg.server.parser.NodeJsOutputParser; + +import java.util.concurrent.Future; + +/** + * Basic implementation of {@link NodeJsDebugCommand}. + * When command is executed, it scans outputs until appropriate found, parses it and returns result of execution. + * + * @author Anatolii Bazko + */ +public class NodeJsDebugCommandImpl implements NodeJsDebugCommand { + + private final SettableFuture result; + public final NodeJsOutputParser parser; + private final String input; + + public NodeJsDebugCommandImpl(NodeJsOutputParser parser, String input) { + this.parser = parser; + this.input = input; + this.result = SettableFuture.create(); + } + + @Override + public Future execute(NodeJsDebugProcess process) throws NodeJsDebuggerException { + process.send(input); + return result; + } + + @Override + public boolean onOutputProduced(NodeJsOutput nodeJsOutput) throws NodeJsDebuggerParseException { + if (parser.match(nodeJsOutput)) { + T t = parser.parse(nodeJsOutput); + return result.set(t); + } + + return false; + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/command/NodeJsDebugCommandsLibrary.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/command/NodeJsDebugCommandsLibrary.java new file mode 100644 index 00000000000..6956b6f0024 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/command/NodeJsDebugCommandsLibrary.java @@ -0,0 +1,261 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server.command; + +import org.eclipse.che.api.debug.shared.model.Breakpoint; +import org.eclipse.che.api.debug.shared.model.Location; +import org.eclipse.che.api.debug.shared.model.impl.BreakpointImpl; +import org.eclipse.che.api.debug.shared.model.impl.LocationImpl; +import org.eclipse.che.plugin.nodejsdbg.server.NodeJsDebugProcess; +import org.eclipse.che.plugin.nodejsdbg.server.NodeJsDebugger; +import org.eclipse.che.plugin.nodejsdbg.server.exception.NodeJsDebuggerException; +import org.eclipse.che.plugin.nodejsdbg.server.parser.NodeJsBackTraceParser; +import org.eclipse.che.plugin.nodejsdbg.server.parser.NodeJsBreakpointsParser; +import org.eclipse.che.plugin.nodejsdbg.server.parser.NodeJsBreakpointsParser.Breakpoints; +import org.eclipse.che.plugin.nodejsdbg.server.parser.NodeJsOutputParser; +import org.eclipse.che.plugin.nodejsdbg.server.parser.NodeJsOutputParser.NodeJsOutputRegExpParser; +import org.eclipse.che.plugin.nodejsdbg.server.parser.NodeJsScriptsParser; +import org.eclipse.che.plugin.nodejsdbg.server.parser.NodeJsScriptsParser.Scripts; +import org.eclipse.che.plugin.nodejsdbg.server.parser.NodeJsStepParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.file.Paths; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.regex.Pattern; + +import static java.lang.String.format; + +/** + * Library of the NodeJs debug commands: https://nodejs.org/api/debugger.html + * + * @author Anatolii Bazko + */ +public class NodeJsDebugCommandsLibrary { + private static final Logger LOG = LoggerFactory.getLogger(NodeJsDebugger.class); + private static final Pattern PROCESS_TITLE_COMMAND_OUTPUT_PATTERN = Pattern.compile("^'?(node|nodejs)'?$"); + private static final Pattern PROCESS_VERSION_COMMAND_OUTPUT_PATTERN = Pattern.compile("^'?(v|)[0-9\\.]+'?$"); + private static final Pattern PROCESS_PID_COMMAND_OUTPUT_PATTERN = Pattern.compile("^[0-9]+$"); + private static final Pattern RUN_COMMAND_OUTPUT_PATTERN = Pattern.compile("(break in.*|App is already running.*)"); + + private final NodeJsDebugProcess process; + private final String name; + private final String version; + private final int pid; + + public NodeJsDebugCommandsLibrary(NodeJsDebugProcess process) throws NodeJsDebuggerException { + this.process = process; + run(); + + this.name = detectName(); + this.version = detectVersion(); + this.pid = detectPid(); + } + + /** + * Execute {@code bt} command. + */ + public Location backtrace() throws NodeJsDebuggerException { + NodeJsDebugCommand nextCommand = createCommand("bt", NodeJsBackTraceParser.INSTANCE); + return doExecute(nextCommand); + } + + /** + * Execute {@code sb} command. + */ + public Void setBreakpoint(String scriptPath, int lineNumber) throws NodeJsDebuggerException { + String scriptName = Paths.get(scriptPath).getFileName().toString(); + String input = format("sb('%s', %d)", scriptName, lineNumber); + NodeJsDebugCommand command = createCommand(input, NodeJsOutputParser.VOID); + return doExecute(command); + } + + /** + * Execute {@code breakpoints} command. + * @see NodeJsBackTraceParser + */ + public List getBreakpoints() throws NodeJsDebuggerException { + NodeJsDebugCommand breakpointsCommand = createCommand("breakpoints", NodeJsBreakpointsParser.INSTANCE); + List breakpoints = doExecute(breakpointsCommand).getAll(); + + NodeJsDebugCommand scriptsCommand = createCommand("scripts", NodeJsScriptsParser.INSTANCE); + Map scripts = doExecute(scriptsCommand).getAll(); + + for (int i = 0; i < breakpoints.size(); i++) { + Breakpoint breakpoint = breakpoints.get(i); + Location location = breakpoint.getLocation(); + + String newTarget; + String[] target = location.getTarget().split(":"); + if (target.length != 2) { + LOG.error(format("Illegal breakpoint location format %s", target)); + continue; + } + + if (target[0].equals("scriptId")) { + newTarget = scripts.get((int)Double.parseDouble(target[1])); + } else { + newTarget = target[1]; + } + + Location newLocation = new LocationImpl(newTarget, location.getLineNumber()); + Breakpoint newBreakpoint = new BreakpointImpl(newLocation, breakpoint.isEnabled(), breakpoint.getCondition()); + breakpoints.set(i, newBreakpoint); + } + + return breakpoints; + } + + /** + * Execute {@code cb} command. + */ + public Void clearBreakpoint(String script, int lineNumber) throws NodeJsDebuggerException { + String input = format("cb('%s', %d)", script, lineNumber); + NodeJsDebugCommand command = createCommand(input, NodeJsOutputParser.VOID); + return doExecute(command); + } + + /** + * Execute {@code run} command. + */ + public String run() throws NodeJsDebuggerException { + NodeJsDebugCommand nextCommand = createCommand("run", + new NodeJsOutputRegExpParser(RUN_COMMAND_OUTPUT_PATTERN)); + return doExecute(nextCommand); + } + + + /** + * Execute {@code next} command. + */ + public Location next() throws NodeJsDebuggerException { + NodeJsDebugCommand nextCommand = createCommand("next", NodeJsStepParser.INSTANCE); + return doExecute(nextCommand); + } + + /** + * Execute {@code cont} command. + */ + public Location cont() throws NodeJsDebuggerException { + NodeJsDebugCommand nextCommand = createCommand("cont", NodeJsStepParser.INSTANCE); + return doExecute(nextCommand); + } + + /** + * Execute {@code step in} command. + */ + public Location stepIn() throws NodeJsDebuggerException { + NodeJsDebugCommand nextCommand = createCommand("step", NodeJsStepParser.INSTANCE); + return doExecute(nextCommand); + } + + /** + * Execute {@code step out} command. + */ + public Location stepOut() throws NodeJsDebuggerException { + NodeJsDebugCommand nextCommand = createCommand("out", NodeJsStepParser.INSTANCE); + return doExecute(nextCommand); + } + + /** + * Execute {@code exec} command to set a new value for the giving variable. + */ + public Void setVar(String varName, String newValue) throws NodeJsDebuggerException { + String input = format("exec %s=%s", varName, newValue); + NodeJsDebugCommand command = createCommand(input, NodeJsOutputParser.VOID); + return doExecute(command); + } + + /** + * Execute {@code exec} command to get value for the giving variable. + */ + public String getVar(String varName) throws NodeJsDebuggerException { + String line = format("exec %s", varName); + NodeJsDebugCommand command = createCommand(line, NodeJsOutputParser.DEFAULT); + return doExecute(command); + } + + /** + * Execute {@code exec} command to evaluate expression. + */ + public String evaluate(String expression) throws NodeJsDebuggerException { + String line = format("exec %s", expression); + NodeJsDebugCommand command = createCommand(line, NodeJsOutputParser.DEFAULT); + return doExecute(command); + } + + /** + * Returns NodeJs version. + */ + public String getVersion() { + return version; + } + + /** + * Returns NodeJs title. + */ + public String getName() { + return name; + } + + /** + * Returns NodeJs pid. + */ + public int getPid() { + return pid; + } + + /** + * Returns NodeJs version. + */ + private String detectVersion() throws NodeJsDebuggerException { + NodeJsDebugCommand command = createCommand("process.version", + new NodeJsOutputRegExpParser(PROCESS_VERSION_COMMAND_OUTPUT_PATTERN)); + return doExecute(command); + } + + /** + * Returns NodeJs pid. + */ + private int detectPid() throws NodeJsDebuggerException { + NodeJsDebugCommand command = createCommand("process.pid", + new NodeJsOutputRegExpParser(PROCESS_PID_COMMAND_OUTPUT_PATTERN)); + return Integer.parseInt(doExecute(command)); + } + + /** + * Returns NodeJs title. + */ + private String detectName() throws NodeJsDebuggerException { + NodeJsDebugCommand command = createCommand("process.title", + new NodeJsOutputRegExpParser(PROCESS_TITLE_COMMAND_OUTPUT_PATTERN)); + return doExecute(command); + } + + private V doExecute(NodeJsDebugCommand command) throws NodeJsDebuggerException { + process.addObserver(command); + try { + Future result = command.execute(process); + return result.get(); + } catch (InterruptedException | ExecutionException e) { + throw new NodeJsDebuggerException(e.getMessage(), e); + } finally { + process.removeObserver(command); + } + } + + private NodeJsDebugCommand createCommand(String input, NodeJsOutputParser outputParser) { + return new NodeJsDebugCommandImpl<>(outputParser, input); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/exception/NodeJsDebuggerException.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/exception/NodeJsDebuggerException.java new file mode 100644 index 00000000000..5fcae57f050 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/exception/NodeJsDebuggerException.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server.exception; + +import org.eclipse.che.api.debugger.server.exceptions.DebuggerException; + +/** + * @author Anatoliy Bazko + */ +public class NodeJsDebuggerException extends DebuggerException { + public NodeJsDebuggerException(String message) { + super(message); + } + + public NodeJsDebuggerException(String message, Exception cause) { + super(message, cause); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/exception/NodeJsDebuggerParseException.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/exception/NodeJsDebuggerParseException.java new file mode 100644 index 00000000000..b7bd25401fe --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/exception/NodeJsDebuggerParseException.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server.exception; + +import static java.lang.Math.min; + +/** + * @author Anatoliy Bazko + */ +@SuppressWarnings("serial") +public class NodeJsDebuggerParseException extends NodeJsDebuggerException { + + public static final int MAX_OUTPUT_LENGTH = 80; + + public NodeJsDebuggerParseException(Class clazz, String output) { + super("Can't parse '" + + output.substring(0, min(output.length(), MAX_OUTPUT_LENGTH)) + + "' into " + + clazz.getSimpleName()); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/exception/NodeJsDebuggerTerminatedException.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/exception/NodeJsDebuggerTerminatedException.java new file mode 100644 index 00000000000..9bcf510d9f2 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/exception/NodeJsDebuggerTerminatedException.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server.exception; + +/** + * @author Anatoliy Bazko + */ +public class NodeJsDebuggerTerminatedException extends NodeJsDebuggerException { + public NodeJsDebuggerTerminatedException(String message) { + super(message); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsBackTraceParser.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsBackTraceParser.java new file mode 100644 index 00000000000..9a8b57d5070 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsBackTraceParser.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server.parser; + +import org.eclipse.che.api.debug.shared.model.Location; +import org.eclipse.che.api.debug.shared.model.impl.LocationImpl; +import org.eclipse.che.plugin.nodejsdbg.server.NodeJsOutput; +import org.eclipse.che.plugin.nodejsdbg.server.exception.NodeJsDebuggerParseException; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * {@code backtrace} command parser. + * + * @author Anatoliy Bazko + */ +public class NodeJsBackTraceParser implements NodeJsOutputParser { + + public static final NodeJsBackTraceParser INSTANCE = new NodeJsBackTraceParser(); + public static final Pattern PATTERN = Pattern.compile("#0(.*) (.*):(.*):(.*)"); + + private NodeJsBackTraceParser() { } + + @Override + public boolean match(NodeJsOutput nodeJsOutput) { + return nodeJsOutput.getOutput().startsWith("#0"); + } + + @Override + public Location parse(NodeJsOutput nodeJsOutput) throws NodeJsDebuggerParseException { + String output = nodeJsOutput.getOutput(); + + for (String line : output.split("\n")) { + Matcher matcher = PATTERN.matcher(line); + if (matcher.find()) { + String file = matcher.group(2); + String lineNumber = matcher.group(3); + return new LocationImpl(file, Integer.parseInt(lineNumber)); + } + } + + throw new NodeJsDebuggerParseException(Location.class, output); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsBreakpointsParser.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsBreakpointsParser.java new file mode 100644 index 00000000000..76b4dc5257e --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsBreakpointsParser.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server.parser; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import org.eclipse.che.api.debug.shared.model.Breakpoint; +import org.eclipse.che.api.debug.shared.model.Location; +import org.eclipse.che.api.debug.shared.model.impl.BreakpointImpl; +import org.eclipse.che.api.debug.shared.model.impl.LocationImpl; +import org.eclipse.che.plugin.nodejsdbg.server.NodeJsOutput; +import org.eclipse.che.plugin.nodejsdbg.server.exception.NodeJsDebuggerParseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * {@code breakpoints} command parser. + * + * @author Anatoliy Bazko + */ +public class NodeJsBreakpointsParser implements NodeJsOutputParser { + private static final Logger LOG = LoggerFactory.getLogger(NodeJsBreakpointsParser.class); + + public static final NodeJsBreakpointsParser INSTANCE = new NodeJsBreakpointsParser(); + + @Override + public boolean match(NodeJsOutput nodeJsOutput) { + return nodeJsOutput.getOutput().startsWith("{ breakpoints:"); + } + + @Override + public Breakpoints parse(NodeJsOutput nodeJsOutput) throws NodeJsDebuggerParseException { + final List breakpoints = new ArrayList<>(); + + JsonObject json = new JsonParser().parse(nodeJsOutput.getOutput()).getAsJsonObject(); + if (json.has("breakpoints")) { + Iterator iter = json.getAsJsonArray("breakpoints").iterator(); + while (iter.hasNext()) { + JsonObject item = iter.next().getAsJsonObject(); + try { + final String condition = item.has("condition") && !item.get("condition").isJsonNull() + ? item.get("condition").getAsString() + : null; + final boolean isEnabled = item.has("active") && !item.get("active").isJsonNull() && item.get("active").getAsBoolean(); + final int lineNumber = item.get("line").getAsInt(); + + final String target; + String targetType = item.get("type").getAsString(); + + switch (targetType) { + case "scriptId": + target = String.valueOf(item.get("script_id").getAsInt()); + break; + case "scriptRegExp": + target = item.get("script_regexp").getAsString(); + break; + default: + throw new IllegalArgumentException("Unsupported 'type' value: " + targetType); + } + + Location location = new LocationImpl(targetType + ":" + target, lineNumber + 1); + Breakpoint breakpoint = new BreakpointImpl(location, isEnabled, condition); + breakpoints.add(breakpoint); + } catch (Exception e) { + LOG.error("Failed to parse breakpoint: " + item.toString(), e); + } + } + } + + return new Breakpoints(breakpoints); + } + + public static class Breakpoints { + private final List breakpoints; + + private Breakpoints(List breakpoints) {this.breakpoints = breakpoints;} + + public List getAll() { + return breakpoints; + } + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsOutputParser.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsOutputParser.java new file mode 100644 index 00000000000..c1af509d313 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsOutputParser.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server.parser; + +import org.eclipse.che.plugin.nodejsdbg.server.NodeJsOutput; +import org.eclipse.che.plugin.nodejsdbg.server.exception.NodeJsDebuggerParseException; + +import java.util.regex.Pattern; + +/** + * @author Anatolii Bazko + */ +public interface NodeJsOutputParser { + + /** + * Indicates if output matches + */ + boolean match(NodeJsOutput nodeJsOutput); + + /** + * Parses {@link NodeJsOutput} into valuable result. + */ + T parse(NodeJsOutput nodeJsOutput) throws NodeJsDebuggerParseException; + + /** + * Doesn't parse output, just returns as is. + */ + NodeJsOutputParser DEFAULT = new NodeJsOutputParser() { + @Override + public boolean match(NodeJsOutput nodeJsOutput) { + return true; + } + + @Override + public String parse(NodeJsOutput nodeJsOutput) throws NodeJsDebuggerParseException { + return nodeJsOutput.getOutput(); + } + }; + + /** + * {@link NodeJsOutputParser} when result will be skipped. + */ + NodeJsOutputParser VOID = new NodeJsOutputParser() { + @Override + public boolean match(NodeJsOutput nodeJsOutput) { + return true; + } + + @Override + public Void parse(NodeJsOutput nodeJsOutput) throws NodeJsDebuggerParseException { + return null; + } + }; + + class NodeJsOutputRegExpParser implements NodeJsOutputParser { + private final Pattern pattern; + + public NodeJsOutputRegExpParser(Pattern pattern) { + this.pattern = pattern; + } + + @Override + public boolean match(NodeJsOutput nodeJsOutput) { + return pattern.matcher(nodeJsOutput.getOutput()).find(); + } + + @Override + public String parse(NodeJsOutput nodeJsOutput) throws NodeJsDebuggerParseException { + return nodeJsOutput.getOutput(); + } + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsScriptsParser.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsScriptsParser.java new file mode 100644 index 00000000000..6895a99e11a --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsScriptsParser.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server.parser; + +import org.eclipse.che.plugin.nodejsdbg.server.NodeJsOutput; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * {@code scripts} command parser. + * + * @author Anatoliy Bazko + */ +public class NodeJsScriptsParser implements NodeJsOutputParser { + private static final Pattern SCRIPT = Pattern.compile(".* ([0-9]*): (.*)"); + + public static final NodeJsScriptsParser INSTANCE = new NodeJsScriptsParser(); + + @Override + public boolean match(NodeJsOutput nodeJsOutput) { + for (String line : nodeJsOutput.getOutput().split("\n")) { + Matcher matcher = SCRIPT.matcher(line); + if (!matcher.find()) { + return false; + } + } + + return !nodeJsOutput.isEmpty(); + } + + @Override + public Scripts parse(NodeJsOutput nodeJsOutput) { + Map scripts = new HashMap<>(); + + for (String line : nodeJsOutput.getOutput().split("\n")) { + Matcher matcher = SCRIPT.matcher(line); + if (matcher.find()) { + int number = Integer.parseInt(matcher.group(1)); + String script = matcher.group(2); + + scripts.put(number, script); + } + } + + return new Scripts(scripts); + } + + public static class Scripts { + private final Map scripts; + + public Scripts(Map scripts) {this.scripts = scripts;} + + public Map getAll() { + return scripts; + } + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsStepParser.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsStepParser.java new file mode 100644 index 00000000000..4176d45e78b --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/main/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsStepParser.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server.parser; + +import org.eclipse.che.api.debug.shared.model.Location; +import org.eclipse.che.api.debug.shared.model.impl.LocationImpl; +import org.eclipse.che.plugin.nodejsdbg.server.NodeJsOutput; +import org.eclipse.che.plugin.nodejsdbg.server.exception.NodeJsDebuggerParseException; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * {@code backtrace} command parser. + * + * @author Anatoliy Bazko + */ +public class NodeJsStepParser implements NodeJsOutputParser { + + public static final NodeJsStepParser INSTANCE = new NodeJsStepParser(); + public static final Pattern PATTERN = Pattern.compile("^break in (.*):([0-9]+)"); + + private NodeJsStepParser() { } + + @Override + public boolean match(NodeJsOutput nodeJsOutput) { + return nodeJsOutput.getOutput().startsWith("break in"); + } + + @Override + public Location parse(NodeJsOutput nodeJsOutput) throws NodeJsDebuggerParseException { + String output = nodeJsOutput.getOutput(); + + for (String line : output.split("\n")) { + Matcher matcher = PATTERN.matcher(line); + if (matcher.find()) { + String file = matcher.group(1); + String lineNumber = matcher.group(2); + return new LocationImpl(file, Integer.parseInt(lineNumber)); + } + } + + throw new NodeJsDebuggerParseException(Location.class, output); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsDebuggerTest.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsDebuggerTest.java new file mode 100644 index 00000000000..a75a251df5e --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/java/org/eclipse/che/plugin/nodejsdbg/server/NodeJsDebuggerTest.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server; + +import org.eclipse.che.api.debug.shared.model.Breakpoint; +import org.eclipse.che.api.debug.shared.model.DebuggerInfo; +import org.eclipse.che.api.debug.shared.model.event.BreakpointActivatedEvent; +import org.eclipse.che.api.debug.shared.model.event.SuspendEvent; +import org.eclipse.che.api.debug.shared.model.impl.BreakpointImpl; +import org.eclipse.che.api.debug.shared.model.impl.LocationImpl; +import org.eclipse.che.api.debug.shared.model.impl.action.StepIntoActionImpl; +import org.eclipse.che.api.debug.shared.model.impl.action.StepOutActionImpl; +import org.eclipse.che.api.debug.shared.model.impl.action.StepOverActionImpl; +import org.eclipse.che.api.debugger.server.Debugger; +import org.mockito.ArgumentCaptor; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.util.List; + +import static com.google.common.base.Strings.isNullOrEmpty; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * @author Anatolii Bazko + */ +public class NodeJsDebuggerTest { + private NodeJsDebugger debugger; + private Debugger.DebuggerCallback callback; + + @BeforeMethod + public void setUp() throws Exception { + String file = NodeJsDebuggerTest.class.getResource("/app.js").getFile(); + + callback = mock(Debugger.DebuggerCallback.class); + debugger = NodeJsDebugger.newInstance(null, null, file, callback); + } + + @Test + public void testGetInfo() throws Exception { + DebuggerInfo info = debugger.getInfo(); + + assertTrue(info.getFile().endsWith("app.js")); + assertTrue(!isNullOrEmpty(info.getVersion())); + assertTrue(info.getName().equals("'node'") || info.getName().equals("'nodejs'")); + } + + @Test + public void testManageBreakpoints() throws Exception { + List breakpoints = debugger.getAllBreakpoints(); + assertEquals(breakpoints.size(), 1); + + debugger.addBreakpoint(new BreakpointImpl(new LocationImpl("app.js", 2))); + + ArgumentCaptor breakpointActivated = ArgumentCaptor.forClass(BreakpointActivatedEvent.class); + verify(callback).onEvent(breakpointActivated.capture()); + BreakpointActivatedEvent event = breakpointActivated.getValue(); + Breakpoint breakpoint = event.getBreakpoint(); + assertEquals(breakpoint.getLocation().getTarget(), "app.js"); + assertEquals(breakpoint.getLocation().getLineNumber(), 2); + + debugger.addBreakpoint(new BreakpointImpl(new LocationImpl("app.js", 5))); + + breakpoints = debugger.getAllBreakpoints(); + assertEquals(breakpoints.size(), 3); + + debugger.deleteBreakpoint(new LocationImpl("app.js", 2)); + breakpoints = debugger.getAllBreakpoints(); + assertEquals(breakpoints.size(), 2); + + debugger.deleteAllBreakpoints(); + breakpoints = debugger.getAllBreakpoints(); + assertEquals(breakpoints.size(), 1); + } + + @Test + public void testEvaluation() throws Exception { + String result = debugger.evaluate("2+2"); + assertEquals(result, "4"); + + result = debugger.evaluate("console.log('hello')"); + assertEquals(result, "< hello"); + + result = debugger.evaluate("var y=1"); + assertEquals(result, "undefined"); + } + + @Test + public void testSteps() throws Exception { + debugger.stepOver(new StepOverActionImpl()); + + ArgumentCaptor suspendEventCaptor = ArgumentCaptor.forClass(SuspendEvent.class); + verify(callback, atLeastOnce()).onEvent(suspendEventCaptor.capture()); + SuspendEvent suspendEvent = suspendEventCaptor.getValue(); + assertEquals(suspendEvent.getLocation().getLineNumber(), 2); + assertTrue(suspendEvent.getLocation().getTarget().endsWith("app.js")); + + debugger.stepInto(new StepIntoActionImpl()); + verify(callback, atLeastOnce()).onEvent(suspendEventCaptor.capture()); + suspendEvent = suspendEventCaptor.getValue(); + assertEquals(suspendEvent.getLocation().getLineNumber(), 5); + assertTrue(suspendEvent.getLocation().getTarget().endsWith("app.js")); + + debugger.stepOut(new StepOutActionImpl()); + verify(callback, atLeastOnce()).onEvent(suspendEventCaptor.capture()); + suspendEvent = suspendEventCaptor.getValue(); + assertEquals(suspendEvent.getLocation().getLineNumber(), 9); + assertTrue(suspendEvent.getLocation().getTarget().endsWith("app.js")); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsBackTraceParserTest.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsBackTraceParserTest.java new file mode 100644 index 00000000000..c60b19ee41f --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsBackTraceParserTest.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server.parser; + +import org.eclipse.che.api.debug.shared.model.Location; +import org.eclipse.che.plugin.nodejsdbg.server.NodeJsOutput; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * @author Anatolii Bazko + */ +public class NodeJsBackTraceParserTest { + + private NodeJsBackTraceParser parser; + + @BeforeMethod + public void setUp() throws Exception { + parser = NodeJsBackTraceParser.INSTANCE; + } + + @Test(dataProvider = "match") + public void testMatch(String output, Boolean result) throws Exception { + NodeJsOutput nodeJsOutput = NodeJsOutput.of(output); + + assertTrue(parser.match(nodeJsOutput) == result); + } + + @Test(dataProvider = "parse") + public void testParse(String output, String script, int line) throws Exception { + NodeJsOutput nodeJsOutput = NodeJsOutput.of(output); + + Location location = parser.parse(nodeJsOutput); + + assertEquals(location.getTarget(), script); + assertEquals(location.getLineNumber(), line); + } + + @DataProvider(name = "match") + public static Object[][] match() { + return new Object[][] {{"#0 app.js:1:71", true}, + {"#0 app.js:1:71\n#1 app.js:1:71", true}, + {"#0 Object.defineProperty.get bootstrap_node.js:253:9", true}, + {"#1 app.js:1:71", false}}; + } + + @DataProvider(name = "parse") + public static Object[][] parse() { + return new Object[][] {{"#0 app.js:1:71", "app.js", 1}, + {"#0 app.js:1:71\n#1 app.js:1:71", "app.js", 1}, + {"#0 Object.defineProperty.get bootstrap_node.js:253:9", "bootstrap_node.js", 253}}; + + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsBreakpointsParserTest.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsBreakpointsParserTest.java new file mode 100644 index 00000000000..55e8de8ad96 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsBreakpointsParserTest.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server.parser; + +import org.eclipse.che.api.debug.shared.model.Breakpoint; +import org.eclipse.che.plugin.nodejsdbg.server.NodeJsOutput; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.util.List; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +/** + * @author Anatolii Bazko + */ +public class NodeJsBreakpointsParserTest { + + private NodeJsBreakpointsParser parser; + + @BeforeMethod + public void setUp() throws Exception { + parser = new NodeJsBreakpointsParser(); + } + + @Test + public void testParseBreakpoints() throws Exception { + NodeJsOutput nodeJsOutput = NodeJsOutput.of("{ breakpoints: \n" + + " [ { number: 1,\n" + + " line: 1,\n" + + " column: null,\n" + + " groupId: null,\n" + + " active: true,\n" + + " condition: null,\n" + + " actual_locations: [Object],\n" + + " type: 'scriptId',\n" + + " script_id: '63' } ],\n" + + " breakOnExceptions: false,\n" + + " breakOnUncaughtExceptions: false }"); + + assertTrue(parser.match(nodeJsOutput)); + + List breakpoints = parser.parse(nodeJsOutput).getAll(); + assertEquals(breakpoints.size(), 1); + + Breakpoint breakpoint = breakpoints.get(0); + assertEquals(breakpoint.getLocation().getLineNumber(), 2); + assertEquals(breakpoint.getLocation().getTarget(), "scriptId:63"); + assertNull(breakpoint.getCondition()); + assertTrue(breakpoint.isEnabled()); + } + + @Test + public void testParseBreakpointsWhenScriptIsNotLoaded() throws Exception { + NodeJsOutput nodeJsOutput = NodeJsOutput.of("{ breakpoints: \n" + + " [ { number: 1,\n" + + " line: 1,\n" + + " column: null,\n" + + " groupId: null,\n" + + " active: true,\n" + + " condition: null,\n" + + " actual_locations: [Object],\n" + + " type: 'scriptRegExp',\n" + + " script_regexp: '^(.*[\\\\/\\\\\\\\])?df3dfasdfs\\\\.js$' } ]," + + " breakOnExceptions: false,\n" + + " breakOnUncaughtExceptions: false }"); + + assertTrue(parser.match(nodeJsOutput)); + + List breakpoints = parser.parse(nodeJsOutput).getAll(); + assertEquals(breakpoints.size(), 1); + + Breakpoint breakpoint = breakpoints.get(0); + assertEquals(breakpoint.getLocation().getLineNumber(), 2); + assertEquals(breakpoint.getLocation().getTarget(), "scriptRegExp:^(.*[\\/\\\\])?df3dfasdfs\\.js$"); + assertNull(breakpoint.getCondition()); + assertTrue(breakpoint.isEnabled()); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsScriptsParserTest.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsScriptsParserTest.java new file mode 100644 index 00000000000..fd9e80a70b6 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsScriptsParserTest.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server.parser; + +import org.eclipse.che.plugin.nodejsdbg.server.NodeJsOutput; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.util.Map; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * @author Anatolii Bazko + */ +public class NodeJsScriptsParserTest { + + private NodeJsScriptsParser parser; + + @BeforeMethod + public void setUp() throws Exception { + parser = new NodeJsScriptsParser(); + } + + @Test + public void testParseScriptCommand() throws Exception { + NodeJsOutput output = NodeJsOutput.of(" 35: bootstrap_node.js\n" + + "* 63: app.js\n"); + + assertTrue(parser.match(output)); + + Map scripts = parser.parse(output).getAll(); + + assertEquals(scripts.size(), 2); + assertEquals(scripts.get(35), "bootstrap_node.js"); + assertEquals(scripts.get(63), "app.js"); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsStepParserTest.java b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsStepParserTest.java new file mode 100644 index 00000000000..60598d4a93d --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/java/org/eclipse/che/plugin/nodejsdbg/server/parser/NodeJsStepParserTest.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * 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: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.nodejsdbg.server.parser; + +import org.eclipse.che.api.debug.shared.model.Location; +import org.eclipse.che.plugin.nodejsdbg.server.NodeJsOutput; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * @author Anatolii Bazko + */ +public class NodeJsStepParserTest { + private NodeJsStepParser parser; + + @BeforeMethod + public void setUp() throws Exception { + parser = NodeJsStepParser.INSTANCE; + } + + @Test + public void testMatch() throws Exception { + NodeJsOutput nodeJsOutput = NodeJsOutput.of("break in module.js:559\n" + + " 557 if (depth === 0) stat.cache = null;\n" + + " 558 return result;\n" + + ">559 };\n" + + " 560 \n" + + " 561"); + + assertTrue(parser.match(nodeJsOutput)); + } + + @Test + public void testParse() throws Exception { + NodeJsOutput nodeJsOutput = NodeJsOutput.of("break in module.js:559\n" + + " 557 if (depth === 0) stat.cache = null;\n" + + " 558 return result;\n" + + ">559 };\n" + + " 560 \n" + + " 561"); + + Location location = parser.parse(nodeJsOutput); + + assertEquals(location.getTarget(), "module.js"); + assertEquals(location.getLineNumber(), 559); + } +} diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/resources/app.js b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/resources/app.js new file mode 100644 index 00000000000..29acbf4c50d --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/resources/app.js @@ -0,0 +1,7 @@ +var x = 1; +say("Hello", x); + +function say(str, num) { + console.log(str + " " + x); +} + diff --git a/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/resources/logback-test.xml b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..33ab360a1fe --- /dev/null +++ b/plugins/plugin-nodejs-debugger/che-plugin-nodejs-debugger-server/src/test/resources/logback-test.xml @@ -0,0 +1,25 @@ + + + + + + %-41(%date[%.25thread]) %-45([%-5level] [%.30logger{30} %L]) - %msg%n%nopex + + + + + + + + diff --git a/plugins/plugin-nodejs-debugger/pom.xml b/plugins/plugin-nodejs-debugger/pom.xml new file mode 100644 index 00000000000..4b10f40ce87 --- /dev/null +++ b/plugins/plugin-nodejs-debugger/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + che-plugin-parent + org.eclipse.che.plugin + 5.0.0-M5-SNAPSHOT + + che-plugin-nodejs-debugger-parent + pom + Che Plugin :: NodeJs Debugger :: Parent + + che-plugin-nodejs-debugger-server + che-plugin-nodejs-debugger-ide + + + + + windows + + + Windows + + + + true + + + + diff --git a/plugins/pom.xml b/plugins/pom.xml index 3cbf987006b..f589b650b05 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -46,6 +46,7 @@ plugin-cpp plugin-csharp plugin-nodejs + plugin-nodejs-debugger plugin-php plugin-ssh-machine plugin-ssh-key diff --git a/pom.xml b/pom.xml index ef0e6a77ff6..892aa8bdb89 100644 --- a/pom.xml +++ b/pom.xml @@ -591,6 +591,16 @@ che-plugin-maven-shared ${che.version} + + org.eclipse.che.plugin + che-plugin-nodejs-debugger-ide + ${che.version} + + + org.eclipse.che.plugin + che-plugin-nodejs-debugger-server + ${che.version} + org.eclipse.che.plugin che-plugin-nodejs-lang-ide