package com.mobilesorcery.sdk.html5.debug;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.Launch;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.ILaunchConfigurationDelegate2;
import org.eclipse.debug.core.model.IPersistableSourceLocator;
import org.eclipse.debug.core.model.IStreamsProxy;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.wst.jsdt.debug.internal.core.launching.JavaScriptProcess;
import org.eclipse.wst.jsdt.debug.internal.core.model.JavaScriptDebugTarget;
import com.mobilesorcery.sdk.core.CoreMoSyncPlugin;
import com.mobilesorcery.sdk.core.MoSyncProject;
import com.mobilesorcery.sdk.core.Util;
import com.mobilesorcery.sdk.html5.Html5Plugin;
import com.mobilesorcery.sdk.html5.debug.jsdt.ReloadListeningConnector;
import com.mobilesorcery.sdk.ui.targetphone.ITargetPhone;
public class JSODDLaunchConfigurationDelegate implements
ILaunchConfigurationDelegate2 {
public static final String ID = "com.mobilesorcery.html5.jsodd.launchconfigurationtype";
private static HashMap<ITargetPhone, ILaunch> associatedLaunches = new HashMap<ITargetPhone, ILaunch>();
private final class JSODDProcess extends JavaScriptProcess {
private IStreamsProxy streams = null;
private final ReloadVirtualMachine vm;
private JSODDProcess(ReloadVirtualMachine vm, ILaunch launch, String name) {
super(launch, name);
this.vm = vm;
}
@Override
public IStreamsProxy getStreamsProxy() {
if (streams == null) {
streams = new JSODDStreamsProxy(vm);
}
return streams;
}
}
private static final String DEFAULT_LAUNCH_CONFIG = "default.launch.config";
private static final String LAUNCH_CONFIG_TYPE = "com.mobilesorcery.html5.jsodd.launchconfigurationtype";
public static final String PROJECT_NAME = "project.name";
@Override
public void launch(ILaunchConfiguration configuration, String mode,
ILaunch launch, IProgressMonitor monitor) throws CoreException {
try {
runJSDTRemoteConnector(launch);
} catch (IOException e) {
throw new CoreException(new Status(IStatus.ERROR,
Html5Plugin.PLUGIN_ID, e.getMessage(), e));
}
}
@Override
public ILaunch getLaunch(ILaunchConfiguration configuration, String mode)
throws CoreException {
Launch launch = new Launch(configuration, mode, null);
setDefaultSourceLocator(launch, configuration);
return launch;
}
protected void setDefaultSourceLocator(ILaunch launch,
ILaunchConfiguration configuration) throws CoreException {
IPersistableSourceLocator sourceLocator = null;
if (launch.getSourceLocator() == null) {
String id = configuration.getAttribute(
ILaunchConfiguration.ATTR_SOURCE_LOCATOR_ID, (String) null);
if (id == null) {
id = "org.eclipse.wst.jsdt.debug.core.sourceLocator";
}
sourceLocator = DebugPlugin.getDefault().getLaunchManager()
.newSourceLocator(id);
if (sourceLocator != null) {
sourceLocator.initializeDefaults(configuration);
launch.setSourceLocator(sourceLocator);
}
}
}
public void runJSDTRemoteConnector(ILaunch launch) throws IOException, CoreException {
// This is more or less extracted from JSDTs remote java script launcher
ReloadListeningConnector connector = ReloadListeningConnector
.getDefault();
Map arguments = new HashMap<String, String>();
ReloadVirtualMachine vm = (ReloadVirtualMachine) connector.accept(arguments);
String projectName = launch.getLaunchConfiguration().getAttribute(PROJECT_NAME, (String) null);
IProject project = projectName == null ? null : ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
vm.setProject(project);
// TODO: refactor
JavaScriptProcess process = new JSODDProcess(vm, launch, "JavaScript On-device Debug");
launch.addProcess(process);
final boolean isDebugging = CoreMoSyncPlugin.getDefault().isDebugging();
JavaScriptDebugTarget target = new JavaScriptDebugTarget(vm, process,
launch, true, true) {
/* Deadlock issue! See https://bugs.eclipse.org/bugs/show_bug.cgi?id=249951
// for more info. What may happen is this:
// 1) The target will initialize breakpoints, locking the target object
// 2) This will trigger an autobuild job (with a delay of >100 ms), and
// if the build job starts before 1) above has completed, this job will
// also lock the target object at this method (via notifications)
// and we have a deadlock. */
public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
// So we just remove the synchronized keyword. Now, there could of
// course be more like this one (sigh!)
// http://bertrandmeyer.com/2011/06/20/concurrent-programming-is-easy/
if (isDebugging) {
//CoreMoSyncPlugin.trace("Breakpoint changed, recv'd event @ " + Thread.currentThread().getName());
}
}
};
launch.addDebugTarget(target);
vm.setDebugTarget(target);
}
@Override
public boolean buildForLaunch(ILaunchConfiguration configuration,
String mode, IProgressMonitor monitor) throws CoreException {
// We never really build; this is just for launching the debugger
return false;
}
@Override
public boolean finalLaunchCheck(ILaunchConfiguration configuration,
String mode, IProgressMonitor monitor) throws CoreException {
return true;
}
@Override
public boolean preLaunchCheck(ILaunchConfiguration configuration,
String mode, IProgressMonitor monitor) throws CoreException {
// TODO Only JS ODD enabled projects here
return true;
}
public static synchronized boolean launchDefault(MoSyncProject project, String terminateToken) throws CoreException {
ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
// TODO: Kill old ones.
ILaunchConfigurationType type = manager.getLaunchConfigurationType(LAUNCH_CONFIG_TYPE);
ILaunchConfiguration cfg = null;
if (type != null) {
ILaunchConfiguration[] configurations = manager.getLaunchConfigurations(type);
for (int i = 0; i < configurations.length; i++) {
ILaunchConfiguration configuration = configurations[i];
if (configuration.getAttribute(DEFAULT_LAUNCH_CONFIG, false)) {
cfg = configuration;
}
}
ILaunchConfigurationWorkingCopy wc = type.newInstance(null, "Default JavaScript On-Device Debug");
if (cfg == null) {
wc.setAttribute(DEFAULT_LAUNCH_CONFIG, true);
wc.setAttribute(IDebugUIConstants.ATTR_PRIVATE, true);
}
wc.setAttribute(PROJECT_NAME, project.getName());
wc.doSave();
cfg = wc;
}
ILaunch launch = null;
if (cfg != null) {
launch = DebugUITools.buildAndLaunch(cfg, ILaunchManager.DEBUG_MODE, new NullProgressMonitor());
if (launch != null) {
launch.setAttribute(Html5Plugin.TERMINATE_TOKEN_LAUNCH_ATTR, terminateToken);
}
ILaunch[] previousLaunches = manager.getLaunches();
for (ILaunch previousLaunch : previousLaunches) {
// To avoid spamming the user with spurious timeout messages...
if (previousLaunch != launch &&
previousLaunch.getLaunchConfiguration().getType().getIdentifier().equals(LAUNCH_CONFIG_TYPE) &&
Util.equals(previousLaunch.getAttribute(Html5Plugin.TERMINATE_TOKEN_LAUNCH_ATTR), terminateToken)) {
previousLaunch.setAttribute(Html5Plugin.SUPPRESS_TIMEOUT_LAUNCH_ATTR, Boolean.TRUE.toString());
}
}
}
return launch != null;
}
public static void killLaunch(String terminateToken) throws CoreException {
ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
ILaunch[] launches = manager.getLaunches();
for (ILaunch launch : launches) {
// To avoid spamming the user with spurious timeout messages...
if (launch.getLaunchConfiguration().getType().getIdentifier().equals(LAUNCH_CONFIG_TYPE) &&
Util.equals(launch.getAttribute(Html5Plugin.TERMINATE_TOKEN_LAUNCH_ATTR), terminateToken)) {
launch.terminate();
}
}
}
private static boolean isDefaultLaunch(ILaunch launch) throws CoreException {
ILaunchConfiguration launchCfg = launch.getLaunchConfiguration();
if (LAUNCH_CONFIG_TYPE.equals(launchCfg.getType().getIdentifier())) {
return launchCfg.getAttribute(DEFAULT_LAUNCH_CONFIG, false);
}
return false;
}
}