Skip to content

Commit a50ea59

Browse files
committed
Filter bundles with preference for the target state
Currently it can happen that one bundle (in this particular case org.eclipse.pde.junit.runtime) is not part of the target platform and therefore selected from the running host. This then pull in even more from the host through the DependencyManager collection leading to duplicates all over the place. As long as target and host are equal enough it does not cause any problem as id+version are the same and so are canceled out but if not this leads to multiple version unnecessary included and this is especially a problem for singletons. This now: - collects all items as before - put everything into a new state and performs a resolve operation - during the resolve make sure we filter out providers whenever there is an alternative from the target platform. Fix #2082
1 parent 668886e commit a50ea59

File tree

4 files changed

+120
-28
lines changed

4 files changed

+120
-28
lines changed

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
import org.eclipse.pde.core.project.IBundleProjectService;
4848
import org.eclipse.pde.core.target.ITargetDefinition;
4949
import org.eclipse.pde.core.target.ITargetPlatformService;
50-
import org.eclipse.pde.internal.build.IPDEBuildConstants;
5150
import org.eclipse.pde.internal.core.bnd.BndResourceChangeListener;
5251
import org.eclipse.pde.internal.core.bnd.BndWorkspaceServiceFactory;
5352
import org.eclipse.pde.internal.core.builders.FeatureRebuilder;
@@ -245,11 +244,6 @@ public PDECore() {
245244
* {@link #VERSION} comparator.
246245
*/
247246
public Stream<IPluginModelBase> findPluginsInHost(String id) {
248-
if (IPDEBuildConstants.BUNDLE_OSGI.equals(id)) {
249-
// Do not expose the OSGi framework from the host see
250-
// https://github.com/eclipse-pde/eclipse.pde/issues/2082
251-
return Stream.empty();
252-
}
253247
Map<String, List<IPluginModelBase>> hostPlugins = getHostPlugins();
254248
if (hostPlugins == null) {
255249
return null;

ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/JUnitLaunchRequirements.java

Lines changed: 109 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,40 +14,127 @@
1414
package org.eclipse.pde.internal.launching;
1515

1616
import java.util.ArrayList;
17+
import java.util.Arrays;
1718
import java.util.Collection;
19+
import java.util.IdentityHashMap;
1820
import java.util.LinkedHashSet;
1921
import java.util.List;
2022
import java.util.Map;
23+
import java.util.Objects;
2124
import java.util.Optional;
2225
import java.util.Set;
2326
import java.util.function.Function;
27+
import java.util.stream.Collectors;
2428
import java.util.stream.Stream;
2529

2630
import org.eclipse.core.runtime.CoreException;
2731
import org.eclipse.core.runtime.Status;
2832
import org.eclipse.debug.core.ILaunchConfiguration;
2933
import org.eclipse.osgi.service.resolver.BundleDescription;
34+
import org.eclipse.osgi.service.resolver.ResolverError;
35+
import org.eclipse.osgi.service.resolver.State;
36+
import org.eclipse.osgi.service.resolver.StateObjectFactory;
3037
import org.eclipse.osgi.util.NLS;
3138
import org.eclipse.pde.core.plugin.IPluginModelBase;
3239
import org.eclipse.pde.core.plugin.PluginRegistry;
40+
import org.eclipse.pde.internal.build.BundleHelper;
3341
import org.eclipse.pde.internal.core.DependencyManager;
3442
import org.eclipse.pde.internal.core.PDECore;
3543
import org.eclipse.pde.internal.launching.launcher.BundleLauncherHelper;
44+
import org.eclipse.pde.internal.launching.launcher.LaunchValidationOperation;
45+
import org.osgi.framework.hooks.resolver.ResolverHook;
46+
import org.osgi.framework.hooks.resolver.ResolverHookFactory;
47+
import org.osgi.framework.wiring.BundleCapability;
48+
import org.osgi.framework.wiring.BundleRequirement;
3649
import org.osgi.framework.wiring.BundleRevision;
3750

3851
public class JUnitLaunchRequirements {
3952

53+
private static final StateObjectFactory FACTORY = BundleHelper.getPlatformAdmin().getFactory();
4054
private static final String PDE_JUNIT_RUNTIME = "org.eclipse.pde.junit.runtime"; //$NON-NLS-1$
4155
private static final String JUNIT4_JDT_RUNTIME_PLUGIN = "org.eclipse.jdt.junit4.runtime"; //$NON-NLS-1$
4256
private static final String JUNIT5_JDT_RUNTIME_PLUGIN = "org.eclipse.jdt.junit5.runtime"; //$NON-NLS-1$
4357

4458
public static void addRequiredJunitRuntimePlugins(ILaunchConfiguration configuration, Map<String, List<IPluginModelBase>> collectedModels, Map<IPluginModelBase, String> startLevelMap) throws CoreException {
45-
Collection<String> runtimePlugins = getRequiredJunitRuntimeEclipsePlugins(configuration);
46-
Set<BundleDescription> addedRuntimeBundles = addAbsentRequirements(runtimePlugins, collectedModels, startLevelMap);
47-
Set<BundleDescription> runtimeRequirements = DependencyManager.findRequirementsClosure(addedRuntimeBundles);
59+
Collection<IPluginModelBase> runtimeBundles = getEclipseJunitRuntimePlugins(configuration, collectedModels, startLevelMap);
60+
List<BundleDescription> roots = runtimeBundles.stream().map(p -> p.getBundleDescription()).filter(Objects::nonNull).toList();
61+
Set<BundleDescription> closure = DependencyManager.findRequirementsClosure(roots);
62+
Collection<BundleDescription> runtimeRequirements = filterRequirementsByState(closure, runtimeBundles, configuration);
4863
addAbsentRequirements(runtimeRequirements, collectedModels, startLevelMap);
4964
}
5065

66+
private static Collection<BundleDescription> filterRequirementsByState(Collection<BundleDescription> bundles, Collection<IPluginModelBase> rootBundles, ILaunchConfiguration configuration) throws CoreException {
67+
//lookup that maps a copy to the original description from the bundles parameter
68+
Map<BundleRevision, BundleDescription> descriptionnMap = new IdentityHashMap<>();
69+
Set<BundleRevision> rootSet = rootBundles.stream().map(p -> p.getBundleDescription()).filter(Objects::nonNull).collect(Collectors.toSet());
70+
State state = FACTORY.createState(true);
71+
State targetState = PDECore.getDefault().getModelManager().getState().getState();
72+
List<BundleDescription> resolveRoots = new ArrayList<>();
73+
long id = 1;
74+
for (BundleDescription bundle : bundles) {
75+
BundleDescription copy = FACTORY.createBundleDescription(id++, bundle);
76+
descriptionnMap.put(copy, bundle);
77+
state.addBundle(copy);
78+
if (rootSet.contains(bundle)) {
79+
resolveRoots.add(copy);
80+
}
81+
}
82+
state.setPlatformProperties(LaunchValidationOperation.getPlatformProperties(LaunchValidationOperation.getMatchingEnvironments(configuration, rootBundles)));
83+
state.setResolverHookFactory(new ResolverHookFactory() {
84+
85+
@Override
86+
public ResolverHook begin(Collection<BundleRevision> triggers) {
87+
return new ResolverHook() {
88+
89+
@Override
90+
public void filterSingletonCollisions(BundleCapability singleton, Collection<BundleCapability> collisionCandidates) {
91+
}
92+
93+
@Override
94+
public void filterResolvable(Collection<BundleRevision> candidates) {
95+
}
96+
97+
@Override
98+
public void filterMatches(BundleRequirement requirement, Collection<BundleCapability> candidates) {
99+
List<BundleCapability> list = candidates.stream().filter(cp -> isFromDifferentState(cp)).toList();
100+
if (list.isEmpty()) {
101+
//nothing to do here...
102+
return;
103+
}
104+
//iterate in reverse order so we remove lower ranked candidates first ...
105+
for (int i = list.size() - 1; i >= 0 && candidates.size() > 1; i--) {
106+
BundleCapability capability = list.get(i);
107+
candidates.remove(capability);
108+
}
109+
}
110+
111+
private boolean isFromDifferentState(BundleCapability capability) {
112+
BundleRevision resource = capability.getResource();
113+
BundleDescription original = descriptionnMap.get(resource);
114+
if (original != null) {
115+
return original.getContainingState() != targetState;
116+
}
117+
return false;
118+
}
119+
120+
@Override
121+
public void end() {
122+
}
123+
};
124+
}
125+
});
126+
state.resolve(false);
127+
for (BundleDescription rootBundle : resolveRoots) {
128+
ResolverError[] errors = state.getResolverErrors(rootBundle);
129+
if (errors.length > 0) {
130+
throw new CoreException(Status.error(String.format("%s can not be resolved: %s", rootBundle, Arrays.toString(errors)))); //$NON-NLS-1$
131+
}
132+
}
133+
Collection<BundleDescription> closure = DependencyManager.findRequirementsClosure(resolveRoots);
134+
// map back to the originals!
135+
return closure.stream().map(bd -> descriptionnMap.get(bd)).filter(Objects::nonNull).toList();
136+
}
137+
51138
@SuppressWarnings("restriction")
52139
public static Collection<String> getRequiredJunitRuntimeEclipsePlugins(ILaunchConfiguration configuration) {
53140
org.eclipse.jdt.internal.junit.launcher.ITestKind testKind = org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants.getTestRunnerKind(configuration);
@@ -59,7 +146,7 @@ public static Collection<String> getRequiredJunitRuntimeEclipsePlugins(ILaunchCo
59146
return List.of(PDE_JUNIT_RUNTIME);
60147
} // Nothing to add for JUnit-3
61148
case org.eclipse.jdt.internal.junit.launcher.TestKindRegistry.JUNIT4_TEST_KIND_ID -> {
62-
return List.of(PDE_JUNIT_RUNTIME,JUNIT4_JDT_RUNTIME_PLUGIN);
149+
return List.of(PDE_JUNIT_RUNTIME, JUNIT4_JDT_RUNTIME_PLUGIN);
63150
}
64151
case org.eclipse.jdt.internal.junit.launcher.TestKindRegistry.JUNIT5_TEST_KIND_ID -> {
65152
return List.of(PDE_JUNIT_RUNTIME, JUNIT5_JDT_RUNTIME_PLUGIN);
@@ -68,21 +155,27 @@ public static Collection<String> getRequiredJunitRuntimeEclipsePlugins(ILaunchCo
68155
}
69156
}
70157

71-
private static Set<BundleDescription> addAbsentRequirements(Collection<String> requirements, Map<String, List<IPluginModelBase>> collectedModels, Map<IPluginModelBase, String> startLevelMap) throws CoreException {
72-
Set<BundleDescription> addedRequirements = new LinkedHashSet<>();
73-
for (String id : requirements) {
74-
List<IPluginModelBase> models = collectedModels.computeIfAbsent(id, k -> new ArrayList<>());
75-
if (models.stream().noneMatch(p -> p.getBundleDescription().isResolved())) {
76-
IPluginModelBase model = findRequiredPluginInTargetOrHost(PluginRegistry.findModel(id), plugins -> plugins.max(PDECore.VERSION), id);
77-
models.add(model);
78-
BundleLauncherHelper.addDefaultStartingBundle(startLevelMap, model);
79-
addedRequirements.add(model.getBundleDescription());
80-
}
158+
private static Collection<IPluginModelBase> getEclipseJunitRuntimePlugins(ILaunchConfiguration configuration, Map<String, List<IPluginModelBase>> collectedModels, Map<IPluginModelBase, String> startLevelMap) throws CoreException {
159+
Set<IPluginModelBase> descriptions = new LinkedHashSet<>();
160+
for (String id : getRequiredJunitRuntimeEclipsePlugins(configuration)) {
161+
addIfAbsent(id, collectedModels, startLevelMap).ifPresent(descriptions::add);
81162
}
82-
return addedRequirements;
163+
return descriptions;
164+
}
165+
166+
private static Optional<IPluginModelBase> addIfAbsent(String id, Map<String, List<IPluginModelBase>> collectedModels, Map<IPluginModelBase, String> startLevelMap) throws CoreException {
167+
List<IPluginModelBase> models = collectedModels.computeIfAbsent(id, k -> new ArrayList<>());
168+
if (models.stream().noneMatch(m -> m.getBundleDescription().isResolved())) {
169+
IPluginModelBase model = findRequiredPluginInTargetOrHost(PluginRegistry.findModel(id), plugins -> plugins.max(PDECore.VERSION), id);
170+
models.add(model);
171+
BundleLauncherHelper.addDefaultStartingBundle(startLevelMap, model);
172+
return Optional.of(model);
173+
}
174+
175+
return models.stream().filter(m -> m.getBundleDescription().isResolved()).findFirst();
83176
}
84177

85-
private static void addAbsentRequirements(Set<BundleDescription> requirements, Map<String, List<IPluginModelBase>> collectedModels, Map<IPluginModelBase, String> startLevelMap) throws CoreException {
178+
private static void addAbsentRequirements(Collection<BundleDescription> requirements, Map<String, List<IPluginModelBase>> collectedModels, Map<IPluginModelBase, String> startLevelMap) throws CoreException {
86179
for (BundleRevision bundle : requirements) {
87180
String id = bundle.getSymbolicName();
88181
List<IPluginModelBase> models = collectedModels.computeIfAbsent(id, k -> new ArrayList<>());

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.io.IOException;
2020
import java.io.InputStream;
2121
import java.util.Arrays;
22+
import java.util.Collection;
2223
import java.util.Dictionary;
2324
import java.util.Map;
2425
import java.util.Properties;
@@ -58,13 +59,12 @@ public LaunchValidationOperation(ILaunchConfiguration configuration, Set<IPlugin
5859

5960
@Override
6061
public void run(IProgressMonitor monitor) throws CoreException {
61-
fOperation = new BundleValidationOperation(fModels, getPlatformProperties());
62+
fOperation = new BundleValidationOperation(fModels, getPlatformProperties(getMatchingEnvironments()));
6263
fOperation.run(monitor);
6364
}
6465

6566
@SuppressWarnings("unchecked")
66-
protected Dictionary<String, String>[] getPlatformProperties() throws CoreException {
67-
IExecutionEnvironment[] envs = getMatchingEnvironments();
67+
public static Dictionary<String, String>[] getPlatformProperties(IExecutionEnvironment[] envs) {
6868
Dictionary<String, String> environmentProperties = TargetPlatformHelper.getTargetEnvironment();
6969
// Only add the highest EE of the supplied VM, to match the runtime's behavior
7070
for (int i = envs.length - 1; i > 0; i--) {
@@ -84,6 +84,10 @@ protected Dictionary<String, String>[] getPlatformProperties() throws CoreExcept
8484
}
8585

8686
protected IExecutionEnvironment[] getMatchingEnvironments() throws CoreException {
87+
return getMatchingEnvironments(fLaunchConfiguration, fModels);
88+
}
89+
90+
public static IExecutionEnvironment[] getMatchingEnvironments(ILaunchConfiguration fLaunchConfiguration, Collection<IPluginModelBase> fModels) throws CoreException {
8791
IVMInstall install = VMHelper.getVMInstall(fLaunchConfiguration, fModels);
8892
return install == null ? new IExecutionEnvironment[0] : getMatchingEEs(install);
8993
}
@@ -94,7 +98,7 @@ static IExecutionEnvironment[] getMatchingEEs(IVMInstall install) {
9498
.toArray(IExecutionEnvironment[]::new);
9599
}
96100

97-
private Properties getJavaProfileProperties(String ee) {
101+
private static Properties getJavaProfileProperties(String ee) {
98102
IPluginModelBase model = PluginRegistry.findModel("system.bundle"); //$NON-NLS-1$
99103
if (model == null) {
100104
return null;

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package org.eclipse.pde.internal.launching.launcher;
1616

1717
import java.util.Arrays;
18+
import java.util.Collection;
1819
import java.util.HashSet;
1920
import java.util.Iterator;
2021
import java.util.LinkedList;
@@ -48,7 +49,7 @@ public class VMHelper {
4849
* @return string id of a valid execution environment with a bound JRE or <code>null</code>
4950
* @throws CoreException if there is a problem reading the bundles from the launch configuration
5051
*/
51-
public static String getDefaultEEName(Set<IPluginModelBase> plugins) throws CoreException {
52+
public static String getDefaultEEName(Collection<IPluginModelBase> plugins) throws CoreException {
5253
// List of all valid EEs, removed if they don't match
5354
List<IExecutionEnvironment> validEEs = new LinkedList<>(); // Use a list to keep order
5455
validEEs.addAll(Arrays.asList(JavaRuntime.getExecutionEnvironmentsManager().getExecutionEnvironments()));
@@ -162,7 +163,7 @@ public static String getDefaultVMInstallName(ILaunchConfiguration configuration)
162163
* @return a vm install from {@link JavaRuntime}
163164
* @throws CoreException if a vm install could not be found for the settings in the configuration
164165
*/
165-
public static IVMInstall getVMInstall(ILaunchConfiguration configuration, Set<IPluginModelBase> plugins) throws CoreException {
166+
public static IVMInstall getVMInstall(ILaunchConfiguration configuration, Collection<IPluginModelBase> plugins) throws CoreException {
166167
String jre = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH, (String) null);
167168

168169
// Launch configuration has a JRE or EE set, throw exception if associated vm not found

0 commit comments

Comments
 (0)