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.collect;
20  
21  import java.lang.ref.WeakReference;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Objects;
28  import java.util.WeakHashMap;
29  import java.util.concurrent.ConcurrentHashMap;
30  
31  import org.eclipse.aether.RepositoryCache;
32  import org.eclipse.aether.RepositorySystemSession;
33  import org.eclipse.aether.artifact.Artifact;
34  import org.eclipse.aether.collection.DependencyManager;
35  import org.eclipse.aether.collection.DependencySelector;
36  import org.eclipse.aether.collection.DependencyTraverser;
37  import org.eclipse.aether.collection.VersionFilter;
38  import org.eclipse.aether.graph.Dependency;
39  import org.eclipse.aether.graph.DependencyNode;
40  import org.eclipse.aether.repository.ArtifactRepository;
41  import org.eclipse.aether.repository.RemoteRepository;
42  import org.eclipse.aether.resolution.ArtifactDescriptorException;
43  import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
44  import org.eclipse.aether.resolution.ArtifactDescriptorResult;
45  import org.eclipse.aether.resolution.VersionRangeRequest;
46  import org.eclipse.aether.resolution.VersionRangeResult;
47  import org.eclipse.aether.util.ConfigUtils;
48  import org.eclipse.aether.version.Version;
49  import org.eclipse.aether.version.VersionConstraint;
50  
51  /**
52   * Internal helper class for collector implementations.
53   */
54  public final class DataPool {
55      private static final String CONFIG_PROP_COLLECTOR_POOL_ARTIFACT = "aether.dependencyCollector.pool.artifact";
56  
57      private static final String CONFIG_PROP_COLLECTOR_POOL_DEPENDENCY = "aether.dependencyCollector.pool.dependency";
58  
59      private static final String CONFIG_PROP_COLLECTOR_POOL_DESCRIPTOR = "aether.dependencyCollector.pool.descriptor";
60  
61      private static final String ARTIFACT_POOL = DataPool.class.getName() + "$Artifact";
62  
63      private static final String DEPENDENCY_POOL = DataPool.class.getName() + "$Dependency";
64  
65      private static final String DESCRIPTORS = DataPool.class.getName() + "$Descriptors";
66  
67      public static final ArtifactDescriptorResult NO_DESCRIPTOR =
68              new ArtifactDescriptorResult(new ArtifactDescriptorRequest());
69  
70      /**
71       * Artifact interning pool, lives across session (if session carries non-null {@link RepositoryCache}).
72       */
73      private final InternPool<Artifact, Artifact> artifacts;
74  
75      /**
76       * Dependency interning pool, lives across session (if session carries non-null {@link RepositoryCache}).
77       */
78      private final InternPool<Dependency, Dependency> dependencies;
79  
80      /**
81       * Descriptor interning pool, lives across session (if session carries non-null {@link RepositoryCache}).
82       */
83      private final InternPool<Object, Descriptor> descriptors;
84  
85      /**
86       * Constraint cache, lives during single collection invocation (same as this DataPool instance).
87       */
88      private final ConcurrentHashMap<Object, Constraint> constraints;
89  
90      /**
91       * DependencyNode cache, lives during single collection invocation (same as this DataPool instance).
92       */
93      private final ConcurrentHashMap<Object, List<DependencyNode>> nodes;
94  
95      @SuppressWarnings("unchecked")
96      public DataPool(RepositorySystemSession session) {
97          final RepositoryCache cache = session.getCache();
98  
99          InternPool<Artifact, Artifact> artifactsPool = null;
100         InternPool<Dependency, Dependency> dependenciesPool = null;
101         InternPool<Object, Descriptor> descriptorsPool = null;
102         if (cache != null) {
103             artifactsPool = (InternPool<Artifact, Artifact>) cache.get(session, ARTIFACT_POOL);
104             dependenciesPool = (InternPool<Dependency, Dependency>) cache.get(session, DEPENDENCY_POOL);
105             descriptorsPool = (InternPool<Object, Descriptor>) cache.get(session, DESCRIPTORS);
106         }
107 
108         if (artifactsPool == null) {
109             String artifactPoolType = ConfigUtils.getString(session, WEAK, CONFIG_PROP_COLLECTOR_POOL_ARTIFACT);
110 
111             artifactsPool = createPool(artifactPoolType);
112             if (cache != null) {
113                 cache.put(session, ARTIFACT_POOL, artifactsPool);
114             }
115         }
116 
117         if (dependenciesPool == null) {
118             String dependencyPoolType = ConfigUtils.getString(session, WEAK, CONFIG_PROP_COLLECTOR_POOL_DEPENDENCY);
119 
120             dependenciesPool = createPool(dependencyPoolType);
121             if (cache != null) {
122                 cache.put(session, DEPENDENCY_POOL, dependenciesPool);
123             }
124         }
125 
126         if (descriptorsPool == null) {
127             String descriptorPoolType = ConfigUtils.getString(session, HARD, CONFIG_PROP_COLLECTOR_POOL_DESCRIPTOR);
128 
129             descriptorsPool = createPool(descriptorPoolType);
130             if (cache != null) {
131                 cache.put(session, DESCRIPTORS, descriptorsPool);
132             }
133         }
134 
135         this.artifacts = artifactsPool;
136         this.dependencies = dependenciesPool;
137         this.descriptors = descriptorsPool;
138 
139         this.constraints = new ConcurrentHashMap<>(256);
140         this.nodes = new ConcurrentHashMap<>(256);
141     }
142 
143     public Artifact intern(Artifact artifact) {
144         return artifacts.intern(artifact, artifact);
145     }
146 
147     public Dependency intern(Dependency dependency) {
148         return dependencies.intern(dependency, dependency);
149     }
150 
151     public Object toKey(ArtifactDescriptorRequest request) {
152         return request.getArtifact();
153     }
154 
155     public ArtifactDescriptorResult getDescriptor(Object key, ArtifactDescriptorRequest request) {
156         Descriptor descriptor = descriptors.get(key);
157         if (descriptor != null) {
158             return descriptor.toResult(request);
159         }
160         return null;
161     }
162 
163     public void putDescriptor(Object key, ArtifactDescriptorResult result) {
164         descriptors.intern(key, new GoodDescriptor(result));
165     }
166 
167     public void putDescriptor(Object key, ArtifactDescriptorException e) {
168         descriptors.intern(key, BadDescriptor.INSTANCE);
169     }
170 
171     public Object toKey(VersionRangeRequest request) {
172         return new ConstraintKey(request);
173     }
174 
175     public VersionRangeResult getConstraint(Object key, VersionRangeRequest request) {
176         Constraint constraint = constraints.get(key);
177         if (constraint != null) {
178             return constraint.toResult(request);
179         }
180         return null;
181     }
182 
183     public void putConstraint(Object key, VersionRangeResult result) {
184         constraints.put(key, new Constraint(result));
185     }
186 
187     public Object toKey(
188             Artifact artifact,
189             List<RemoteRepository> repositories,
190             DependencySelector selector,
191             DependencyManager manager,
192             DependencyTraverser traverser,
193             VersionFilter filter) {
194         return new GraphKey(artifact, repositories, selector, manager, traverser, filter);
195     }
196 
197     public List<DependencyNode> getChildren(Object key) {
198         return nodes.get(key);
199     }
200 
201     public void putChildren(Object key, List<DependencyNode> children) {
202         nodes.put(key, children);
203     }
204 
205     abstract static class Descriptor {
206 
207         public abstract ArtifactDescriptorResult toResult(ArtifactDescriptorRequest request);
208     }
209 
210     static final class GoodDescriptor extends Descriptor {
211 
212         final Artifact artifact;
213 
214         final List<Artifact> relocations;
215 
216         final Collection<Artifact> aliases;
217 
218         final List<RemoteRepository> repositories;
219 
220         final List<Dependency> dependencies;
221 
222         final List<Dependency> managedDependencies;
223 
224         GoodDescriptor(ArtifactDescriptorResult result) {
225             artifact = result.getArtifact();
226             relocations = result.getRelocations();
227             aliases = result.getAliases();
228             dependencies = result.getDependencies();
229             managedDependencies = result.getManagedDependencies();
230             repositories = result.getRepositories();
231         }
232 
233         public ArtifactDescriptorResult toResult(ArtifactDescriptorRequest request) {
234             ArtifactDescriptorResult result = new ArtifactDescriptorResult(request);
235             result.setArtifact(artifact);
236             result.setRelocations(relocations);
237             result.setAliases(aliases);
238             result.setDependencies(dependencies);
239             result.setManagedDependencies(managedDependencies);
240             result.setRepositories(repositories);
241             return result;
242         }
243     }
244 
245     static final class BadDescriptor extends Descriptor {
246 
247         static final BadDescriptor INSTANCE = new BadDescriptor();
248 
249         public ArtifactDescriptorResult toResult(ArtifactDescriptorRequest request) {
250             return NO_DESCRIPTOR;
251         }
252     }
253 
254     private static final class Constraint {
255         final VersionRepo[] repositories;
256 
257         final VersionConstraint versionConstraint;
258 
259         Constraint(VersionRangeResult result) {
260             versionConstraint = result.getVersionConstraint();
261             List<Version> versions = result.getVersions();
262             repositories = new VersionRepo[versions.size()];
263             int i = 0;
264             for (Version version : versions) {
265                 repositories[i++] = new VersionRepo(version, result.getRepository(version));
266             }
267         }
268 
269         VersionRangeResult toResult(VersionRangeRequest request) {
270             VersionRangeResult result = new VersionRangeResult(request);
271             for (VersionRepo vr : repositories) {
272                 result.addVersion(vr.version);
273                 result.setRepository(vr.version, vr.repo);
274             }
275             result.setVersionConstraint(versionConstraint);
276             return result;
277         }
278 
279         static final class VersionRepo {
280             final Version version;
281 
282             final ArtifactRepository repo;
283 
284             VersionRepo(Version version, ArtifactRepository repo) {
285                 this.version = version;
286                 this.repo = repo;
287             }
288         }
289     }
290 
291     static final class ConstraintKey {
292         private final Artifact artifact;
293 
294         private final List<RemoteRepository> repositories;
295 
296         private final int hashCode;
297 
298         ConstraintKey(VersionRangeRequest request) {
299             artifact = request.getArtifact();
300             repositories = request.getRepositories();
301             hashCode = artifact.hashCode();
302         }
303 
304         @Override
305         public boolean equals(Object obj) {
306             if (obj == this) {
307                 return true;
308             } else if (!(obj instanceof ConstraintKey)) {
309                 return false;
310             }
311             ConstraintKey that = (ConstraintKey) obj;
312             return artifact.equals(that.artifact) && equals(repositories, that.repositories);
313         }
314 
315         private static boolean equals(List<RemoteRepository> repos1, List<RemoteRepository> repos2) {
316             if (repos1.size() != repos2.size()) {
317                 return false;
318             }
319             for (Iterator<RemoteRepository> it1 = repos1.iterator(), it2 = repos2.iterator();
320                     it1.hasNext() && it2.hasNext(); ) {
321                 RemoteRepository repo1 = it1.next();
322                 RemoteRepository repo2 = it2.next();
323                 if (repo1.isRepositoryManager() != repo2.isRepositoryManager()) {
324                     return false;
325                 }
326                 if (repo1.isRepositoryManager()) {
327                     if (!equals(repo1.getMirroredRepositories(), repo2.getMirroredRepositories())) {
328                         return false;
329                     }
330                 } else if (!repo1.getUrl().equals(repo2.getUrl())) {
331                     return false;
332                 } else if (repo1.getPolicy(true).isEnabled()
333                         != repo2.getPolicy(true).isEnabled()) {
334                     return false;
335                 } else if (repo1.getPolicy(false).isEnabled()
336                         != repo2.getPolicy(false).isEnabled()) {
337                     return false;
338                 }
339             }
340             return true;
341         }
342 
343         @Override
344         public int hashCode() {
345             return hashCode;
346         }
347     }
348 
349     static final class GraphKey {
350         private final Artifact artifact;
351 
352         private final List<RemoteRepository> repositories;
353 
354         private final DependencySelector selector;
355 
356         private final DependencyManager manager;
357 
358         private final DependencyTraverser traverser;
359 
360         private final VersionFilter filter;
361 
362         private final int hashCode;
363 
364         GraphKey(
365                 Artifact artifact,
366                 List<RemoteRepository> repositories,
367                 DependencySelector selector,
368                 DependencyManager manager,
369                 DependencyTraverser traverser,
370                 VersionFilter filter) {
371             this.artifact = artifact;
372             this.repositories = repositories;
373             this.selector = selector;
374             this.manager = manager;
375             this.traverser = traverser;
376             this.filter = filter;
377 
378             hashCode = Objects.hash(artifact, repositories, selector, manager, traverser, filter);
379         }
380 
381         @Override
382         public boolean equals(Object obj) {
383             if (obj == this) {
384                 return true;
385             } else if (!(obj instanceof GraphKey)) {
386                 return false;
387             }
388             GraphKey that = (GraphKey) obj;
389             return Objects.equals(artifact, that.artifact)
390                     && Objects.equals(repositories, that.repositories)
391                     && Objects.equals(selector, that.selector)
392                     && Objects.equals(manager, that.manager)
393                     && Objects.equals(traverser, that.traverser)
394                     && Objects.equals(filter, that.filter);
395         }
396 
397         @Override
398         public int hashCode() {
399             return hashCode;
400         }
401     }
402 
403     private static <K, V> InternPool<K, V> createPool(String type) {
404         if (HARD.equals(type)) {
405             return new HardInternPool<>();
406         } else if (WEAK.equals(type)) {
407             return new WeakInternPool<>();
408         } else {
409             throw new IllegalArgumentException("Unknown object pool type: '" + type + "'");
410         }
411     }
412 
413     private static final String HARD = "hard";
414 
415     private static final String WEAK = "weak";
416 
417     private interface InternPool<K, V> {
418         V get(K key);
419 
420         V intern(K key, V value);
421     }
422 
423     private static class HardInternPool<K, V> implements InternPool<K, V> {
424         private final ConcurrentHashMap<K, V> map = new ConcurrentHashMap<>(256);
425 
426         @Override
427         public V get(K key) {
428             return map.get(key);
429         }
430 
431         @Override
432         public V intern(K key, V value) {
433             return map.computeIfAbsent(key, k -> value);
434         }
435     }
436 
437     private static class WeakInternPool<K, V> implements InternPool<K, V> {
438         private final Map<K, WeakReference<V>> map = Collections.synchronizedMap(new WeakHashMap<>(256));
439 
440         @Override
441         public V get(K key) {
442             WeakReference<V> ref = map.get(key);
443             return ref != null ? ref.get() : null;
444         }
445 
446         @Override
447         public V intern(K key, V value) {
448             WeakReference<V> pooledRef = map.get(key);
449             if (pooledRef != null) {
450                 V pooled = pooledRef.get();
451                 if (pooled != null) {
452                     return pooled;
453                 }
454             }
455             map.put(key, new WeakReference<>(value));
456             return value;
457         }
458     }
459 }