Skip to content

Commit 77377b4

Browse files
committed
Prefer explicitly included plugins for Eclipse/OSGi launches
When launching an application create a new Equinox resolver state, where explicitly included plugins/bundles are preferred and compute the requirements closure from that state. This avoids adding other providers of a capability if another provider is already (explicitly) included in a launch, just because the former is wired to the requirement in the target-platform state. Potentially this also makes the set of launched plugins more compact. Fixes #1604
1 parent 6e61a52 commit 77377b4

File tree

2 files changed

+86
-14
lines changed

2 files changed

+86
-14
lines changed

ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/DependencyManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ private static boolean isTestWorkspaceProject(Resource f) {
244244
*
245245
* @return a set of bundle ids
246246
*/
247-
private static Collection<NameVersionDescriptor> getImplicitDependencies() {
247+
public static Collection<NameVersionDescriptor> getImplicitDependencies() {
248248
try {
249249
ITargetPlatformService service = PDECore.getDefault().acquireService(ITargetPlatformService.class);
250250
if (service != null) {

ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/BundleLauncherHelper.java

Lines changed: 85 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
import java.util.ArrayDeque;
2929
import java.util.ArrayList;
3030
import java.util.Arrays;
31+
import java.util.Collection;
3132
import java.util.Collections;
3233
import java.util.Comparator;
34+
import java.util.Dictionary;
3335
import java.util.HashMap;
3436
import java.util.HashSet;
3537
import java.util.LinkedHashMap;
@@ -51,6 +53,8 @@
5153
import org.eclipse.debug.core.ILaunchConfiguration;
5254
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
5355
import org.eclipse.osgi.service.resolver.BundleDescription;
56+
import org.eclipse.osgi.service.resolver.State;
57+
import org.eclipse.osgi.service.resolver.StateObjectFactory;
5458
import org.eclipse.osgi.util.NLS;
5559
import org.eclipse.pde.core.plugin.IMatchRules;
5660
import org.eclipse.pde.core.plugin.IPluginBase;
@@ -59,6 +63,7 @@
5963
import org.eclipse.pde.core.plugin.PluginRegistry;
6064
import org.eclipse.pde.core.target.ITargetDefinition;
6165
import org.eclipse.pde.core.target.ITargetPlatformService;
66+
import org.eclipse.pde.internal.build.BundleHelper;
6267
import org.eclipse.pde.internal.build.IPDEBuildConstants;
6368
import org.eclipse.pde.internal.core.DependencyManager;
6469
import org.eclipse.pde.internal.core.FeatureModelManager;
@@ -75,7 +80,6 @@
7580
import org.eclipse.pde.internal.launching.PDEMessages;
7681
import org.eclipse.pde.launching.IPDELauncherConstants;
7782
import org.osgi.framework.Version;
78-
import org.osgi.resource.Resource;
7983

8084
public class BundleLauncherHelper {
8185

@@ -166,12 +170,7 @@ private static void addRequiredBundles(Map<IPluginModelBase, String> bundle2star
166170
RequirementHelper.addApplicationLaunchRequirements(appRequirements, configuration, bundle2startLevel);
167171

168172
boolean includeOptional = configuration.getAttribute(IPDELauncherConstants.INCLUDE_OPTIONAL, true);
169-
Set<BundleDescription> requiredDependencies = includeOptional //
170-
? DependencyManager.getDependencies(bundle2startLevel.keySet(), DependencyManager.Options.INCLUDE_OPTIONAL_DEPENDENCIES)
171-
: DependencyManager.getDependencies(bundle2startLevel.keySet());
172-
173-
requiredDependencies.stream().map(Resource.class::cast) //
174-
.map(PluginRegistry::findModel).filter(Objects::nonNull) //
173+
computeDependencyClosure0(bundle2startLevel.keySet(), includeOptional, IPDELauncherConstants.LOCATION_WORKSPACE) //
175174
.forEach(p -> addDefaultStartingBundle(bundle2startLevel, p));
176175
}
177176

@@ -227,12 +226,8 @@ private static Map<IPluginModelBase, String> getMergedBundleMapFeatureBased(ILau
227226
List<String> appRequirements = RequirementHelper.getApplicationLaunchRequirements(configuration);
228227
RequirementHelper.addApplicationLaunchRequirements(appRequirements, configuration, launchPlugins, launchPlugins::add);
229228

230-
// Get all required plugins
231-
Set<BundleDescription> additionalBundles = DependencyManager.getDependencies(launchPlugins);
232-
for (BundleDescription bundle : additionalBundles) {
233-
IPluginModelBase plugin = getRequiredPlugin(bundle.getSymbolicName(), bundle.getVersion().toString(), IMatchRules.PERFECT, defaultPluginResolution);
234-
launchPlugins.add(Objects.requireNonNull(plugin));// should never be null
235-
}
229+
computeDependencyClosure0(launchPlugins, false, defaultPluginResolution) //
230+
.map(Objects::requireNonNull).forEach(launchPlugins::add);
236231
}
237232

238233
// Create the start levels for the selected plugins and add them to the map
@@ -546,6 +541,83 @@ private static void addBundleToMap(Map<IPluginModelBase, String> map, IPluginMod
546541
map.put(bundle, startData);
547542
}
548543

544+
// --- dependency resolution ---
545+
546+
private static Stream<IPluginModelBase> computeDependencyClosure0(Set<IPluginModelBase> includedPlugins, boolean includeOptional, String defaultPluginResolution) {
547+
DependencyManager.Options[] options = includeOptional //
548+
? new DependencyManager.Options[] {DependencyManager.Options.INCLUDE_OPTIONAL_DEPENDENCIES}
549+
: new DependencyManager.Options[] {};
550+
if (includedPlugins.isEmpty()) {
551+
return Stream.empty();
552+
}
553+
Set<BundleDescription> initialLaunchBundles = createNewState(includedPlugins, defaultPluginResolution).keySet();
554+
State launchState = initialLaunchBundles.iterator().next().getContainingState();
555+
Set<BundleDescription> bundles = new HashSet<>(initialLaunchBundles);
556+
DependencyManager.getImplicitDependencies().stream().map(descriptor -> {
557+
String versionStr = descriptor.getVersion();
558+
Version version = versionStr != null ? Version.parseVersion(versionStr) : null;
559+
return launchState.getBundle(descriptor.getId(), version);
560+
}).forEach(bundles::add);
561+
562+
Set<BundleDescription> closure = DependencyManager.findRequirementsClosure(bundles, options);
563+
return closure.stream() //
564+
.map(bundle -> {
565+
// Ensure that the preferred plugin-location is respected (therefore not just use PluginRegistry.findModel)
566+
//TODO: incooporate this when creating the new state?!
567+
return getRequiredPlugin(bundle.getSymbolicName(), bundle.getVersion().toString(), IMatchRules.PERFECT, defaultPluginResolution);
568+
}).map(Objects::requireNonNull) //TODO: check if this can ever be null?!
569+
.filter(p -> !includedPlugins.contains(p));
570+
}
571+
572+
private static Map<BundleDescription, IPluginModelBase> createNewState(Collection<IPluginModelBase> initialPlugins, String defaultPluginResolution) {
573+
StateObjectFactory factory = BundleHelper.getPlatformAdmin().getFactory();
574+
IPluginModelBase any = initialPlugins.iterator().next();
575+
// The state is the same as TargetPlatformHelper.getState()
576+
State tpState = any.getBundleDescription().getContainingState();
577+
Set<BundleDescription> initialBundles = new HashSet<>();
578+
State launchState = factory.createState(true);
579+
Map<BundleDescription, IPluginModelBase> launchBundle2plugin = new HashMap<>();
580+
for (IPluginModelBase plugin : initialPlugins) {
581+
BundleDescription bundle = plugin.getBundleDescription();
582+
if (bundle != null) {
583+
if (bundle.getContainingState() != tpState) {
584+
throw new IllegalStateException("Plugins have different TP state"); //$NON-NLS-1$
585+
}
586+
initialBundles.add(bundle);
587+
BundleDescription launchBundle = factory.createBundleDescription(bundle);
588+
if (!launchState.addBundle(launchBundle)) {
589+
throw new IllegalStateException("Failed to add bundle to launch state: " + launchBundle); //$NON-NLS-1$
590+
}
591+
if (launchBundle2plugin.put(launchBundle, plugin) != null) {
592+
throw new IllegalStateException("Duplicated launch bundle for plugin: " + plugin); //$NON-NLS-1$
593+
}
594+
}
595+
}
596+
// Add all other bundles from tpState later to ensure that the initial plugins are preferd.
597+
//TODO: check how this is ensured?! Maybe resolve inbeteen, which is probably incomplete?
598+
BundleDescription[] bundles = tpState.getBundles();
599+
//TODO: or use PluginRegistry.getAllModels(); and check compare against the initialPlugins. But this has different semantic
600+
for (BundleDescription bundle : bundles) {
601+
if (!initialBundles.contains(bundle)) {
602+
BundleDescription launchBundle = factory.createBundleDescription(bundle);
603+
if (!launchState.addBundle(launchBundle)) {
604+
throw new IllegalStateException("Failed to add bundle to launch state: " + launchBundle); //$NON-NLS-1$
605+
}
606+
// IPluginModelBase plugin = getRequiredPlugin(bundle.getSymbolicName(), bundle.getVersion().toString(), IMatchRules.PERFECT, defaultPluginResolution);
607+
// if (launchBundle2plugin.put(launchBundle, plugin) != null) {
608+
// throw new IllegalStateException("Duplicated launch bundle for plugin: " + plugin); //$NON-NLS-1$
609+
// }
610+
}
611+
}
612+
//TODO: ensure this is correct?!
613+
Dictionary<?, ?>[] platformProperties = tpState.getPlatformProperties();
614+
launchState.setPlatformProperties(platformProperties);
615+
launchState.resolve(false);
616+
return launchBundle2plugin;
617+
}
618+
619+
// -- start data ---
620+
549621
public static String getStartData(BundleDescription desc, String defaultStartData) {
550622
String runLevel = resolveSystemRunLevelText(desc);
551623
String auto = resolveSystemAutoText(desc);

0 commit comments

Comments
 (0)