blob: 24423804d9afbce1c0bc5f0f7545a5c02317d6d9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 Red Hat, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat Inc. - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.wst.jsdt.js.node.internal.launch;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.wst.jsdt.chromium.ConnectionLogger;
import org.eclipse.wst.jsdt.chromium.debug.core.model.BreakpointSynchronizer;
import org.eclipse.wst.jsdt.chromium.debug.core.model.ConnectionLoggerImpl;
import org.eclipse.wst.jsdt.chromium.debug.core.model.ConsolePseudoProcess;
import org.eclipse.wst.jsdt.chromium.debug.core.model.DebugTargetImpl;
import org.eclipse.wst.jsdt.chromium.debug.core.model.IPredefinedSourceWrapProvider;
import org.eclipse.wst.jsdt.chromium.debug.core.model.JavascriptVmEmbedder;
import org.eclipse.wst.jsdt.chromium.debug.core.model.JavascriptVmEmbedderFactory;
import org.eclipse.wst.jsdt.chromium.debug.core.model.LaunchParams;
import org.eclipse.wst.jsdt.chromium.debug.core.model.NamedConnectionLoggerFactory;
import org.eclipse.wst.jsdt.chromium.debug.core.model.SourceWrapSupport;
import org.eclipse.wst.jsdt.chromium.debug.core.model.VProjectWorkspaceBridge;
import org.eclipse.wst.jsdt.chromium.debug.core.model.WorkspaceBridge;
import org.eclipse.wst.jsdt.chromium.debug.ui.listeners.JavaScriptChangeListener;
import org.eclipse.wst.jsdt.chromium.debug.ui.listeners.LaunchTerminateListener;
import org.eclipse.wst.jsdt.chromium.util.Destructable;
import org.eclipse.wst.jsdt.chromium.util.DestructingGuard;
import org.eclipse.wst.jsdt.js.node.NodePlugin;
import org.eclipse.wst.jsdt.js.node.internal.NodeConstants;
/**
* Mimic {@link LaunchTypeBase} for V8 debugger launch
*
* @author "Gorkem Ercan (gercan)"
* @author "Ilya Buziuk (ibuziuk)"
*/
final public class NodeDebugConnector {
private static final String PROJECT_EXPLORER_ID = "org.eclipse.ui.navigator.ProjectExplorer"; //$NON-NLS-1$
private final ILaunchConfiguration configuration;
private final ILaunch launch;
public NodeDebugConnector(ILaunchConfiguration configuration, final ILaunch launch) {
this.configuration = configuration;
this.launch = launch;
}
boolean attach() throws CoreException {
String host = configuration.getAttribute(NodeConstants.ATTR_HOST_FIELD, NodeConstants.DEFAULT_HOST);
int port = Integer.parseInt(
configuration.getAttribute(NodeConstants.ATTR_PORT_FIELD, String.valueOf(NodeConstants.DEFAULT_PORT)));
NamedConnectionLoggerFactory consoleFactory = NO_CONNECTION_LOGGER_FACTORY;
if (configuration.getAttribute(NodeConstants.ATTR_ADD_NETWORK_CONSOLE_FIELD, false)) {
consoleFactory = new NamedConnectionLoggerFactory() {
public ConnectionLogger createLogger(String title) {
return createConsoleAndLogger(launch, true, title);
}
};
}
SourceWrapSupport sourceWrapSupport = createSourceWrapSupportFromConfig(configuration);
JavascriptVmEmbedder.ConnectionToRemote remoteServer = JavascriptVmEmbedderFactory.connectToStandalone(host,
port, consoleFactory);
remoteServer.selectVm();
DestructingGuard destructingGuard = new DestructingGuard();
Destructable lauchDestructor = new Destructable() {
public void destruct() {
if (!launch.hasChildren()) {
DebugPlugin.getDefault().getLaunchManager().removeLaunch(launch);
}
}
};
try {
destructingGuard.addValue(lauchDestructor);
WorkspaceBridge.Factory bridgeFactory = new VProjectWorkspaceBridge.FactoryImpl(configuration.getName());
final DebugTargetImpl target = new DebugTargetImpl(launch, bridgeFactory, sourceWrapSupport,
BreakpointSynchronizer.Direction.MERGE);
Destructable targetDestructor = new Destructable() {
public void destruct() {
try {
target.terminate();
} catch (DebugException e) {
NodePlugin.logError(e);
}
}
};
destructingGuard.addValue(targetDestructor);
boolean attached = DebugTargetImpl.attach(target, remoteServer, destructingGuard,
OPENING_VIEW_ATTACH_CALLBACK, new NullProgressMonitor());
if (!attached) {
// Cancel pressed.
return false;
}
launch.addDebugTarget(target);
destructingGuard.discharge();
addListeners(launch);
return true;
} finally {
destructingGuard.doFinally();
}
}
private void addListeners(ILaunch launch) {
IResourceChangeListener listener = new JavaScriptChangeListener();
ResourcesPlugin.getWorkspace().addResourceChangeListener(listener);
DebugPlugin.getDefault().addDebugEventListener(new LaunchTerminateListener(launch, listener));
}
private static ConnectionLogger createConsoleAndLogger(final ILaunch launch, final boolean addLaunchToManager,
final String title) {
final ConsolePseudoProcess.Retransmitter consoleRetransmitter = new ConsolePseudoProcess.Retransmitter();
// This controller is responsible for creating ConsolePseudoProcess only
// on logStarted call. Before this ConnectionLoggerImpl with all it fields should stay
// garbage-collectible, because connection may not even start.
ConnectionLoggerImpl.LogLifecycleListener consoleController = new ConnectionLoggerImpl.LogLifecycleListener() {
private final AtomicBoolean alreadyStarted = new AtomicBoolean(false);
public void logClosed() {
consoleRetransmitter.processClosed();
}
public void logStarted(ConnectionLoggerImpl connectionLogger) {
boolean res = alreadyStarted.compareAndSet(false, true);
if (!res) {
throw new IllegalStateException();
}
new ConsolePseudoProcess(launch, title, consoleRetransmitter,
connectionLogger.getConnectionTerminate());
consoleRetransmitter.startFlushing();
if (addLaunchToManager) {
// Active the launch (again if it has already been removed)
DebugPlugin.getDefault().getLaunchManager().addLaunch(launch);
}
}
};
return new ConnectionLoggerImpl(consoleRetransmitter, consoleController);
}
private static final NamedConnectionLoggerFactory NO_CONNECTION_LOGGER_FACTORY = new NamedConnectionLoggerFactory() {
public ConnectionLogger createLogger(String title) {
return null;
}
};
private static SourceWrapSupport createSourceWrapSupportFromConfig(ILaunchConfiguration config)
throws CoreException {
List<IPredefinedSourceWrapProvider.Entry> entries = LaunchParams.PredefinedSourceWrapperIds
.resolveEntries(config);
List<SourceWrapSupport.Wrapper> wrappers = new ArrayList<SourceWrapSupport.Wrapper>(entries.size());
for (IPredefinedSourceWrapProvider.Entry en : entries) {
wrappers.add(en.getWrapper());
}
return new SourceWrapSupport(wrappers);
}
/**
* Brings up the "Project Explorer" view in the active workbench window.
*/
private static final Runnable OPENING_VIEW_ATTACH_CALLBACK = new Runnable() {
public void run() {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
IWorkbench workbench = PlatformUI.getWorkbench();
IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
if (window == null) {
if (workbench.getWorkbenchWindowCount() == 1) {
window = workbench.getWorkbenchWindows()[0];
}
}
if (window != null) {
try {
window.getActivePage().showView(PROJECT_EXPLORER_ID);
} catch (PartInitException e) {
// ignore
}
}
}
});
}
};
}