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}