001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.internal.impl.collect;
020
021import java.lang.ref.WeakReference;
022import java.util.Collection;
023import java.util.Collections;
024import java.util.Iterator;
025import java.util.List;
026import java.util.Map;
027import java.util.Objects;
028import java.util.WeakHashMap;
029import java.util.concurrent.ConcurrentHashMap;
030
031import org.eclipse.aether.RepositoryCache;
032import org.eclipse.aether.RepositorySystemSession;
033import org.eclipse.aether.artifact.Artifact;
034import org.eclipse.aether.collection.DependencyManager;
035import org.eclipse.aether.collection.DependencySelector;
036import org.eclipse.aether.collection.DependencyTraverser;
037import org.eclipse.aether.collection.VersionFilter;
038import org.eclipse.aether.graph.Dependency;
039import org.eclipse.aether.graph.DependencyNode;
040import org.eclipse.aether.repository.ArtifactRepository;
041import org.eclipse.aether.repository.RemoteRepository;
042import org.eclipse.aether.resolution.ArtifactDescriptorException;
043import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
044import org.eclipse.aether.resolution.ArtifactDescriptorResult;
045import org.eclipse.aether.resolution.VersionRangeRequest;
046import org.eclipse.aether.resolution.VersionRangeResult;
047import org.eclipse.aether.util.ConfigUtils;
048import org.eclipse.aether.version.Version;
049import org.eclipse.aether.version.VersionConstraint;
050
051/**
052 * Internal helper class for collector implementations.
053 */
054public final class DataPool {
055    private static final String CONFIG_PROP_COLLECTOR_POOL_ARTIFACT = "aether.dependencyCollector.pool.artifact";
056
057    private static final String CONFIG_PROP_COLLECTOR_POOL_DEPENDENCY = "aether.dependencyCollector.pool.dependency";
058
059    private static final String CONFIG_PROP_COLLECTOR_POOL_DESCRIPTOR = "aether.dependencyCollector.pool.descriptor";
060
061    private static final String CONFIG_PROP_COLLECTOR_POOL_DEPENDENCY_LISTS =
062            "aether.dependencyCollector.pool.dependencyLists";
063
064    private static final String CONFIG_PROP_COLLECTOR_POOL_INTERN_ARTIFACT_DESCRIPTOR_DEPENDENCIES =
065            "aether.dependencyCollector.pool.internArtifactDescriptorDependencies";
066
067    private static final String CONFIG_PROP_COLLECTOR_POOL_INTERN_ARTIFACT_DESCRIPTOR_MANAGED_DEPENDENCIES =
068            "aether.dependencyCollector.pool.internArtifactDescriptorManagedDependencies";
069
070    private static final String ARTIFACT_POOL = DataPool.class.getName() + "$Artifact";
071
072    private static final String DEPENDENCY_POOL = DataPool.class.getName() + "$Dependency";
073
074    private static final String DESCRIPTORS = DataPool.class.getName() + "$Descriptors";
075
076    private static final String DEPENDENCY_LISTS_POOL = DataPool.class.getName() + "$DependencyLists";
077
078    public static final ArtifactDescriptorResult NO_DESCRIPTOR =
079            new ArtifactDescriptorResult(new ArtifactDescriptorRequest());
080
081    /**
082     * Artifact interning pool, lives across session (if session carries non-null {@link RepositoryCache}).
083     */
084    private final InternPool<Artifact, Artifact> artifacts;
085
086    /**
087     * Dependency interning pool, lives across session (if session carries non-null {@link RepositoryCache}).
088     */
089    private final InternPool<Dependency, Dependency> dependencies;
090
091    /**
092     * Descriptor interning pool, lives across session (if session carries non-null {@link RepositoryCache}).
093     */
094    private final InternPool<DescriptorKey, Descriptor> descriptors;
095
096    /**
097     * {@link Dependency} list interning pool, lives across session (if session carries non-null {@link RepositoryCache}).
098     */
099    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}