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.RepositorySystemSession;
34  import org.eclipse.aether.artifact.Artifact;
35  import org.eclipse.aether.collection.CollectResult;
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.visitor.CloningDependencyVisitor;
52  import org.eclipse.aether.util.graph.visitor.FilteringDependencyVisitor;
53  
54  import static java.util.Objects.requireNonNull;
55  
56  public final class ScopeManagerImpl implements InternalScopeManager {
57      private final String id;
58      private final boolean strictDependencyScopes;
59      private final boolean strictResolutionScopes;
60      private final BuildScopeSource buildScopeSource;
61      private final AtomicReference<SystemDependencyScopeImpl> systemDependencyScope;
62      private final Map<String, DependencyScopeImpl> dependencyScopes;
63      private final Collection<DependencyScope> dependencyScopesUniverse;
64      private final Map<String, ResolutionScopeImpl> resolutionScopes;
65      private final Collection<ResolutionScope> resolutionScopesUniverse;
66  
67      public ScopeManagerImpl(ScopeManagerConfiguration configuration) {
68          this.id = configuration.getId();
69          this.strictDependencyScopes = configuration.isStrictDependencyScopes();
70          this.strictResolutionScopes = configuration.isStrictResolutionScopes();
71          this.buildScopeSource = configuration.getBuildScopeSource();
72          this.systemDependencyScope = new AtomicReference<>(null);
73          this.dependencyScopes = Collections.unmodifiableMap(buildDependencyScopes(configuration));
74          this.dependencyScopesUniverse = Collections.unmodifiableCollection(new HashSet<>(dependencyScopes.values()));
75          this.resolutionScopes = Collections.unmodifiableMap(buildResolutionScopes(configuration));
76          this.resolutionScopesUniverse = Collections.unmodifiableCollection(new HashSet<>(resolutionScopes.values()));
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 dependencyScopesUniverse;
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 resolutionScopesUniverse;
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(RepositorySystemSession session, ResolutionScope resolutionScope) {
143         ResolutionScopeImpl rs = translate(resolutionScope);
144         Set<String> directlyExcludedLabels = getDirectlyExcludedLabels(rs);
145         Set<String> transitivelyExcludedLabels = getTransitivelyExcludedLabels(rs);
146         if (session.getDependencySelector() != null) {
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                     session.getDependencySelector());
155         } else {
156             return new AndDependencySelector(
157                     rs.getMode() == Mode.ELIMINATE
158                             ? ScopeDependencySelector.fromTo(2, 2, null, directlyExcludedLabels)
159                             : ScopeDependencySelector.fromTo(1, 2, null, directlyExcludedLabels),
160                     ScopeDependencySelector.from(2, null, transitivelyExcludedLabels),
161                     OptionalDependencySelector.fromDirect(),
162                     new ExclusionDependencySelector());
163         }
164     }
165 
166     @Override
167     public DependencyFilter getDependencyFilter(RepositorySystemSession session, ResolutionScope resolutionScope) {
168         return new ScopeDependencyFilter(null, getDirectlyExcludedLabels(translate(resolutionScope)));
169     }
170 
171     @Override
172     public CollectResult postProcess(
173             RepositorySystemSession session, ResolutionScope resolutionScope, CollectResult collectResult) {
174         ResolutionScopeImpl rs = translate(resolutionScope);
175         if (rs.getMode() == Mode.ELIMINATE) {
176             CloningDependencyVisitor cloning = new CloningDependencyVisitor();
177             FilteringDependencyVisitor filter = new FilteringDependencyVisitor(
178                     cloning, new ScopeDependencyFilter(null, getDirectlyExcludedLabels(rs)));
179             collectResult.getRoot().accept(filter);
180             collectResult.setRoot(cloning.getRootNode());
181         }
182         return collectResult;
183     }
184 
185     @Override
186     public DependencyScope createDependencyScope(String id, boolean transitive, Collection<BuildScopeQuery> presence) {
187         return new DependencyScopeImpl(id, transitive, presence);
188     }
189 
190     @Override
191     public SystemDependencyScope createSystemDependencyScope(
192             String id, boolean transitive, Collection<BuildScopeQuery> presence, String systemPathProperty) {
193         SystemDependencyScopeImpl system = new SystemDependencyScopeImpl(id, transitive, presence, systemPathProperty);
194         if (systemDependencyScope.compareAndSet(null, system)) {
195             return system;
196         } else {
197             throw new IllegalStateException("system dependency scope already created");
198         }
199     }
200 
201     @Override
202     public ResolutionScope createResolutionScope(
203             String id,
204             Set<String> aliases,
205             Mode mode,
206             Collection<BuildScopeQuery> wantedPresence,
207             Collection<DependencyScope> explicitlyIncluded,
208             Collection<DependencyScope> transitivelyExcluded) {
209         return new ResolutionScopeImpl(id, aliases, mode, wantedPresence, explicitlyIncluded, transitivelyExcluded);
210     }
211 
212     private Set<DependencyScope> collectScopes(Collection<BuildScopeQuery> wantedPresence) {
213         HashSet<DependencyScope> result = new HashSet<>();
214         for (BuildScope buildScope : buildScopeSource.query(wantedPresence)) {
215             dependencyScopes.values().stream()
216                     .filter(s -> buildScopeSource.query(s.getPresence()).contains(buildScope))
217                     .filter(s -> systemDependencyScope.get() == null
218                             || !systemDependencyScope.get().is(s.id)) // system scope must be always explicitly added
219                     .forEach(result::add);
220         }
221         return result;
222     }
223 
224     private int calculateDependencyScopeWidth(DependencyScopeImpl dependencyScope) {
225         int result = 0;
226         if (dependencyScope.isTransitive()) {
227             result += 1000;
228         }
229         for (BuildScope buildScope : buildScopeSource.query(dependencyScope.getPresence())) {
230             result += 1000
231                     / buildScope.getProjectPaths().stream()
232                             .map(ProjectPath::order)
233                             .reduce(0, Integer::sum);
234         }
235         return result;
236     }
237 
238     private BuildScope calculateMainProjectBuildScope(DependencyScopeImpl dependencyScope) {
239         for (ProjectPath projectPath : buildScopeSource.allProjectPaths().stream()
240                 .sorted(Comparator.comparing(ProjectPath::order))
241                 .collect(Collectors.toList())) {
242             for (BuildPath buildPath : buildScopeSource.allBuildPaths().stream()
243                     .sorted(Comparator.comparing(BuildPath::order))
244                     .collect(Collectors.toList())) {
245                 for (BuildScope buildScope : buildScopeSource.query(dependencyScope.getPresence())) {
246                     if (buildScope.getProjectPaths().contains(projectPath)
247                             && buildScope.getBuildPaths().contains(buildPath)) {
248                         return buildScope;
249                     }
250                 }
251             }
252         }
253         return null;
254     }
255 
256     /**
257      * Visible for testing.
258      */
259     Set<String> getDirectlyIncludedLabels(ResolutionScope resolutionScope) {
260         return translate(resolutionScope).getDirectlyIncluded().stream()
261                 .map(DependencyScope::getId)
262                 .collect(Collectors.toSet());
263     }
264 
265     /**
266      * Visible for testing.
267      */
268     Set<String> getDirectlyExcludedLabels(ResolutionScope resolutionScope) {
269         ResolutionScopeImpl rs = translate(resolutionScope);
270         return dependencyScopes.values().stream()
271                 .filter(s -> !rs.getDirectlyIncluded().contains(s))
272                 .map(DependencyScope::getId)
273                 .collect(Collectors.toSet());
274     }
275 
276     /**
277      * Visible for testing.
278      */
279     Set<String> getTransitivelyExcludedLabels(ResolutionScope resolutionScope) {
280         return translate(resolutionScope).getTransitivelyExcluded().stream()
281                 .map(DependencyScope::getId)
282                 .collect(Collectors.toSet());
283     }
284 
285     /**
286      * Visible for testing.
287      */
288     Set<BuildScopeQuery> getPresence(DependencyScope dependencyScope) {
289         return translate(dependencyScope).getPresence();
290     }
291 
292     /**
293      * Visible for testing.
294      */
295     BuildScopeSource getBuildScopeSource() {
296         return buildScopeSource;
297     }
298 
299     private DependencyScopeImpl translate(DependencyScope dependencyScope) {
300         return requireNonNull(dependencyScopes.get(dependencyScope.getId()), "unknown dependency scope");
301     }
302 
303     private ResolutionScopeImpl translate(ResolutionScope resolutionScope) {
304         return requireNonNull(resolutionScopes.get(resolutionScope.getId()), "unknown resolution scope");
305     }
306 
307     @Override
308     public boolean equals(Object o) {
309         if (this == o) {
310             return true;
311         }
312         if (o == null || getClass() != o.getClass()) {
313             return false;
314         }
315         ScopeManagerImpl that = (ScopeManagerImpl) o;
316         return Objects.equals(id, that.id);
317     }
318 
319     @Override
320     public int hashCode() {
321         return Objects.hash(id);
322     }
323 
324     @Override
325     public String toString() {
326         return id;
327     }
328 
329     private class DependencyScopeImpl implements DependencyScope {
330         private final String id;
331         private final boolean transitive;
332         private final Set<BuildScopeQuery> presence;
333         private final BuildScope mainBuildScope;
334         private final int width;
335 
336         private DependencyScopeImpl(String id, boolean transitive, Collection<BuildScopeQuery> presence) {
337             this.id = requireNonNull(id, "id");
338             this.transitive = transitive;
339             this.presence = Collections.unmodifiableSet(new HashSet<>(presence));
340             this.mainBuildScope = calculateMainProjectBuildScope(this);
341             this.width = calculateDependencyScopeWidth(this);
342         }
343 
344         @Override
345         public String getId() {
346             return id;
347         }
348 
349         @Override
350         public boolean isTransitive() {
351             return transitive;
352         }
353 
354         public Set<BuildScopeQuery> getPresence() {
355             return presence;
356         }
357 
358         public BuildScope getMainBuildScope() {
359             return mainBuildScope;
360         }
361 
362         public int getWidth() {
363             return width;
364         }
365 
366         @Override
367         public boolean equals(Object o) {
368             if (this == o) {
369                 return true;
370             }
371             if (o == null || getClass() != o.getClass()) {
372                 return false;
373             }
374             DependencyScopeImpl that = (DependencyScopeImpl) o;
375             return Objects.equals(id, that.id);
376         }
377 
378         @Override
379         public int hashCode() {
380             return Objects.hash(id);
381         }
382 
383         @Override
384         public String toString() {
385             return id;
386         }
387     }
388 
389     private class SystemDependencyScopeImpl extends DependencyScopeImpl implements SystemDependencyScope {
390         private final String systemPathProperty;
391 
392         private SystemDependencyScopeImpl(
393                 String id, boolean transitive, Collection<BuildScopeQuery> presence, String systemPathProperty) {
394             super(id, transitive, presence);
395             this.systemPathProperty = requireNonNull(systemPathProperty);
396         }
397 
398         @Override
399         public String getSystemPath(Artifact artifact) {
400             return artifact.getProperty(systemPathProperty, null);
401         }
402 
403         @Override
404         public void setSystemPath(Map<String, String> properties, String systemPath) {
405             if (systemPath == null) {
406                 properties.remove(systemPathProperty);
407             } else {
408                 properties.put(systemPathProperty, systemPath);
409             }
410         }
411     }
412 
413     private class ResolutionScopeImpl implements ResolutionScope {
414         private final String id;
415         private final Set<String> aliases;
416         private final Mode mode;
417         private final Set<BuildScopeQuery> wantedPresence;
418         private final Set<DependencyScope> directlyIncluded;
419         private final Set<DependencyScope> transitivelyExcluded;
420 
421         private ResolutionScopeImpl(
422                 String id,
423                 Set<String> aliases,
424                 Mode mode,
425                 Collection<BuildScopeQuery> wantedPresence,
426                 Collection<DependencyScope> explicitlyIncluded,
427                 Collection<DependencyScope> transitivelyExcluded) {
428             this.id = requireNonNull(id, "id");
429             this.aliases = Collections.unmodifiableSet(new HashSet<>(aliases));
430             this.mode = requireNonNull(mode, "mode");
431             this.wantedPresence = Collections.unmodifiableSet(new HashSet<>(wantedPresence));
432             Set<DependencyScope> included = collectScopes(wantedPresence);
433             // here we may have null elements, based on existence of system scope
434             if (explicitlyIncluded != null && !explicitlyIncluded.isEmpty()) {
435                 explicitlyIncluded.stream().filter(Objects::nonNull).forEach(included::add);
436             }
437             this.directlyIncluded = Collections.unmodifiableSet(included);
438             this.transitivelyExcluded = Collections.unmodifiableSet(
439                     transitivelyExcluded.stream().filter(Objects::nonNull).collect(Collectors.toSet()));
440         }
441 
442         @Override
443         public String getId() {
444             return id;
445         }
446 
447         @Override
448         public Set<String> getAliases() {
449             return aliases;
450         }
451 
452         public Mode getMode() {
453             return mode;
454         }
455 
456         public Set<BuildScopeQuery> getWantedPresence() {
457             return wantedPresence;
458         }
459 
460         public Set<DependencyScope> getDirectlyIncluded() {
461             return directlyIncluded;
462         }
463 
464         public Set<DependencyScope> getTransitivelyExcluded() {
465             return transitivelyExcluded;
466         }
467 
468         @Override
469         public boolean equals(Object o) {
470             if (this == o) {
471                 return true;
472             }
473             if (o == null || getClass() != o.getClass()) {
474                 return false;
475             }
476             ResolutionScopeImpl that = (ResolutionScopeImpl) o;
477             return Objects.equals(id, that.id);
478         }
479 
480         @Override
481         public int hashCode() {
482             return Objects.hash(id);
483         }
484 
485         @Override
486         public String toString() {
487             return id;
488         }
489     }
490 }