blob: 660e896f35a7d050801f2a1a5cc2caf78f7950e7 [file] [log] [blame]
package org.eclipse.jdt.internal.debug.ui.snippeteditor;
/**********************************************************************
Copyright (c) 2002 IBM Corp. All rights reserved.
This file is made available under the terms of the Common Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/cpl-v10.html
**********************************************************************/
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.StringTokenizer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IDebugEventSetListener;
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.model.IBreakpoint;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.debug.core.IJavaLineBreakpoint;
import org.eclipse.jdt.debug.core.JDIDebugModel;
import org.eclipse.jdt.debug.ui.IJavaDebugUIConstants;
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jface.dialogs.MessageDialog;
/**
* Support for launching scrapbook using launch configurations.
*/
public class ScrapbookLauncher implements IDebugEventSetListener {
public static final String SCRAPBOOK_LAUNCH = IJavaDebugUIConstants.PLUGIN_ID + ".scrapbook_launch"; //$NON-NLS-1$
public static final String SCRAPBOOK_FILE_PATH = IJavaDebugUIConstants.PLUGIN_ID + ".scrapbook_file_path"; //$NON-NLS-1$
/**
* Persistent property associated with snippet files specifying working directory.
* Same format as the associated launch configuration attribute
* <code>ATTR_WORKING_DIR</code>.
*/
public static final QualifiedName SNIPPET_EDITOR_LAUNCH_CONFIG_HANDLE_MEMENTO = new QualifiedName(IJavaDebugUIConstants.PLUGIN_ID, "snippet_editor_launch_config"); //$NON-NLS-1$
private IJavaLineBreakpoint fMagicBreakpoint;
private HashMap fScrapbookToVMs = new HashMap(10);
private HashMap fVMsToBreakpoints = new HashMap(10);
private HashMap fVMsToScrapbooks = new HashMap(10);
private static ScrapbookLauncher fgDefault = null;
private ScrapbookLauncher() {
//see getDefault()
}
public static ScrapbookLauncher getDefault() {
if (fgDefault == null) {
fgDefault = new ScrapbookLauncher();
}
return fgDefault;
}
/**
* Launches a VM for the given srapbook page, in debug mode.
* Returns an existing launch if the page is already running.
*
* @param file scrapbook page file
* @return resulting launch, or <code>null</code> on failure
*/
protected ILaunch launch(IFile page) {
// clean up orphaned launch cofigs
cleanupLaunchConfigurations();
if (!page.getFileExtension().equals("jpage")) { //$NON-NLS-1$
showNoPageDialog();
return null;
}
IDebugTarget vm = getDebugTarget(page);
if (vm != null) {
//already launched
return vm.getLaunch();
}
IJavaProject javaProject= JavaCore.create(page.getProject());
URL pluginInstallURL= JDIDebugUIPlugin.getDefault().getDescriptor().getInstallURL();
URL jarURL = null;
try {
jarURL = new URL(pluginInstallURL, "snippetsupport.jar"); //$NON-NLS-1$
jarURL = Platform.asLocalURL(jarURL);
} catch (MalformedURLException e) {
JDIDebugUIPlugin.errorDialog(SnippetMessages.getString("ScrapbookLauncher.Exception_occurred_launching_scrapbook_1"), e); //$NON-NLS-1$
return null;
} catch (IOException e) {
JDIDebugUIPlugin.errorDialog(SnippetMessages.getString("ScrapbookLauncher.Exception_occurred_launching_scrapbook_1"), e); //$NON-NLS-1$
return null;
}
List cp = new ArrayList(3);
IRuntimeClasspathEntry supportEntry = JavaRuntime.newArchiveRuntimeClasspathEntry(new Path(jarURL.getFile()));
cp.add(supportEntry);
// get bootpath entries
try {
IRuntimeClasspathEntry[] entries = JavaRuntime.computeUnresolvedRuntimeClasspath(javaProject);
for (int i = 0; i < entries.length; i++) {
if (entries[i].getClasspathProperty() != IRuntimeClasspathEntry.USER_CLASSES) {
cp.add(entries[i]);
}
}
IRuntimeClasspathEntry[] classPath = (IRuntimeClasspathEntry[])cp.toArray(new IRuntimeClasspathEntry[cp.size()]);
return doLaunch(javaProject, page, classPath);
} catch (CoreException e) {
JDIDebugUIPlugin.errorDialog(SnippetMessages.getString("ScrapbookLauncher.Unable_to_launch_scrapbook_VM_6"), e); //$NON-NLS-1$
}
return null;
}
private ILaunch doLaunch(IJavaProject p, IFile page, IRuntimeClasspathEntry[] classPath) {
try {
if (fVMsToScrapbooks.isEmpty()) {
// register for debug events if a scrapbook is not currently running
DebugPlugin.getDefault().addDebugEventListener(this);
}
ILaunchConfiguration config = null;
ILaunchConfigurationWorkingCopy wc = null;
try {
config = getLaunchConfigurationTemplate(page);
if (config != null) {
wc = config.getWorkingCopy();
}
} catch (CoreException e) {
config = null;
wc = null;
JDIDebugUIPlugin.errorDialog(SnippetMessages.getString("ScrapbookLauncher.Unable_to_retrieve_scrapbook_runtime_settings._Settings_will_revert_to_default._1"), e); //$NON-NLS-1$
}
if (config == null) {
config = createLaunchConfigurationTemplate(page);
wc = config.getWorkingCopy();
}
IPath outputLocation = p.getProject().getPluginWorkingLocation(JDIDebugUIPlugin.getDefault().getDescriptor());
File f = outputLocation.toFile();
URL u = null;
try {
u = getEncodedURL(f);
} catch (MalformedURLException e) {
JDIDebugUIPlugin.errorDialog(SnippetMessages.getString("ScrapbookLauncher.Exception_occurred_launching_scrapbook_1"),e); //$NON-NLS-1$
return null;
}
String[] defaultClasspath = JavaRuntime.computeDefaultRuntimeClassPath(p);
String[] urls = new String[defaultClasspath.length + 1];
urls[0] = u.toExternalForm();
for (int i = 0; i < defaultClasspath.length; i++) {
f = new File(defaultClasspath[i]);
try {
urls[i + 1] = getEncodedURL(f).toExternalForm();
} catch (MalformedURLException e) {
JDIDebugUIPlugin.errorDialog(SnippetMessages.getString("ScrapbookLauncher.Exception_occurred_launching_scrapbook_1"), e); //$NON-NLS-1$
return null;
}
}
// convert to mementos
List classpathList= new ArrayList(classPath.length);
for (int i = 0; i < classPath.length; i++) {
classpathList.add(classPath[i].getMemento());
}
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, false);
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, classpathList);
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, p.getElementName());
StringBuffer urlsString = new StringBuffer();
for (int i = 0; i < urls.length; i++) {
urlsString.append(' ');
urlsString.append(urls[i]);
}
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, urlsString.toString());
wc.setAttribute(SCRAPBOOK_LAUNCH, SCRAPBOOK_LAUNCH);
config = wc.doSave();
ILaunch launch = config.launch(ILaunchManager.DEBUG_MODE, null);
if (launch != null) {
IDebugTarget dt = launch.getDebugTarget();
IBreakpoint magicBreakpoint = createMagicBreakpoint("org.eclipse.jdt.internal.debug.ui.snippeteditor.ScrapbookMain"); //$NON-NLS-1$
fScrapbookToVMs.put(page, dt);
fVMsToScrapbooks.put(dt, page);
fVMsToBreakpoints.put(dt, magicBreakpoint);
dt.breakpointAdded(magicBreakpoint);
launch.setAttribute(SCRAPBOOK_LAUNCH, SCRAPBOOK_LAUNCH);
return launch;
}
} catch (CoreException e) {
JDIDebugUIPlugin.errorDialog(SnippetMessages.getString("ScrapbookLauncher.Unable_to_launch_scrapbook_VM_6"), e); //$NON-NLS-1$
}
return null;
}
/**
* Creates an "invisible" line breakpoint.
*/
IBreakpoint createMagicBreakpoint(String typeName) throws CoreException{
fMagicBreakpoint= JDIDebugModel.createLineBreakpoint(ResourcesPlugin.getWorkspace().getRoot(), typeName, 51, -1, -1, 0, false, null);
fMagicBreakpoint.setPersisted(false);
return fMagicBreakpoint;
}
/**
* @see IDebugEventSetListener#handleDebugEvents(DebugEvent[])
*/
public void handleDebugEvents(DebugEvent[] events) {
for (int i = 0; i < events.length; i++) {
DebugEvent event = events[i];
if (event.getSource() instanceof IDebugTarget && event.getKind() == DebugEvent.TERMINATE) {
cleanup((IDebugTarget)event.getSource());
}
}
}
/**
* Returns the debug target associated with the given
* scrapbook page, or <code>null</code> if none.
*
* @param page file representing scrapbook page
* @return associated debug target or <code>null</code>
*/
public IDebugTarget getDebugTarget(IFile page) {
return (IDebugTarget)fScrapbookToVMs.get(page);
}
/**
* Returns the magic breakpoint associated with the given
* scrapbook VM. The magic breakpoint is the location at
* which an evaluation begins.
*
* @param target a scrapbook debug target
* @return the breakpoint at which an evaluation begins
* or <code>null</code> if none
*/
public IBreakpoint getMagicBreakpoint(IDebugTarget target) {
return (IBreakpoint)fVMsToBreakpoints.get(target);
}
protected void showNoPageDialog() {
String title= SnippetMessages.getString("ScrapbookLauncher.error.title"); //$NON-NLS-1$
String msg= SnippetMessages.getString("ScrapbookLauncher.error.pagenotfound"); //$NON-NLS-1$
MessageDialog.openError(JDIDebugUIPlugin.getActiveWorkbenchShell(),title, msg);
}
protected void cleanup(IDebugTarget target) {
Object page = fVMsToScrapbooks.get(target);
if (page != null) {
fVMsToScrapbooks.remove(target);
fScrapbookToVMs.remove(page);
fVMsToBreakpoints.remove(target);
ILaunch launch = target.getLaunch();
if (launch != null) {
getLaunchManager().removeLaunch(launch);
}
if (fVMsToScrapbooks.isEmpty()) {
// no need to listen to events if no scrapbooks running
DebugPlugin.getDefault().removeDebugEventListener(this);
}
}
}
protected URL getEncodedURL(File file) throws MalformedURLException {
//looking at File.toURL the delimiter is always '/'
// NOT File.separatorChar
String urlDelimiter= "/"; //$NON-NLS-1$
String unencoded= file.toURL().toExternalForm();
StringBuffer encoded= new StringBuffer();
StringTokenizer tokenizer= new StringTokenizer(unencoded, urlDelimiter);
encoded.append(tokenizer.nextToken()); //file:
encoded.append(urlDelimiter);
encoded.append(tokenizer.nextToken()); //drive letter and ':'
while (tokenizer.hasMoreElements()) {
encoded.append(urlDelimiter);
String token= tokenizer.nextToken();
encoded.append(URLEncoder.encode(token));
}
if (file.isDirectory()) {
encoded.append(urlDelimiter);
}
return new URL(encoded.toString());
}
/**
* Returns the launch configuration used as a template for launching the
* given scrapbook file, or <code>null</code> if none. The template contains
* working directory and JRE settings to use when launching the scrapbook.
*/
public static ILaunchConfiguration getLaunchConfigurationTemplate(IFile file) throws CoreException {
String memento = getLaunchConfigMemento(file);
if (memento != null) {
return getLaunchManager().getLaunchConfiguration(memento);
}
return null;
}
/**
* Creates and saves template launch configuration for the given scrapbook file.
*/
public static ILaunchConfiguration createLaunchConfigurationTemplate(IFile page) throws CoreException {
ILaunchConfigurationType lcType = getLaunchManager().getLaunchConfigurationType(IJavaLaunchConfigurationConstants.ID_JAVA_APPLICATION);
String name = page.getName() + "-Scrapbook-" + System.currentTimeMillis(); //$NON-NLS-1$
ILaunchConfigurationWorkingCopy wc = lcType.newInstance(null, name);
wc.setAttribute(IDebugUIConstants.ATTR_PRIVATE, true);
wc.setAttribute(IDebugUIConstants.ATTR_TARGET_DEBUG_PERSPECTIVE, IDebugUIConstants.PERSPECTIVE_NONE);
wc.setAttribute(IDebugUIConstants.ATTR_TARGET_RUN_PERSPECTIVE, IDebugUIConstants.PERSPECTIVE_NONE);
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, "org.eclipse.jdt.internal.debug.ui.snippeteditor.ScrapbookMain"); //$NON-NLS-1$
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, page.getProject().getName());
wc.setAttribute(SCRAPBOOK_LAUNCH, SCRAPBOOK_LAUNCH);
wc.setAttribute(SCRAPBOOK_FILE_PATH, page.getFullPath().toString());
ILaunchConfiguration config = wc.doSave();
setLaunchConfigMemento(page, config.getMemento());
return config;
}
/**
* Returns the handle memento for the given scrapbook's launch configuration
* template, or <code>null</code> if none.
*/
private static String getLaunchConfigMemento(IFile file) {
try {
return file.getPersistentProperty(SNIPPET_EDITOR_LAUNCH_CONFIG_HANDLE_MEMENTO);
} catch (CoreException e) {
JDIDebugUIPlugin.log(e);
}
return null;
}
/**
* Sets the handle memento for the given scrapbook's launch configuration
* template.
*/
protected static void setLaunchConfigMemento(IFile file, String memento) {
try {
file.setPersistentProperty(SNIPPET_EDITOR_LAUNCH_CONFIG_HANDLE_MEMENTO, memento);
} catch (CoreException e) {
JDIDebugUIPlugin.log(e);
}
}
/**
* Returns the launch manager.
*/
protected static ILaunchManager getLaunchManager() {
return DebugPlugin.getDefault().getLaunchManager();
}
/**
* Returns the working directory attribute for the given snippet file,
* possibly <code>null</code>.
*
* @exception CoreException if unable to retrieve the attribute
*/
public static String getWorkingDirectoryAttribute(IFile file) throws CoreException {
ILaunchConfiguration config = getLaunchConfigurationTemplate(file);
if (config != null) {
return config.getAttribute(IJavaLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, (String)null);
}
return null;
}
/**
* Returns the VM install used to launch the given snippet file.
*
* @exception CoreException if unable to retrieve the attribute
*/
public static IVMInstall getVMInstall(IFile file) throws CoreException {
ILaunchConfiguration config = getLaunchConfigurationTemplate(file);
if (config == null) {
IJavaProject pro = JavaCore.create(file.getProject());
return JavaRuntime.getVMInstall(pro);
} else {
return JavaRuntime.computeVMInstall(config);
}
}
/**
* Deletes any scrapbook launch configurations for scrapbooks that
* have been deleted. Rather than listening to all resource deltas,
* configs are deleted each time a scrapbook is launched - which is
* infrequent.
*/
public void cleanupLaunchConfigurations() {
try {
ILaunchConfigurationType lcType = getLaunchManager().getLaunchConfigurationType(IJavaLaunchConfigurationConstants.ID_JAVA_APPLICATION);
ILaunchConfiguration[] configs = getLaunchManager().getLaunchConfigurations(lcType);
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
for (int i = 0; i < configs.length; i++) {
String path = configs[i].getAttribute(SCRAPBOOK_FILE_PATH, (String)null);
if (path != null) {
IPath pagePath = new Path(path);
IResource res = root.findMember(pagePath);
if (res == null) {
// config without a page - delete it
configs[i].delete();
}
}
}
} catch (CoreException e) {
// log quietly
JDIDebugUIPlugin.log(e);
}
}
}