blob: 58ce204d60e4ee906bc05727e039bcbbd03b5d75 [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2008, 2015 Technical University Berlin, Germany.
*
* 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
*
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors:
* Technical University Berlin - Initial API and implementation
**********************************************************************/
package org.eclipse.objectteams.otdt.internal.debug.adaptor.launching;
import java.util.Map;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.core.util.Util;
import org.eclipse.objectteams.otdt.core.ext.OTREContainer;
import org.eclipse.objectteams.otdt.core.ext.WeavingScheme;
import org.eclipse.objectteams.otdt.debug.OTDebugPlugin;
import org.eclipse.objectteams.otdt.debug.OTBreakpointInstaller;
import org.eclipse.objectteams.otdt.internal.debug.adaptor.DebugMessages;
import org.eclipse.objectteams.otdt.internal.debug.adaptor.OTDebugAdaptorPlugin;
import org.eclipse.objectteams.otdt.internal.debug.adaptor.dynamic.RedefineClassesBPListener;
import org.eclipse.objectteams.otequinox.TransformerPlugin;
import org.eclipse.osgi.util.NLS;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.ISharedPluginModel;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import base org.eclipse.pde.internal.launching.launcher.BundleLauncherHelper;
import base org.eclipse.pde.internal.ui.launcher.JREBlock;
import base org.eclipse.pde.launching.AbstractPDELaunchConfiguration;
import base org.eclipse.pde.launching.JUnitLaunchConfigurationDelegate;
import base org.eclipse.pde.ui.launcher.AbstractLauncherTab;
import base org.eclipse.pde.ui.launcher.MainTab;
import base org.eclipse.pde.ui.launcher.OSGiSettingsTab;
/**
* This team adapts all Eclipse and OSGi launches (Launcher) and launch configurations (JREBlock and LauncherTab).
*
* @author stephan
* @since 1.2.2
*/
@SuppressWarnings("restriction")
public team class PDELaunchingAdaptor {
/** Mediating between LauncherTab and JREBlock: */
LauncherTab currentTab = null;
static final String ENABLE_OTEQUINOX = "-Dot.equinox=1"; //$NON-NLS-1$ // this also causes the WORKAROUND_REPOSITORY flag being set to true in OTRE.
static final String DISABLE_OTEQUINOX = "-Dot.equinox=false"; //$NON-NLS-1$ // prevents OTWeavingHook installation
static final String OT_DEBUG_VMARG = "-Dot.debug"; //$NON-NLS-1$
static final String OTE_AGENT_ARG = "-javaagent:" + TransformerPlugin.getOtequinoxAgentPath();
static final String OT_WEAVING = "-Dot.weaving="; // need to append either "otre" or "otdre"
static final String[] OT_VM_ARGS = { ENABLE_OTEQUINOX, OT_WEAVING };
static final String[] OTDRE_VM_ARGS = { ENABLE_OTEQUINOX, OTE_AGENT_ARG, OT_WEAVING };
static final String[] OT_VM_DEBUG_ARGS = { ENABLE_OTEQUINOX, OT_DEBUG_VMARG, OTE_AGENT_ARG, OT_WEAVING };
static final String[] VM_ARGS = { DISABLE_OTEQUINOX };
static final String[] VM_DEBUG_ARGS = { DISABLE_OTEQUINOX, OT_DEBUG_VMARG };
/** select proper set of arguments for an OT-launch, insert otequinox.hook using it's actual install location. */
static String[] getOTArgs(String mode, String weavingMode) {
String[] otArgs;
if (mode != null && mode.equals(ILaunchManager.DEBUG_MODE)) {
otArgs = OT_VM_DEBUG_ARGS;
} else {
if (WeavingScheme.OTDRE.name().equals(weavingMode))
otArgs = OTDRE_VM_ARGS;
else
otArgs = OT_VM_ARGS;
}
int length = otArgs.length;
System.arraycopy(otArgs, 0, otArgs = new String[otArgs.length], 0, length);
otArgs[length-1] += weavingMode;
return otArgs;
}
/**
* Extend pre-built vm arguments with OT/Equinox specifics (depending on run/debug mode).
*/
static String[] extendVMArguments(String[] args, String mode, String weavingMode) {
String[] otArgs = getOTArgs(mode, weavingMode);
if (otArgs == null)
return args;
if (args == null || args.length == 0)
return otArgs;
String[] combinedArgs = new String[args.length + otArgs.length];
System.arraycopy(args, 0, combinedArgs, 0, args.length);
System.arraycopy(otArgs, 0, combinedArgs, args.length, otArgs.length);
return combinedArgs;
}
static String[] addDisableOTEquinoxArgument(String[] args) {
String[] combinedArgs = new String[args.length + 1];
System.arraycopy(args, 0, combinedArgs, 0, args.length);
combinedArgs[args.length] = DISABLE_OTEQUINOX;
return combinedArgs;
}
/* alternate version for single string signature. */
static String extendVMArguments(String args, ISharedPluginModel hookModel, String mode, String weavingMode) {
String[] otArgss = getOTArgs(mode, weavingMode);
if (otArgss == null)
return args;
String otArgs = Util.concatWith(otArgss, ' ');
if (args == null || args.length() == 0)
return otArgs;
return args+' '+otArgs;
}
/**
* Installs breakpoints needed for the TeamMonitor into org.objectteams.Team.
* Needs to find a project with OTJavaNature to do this.
*/
static void installOOTBreakpoints(IProject[] projects, String weavingMode) throws CoreException
{
if (projects != null)
for (IProject project : projects)
// find org.objectteams.Team in any OT/J Project:
if (project.getNature(JavaCore.OTJ_NATURE_ID) != null) {
IJavaProject javaProject = JavaCore.create(project);
OTBreakpointInstaller.installOTBreakpoints(javaProject,
RedefineClassesBPListener.get(WeavingScheme.valueOf(weavingMode)));
return; // good, done.
}
logException(null, Status.WARNING, DebugMessages.OTLaunching_no_OTJ_project_found);
}
static void logException(CoreException ex, int level, String msg) {
OTDebugPlugin.getDefault().getLog().log(
new Status(level, OTDebugAdaptorPlugin.PLUGIN_ID, 0, msg, ex));
}
// helper needed to protect a guard from exception:
static boolean isOTLaunch (ILaunchConfiguration configuration) {
try {
return configuration.getAttribute(OTDebugPlugin.OT_LAUNCH, false);
} catch (CoreException ce) {
return false;
}
}
/** Generalizes over normal pde launches and JUnit plugin launches. */
abstract protected class AbstractLauncher
{
abstract IProject[] getProjectsForProblemSearch(ILaunchConfiguration config, String mode)
throws CoreException;
String mode;
String weavingMode;
void prepareLaunch(ILaunchConfiguration configuration, String mode) throws DebugException
{
this.mode = mode;
if (isOTLaunch(configuration))
try {
IProject[] projects = getProjectsForProblemSearch(configuration, mode);
determineWeavingMode(projects);
if (ILaunchManager.DEBUG_MODE.equals(mode))
PDELaunchingAdaptor.installOOTBreakpoints(projects, weavingMode);
} catch (DebugException dex) {
throw dex;
} catch (CoreException ex) {
logException(ex, Status.WARNING, DebugMessages.OTLaunching_no_OTJ_project_found);
}
}
private void determineWeavingMode(IProject[] projects) throws CoreException {
this.weavingMode = null;
if (projects != null) {
IProject otjProject = null;
for (IProject project : projects) {
// check weaving mode in all relevant OT/J Project:
if (project.getNature(JavaCore.OTJ_NATURE_ID) != null) {
String wMode = JavaCore.create(project).getOption(JavaCore.COMPILER_OPT_WEAVING_SCHEME, false);
if (wMode != null) {
if (this.weavingMode != null && !wMode.equals(this.weavingMode))
throw new DebugException(new Status(IStatus.ERROR, OTDebugPlugin.PLUGIN_ID,
NLS.bind(DebugMessages.OTLaunching_conflicting_weaving_modes,
new String[] {
otjProject.getName(), this.weavingMode,
project.getName(), wMode
})));
this.weavingMode = wMode;
}
otjProject = project;
}
}
if (this.weavingMode == null && otjProject != null)
this.weavingMode = JavaCore.getDefaultOptions().get(JavaCore.COMPILER_OPT_WEAVING_SCHEME);
}
if (this.weavingMode == null)
logException(null, Status.WARNING, DebugMessages.OTLaunching_no_OTJ_project_found);
}
}
/**
* This role adapts any pde-launch where the OT/Equinox flag is set to true:
* <ul>
* <li>add OT-specific arguments to the vm args (extendVMArguments)</li>
* <li>install the breakpoints needed by the TeamMonitor (prepareLaunch)</li>
* </ul>
*/
protected class Launcher extends AbstractLauncher playedBy AbstractPDELaunchConfiguration
{
@SuppressWarnings("decapsulation")
getProjectsForProblemSearch -> getProjectsForProblemSearch;
// Extend VM arguments:
String[] extendVMArguments(ILaunchConfiguration config) <- replace String[] getVMArguments(ILaunchConfiguration config);
callin String[] extendVMArguments(ILaunchConfiguration config) throws CoreException
{
String[] args = base.extendVMArguments(config);
if (isOTLaunch(config))
return PDELaunchingAdaptor.extendVMArguments(args, this.mode, this.weavingMode);
else
return PDELaunchingAdaptor.addDisableOTEquinoxArgument(args);
}
// install breakpoints and record launch mode (run/debug):
prepareLaunch <- before launch;
}
/** Unfortunately JUnit launches are slightly different (and not related by inheritance) */
protected class JUnitLauncher extends AbstractLauncher playedBy JUnitLaunchConfigurationDelegate
{
@SuppressWarnings("decapsulation")
getProjectsForProblemSearch -> getProjectsForProblemSearch;
@SuppressWarnings({ "decapsulation", "rawtypes" })
protected ISharedPluginModel getBundle(String id) => get Map fAllBundles
with { result <- (ISharedPluginModel)result.get(id) }
// Extend VM arguments:
String extendVMArgument(ILaunchConfiguration config) <- replace String getVMArguments(ILaunchConfiguration config);
callin String extendVMArgument(ILaunchConfiguration config) throws CoreException
{
String result = base.extendVMArgument(config);
if (isOTLaunch(config))
return PDELaunchingAdaptor.extendVMArguments(result, null /*getBundle(OTEQUINOX_HOOK)*/, this.mode, this.weavingMode);
else
return result+' '+DISABLE_OTEQUINOX;
}
// install breakpoints and record launch mode (run/debug):
prepareLaunch <- before launch;
}
protected class SetOTEquinoxStartlevel playedBy BundleLauncherHelper {
void getMergedBundleMap(Map<IPluginModelBase, String> map, boolean enable)
<- after
Map<IPluginModelBase, String> getMergedBundleMap(ILaunchConfiguration configuration, boolean osgi)
with { map <- result, enable <- isOTLaunch(configuration) }
static void getMergedBundleMap(Map<IPluginModelBase, String> map, boolean enable) {
for (IPluginModelBase plugin: map.keySet())
if (plugin.getPluginBase().getId().equals("org.eclipse.objectteams.otequinox")) {
if (enable)
map.put(plugin, "1:true");
else
map.put(plugin, "default:default");
return;
}
}
}
/**
* UI: This role allows us to insert our "Object Teams Runtime" block just after the JREBlock.
*/
protected class JREBlock playedBy JREBlock {
// build the GUI:
void appendOTOption(Composite parent) <- after void createControl(Composite parent)
base when (PDELaunchingAdaptor.this.currentTab != null); // only within the LauncherTab#launcherTabCFlow() (see below)
void appendOTOption(Composite parent) {
PDELaunchingAdaptor.this.currentTab.createOTRESection(parent);
}
}
/**
* This role manages the UI-part of this team:
* <ul>
* <li>insert a new group after the JREBlock.</li>
* <li>read (initializeFrom) and apply (performApply) the new flag.</li></ul>
*/
@SuppressWarnings("abstractrelevantrole")
protected abstract class LauncherTab extends OTREBlock playedBy AbstractLauncherTab {
LauncherTab(AbstractLauncherTab b) {
// different label than default:
this.enableCheckboxLabel = DebugMessages.OTLaunching_OTEquinox_checkbox_label;
}
// callout interface:
@SuppressWarnings("decapsulation")
Button createCheckButton(Composite parent, String label) -> Button createCheckButton(Composite parent, String label);
@SuppressWarnings("decapsulation")
void setDirty(boolean dirty) -> void setDirty(boolean dirty);
void updateLaunchConfigurationDialog() -> void updateLaunchConfigurationDialog();
// CFlow to let the JREBlock trigger building the GUI:
callin void launcherTabCFlow(Composite parent) {
try {
PDELaunchingAdaptor.this.currentTab = this;
base.launcherTabCFlow(parent);
} finally {
PDELaunchingAdaptor.this.currentTab = null;
}
}
@Override
boolean hasOTJProject(ILaunchConfiguration config) {
return true; // assume we might have an OT project - even without scanning through all projects; always want to enable our options
}
}
protected class MainTab extends LauncherTab playedBy MainTab {
launcherTabCFlow <- replace createControl;
// connect triggers to inherited methods:
void initializeFrom(ILaunchConfiguration config) <- after void initializeFrom(ILaunchConfiguration config)
when (this._otreToggleButton != null); // i.e.: is this the tab containing the JREBlock?
void performApply(ILaunchConfigurationWorkingCopy config)
<- after void performApply(ILaunchConfigurationWorkingCopy config);
}
protected class OSGiSettingsTab extends LauncherTab playedBy OSGiSettingsTab {
launcherTabCFlow <- replace createControl;
// connect triggers to inherited methods:
void initializeFrom(ILaunchConfiguration config) <- after void initializeFrom(ILaunchConfiguration config)
when (this._otreToggleButton != null); // i.e.: is this the tab containing the JREBlock?
void performApply(ILaunchConfigurationWorkingCopy config)
<- after void performApply(ILaunchConfigurationWorkingCopy config);
}
}