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