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