Bug 576860 - Specify all launch-type requirements in RequirementHelper

Change-Id: I65fea9ec059d9f9bb3b3912b390d65947f1dcf40
Signed-off-by: Hannes Wellmann <wellmann.hannes1@gmx.net>
Reviewed-on: https://git.eclipse.org/r/c/pde/eclipse.pde.ui/+/186860
Tested-by: PDE Bot <pde-bot@eclipse.org>
diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/BundleLauncherHelper.java b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/BundleLauncherHelper.java
index ec2c109..35b302b 100644
--- a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/BundleLauncherHelper.java
+++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/BundleLauncherHelper.java
@@ -90,7 +90,7 @@
 		}
 
 		if (wc.getAttribute(IPDELauncherConstants.USE_CUSTOM_FEATURES, false)) {
-			return getMergedBundleMapFeatureBased(wc, osgi);
+			return getMergedBundleMapFeatureBased(wc);
 		}
 
 		return getAllSelectedPluginBundles(wc);
@@ -105,7 +105,7 @@
 
 	// --- feature based launches ---
 
-	private static Map<IPluginModelBase, String> getMergedBundleMapFeatureBased(ILaunchConfiguration configuration, boolean osgi) throws CoreException {
+	private static Map<IPluginModelBase, String> getMergedBundleMapFeatureBased(ILaunchConfiguration configuration) throws CoreException {
 
 		String defaultPluginResolution = configuration.getAttribute(IPDELauncherConstants.FEATURE_PLUGIN_RESOLUTION, IPDELauncherConstants.LOCATION_WORKSPACE);
 		ITargetDefinition target = PDECore.getDefault().acquireService(ITargetPlatformService.class).getWorkspaceTargetDefinition();
@@ -143,13 +143,11 @@
 		launchPlugins.addAll(additionalPlugins.keySet());
 
 		// Get any plug-ins required by the application/product set on the config
-		if (!osgi) {
-			String[] applicationIds = RequirementHelper.getApplicationRequirements(configuration);
-			for (String applicationId : applicationIds) {
-				IPluginModelBase plugin = getRequiredPlugin(applicationId, null, IMatchRules.NONE, defaultPluginResolution);
-				if (plugin != null) {
-					launchPlugins.add(plugin);
-				}
+		List<String> applicationIds = RequirementHelper.getApplicationLaunchRequirements(configuration);
+		for (String applicationId : applicationIds) {
+			IPluginModelBase plugin = getLatestPlugin(applicationId, defaultPluginResolution);
+			if (plugin != null) {
+				launchPlugins.add(plugin);
 			}
 		}
 		// Get all required plugins
@@ -270,6 +268,10 @@
 		return getRequired(plugins, ENABLED_VALID_PLUGIN_FILTER, GET_PLUGIN_VERSION, COMPARE_PLUGIN_RESOLVED, version, versionMatchRule);
 	}
 
+	static IPluginModelBase getLatestPlugin(String id, String pluginLocation) {
+		return getRequiredPlugin(id, null, IMatchRules.NONE, pluginLocation);
+	}
+
 	private static List<List<IPluginModelBase>> getPlugins(String id, String pluginLocation) {
 		ModelEntry entry = PluginRegistry.findEntry(id);
 		if (entry == null) {
@@ -460,6 +462,10 @@
 		map.put(bundle, startData);
 	}
 
+	public static void addDefaultStartingBundle(Map<IPluginModelBase, String> map, IPluginModelBase bundle) {
+		addBundleToMap(map, bundle, DEFAULT_START_LEVELS);
+	}
+
 	private static final Map<String, String> AUTO_STARTED_BUNDLE_LEVELS = Map.ofEntries( //
 			Map.entry(IPDEBuildConstants.BUNDLE_DS, "1"), //$NON-NLS-1$
 			Map.entry(IPDEBuildConstants.BUNDLE_SIMPLE_CONFIGURATOR, "1"), //$NON-NLS-1$
diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/EclipsePluginValidationOperation.java b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/EclipsePluginValidationOperation.java
index 9e3be67..fc52eb4 100644
--- a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/EclipsePluginValidationOperation.java
+++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/EclipsePluginValidationOperation.java
@@ -42,7 +42,7 @@
 
 	private void validateExtensions() {
 		try {
-			String[] required = RequirementHelper.getApplicationRequirements(fLaunchConfiguration);
+			List<String> required = RequirementHelper.getApplicationLaunchRequirements(fLaunchConfiguration);
 			for (String element : required) {
 				BundleDescription bundle = getState().getBundle(element, null);
 				if (bundle == null) {
diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/RequirementHelper.java b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/RequirementHelper.java
index 455a805..26d9ad7 100644
--- a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/RequirementHelper.java
+++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/RequirementHelper.java
@@ -15,12 +15,14 @@
 package org.eclipse.pde.internal.launching.launcher;
 
 import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Stream;
 import org.eclipse.core.runtime.*;
-import org.eclipse.debug.core.ILaunchConfiguration;
-import org.eclipse.pde.core.plugin.TargetPlatform;
+import org.eclipse.debug.core.*;
+import org.eclipse.pde.core.plugin.*;
 import org.eclipse.pde.internal.core.PDECore;
 import org.eclipse.pde.internal.core.PDEExtensionRegistry;
-import org.eclipse.pde.internal.launching.IPDEConstants;
 import org.eclipse.pde.launching.IPDELauncherConstants;
 
 /**
@@ -31,6 +33,34 @@
  */
 public class RequirementHelper {
 
+	private RequirementHelper() {
+	} // static use only
+
+	@FunctionalInterface // like java.util.function.Function but can throw CoreException
+	public static interface ILaunchRequirementsFunction {
+		List<String> getRequiredBundleIds(ILaunchConfiguration lc) throws CoreException;
+	}
+
+	private static final ConcurrentMap<String, ILaunchRequirementsFunction> APPLICATION_REQUIREMENTS = new ConcurrentHashMap<>();
+
+	public static void registerLaunchTypeRequirements(String launchTypeId, ILaunchRequirementsFunction requirementsFunction) {
+		APPLICATION_REQUIREMENTS.put(launchTypeId, Objects.requireNonNull(requirementsFunction));
+	}
+
+	public static void registerSameRequirementsAsFor(String launchTypeId, String sourceLaunchTypeId) {
+		// enforce  initialization of source class to register required plug-ins
+		ILaunchConfigurationType type = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurationType(sourceLaunchTypeId);
+		for (Set<String> modes : type.getSupportedModeCombinations()) {
+			try {
+				for (ILaunchDelegate delegate : type.getDelegates(modes)) {
+					delegate.getDelegate();
+				}
+			} catch (CoreException e) {
+			}
+		}
+		APPLICATION_REQUIREMENTS.put(launchTypeId, APPLICATION_REQUIREMENTS.get(sourceLaunchTypeId));
+	}
+
 	/**
 	 * Returns a list of string plug-in ids that are required to launch the product, application
 	 * or application to test that the given launch configuration specifies.  Which attributes are
@@ -41,67 +71,69 @@
 	 * @return list of string plug-in IDs that are required by the config's application/product settings
 	 * @throws CoreException if there is a problem reading the launch config
 	 */
-	public static String[] getApplicationRequirements(ILaunchConfiguration config) throws CoreException {
-		Set<String> requiredIds = new HashSet<>();
-		if (config.getAttribute(IPDELauncherConstants.USE_PRODUCT, false)) {
-			String product = config.getAttribute(IPDELauncherConstants.PRODUCT, (String) null);
-			if (product != null) {
-				getProductRequirements(product, requiredIds);
-			}
-		} else {
-			String configType = config.getType().getIdentifier();
-			if (configType.equals(IPDELauncherConstants.ECLIPSE_APPLICATION_LAUNCH_CONFIGURATION_TYPE)) {
-				String application = config.getAttribute(IPDELauncherConstants.APPLICATION, TargetPlatform.getDefaultApplication());
-				if (!IPDEConstants.CORE_TEST_APPLICATION.equals(application)) {
-					getApplicationRequirements(application, requiredIds);
-				}
-			} else {
-				// Junit launch configs can have the core test application set in either the 'app to test' or the 'application' attribute
-				String application = config.getAttribute(IPDELauncherConstants.APP_TO_TEST, (String) null);
-				if (application == null) {
-					application = config.getAttribute(IPDELauncherConstants.APPLICATION, (String) null);
-				}
-				if (application == null) {
-					application = TargetPlatform.getDefaultApplication();
-				}
-				if (!IPDEConstants.CORE_TEST_APPLICATION.equals(application)) {
-					getApplicationRequirements(application, requiredIds);
-				}
-			}
-		}
-		return requiredIds.toArray(new String[requiredIds.size()]);
+	public static List<String> getApplicationLaunchRequirements(ILaunchConfiguration config) throws CoreException {
+		ILaunchRequirementsFunction requirementsFunction = APPLICATION_REQUIREMENTS.get(config.getType().getIdentifier());
+		return requirementsFunction != null ? Objects.requireNonNull(requirementsFunction.getRequiredBundleIds(config)) : Collections.emptyList();
 	}
 
-	private static void getProductRequirements(String product, Collection<String> requiredIds) {
+	public static boolean addApplicationLaunchRequirements(Map<IPluginModelBase, String> bundle2startLevel, ILaunchConfiguration configuration) throws CoreException {
+		boolean isFeatureBasedLaunch = configuration.getAttribute(IPDELauncherConstants.USE_CUSTOM_FEATURES, false);
+		String pluginResolution = isFeatureBasedLaunch ? configuration.getAttribute(IPDELauncherConstants.FEATURE_PLUGIN_RESOLUTION, IPDELauncherConstants.LOCATION_WORKSPACE) : IPDELauncherConstants.LOCATION_WORKSPACE;
+
+		List<String> appRequirements = getApplicationLaunchRequirements(configuration);
+		boolean missingRequirements = false;
+		for (String requiredBundleId : appRequirements) {
+			ModelEntry entry = PluginRegistry.findEntry(requiredBundleId);
+			if (entry != null) {
+				// add required plug-in if not yet already included
+				var allPluginsWithId = Stream.of(entry.getWorkspaceModels(), entry.getExternalModels()).flatMap(Arrays::stream);
+				if (allPluginsWithId.noneMatch(bundle2startLevel::containsKey)) {
+					IPluginModelBase plugin = BundleLauncherHelper.getLatestPlugin(requiredBundleId, pluginResolution);
+					BundleLauncherHelper.addDefaultStartingBundle(bundle2startLevel, plugin);
+				}
+			} else {
+				missingRequirements = true;
+			}
+		}
+		return missingRequirements;
+	}
+
+	public static List<String> getProductRequirements(ILaunchConfiguration config) throws CoreException {
+		String product = config.getAttribute(IPDELauncherConstants.PRODUCT, (String) null);
+		if (product == null) {
+			return Collections.emptyList();
+		}
 		PDEExtensionRegistry registry = PDECore.getDefault().getExtensionsRegistry();
 		IExtension[] extensions = registry.findExtensions("org.eclipse.core.runtime.products", true); //$NON-NLS-1$
 		for (IExtension extension : extensions) {
 
 			if (product.equals(extension.getUniqueIdentifier()) || product.equals(extension.getSimpleIdentifier())) {
+				Set<String> requiredIds = new LinkedHashSet<>();
 				requiredIds.add(extension.getContributor().getName());
 
 				IConfigurationElement[] elements = extension.getConfigurationElements();
 				for (IConfigurationElement element : elements) {
-					String application = element.getAttribute("application"); //$NON-NLS-1$
-					if (application != null && application.length() > 0) {
-						getApplicationRequirements(application, requiredIds);
+					String application = element.getAttribute(IPDELauncherConstants.APPLICATION);
+					if (application != null && !application.isEmpty()) {
+						requiredIds.addAll(getApplicationRequirements(application));
 					}
 				}
-				// Only one extension should match the product so break out of the looop
-				break;
+				// Only one extension should match the product so break out of the loop
+				return List.copyOf(requiredIds);
 			}
 		}
+		return Collections.emptyList();
 	}
 
-	private static void getApplicationRequirements(String application, Collection<String> requiredIds) {
+	public static List<String> getApplicationRequirements(String application) {
 		PDEExtensionRegistry registry = PDECore.getDefault().getExtensionsRegistry();
 		IExtension[] extensions = registry.findExtensions("org.eclipse.core.runtime.applications", true); //$NON-NLS-1$
 		for (IExtension extension : extensions) {
 			if (application.equals(extension.getUniqueIdentifier()) || application.equals(extension.getSimpleIdentifier())) {
-				requiredIds.add(extension.getContributor().getName());
-				// Only one extension should match the application so break out of the looop
-				break;
+				return List.of(extension.getContributor().getName());
+				// Only one extension should match the application so break out of the loop
 			}
 		}
+		return Collections.emptyList();
 	}
 }
diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/EclipseApplicationLaunchConfiguration.java b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/EclipseApplicationLaunchConfiguration.java
index a886741..bed9977 100644
--- a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/EclipseApplicationLaunchConfiguration.java
+++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/EclipseApplicationLaunchConfiguration.java
@@ -28,6 +28,7 @@
 import org.eclipse.pde.internal.core.ClasspathHelper;
 import org.eclipse.pde.internal.core.TargetPlatformHelper;
 import org.eclipse.pde.internal.core.util.CoreUtility;
+import org.eclipse.pde.internal.launching.IPDEConstants;
 import org.eclipse.pde.internal.launching.launcher.*;
 
 /**
@@ -43,6 +44,19 @@
  */
 public class EclipseApplicationLaunchConfiguration extends AbstractPDELaunchConfiguration {
 
+	static {
+		RequirementHelper.registerLaunchTypeRequirements(IPDELauncherConstants.ECLIPSE_APPLICATION_LAUNCH_CONFIGURATION_TYPE, lc -> {
+			if (lc.getAttribute(IPDELauncherConstants.USE_PRODUCT, false)) {
+				return RequirementHelper.getProductRequirements(lc);
+			}
+			String application = lc.getAttribute(IPDELauncherConstants.APPLICATION, TargetPlatform.getDefaultApplication());
+			if (!IPDEConstants.CORE_TEST_APPLICATION.equals(application)) {
+				return RequirementHelper.getApplicationRequirements(application);
+			}
+			return Collections.emptyList();
+		});
+	}
+
 	// used to generate the dev classpath entries
 	// key is bundle ID, value is a List of models
 	private Map<String, List<IPluginModelBase>> fAllBundles;
diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/EquinoxLaunchConfiguration.java b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/EquinoxLaunchConfiguration.java
index a6b6032..3bc8b19 100644
--- a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/EquinoxLaunchConfiguration.java
+++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/EquinoxLaunchConfiguration.java
@@ -27,8 +27,7 @@
 import org.eclipse.pde.internal.core.ClasspathHelper;
 import org.eclipse.pde.internal.core.P2Utils;
 import org.eclipse.pde.internal.core.util.CoreUtility;
-import org.eclipse.pde.internal.launching.IPDEConstants;
-import org.eclipse.pde.internal.launching.PDEMessages;
+import org.eclipse.pde.internal.launching.*;
 import org.eclipse.pde.internal.launching.launcher.*;
 
 /**
@@ -44,6 +43,17 @@
  */
 public class EquinoxLaunchConfiguration extends AbstractPDELaunchConfiguration {
 
+	static {
+		RequirementHelper.registerLaunchTypeRequirements(IPDELauncherConstants.OSGI_CONFIGURATION_TYPE, lc -> {
+			OSGiFrameworkManager framworkManager = PDELaunchingPlugin.getDefault().getOSGiFrameworkManager();
+			String id = lc.getAttribute(IPDELauncherConstants.OSGI_FRAMEWORK_ID, framworkManager.getDefaultFramework());
+			if ("org.eclipse.pde.ui.EquinoxFramework".equals(id)) { //$NON-NLS-1$
+				return List.of(IPDEBuildConstants.BUNDLE_OSGI);
+			}
+			return Collections.emptyList();
+		});
+	}
+
 	// used to generate the dev classpath entries
 	// key is bundle ID, value is a List of models
 	protected Map<String, List<IPluginModelBase>> fAllBundles;
@@ -153,19 +163,12 @@
 	@Override
 	protected void preLaunchCheck(ILaunchConfiguration configuration, ILaunch launch, IProgressMonitor monitor) throws CoreException {
 		fModels = BundleLauncherHelper.getMergedBundleMap(configuration, true);
+
+		if (!RequirementHelper.addApplicationLaunchRequirements(fModels, configuration)) {
+			throw new CoreException(Status.error(PDEMessages.EquinoxLaunchConfiguration_oldTarget));
+		}
 		fAllBundles = fModels.keySet().stream().collect(Collectors.groupingBy(m -> m.getPluginBase().getId(), HashMap::new, Collectors.toCollection(ArrayList::new)));
 
-		if (!fAllBundles.containsKey(IPDEBuildConstants.BUNDLE_OSGI)) {
-			// implicitly add it
-			IPluginModelBase model = PluginRegistry.findModel(IPDEBuildConstants.BUNDLE_OSGI);
-			if (model != null) {
-				fModels.put(model, "default:default"); //$NON-NLS-1$
-				fAllBundles.computeIfAbsent(model.getPluginBase().getId(), i -> new ArrayList<>()).add(model);
-			} else {
-				String message = PDEMessages.EquinoxLaunchConfiguration_oldTarget;
-				throw new CoreException(Status.error(message));
-			}
-		}
 		super.preLaunchCheck(configuration, launch, monitor);
 	}
 
diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/JUnitLaunchConfigurationDelegate.java b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/JUnitLaunchConfigurationDelegate.java
index a017aad..ca775d4 100644
--- a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/JUnitLaunchConfigurationDelegate.java
+++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/JUnitLaunchConfigurationDelegate.java
@@ -46,6 +46,23 @@
  */
 public class JUnitLaunchConfigurationDelegate extends org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate {
 
+	static {
+		RequirementHelper.registerLaunchTypeRequirements("org.eclipse.pde.ui.JunitLaunchConfig", lc -> { //$NON-NLS-1$
+			// Junit launch configs can have the core test application set in either the 'app to test' or the 'application' attribute
+			String application = lc.getAttribute(IPDELauncherConstants.APP_TO_TEST, (String) null);
+			if (application == null) {
+				application = lc.getAttribute(IPDELauncherConstants.APPLICATION, (String) null);
+			}
+			if (application == null) {
+				application = TargetPlatform.getDefaultApplication();
+			}
+			if (!IPDEConstants.CORE_TEST_APPLICATION.equals(application)) {
+				return RequirementHelper.getApplicationRequirements(application);
+			}
+			return Collections.emptyList();
+		});
+	}
+
 	/**
 	 * To avoid duplicating variable substitution (and duplicate prompts)
 	 * this variable will store the substituted workspace location.
diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/launcher/PluginBlock.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/launcher/PluginBlock.java
index f501a43..5699252 100644
--- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/launcher/PluginBlock.java
+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/launcher/PluginBlock.java
@@ -165,10 +165,11 @@
 	protected void addRequiredPlugins() {
 		// Check that the application or product we are launching has its requirements included
 		try {
-			String[] requiredIds = RequirementHelper.getApplicationRequirements(fLaunchConfig);
+			List<String> requiredIds = RequirementHelper.getApplicationLaunchRequirements(fLaunchConfig);
 			ITestKind testKind = JUnitLaunchConfigurationConstants.getTestRunnerKind(fLaunchConfig);
-			if (TestKindRegistry.JUNIT4_TEST_KIND_ID.equals(testKind.getId()) || TestKindRegistry.JUNIT5_TEST_KIND_ID.equals(testKind.getId())) {
-				requiredIds = addJunitPlugins(requiredIds, testKind);
+			if (TestKindRegistry.JUNIT4_TEST_KIND_ID.equals(testKind.getId())
+					|| TestKindRegistry.JUNIT5_TEST_KIND_ID.equals(testKind.getId())) {
+				requiredIds = Arrays.asList(addJunitPlugins(requiredIds.toArray(String[]::new), testKind));
 			}
 
 			for (String requiredId : requiredIds) {
diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/ui/launcher/EclipseApplicationLaunchConfiguration.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/ui/launcher/EclipseApplicationLaunchConfiguration.java
index 9b2fa97..9ef4f8e 100644
--- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/ui/launcher/EclipseApplicationLaunchConfiguration.java
+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/ui/launcher/EclipseApplicationLaunchConfiguration.java
@@ -27,6 +27,7 @@
 import org.eclipse.pde.internal.core.util.VersionUtil;
 import org.eclipse.pde.internal.launching.PDEMessages;
 import org.eclipse.pde.internal.launching.launcher.*;
+import org.eclipse.pde.launching.IPDELauncherConstants;
 import org.osgi.framework.Version;
 
 /**
@@ -45,6 +46,11 @@
 @Deprecated
 public class EclipseApplicationLaunchConfiguration extends AbstractPDELaunchConfiguration {
 
+	static {
+		RequirementHelper.registerSameRequirementsAsFor("org.eclipse.pde.ui.RuntimeWorkbench", //$NON-NLS-1$
+				IPDELauncherConstants.ECLIPSE_APPLICATION_LAUNCH_CONFIGURATION_TYPE);
+	}
+
 	// used to generate the dev classpath entries
 	// key is bundle ID, value is a model
 	private Map<String, IPluginModelBase> fAllBundles;
@@ -60,7 +66,7 @@
 
 	@Override
 	public String[] getProgramArguments(ILaunchConfiguration configuration) throws CoreException {
-		throw new CoreException(Status.error((String) PDEMessages.PDE_updateManagerNotSupported));
+		throw new CoreException(Status.error(PDEMessages.PDE_updateManagerNotSupported));
 	}
 
 	@Override
diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/ui/launcher/EquinoxLaunchConfiguration.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/ui/launcher/EquinoxLaunchConfiguration.java
index f9da72b..b896e33 100644
--- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/ui/launcher/EquinoxLaunchConfiguration.java
+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/ui/launcher/EquinoxLaunchConfiguration.java
@@ -30,6 +30,7 @@
 import org.eclipse.pde.internal.launching.IPDEConstants;
 import org.eclipse.pde.internal.launching.PDEMessages;
 import org.eclipse.pde.internal.launching.launcher.*;
+import org.eclipse.pde.launching.IPDELauncherConstants;
 
 /**
  * A launch delegate for launching the Equinox framework
@@ -47,6 +48,11 @@
 @Deprecated
 public class EquinoxLaunchConfiguration extends AbstractPDELaunchConfiguration {
 
+	static {
+		RequirementHelper.registerSameRequirementsAsFor("org.eclipse.pde.ui.EquinoxLauncher", //$NON-NLS-1$
+				IPDELauncherConstants.OSGI_CONFIGURATION_TYPE);
+	}
+
 	// used to generate the dev classpath entries
 	// key is bundle ID, value is a List of models
 	protected Map<String, List<IPluginModelBase>> fAllBundles;
@@ -155,20 +161,13 @@
 	@Override
 	protected void preLaunchCheck(ILaunchConfiguration configuration, ILaunch launch, IProgressMonitor monitor) throws CoreException {
 		fModels = BundleLauncherHelper.getMergedBundleMap(configuration, true);
+
+		if (!RequirementHelper.addApplicationLaunchRequirements(fModels, configuration)) {
+			throw new CoreException(Status.error(PDEMessages.EquinoxLaunchConfiguration_oldTarget));
+		}
 		fAllBundles = fModels.keySet().stream().collect(Collectors.groupingBy(m -> m.getPluginBase().getId(),
 				HashMap::new, Collectors.toCollection(ArrayList::new)));
 
-		if (!fAllBundles.containsKey(IPDEBuildConstants.BUNDLE_OSGI)) {
-			// implicitly add it
-			IPluginModelBase model = PluginRegistry.findModel(IPDEBuildConstants.BUNDLE_OSGI);
-			if (model != null) {
-				fModels.put(model, "default:default"); //$NON-NLS-1$
-				fAllBundles.computeIfAbsent(IPDEBuildConstants.BUNDLE_OSGI, i -> new ArrayList<>()).add(model);
-			} else {
-				String message = PDEMessages.EquinoxLaunchConfiguration_oldTarget;
-				throw new CoreException(Status.error((String) message));
-			}
-		}
 		super.preLaunchCheck(configuration, launch, monitor);
 	}
 
diff --git a/ui/org.eclipse.pde.unittest.junit/src/org/eclipse/pde/unittest/junit/launcher/JUnitPluginLaunchConfigurationDelegate.java b/ui/org.eclipse.pde.unittest.junit/src/org/eclipse/pde/unittest/junit/launcher/JUnitPluginLaunchConfigurationDelegate.java
index bb8ed51..30a17ea 100644
--- a/ui/org.eclipse.pde.unittest.junit/src/org/eclipse/pde/unittest/junit/launcher/JUnitPluginLaunchConfigurationDelegate.java
+++ b/ui/org.eclipse.pde.unittest.junit/src/org/eclipse/pde/unittest/junit/launcher/JUnitPluginLaunchConfigurationDelegate.java
@@ -52,6 +52,7 @@
 import org.eclipse.pde.internal.launching.launcher.LaunchConfigurationHelper;
 import org.eclipse.pde.internal.launching.launcher.LaunchPluginValidator;
 import org.eclipse.pde.internal.launching.launcher.LauncherUtils;
+import org.eclipse.pde.internal.launching.launcher.RequirementHelper;
 import org.eclipse.pde.internal.launching.launcher.VMHelper;
 import org.eclipse.pde.launching.IPDELauncherConstants;
 import org.eclipse.pde.launching.PDESourcePathProvider;
@@ -118,6 +119,11 @@
  */
 public class JUnitPluginLaunchConfigurationDelegate extends AbstractJavaLaunchConfigurationDelegate {
 
+	static {
+		RequirementHelper.registerSameRequirementsAsFor("org.eclipse.pde.unittest.junit.launchConfiguration", //$NON-NLS-1$
+				"org.eclipse.pde.ui.JunitLaunchConfig"); //$NON-NLS-1$
+	}
+
 	// This needs to be differnet from JunitLaunchConfigurationConstants.ATTR_PORT
 	// or the "legacy" view handles it first
 	public static final String ATTR_PORT = JUnitPluginTestPlugin.PLUGIN_ID + ".PORT"; //$NON-NLS-1$