View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.internal.impl.scope;
20  
21  import java.util.Collection;
22  import java.util.Collections;
23  import java.util.Comparator;
24  import java.util.HashMap;
25  import java.util.HashSet;
26  import java.util.Map;
27  import java.util.Objects;
28  import java.util.Optional;
29  import java.util.Set;
30  import java.util.concurrent.atomic.AtomicReference;
31  import java.util.stream.Collectors;
32  
33  import org.eclipse.aether.artifact.Artifact;
34  import org.eclipse.aether.collection.CollectResult;
35  import org.eclipse.aether.collection.DependencyGraphTransformer;
36  import org.eclipse.aether.collection.DependencySelector;
37  import org.eclipse.aether.graph.DependencyFilter;
38  import org.eclipse.aether.impl.scope.BuildPath;
39  import org.eclipse.aether.impl.scope.BuildScope;
40  import org.eclipse.aether.impl.scope.BuildScopeQuery;
41  import org.eclipse.aether.impl.scope.BuildScopeSource;
42  import org.eclipse.aether.impl.scope.InternalScopeManager;
43  import org.eclipse.aether.impl.scope.ProjectPath;
44  import org.eclipse.aether.impl.scope.ScopeManagerConfiguration;
45  import org.eclipse.aether.scope.DependencyScope;
46  import org.eclipse.aether.scope.ResolutionScope;
47  import org.eclipse.aether.scope.SystemDependencyScope;
48  import org.eclipse.aether.util.filter.ScopeDependencyFilter;
49  import org.eclipse.aether.util.graph.selector.AndDependencySelector;
50  import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector;
51  import org.eclipse.aether.util.graph.transformer.ChainedDependencyGraphTransformer;
52  import org.eclipse.aether.util.graph.transformer.ConfigurableVersionSelector;
53  import org.eclipse.aether.util.graph.transformer.ConflictResolver;
54  import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector;
55  import org.eclipse.aether.util.graph.visitor.CloningDependencyVisitor;
56  import org.eclipse.aether.util.graph.visitor.FilteringDependencyVisitor;
57  
58  import static java.util.Objects.requireNonNull;
59  
60  public final class ScopeManagerImpl implements InternalScopeManager {
61      private final String id;
62      private final boolean strictDependencyScopes;
63      private final boolean strictResolutionScopes;
64      private final BuildScopeSource buildScopeSource;
65      private final AtomicReference<SystemDependencyScopeImpl> systemDependencyScope;
66      private final Map<String, DependencyScopeImpl> dependencyScopes;
67      private final Map<String, ResolutionScopeImpl> resolutionScopes;
68  
69      public ScopeManagerImpl(ScopeManagerConfiguration configuration) {
70          this.id = configuration.getId();
71          this.strictDependencyScopes = configuration.isStrictDependencyScopes();
72          this.strictResolutionScopes = configuration.isStrictResolutionScopes();
73          this.buildScopeSource = configuration.getBuildScopeSource();
74          this.systemDependencyScope = new AtomicReference<>(null);
75          this.dependencyScopes = Collections.unmodifiableMap(buildDependencyScopes(configuration));
76          this.resolutionScopes = Collections.unmodifiableMap(buildResolutionScopes(configuration));
77      }
78  
79      private Map<String, DependencyScopeImpl> buildDependencyScopes(ScopeManagerConfiguration configuration) {
80          Collection<DependencyScope> dependencyScopes = configuration.buildDependencyScopes(this);
81          HashMap<String, DependencyScopeImpl> result = new HashMap<>(dependencyScopes.size());
82          dependencyScopes.forEach(d -> result.put(d.getId(), (DependencyScopeImpl) d));
83          return result;
84      }
85  
86      private Map<String, ResolutionScopeImpl> buildResolutionScopes(ScopeManagerConfiguration configuration) {
87          Collection<ResolutionScope> resolutionScopes = configuration.buildResolutionScopes(this);
88          HashMap<String, ResolutionScopeImpl> result = new HashMap<>(resolutionScopes.size());
89          resolutionScopes.forEach(r -> result.put(r.getId(), (ResolutionScopeImpl) r));
90          return result;
91      }
92  
93      @Override
94      public String getId() {
95          return id;
96      }
97  
98      @Override
99      public Optional<SystemDependencyScope> getSystemDependencyScope() {
100         return Optional.ofNullable(systemDependencyScope.get());
101     }
102 
103     @Override
104     public Optional<DependencyScope> getDependencyScope(String id) {
105         DependencyScope dependencyScope = dependencyScopes.get(id);
106         if (strictDependencyScopes && dependencyScope == null) {
107             throw new IllegalArgumentException("unknown dependency scope");
108         }
109         return Optional.ofNullable(dependencyScope);
110     }
111 
112     @Override
113     public Collection<DependencyScope> getDependencyScopeUniverse() {
114         return new HashSet<>(dependencyScopes.values());
115     }
116 
117     @Override
118     public Optional<ResolutionScope> getResolutionScope(String id) {
119         ResolutionScope resolutionScope = resolutionScopes.get(id);
120         if (strictResolutionScopes && resolutionScope == null) {
121             throw new IllegalArgumentException("unknown resolution scope");
122         }
123         return Optional.ofNullable(resolutionScope);
124     }
125 
126     @Override
127     public Collection<ResolutionScope> getResolutionScopeUniverse() {
128         return new HashSet<>(resolutionScopes.values());
129     }
130 
131     @Override
132     public int getDependencyScopeWidth(DependencyScope dependencyScope) {
133         return translate(dependencyScope).getWidth();
134     }
135 
136     @Override
137     public Optional<BuildScope> getDependencyScopeMainProjectBuildScope(DependencyScope dependencyScope) {
138         return Optional.ofNullable(translate(dependencyScope).getMainBuildScope());
139     }
140 
141     @Override
142     public DependencySelector getDependencySelector(ResolutionScope resolutionScope) {
143         ResolutionScopeImpl rs = translate(resolutionScope);
144         Set<String> directlyExcludedLabels = getDirectlyExcludedLabels(rs);
145         Set<String> transitivelyExcludedLabels = getTransitivelyExcludedLabels(rs);
146 
147         return new AndDependencySelector(
148                 rs.getMode() == Mode.ELIMINATE
149                         ? ScopeDependencySelector.fromTo(2, 2, null, directlyExcludedLabels)
150                         : ScopeDependencySelector.fromTo(1, 2, null, directlyExcludedLabels),
151                 ScopeDependencySelector.from(2, null, transitivelyExcludedLabels),
152                 OptionalDependencySelector.fromDirect(),
153                 new ExclusionDependencySelector());
154     }
155 
156     @Override
157     public DependencyGraphTransformer getDependencyGraphTransformer(ResolutionScope resolutionScope) {
158         return new ChainedDependencyGraphTransformer(
159                 new ConflictResolver(
160                         new ConfigurableVersionSelector(), new ManagedScopeSelector(this),
161                         new SimpleOptionalitySelector(), new ManagedScopeDeriver(this)),
162                 new ManagedDependencyContextRefiner(this));
163     }
164 
165     @Override
166     public CollectResult postProcess(ResolutionScope resolutionScope, CollectResult collectResult) {
167         ResolutionScopeImpl rs = translate(resolutionScope);
168         if (rs.getMode() == Mode.ELIMINATE) {
169             CloningDependencyVisitor cloning = new CloningDependencyVisitor();
170             FilteringDependencyVisitor filter = new FilteringDependencyVisitor(
171                     cloning, new ScopeDependencyFilter(null, getDirectlyExcludedLabels(rs)));
172             collectResult.getRoot().accept(filter);
173             collectResult.setRoot(cloning.getRootNode());
174         }
175         return collectResult;
176     }
177 
178     @Override
179     public DependencyFilter getDependencyFilter(ResolutionScope resolutionScope) {
180         return new ScopeDependencyFilter(null, getDirectlyExcludedLabels(translate(resolutionScope)));
181     }
182 
183     @Override
184     public DependencyScope createDependencyScope(String id, boolean transitive, Collection<BuildScopeQuery> presence) {
185         return new DependencyScopeImpl(id, transitive, presence);
186     }
187 
188     @Override
189     public SystemDependencyScope createSystemDependencyScope(
190             String id, boolean transitive, Collection<BuildScopeQuery> presence, String systemPathProperty) {
191         SystemDependencyScopeImpl system = new SystemDependencyScopeImpl(id, transitive, presence, systemPathProperty);
192         if (systemDependencyScope.compareAndSet(null, system)) {
193             return system;
194         } else {
195             throw new IllegalStateException("system dependency scope already created");
196         }
197     }
198 
199     @Override
200     public ResolutionScope createResolutionScope(
201             String id,
202             Mode mode,
203             Collection<BuildScopeQuery> wantedPresence,
204             Collection<DependencyScope> explicitlyIncluded,
205             Collection<DependencyScope> transitivelyExcluded) {
206         return new ResolutionScopeImpl(id, mode, wantedPresence, explicitlyIncluded, transitivelyExcluded);
207     }
208 
209     private Set<DependencyScope> collectScopes(Collection<BuildScopeQuery> wantedPresence) {
210         HashSet<DependencyScope> result = new HashSet<>();
211         for (BuildScope buildScope : buildScopeSource.query(wantedPresence)) {
212             dependencyScopes.values().stream()
213                     .filter(s -> buildScopeSource.query(s.getPresence()).contains(buildScope))
214                     .filter(s -> systemDependencyScope.get() == null
215                             || !systemDependencyScope.get().is(s.id)) // system scope must be always explicitly added
216                     .forEach(result::add);
217         }
218         return result;
219     }
220 
221     private int calculateDependencyScopeWidth(DependencyScopeImpl dependencyScope) {
222         int result = 0;
223         if (dependencyScope.isTransitive()) {
224             result += 1000;
225         }
226         for (BuildScope buildScope : buildScopeSource.query(dependencyScope.getPresence())) {
227             result += 1000
228                     / buildScope.getProjectPaths().stream()
229                             .map(ProjectPath::order)
230                             .reduce(0, Integer::sum);
231         }
232         return result;
233     }
234 
235     private BuildScope calculateMainProjectBuildScope(DependencyScopeImpl dependencyScope) {
236         for (ProjectPath projectPath : buildScopeSource.allProjectPaths().stream()
237                 .sorted(Comparator.comparing(ProjectPath::order))
238                 .collect(Collectors.toList())) {
239             for (BuildPath buildPath : buildScopeSource.allBuildPaths().stream()
240                     .sorted(Comparator.comparing(BuildPath::order))
241                     .collect(Collectors.toList())) {
242                 for (BuildScope buildScope : buildScopeSource.query(dependencyScope.getPresence())) {
243                     if (buildScope.getProjectPaths().contains(projectPath)
244                             && buildScope.getBuildPaths().contains(buildPath)) {
245                         return buildScope;
246                     }
247                 }
248             }
249         }
250         return null;
251     }
252 
253     /**
254      * Visible for testing.
255      */
256     Set<String> getDirectlyIncludedLabels(ResolutionScope resolutionScope) {
257         return translate(resolutionScope).getDirectlyIncluded().stream()
258                 .map(DependencyScope::getId)
259                 .collect(Collectors.toSet());
260     }
261 
262     /**
263      * Visible for testing.
264      */
265     Set<String> getDirectlyExcludedLabels(ResolutionScope resolutionScope) {
266         ResolutionScopeImpl rs = translate(resolutionScope);
267         return dependencyScopes.values().stream()
268                 .filter(s -> !rs.getDirectlyIncluded().contains(s))
269                 .map(DependencyScope::getId)
270                 .collect(Collectors.toSet());
271     }
272 
273     /**
274      * Visible for testing.
275      */
276     Set<String> getTransitivelyExcludedLabels(ResolutionScope resolutionScope) {
277         return translate(resolutionScope).getTransitivelyExcluded().stream()
278                 .map(DependencyScope::getId)
279                 .collect(Collectors.toSet());
280     }
281 
282     /**
283      * Visible for testing.
284      */
285     Set<BuildScopeQuery> getPresence(DependencyScope dependencyScope) {
286         return translate(dependencyScope).getPresence();
287     }
288 
289     /**
290      * Visible for testing.
291      */
292     BuildScopeSource getBuildScopeSource() {
293         return buildScopeSource;
294     }
295 
296     private DependencyScopeImpl translate(DependencyScope dependencyScope) {
297         return requireNonNull(dependencyScopes.get(dependencyScope.getId()), "unknown dependency scope");
298     }
299 
300     private ResolutionScopeImpl translate(ResolutionScope resolutionScope) {
301         return requireNonNull(resolutionScopes.get(resolutionScope.getId()), "unknown resolution scope");
302     }
303 
304     @Override
305     public boolean equals(Object o) {
306         if (this == o) {
307             return true;
308         }
309         if (o == null || getClass() != o.getClass()) {
310             return false;
311         }
312         ScopeManagerImpl that = (ScopeManagerImpl) o;
313         return Objects.equals(id, that.id);
314     }
315 
316     @Override
317     public int hashCode() {
318         return Objects.hash(id);
319     }
320 
321     @Override
322     public String toString() {
323         return id;
324     }
325 
326     private class DependencyScopeImpl implements DependencyScope {
327         private final String id;
328         private final boolean transitive;
329         private final Set<BuildScopeQuery> presence;
330         private final BuildScope mainBuildScope;
331         private final int width;
332 
333         private DependencyScopeImpl(String id, boolean transitive, Collection<BuildScopeQuery> presence) {
334             this.id = requireNonNull(id, "id");
335             this.transitive = transitive;
336             this.presence = Collections.unmodifiableSet(new HashSet<>(presence));
337             this.mainBuildScope = calculateMainProjectBuildScope(this);
338             this.width = calculateDependencyScopeWidth(this);
339         }
340 
341         @Override
342         public String getId() {
343             return id;
344         }
345 
346         @Override
347         public boolean isTransitive() {
348             return transitive;
349         }
350 
351         public Set<BuildScopeQuery> getPresence() {
352             return presence;
353         }
354 
355         public BuildScope getMainBuildScope() {
356             return mainBuildScope;
357         }
358 
359         public int getWidth() {
360             return width;
361         }
362 
363         @Override
364         public boolean equals(Object o) {
365             if (this == o) {
366                 return true;
367             }
368             if (o == null || getClass() != o.getClass()) {
369                 return false;
370             }
371             DependencyScopeImpl that = (DependencyScopeImpl) o;
372             return Objects.equals(id, that.id);
373         }
374 
375         @Override
376         public int hashCode() {
377             return Objects.hash(id);
378         }
379 
380         @Override
381         public String toString() {
382             return id;
383         }
384     }
385 
386     private class SystemDependencyScopeImpl extends DependencyScopeImpl implements SystemDependencyScope {
387         private final String systemPathProperty;
388 
389         private SystemDependencyScopeImpl(
390                 String id, boolean transitive, Collection<BuildScopeQuery> presence, String systemPathProperty) {
391             super(id, transitive, presence);
392             this.systemPathProperty = requireNonNull(systemPathProperty);
393         }
394 
395         @Override
396         public String getSystemPath(Artifact artifact) {
397             return artifact.getProperty(systemPathProperty, null);
398         }
399 
400         @Override
401         public void setSystemPath(Map<String, String> properties, String systemPath) {
402             if (systemPath == null) {
403                 properties.remove(systemPathProperty);
404             } else {
405                 properties.put(systemPathProperty, systemPath);
406             }
407         }
408     }
409 
410     private class ResolutionScopeImpl implements ResolutionScope {
411 
412         private final String id;
413         private final Mode mode;
414         private final Set<BuildScopeQuery> wantedPresence;
415         private final Set<DependencyScope> directlyIncluded;
416         private final Set<DependencyScope> transitivelyExcluded;
417 
418         private ResolutionScopeImpl(
419                 String id,
420                 Mode mode,
421                 Collection<BuildScopeQuery> wantedPresence,
422                 Collection<DependencyScope> explicitlyIncluded,
423                 Collection<DependencyScope> transitivelyExcluded) {
424             this.id = requireNonNull(id, "id");
425             this.mode = requireNonNull(mode, "mode");
426             this.wantedPresence = Collections.unmodifiableSet(new HashSet<>(wantedPresence));
427             Set<DependencyScope> included = collectScopes(wantedPresence);
428             // here we may have null elements, based on existence of system scope
429             if (explicitlyIncluded != null && !explicitlyIncluded.isEmpty()) {
430                 explicitlyIncluded.stream().filter(Objects::nonNull).forEach(included::add);
431             }
432             this.directlyIncluded = Collections.unmodifiableSet(included);
433             this.transitivelyExcluded = Collections.unmodifiableSet(
434                     transitivelyExcluded.stream().filter(Objects::nonNull).collect(Collectors.toSet()));
435         }
436 
437         @Override
438         public String getId() {
439             return id;
440         }
441 
442         public Mode getMode() {
443             return mode;
444         }
445 
446         public Set<BuildScopeQuery> getWantedPresence() {
447             return wantedPresence;
448         }
449 
450         public Set<DependencyScope> getDirectlyIncluded() {
451             return directlyIncluded;
452         }
453 
454         public Set<DependencyScope> getTransitivelyExcluded() {
455             return transitivelyExcluded;
456         }
457 
458         @Override
459         public boolean equals(Object o) {
460             if (this == o) {
461                 return true;
462             }
463             if (o == null || getClass() != o.getClass()) {
464                 return false;
465             }
466             ResolutionScopeImpl that = (ResolutionScopeImpl) o;
467             return Objects.equals(id, that.id);
468         }
469 
470         @Override
471         public int hashCode() {
472             return Objects.hash(id);
473         }
474 
475         @Override
476         public String toString() {
477             return id;
478         }
479     }
480 }