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.util.graph.manager;
20  
21  import java.util.Collection;
22  import java.util.Collections;
23  import java.util.HashMap;
24  import java.util.LinkedHashSet;
25  import java.util.Map;
26  import java.util.Objects;
27  
28  import org.eclipse.aether.artifact.Artifact;
29  import org.eclipse.aether.artifact.ArtifactProperties;
30  import org.eclipse.aether.collection.DependencyCollectionContext;
31  import org.eclipse.aether.collection.DependencyManagement;
32  import org.eclipse.aether.collection.DependencyManager;
33  import org.eclipse.aether.graph.Dependency;
34  import org.eclipse.aether.graph.Exclusion;
35  import org.eclipse.aether.util.artifact.JavaScopes;
36  
37  import static java.util.Objects.requireNonNull;
38  
39  /**
40   * A dependency manager managing dependencies on all levels supporting transitive dependency management.
41   * <p>
42   * <b>Note:</b>Unlike the {@code ClassicDependencyManager} and the {@code TransitiveDependencyManager} this
43   * implementation applies management also on the first level. This is considered the resolver's default behaviour.
44   * It ignores all management overrides supported by the {@code MavenModelBuilder}.
45   * </p>
46   *
47   * @author Christian Schulte
48   * @since 1.4.0
49   */
50  public final class DefaultDependencyManager implements DependencyManager {
51  
52      private final Map<Object, String> managedVersions;
53  
54      private final Map<Object, String> managedScopes;
55  
56      private final Map<Object, Boolean> managedOptionals;
57  
58      private final Map<Object, String> managedLocalPaths;
59  
60      private final Map<Object, Collection<Exclusion>> managedExclusions;
61  
62      private int hashCode;
63  
64      /**
65       * Creates a new dependency manager without any management information.
66       */
67      public DefaultDependencyManager() {
68          this(
69                  Collections.<Object, String>emptyMap(),
70                  Collections.<Object, String>emptyMap(),
71                  Collections.<Object, Boolean>emptyMap(),
72                  Collections.<Object, String>emptyMap(),
73                  Collections.<Object, Collection<Exclusion>>emptyMap());
74      }
75  
76      private DefaultDependencyManager(
77              final Map<Object, String> managedVersions,
78              final Map<Object, String> managedScopes,
79              final Map<Object, Boolean> managedOptionals,
80              final Map<Object, String> managedLocalPaths,
81              final Map<Object, Collection<Exclusion>> managedExclusions) {
82          super();
83          this.managedVersions = managedVersions;
84          this.managedScopes = managedScopes;
85          this.managedOptionals = managedOptionals;
86          this.managedLocalPaths = managedLocalPaths;
87          this.managedExclusions = managedExclusions;
88      }
89  
90      public DependencyManager deriveChildManager(final DependencyCollectionContext context) {
91          requireNonNull(context, "context cannot be null");
92          Map<Object, String> versions = this.managedVersions;
93          Map<Object, String> scopes = this.managedScopes;
94          Map<Object, Boolean> optionals = this.managedOptionals;
95          Map<Object, String> localPaths = this.managedLocalPaths;
96          Map<Object, Collection<Exclusion>> exclusions = this.managedExclusions;
97  
98          for (Dependency managedDependency : context.getManagedDependencies()) {
99              Artifact artifact = managedDependency.getArtifact();
100             Object key = getKey(artifact);
101 
102             String version = artifact.getVersion();
103             if (version.length() > 0 && !versions.containsKey(key)) {
104                 if (versions == this.managedVersions) {
105                     versions = new HashMap<>(this.managedVersions);
106                 }
107                 versions.put(key, version);
108             }
109 
110             String scope = managedDependency.getScope();
111             if (scope.length() > 0 && !scopes.containsKey(key)) {
112                 if (scopes == this.managedScopes) {
113                     scopes = new HashMap<>(this.managedScopes);
114                 }
115                 scopes.put(key, scope);
116             }
117 
118             Boolean optional = managedDependency.getOptional();
119             if (optional != null && !optionals.containsKey(key)) {
120                 if (optionals == this.managedOptionals) {
121                     optionals = new HashMap<>(this.managedOptionals);
122                 }
123                 optionals.put(key, optional);
124             }
125 
126             String localPath = managedDependency.getArtifact().getProperty(ArtifactProperties.LOCAL_PATH, null);
127             if (localPath != null && !localPaths.containsKey(key)) {
128                 if (localPaths == this.managedLocalPaths) {
129                     localPaths = new HashMap<>(this.managedLocalPaths);
130                 }
131                 localPaths.put(key, localPath);
132             }
133 
134             if (!managedDependency.getExclusions().isEmpty()) {
135                 if (exclusions == this.managedExclusions) {
136                     exclusions = new HashMap<>(this.managedExclusions);
137                 }
138                 Collection<Exclusion> managed = exclusions.computeIfAbsent(key, k -> new LinkedHashSet<>());
139                 managed.addAll(managedDependency.getExclusions());
140             }
141         }
142 
143         return new DefaultDependencyManager(versions, scopes, optionals, localPaths, exclusions);
144     }
145 
146     public DependencyManagement manageDependency(Dependency dependency) {
147         requireNonNull(dependency, "dependency cannot be null");
148         DependencyManagement management = null;
149 
150         Object key = getKey(dependency.getArtifact());
151 
152         String version = managedVersions.get(key);
153         if (version != null) {
154             management = new DependencyManagement();
155             management.setVersion(version);
156         }
157 
158         String scope = managedScopes.get(key);
159         if (scope != null) {
160             if (management == null) {
161                 management = new DependencyManagement();
162             }
163             management.setScope(scope);
164 
165             if (!JavaScopes.SYSTEM.equals(scope)
166                     && dependency.getArtifact().getProperty(ArtifactProperties.LOCAL_PATH, null) != null) {
167                 Map<String, String> properties =
168                         new HashMap<>(dependency.getArtifact().getProperties());
169 
170                 properties.remove(ArtifactProperties.LOCAL_PATH);
171                 management.setProperties(properties);
172             }
173         }
174 
175         if ((scope != null && JavaScopes.SYSTEM.equals(scope))
176                 || (scope == null && JavaScopes.SYSTEM.equals(dependency.getScope()))) {
177             String localPath = managedLocalPaths.get(key);
178             if (localPath != null) {
179                 if (management == null) {
180                     management = new DependencyManagement();
181                 }
182 
183                 Map<String, String> properties =
184                         new HashMap<>(dependency.getArtifact().getProperties());
185 
186                 properties.put(ArtifactProperties.LOCAL_PATH, localPath);
187                 management.setProperties(properties);
188             }
189         }
190 
191         Boolean optional = managedOptionals.get(key);
192         if (optional != null) {
193             if (management == null) {
194                 management = new DependencyManagement();
195             }
196             management.setOptional(optional);
197         }
198 
199         Collection<Exclusion> exclusions = managedExclusions.get(key);
200         if (exclusions != null) {
201             if (management == null) {
202                 management = new DependencyManagement();
203             }
204             Collection<Exclusion> result = new LinkedHashSet<>(dependency.getExclusions());
205             result.addAll(exclusions);
206             management.setExclusions(result);
207         }
208 
209         return management;
210     }
211 
212     private Object getKey(Artifact a) {
213         return new Key(a);
214     }
215 
216     @Override
217     public boolean equals(final Object obj) {
218         boolean equal = obj instanceof DefaultDependencyManager;
219 
220         if (equal) {
221             final DefaultDependencyManager that = (DefaultDependencyManager) obj;
222             equal = Objects.equals(managedVersions, that.managedVersions)
223                     && Objects.equals(managedScopes, that.managedScopes)
224                     && Objects.equals(managedOptionals, that.managedOptionals)
225                     && Objects.equals(managedExclusions, that.managedExclusions);
226         }
227 
228         return equal;
229     }
230 
231     @Override
232     public int hashCode() {
233         if (hashCode == 0) {
234             hashCode = Objects.hash(managedVersions, managedScopes, managedOptionals, managedExclusions);
235         }
236         return hashCode;
237     }
238 
239     static class Key {
240 
241         private final Artifact artifact;
242 
243         private final int hashCode;
244 
245         Key(final Artifact artifact) {
246             this.artifact = artifact;
247             this.hashCode = Objects.hash(artifact.getGroupId(), artifact.getArtifactId());
248         }
249 
250         @Override
251         public boolean equals(final Object obj) {
252             boolean equal = obj instanceof Key;
253 
254             if (equal) {
255                 final Key that = (Key) obj;
256                 return Objects.equals(artifact.getArtifactId(), that.artifact.getArtifactId())
257                         && Objects.equals(artifact.getGroupId(), that.artifact.getGroupId())
258                         && Objects.equals(artifact.getExtension(), that.artifact.getExtension())
259                         && Objects.equals(artifact.getClassifier(), that.artifact.getClassifier());
260             }
261 
262             return false;
263         }
264 
265         @Override
266         public int hashCode() {
267             return this.hashCode;
268         }
269     }
270 }