4646import org .osgi .framework .wiring .BundleCapability ;
4747import org .osgi .framework .wiring .BundleRequirement ;
4848import org .osgi .framework .wiring .BundleRevision ;
49+ import org .osgi .resource .Namespace ;
50+ import org .osgi .resource .Requirement ;
4951
5052public class JUnitLaunchRequirements {
5153
@@ -56,16 +58,16 @@ public class JUnitLaunchRequirements {
5658
5759 public static void addRequiredJunitRuntimePlugins (ILaunchConfiguration configuration , Map <String , List <IPluginModelBase >> collectedModels , Map <IPluginModelBase , String > startLevelMap ) throws CoreException {
5860 Collection <IPluginModelBase > runtimeBundles = getEclipseJunitRuntimePlugins (configuration , collectedModels , startLevelMap );
59- List <BundleDescription > roots = runtimeBundles . stream (). map ( p -> p . getBundleDescription ()). filter ( Objects :: nonNull ).toList ();
60- Set <BundleDescription > closure = DependencyManager .findRequirementsClosure (roots );
61- Collection <BundleDescription > runtimeRequirements = filterRequirementsByState (closure , runtimeBundles , configuration );
61+ List <BundleDescription > roots = modelsAsDescriptions ( runtimeBundles ).toList ();
62+ List <BundleDescription > bundles = Stream . concat ( DependencyManager .findRequirementsClosure (roots ). stream (), collectedModels . values (). stream (). flatMap ( pl -> modelsAsDescriptions ( pl ))). distinct (). toList ( );
63+ Collection <BundleDescription > runtimeRequirements = filterRequirementsByState (bundles , runtimeBundles , configuration );
6264 addAbsentRequirements (runtimeRequirements , collectedModels , startLevelMap );
6365 }
6466
6567 private static Collection <BundleDescription > filterRequirementsByState (Collection <BundleDescription > bundles , Collection <IPluginModelBase > rootBundles , ILaunchConfiguration configuration ) throws CoreException {
6668 //lookup that maps a copy to the original description from the bundles parameter
6769 Map <BundleRevision , BundleDescription > descriptionnMap = new IdentityHashMap <>();
68- Set <BundleRevision > rootSet = rootBundles . stream (). map ( p -> p . getBundleDescription ()). filter ( Objects :: nonNull ).collect (Collectors .toSet ());
70+ Set <BundleRevision > rootSet = modelsAsDescriptions ( rootBundles ).collect (Collectors .toSet ());
6971 State state = FACTORY .createState (true );
7072 State targetState = PDECore .getDefault ().getModelManager ().getState ().getState ();
7173 List <BundleDescription > resolveRoots = new ArrayList <>();
@@ -95,13 +97,18 @@ public void filterResolvable(Collection<BundleRevision> candidates) {
9597
9698 @ Override
9799 public void filterMatches (BundleRequirement requirement , Collection <BundleCapability > candidates ) {
100+ boolean optional = isOptional (requirement );
101+ if (candidates .size () == 1 && !optional ) {
102+ //We only have one candidate and requirement is not optional, so keep it or we get an error!
103+ return ;
104+ }
98105 List <BundleCapability > list = candidates .stream ().filter (cp -> isFromDifferentState (cp )).toList ();
99106 if (list .isEmpty ()) {
100107 //nothing to do here...
101108 return ;
102109 }
103110 //iterate in reverse order so we remove lower ranked candidates first ...
104- for (int i = list .size () - 1 ; i >= 0 && candidates .size () > 1 ; i --) {
111+ for (int i = list .size () - 1 ; i >= 0 && ( optional || candidates .size () > 1 ) ; i --) {
105112 BundleCapability capability = list .get (i );
106113 candidates .remove (capability );
107114 }
@@ -129,6 +136,7 @@ public void end() {
129136 throw new CoreException (Status .error (String .format ("%s can not be resolved: %s" , rootBundle , Arrays .toString (errors )))); //$NON-NLS-1$
130137 }
131138 }
139+
132140 Collection <BundleDescription > closure = DependencyManager .findRequirementsClosure (resolveRoots );
133141 // map back to the originals!
134142 return closure .stream ().map (bd -> descriptionnMap .get (bd )).filter (Objects ::nonNull ).toList ();
@@ -195,4 +203,13 @@ private static IPluginModelBase findRequiredPluginInTargetOrHost(IPluginModelBas
195203 return model ;
196204 }
197205
206+ private static Stream <BundleDescription > modelsAsDescriptions (Collection <IPluginModelBase > runtimeBundles ) {
207+ return runtimeBundles .stream ().map (p -> p .getBundleDescription ()).filter (Objects ::nonNull );
208+ }
209+
210+ private static boolean isOptional (Requirement req ) {
211+ String resolution = req .getDirectives ().get (Namespace .REQUIREMENT_RESOLUTION_DIRECTIVE );
212+ return Namespace .RESOLUTION_OPTIONAL .equalsIgnoreCase (resolution );
213+ }
214+
198215}
0 commit comments