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