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 Collection<DependencyScope> dependencyScopesUniverse;
68      private final Map<String, ResolutionScopeImpl> resolutionScopes;
69      private final Collection<ResolutionScope> resolutionScopesUniverse;
70  
71      public ScopeManagerImpl(ScopeManagerConfiguration configuration) {
72          this.id = configuration.getId();
73          this.strictDependencyScopes = configuration.isStrictDependencyScopes();
74          this.strictResolutionScopes = configuration.isStrictResolutionScopes();
75          this.buildScopeSource = configuration.getBuildScopeSource();
76          this.systemDependencyScope = new AtomicReference<>(null);
77          this.dependencyScopes = Collections.unmodifiableMap(buildDependencyScopes(configuration));
78          this.dependencyScopesUniverse = Collections.unmodifiableCollection(new HashSet<>(dependencyScopes.values()));
79          this.resolutionScopes = Collections.unmodifiableMap(buildResolutionScopes(configuration));
80          this.resolutionScopesUniverse = Collections.unmodifiableCollection(new HashSet<>(resolutionScopes.values()));
81      }
82  
83      private Map<String, DependencyScopeImpl> buildDependencyScopes(ScopeManagerConfiguration configuration) {
84          Collection<DependencyScope> dependencyScopes = configuration.buildDependencyScopes(this);
85          HashMap<String, DependencyScopeImpl> result = new HashMap<>(dependencyScopes.size());
86          dependencyScopes.forEach(d -> result.put(d.getId(), (DependencyScopeImpl) d));
87          return result;
88      }
89  
90      private Map<String, ResolutionScopeImpl> buildResolutionScopes(ScopeManagerConfiguration configuration) {
91          Collection<ResolutionScope> resolutionScopes = configuration.buildResolutionScopes(this);
92          HashMap<String, ResolutionScopeImpl> result = new HashMap<>(resolutionScopes.size());
93          resolutionScopes.forEach(r -> result.put(r.getId(), (ResolutionScopeImpl) r));
94          return result;
95      }
96  
97      @Override
98      public String getId() {
99          return id;
100     }
101 
102     @Override
103     public Optional<SystemDependencyScope> getSystemDependencyScope() {
104         return Optional.ofNullable(systemDependencyScope.get());
105     }
106 
107     @Override
108     public Optional<DependencyScope> getDependencyScope(String id) {
109         DependencyScope dependencyScope = dependencyScopes.get(id);
110         if (strictDependencyScopes && dependencyScope == null) {
111             throw new IllegalArgumentException("unknown dependency scope");
112         }
113         return Optional.ofNullable(dependencyScope);
114     }
115 
116     @Override
117     public Collection<DependencyScope> getDependencyScopeUniverse() {
118         return dependencyScopesUniverse;
119     }
120 
121     @Override
122     public Optional<ResolutionScope> getResolutionScope(String id) {
123         ResolutionScope resolutionScope = resolutionScopes.get(id);
124         if (strictResolutionScopes && resolutionScope == null) {
125             throw new IllegalArgumentException("unknown resolution scope");
126         }
127         return Optional.ofNullable(resolutionScope);
128     }
129 
130     @Override
131     public Collection<ResolutionScope> getResolutionScopeUniverse() {
132         return resolutionScopesUniverse;
133     }
134 
135     @Override
136     public int getDependencyScopeWidth(DependencyScope dependencyScope) {
137         return translate(dependencyScope).getWidth();
138     }
139 
140     @Override
141     public Optional<BuildScope> getDependencyScopeMainProjectBuildScope(DependencyScope dependencyScope) {
142         return Optional.ofNullable(translate(dependencyScope).getMainBuildScope());
143     }
144 
145     @Override
146     public DependencySelector getDependencySelector(ResolutionScope resolutionScope) {
147         ResolutionScopeImpl rs = translate(resolutionScope);
148         Set<String> directlyExcludedLabels = getDirectlyExcludedLabels(rs);
149         Set<String> transitivelyExcludedLabels = getTransitivelyExcludedLabels(rs);
150 
151         return new AndDependencySelector(
152                 rs.getMode() == Mode.ELIMINATE
153                         ? ScopeDependencySelector.fromTo(2, 2, null, directlyExcludedLabels)
154                         : ScopeDependencySelector.fromTo(1, 2, null, directlyExcludedLabels),
155                 ScopeDependencySelector.from(2, null, transitivelyExcludedLabels),
156                 OptionalDependencySelector.fromDirect(),
157                 new ExclusionDependencySelector());
158     }
159 
160     @Override
161     public DependencyGraphTransformer getDependencyGraphTransformer(ResolutionScope resolutionScope) {
162         return new ChainedDependencyGraphTransformer(
163                 new ConflictResolver(
164                         new ConfigurableVersionSelector(), new ManagedScopeSelector(this),
165                         new SimpleOptionalitySelector(), new ManagedScopeDeriver(this)),
166                 new ManagedDependencyContextRefiner(this));
167     }
168 
169     @Override
170     public CollectResult postProcess(ResolutionScope resolutionScope, CollectResult collectResult) {
171         ResolutionScopeImpl rs = translate(resolutionScope);
172         if (rs.getMode() == Mode.ELIMINATE) {
173             CloningDependencyVisitor cloning = new CloningDependencyVisitor();
174             FilteringDependencyVisitor filter = new FilteringDependencyVisitor(
175                     cloning, new ScopeDependencyFilter(null, getDirectlyExcludedLabels(rs)));
176             collectResult.getRoot().accept(filter);
177             collectResult.setRoot(cloning.getRootNode());
178         }
179         return collectResult;
180     }
181 
182     @Override
183     public DependencyFilter getDependencyFilter(ResolutionScope resolutionScope) {
184         return new ScopeDependencyFilter(null, getDirectlyExcludedLabels(translate(resolutionScope)));
185     }
186 
187     @Override
188     public DependencyScope createDependencyScope(String id, boolean transitive, Collection<BuildScopeQuery> presence) {
189         return new DependencyScopeImpl(id, transitive, presence);
190     }
191 
192     @Override
193     public SystemDependencyScope createSystemDependencyScope(
194             String id, boolean transitive, Collection<BuildScopeQuery> presence, String systemPathProperty) {
195         SystemDependencyScopeImpl system = new SystemDependencyScopeImpl(id, transitive, presence, systemPathProperty);
196         if (systemDependencyScope.compareAndSet(null, system)) {
197             return system;
198         } else {
199             throw new IllegalStateException("system dependency scope already created");
200         }
201     }
202 
203     @Override
204     public ResolutionScope createResolutionScope(
205             String id,
206             Mode mode,
207             Collection<BuildScopeQuery> wantedPresence,
208             Collection<DependencyScope> explicitlyIncluded,
209             Collection<DependencyScope> transitivelyExcluded) {
210         return new ResolutionScopeImpl(id, mode, wantedPresence, explicitlyIncluded, transitivelyExcluded);
211     }
212 
213     private Set<DependencyScope> collectScopes(Collection<BuildScopeQuery> wantedPresence) {
214         HashSet<DependencyScope> result = new HashSet<>();
215         for (BuildScope buildScope : buildScopeSource.query(wantedPresence)) {
216             dependencyScopes.values().stream()
217                     .filter(s -> buildScopeSource.query(s.getPresence()).contains(buildScope))
218                     .filter(s -> systemDependencyScope.get() == null
219                             || !systemDependencyScope.get().is(s.id)) // system scope must be always explicitly added
220                     .forEach(result::add);
221         }
222         return result;
223     }
224 
225     private int calculateDependencyScopeWidth(DependencyScopeImpl dependencyScope) {
226         int result = 0;
227         if (dependencyScope.isTransitive()) {
228             result += 1000;
229         }
230         for (BuildScope buildScope : buildScopeSource.query(dependencyScope.getPresence())) {
231             result += 1000
232                     / buildScope.getProjectPaths().stream()
233                             .map(ProjectPath::order)
234                             .reduce(0, Integer::sum);
235         }
236         return result;
237     }
238 
239     private BuildScope calculateMainProjectBuildScope(DependencyScopeImpl dependencyScope) {
240         for (ProjectPath projectPath : buildScopeSource.allProjectPaths().stream()
241                 .sorted(Comparator.comparing(ProjectPath::order))
242                 .collect(Collectors.toList())) {
243             for (BuildPath buildPath : buildScopeSource.allBuildPaths().stream()
244                     .sorted(Comparator.comparing(BuildPath::order))
245                     .collect(Collectors.toList())) {
246                 for (BuildScope buildScope : buildScopeSource.query(dependencyScope.getPresence())) {
247                     if (buildScope.getProjectPaths().contains(projectPath)
248                             && buildScope.getBuildPaths().contains(buildPath)) {
249                         return buildScope;
250                     }
251                 }
252             }
253         }
254         return null;
255     }
256 
257     /**
258      * Visible for testing.
259      */
260     Set<String> getDirectlyIncludedLabels(ResolutionScope resolutionScope) {
261         return translate(resolutionScope).getDirectlyIncluded().stream()
262                 .map(DependencyScope::getId)
263                 .collect(Collectors.toSet());
264     }
265 
266     /**
267      * Visible for testing.
268      */
269     Set<String> getDirectlyExcludedLabels(ResolutionScope resolutionScope) {
270         ResolutionScopeImpl rs = translate(resolutionScope);
271         return dependencyScopes.values().stream()
272                 .filter(s -> !rs.getDirectlyIncluded().contains(s))
273                 .map(DependencyScope::getId)
274                 .collect(Collectors.toSet());
275     }
276 
277     /**
278      * Visible for testing.
279      */
280     Set<String> getTransitivelyExcludedLabels(ResolutionScope resolutionScope) {
281         return translate(resolutionScope).getTransitivelyExcluded().stream()
282                 .map(DependencyScope::getId)
283                 .collect(Collectors.toSet());
284     }
285 
286     /**
287      * Visible for testing.
288      */
289     Set<BuildScopeQuery> getPresence(DependencyScope dependencyScope) {
290         return translate(dependencyScope).getPresence();
291     }
292 
293     /**
294      * Visible for testing.
295      */
296     BuildScopeSource getBuildScopeSource() {
297         return buildScopeSource;
298     }
299 
300     private DependencyScopeImpl translate(DependencyScope dependencyScope) {
301         return requireNonNull(dependencyScopes.get(dependencyScope.getId()), "unknown dependency scope");
302     }
303 
304     private ResolutionScopeImpl translate(ResolutionScope resolutionScope) {
305         return requireNonNull(resolutionScopes.get(resolutionScope.getId()), "unknown resolution scope");
306     }
307 
308     @Override
309     public boolean equals(Object o) {
310         if (this == o) {
311             return true;
312         }
313         if (o == null || getClass() != o.getClass()) {
314             return false;
315         }
316         ScopeManagerImpl that = (ScopeManagerImpl) o;
317         return Objects.equals(id, that.id);
318     }
319 
320     @Override
321     public int hashCode() {
322         return Objects.hash(id);
323     }
324 
325     @Override
326     public String toString() {
327         return id;
328     }
329 
330     private class DependencyScopeImpl implements DependencyScope {
331         private final String id;
332         private final boolean transitive;
333         private final Set<BuildScopeQuery> presence;
334         private final BuildScope mainBuildScope;
335         private final int width;
336 
337         private DependencyScopeImpl(String id, boolean transitive, Collection<BuildScopeQuery> presence) {
338             this.id = requireNonNull(id, "id");
339             this.transitive = transitive;
340             this.presence = Collections.unmodifiableSet(new HashSet<>(presence));
341             this.mainBuildScope = calculateMainProjectBuildScope(this);
342             this.width = calculateDependencyScopeWidth(this);
343         }
344 
345         @Override
346         public String getId() {
347             return id;
348         }
349 
350         @Override
351         public boolean isTransitive() {
352             return transitive;
353         }
354 
355         public Set<BuildScopeQuery> getPresence() {
356             return presence;
357         }
358 
359         public BuildScope getMainBuildScope() {
360             return mainBuildScope;
361         }
362 
363         public int getWidth() {
364             return width;
365         }
366 
367         @Override
368         public boolean equals(Object o) {
369             if (this == o) {
370                 return true;
371             }
372             if (o == null || getClass() != o.getClass()) {
373                 return false;
374             }
375             DependencyScopeImpl that = (DependencyScopeImpl) o;
376             return Objects.equals(id, that.id);
377         }
378 
379         @Override
380         public int hashCode() {
381             return Objects.hash(id);
382         }
383 
384         @Override
385         public String toString() {
386             return id;
387         }
388     }
389 
390     private class SystemDependencyScopeImpl extends DependencyScopeImpl implements SystemDependencyScope {
391         private final String systemPathProperty;
392 
393         private SystemDependencyScopeImpl(
394                 String id, boolean transitive, Collection<BuildScopeQuery> presence, String systemPathProperty) {
395             super(id, transitive, presence);
396             this.systemPathProperty = requireNonNull(systemPathProperty);
397         }
398 
399         @Override
400         public String getSystemPath(Artifact artifact) {
401             return artifact.getProperty(systemPathProperty, null);
402         }
403 
404         @Override
405         public void setSystemPath(Map<String, String> properties, String systemPath) {
406             if (systemPath == null) {
407                 properties.remove(systemPathProperty);
408             } else {
409                 properties.put(systemPathProperty, systemPath);
410             }
411         }
412     }
413 
414     private class ResolutionScopeImpl implements ResolutionScope {
415         private final String id;
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                 Mode mode,
424                 Collection<BuildScopeQuery> wantedPresence,
425                 Collection<DependencyScope> explicitlyIncluded,
426                 Collection<DependencyScope> transitivelyExcluded) {
427             this.id = requireNonNull(id, "id");
428             this.mode = requireNonNull(mode, "mode");
429             this.wantedPresence = Collections.unmodifiableSet(new HashSet<>(wantedPresence));
430             Set<DependencyScope> included = collectScopes(wantedPresence);
431             // here we may have null elements, based on existence of system scope
432             if (explicitlyIncluded != null && !explicitlyIncluded.isEmpty()) {
433                 explicitlyIncluded.stream().filter(Objects::nonNull).forEach(included::add);
434             }
435             this.directlyIncluded = Collections.unmodifiableSet(included);
436             this.transitivelyExcluded = Collections.unmodifiableSet(
437                     transitivelyExcluded.stream().filter(Objects::nonNull).collect(Collectors.toSet()));
438         }
439 
440         @Override
441         public String getId() {
442             return id;
443         }
444 
445         public Mode getMode() {
446             return mode;
447         }
448 
449         public Set<BuildScopeQuery> getWantedPresence() {
450             return wantedPresence;
451         }
452 
453         public Set<DependencyScope> getDirectlyIncluded() {
454             return directlyIncluded;
455         }
456 
457         public Set<DependencyScope> getTransitivelyExcluded() {
458             return transitivelyExcluded;
459         }
460 
461         @Override
462         public boolean equals(Object o) {
463             if (this == o) {
464                 return true;
465             }
466             if (o == null || getClass() != o.getClass()) {
467                 return false;
468             }
469             ResolutionScopeImpl that = (ResolutionScopeImpl) o;
470             return Objects.equals(id, that.id);
471         }
472 
473         @Override
474         public int hashCode() {
475             return Objects.hash(id);
476         }
477 
478         @Override
479         public String toString() {
480             return id;
481         }
482     }
483 }