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