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.util.Collection; 022import java.util.Iterator; 023import java.util.List; 024import java.util.Objects; 025import java.util.concurrent.ConcurrentHashMap; 026 027import org.eclipse.aether.Keys; 028import org.eclipse.aether.RepositoryCache; 029import org.eclipse.aether.RepositorySystemSession; 030import org.eclipse.aether.artifact.Artifact; 031import org.eclipse.aether.collection.DependencyManager; 032import org.eclipse.aether.collection.DependencySelector; 033import org.eclipse.aether.collection.DependencyTraverser; 034import org.eclipse.aether.collection.VersionFilter; 035import org.eclipse.aether.graph.Dependency; 036import org.eclipse.aether.graph.DependencyNode; 037import org.eclipse.aether.repository.ArtifactRepository; 038import org.eclipse.aether.repository.RemoteRepository; 039import org.eclipse.aether.resolution.ArtifactDescriptorException; 040import org.eclipse.aether.resolution.ArtifactDescriptorRequest; 041import org.eclipse.aether.resolution.ArtifactDescriptorResult; 042import org.eclipse.aether.resolution.VersionRangeRequest; 043import org.eclipse.aether.resolution.VersionRangeResult; 044import org.eclipse.aether.util.ConfigUtils; 045import org.eclipse.aether.util.concurrency.ConcurrentWeakCache; 046import org.eclipse.aether.version.Version; 047import org.eclipse.aether.version.VersionConstraint; 048 049/** 050 * Internal helper class for collector implementations. 051 */ 052public final class DataPool { 053 public static final String CONFIG_PROPS_PREFIX = DefaultDependencyCollector.CONFIG_PROPS_PREFIX + "pool."; 054 055 /** 056 * Flag controlling interning data pool type used by dependency collector for Artifact instances, matters for 057 * heap consumption. By default, uses “weak” references (consume less heap). Using “hard” will make it much 058 * more memory aggressive and possibly faster (system and Java dependent). Supported values: "hard", "weak". 059 * 060 * @since 1.9.5 061 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} 062 * @configurationType {@link java.lang.String} 063 * @configurationDefaultValue {@link #WEAK} 064 */ 065 public static final String CONFIG_PROP_COLLECTOR_POOL_ARTIFACT = CONFIG_PROPS_PREFIX + "artifact"; 066 067 /** 068 * Flag controlling interning data pool type used by dependency collector for Dependency instances, matters for 069 * heap consumption. By default, uses “weak” references (consume less heap). Using “hard” will make it much 070 * more memory aggressive and possibly faster (system and Java dependent). Supported values: "hard", "weak". 071 * 072 * @since 1.9.5 073 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} 074 * @configurationType {@link java.lang.String} 075 * @configurationDefaultValue {@link #WEAK} 076 */ 077 public static final String CONFIG_PROP_COLLECTOR_POOL_DEPENDENCY = CONFIG_PROPS_PREFIX + "dependency"; 078 079 /** 080 * Flag controlling interning data pool type used by dependency collector for ArtifactDescriptor (POM) instances, 081 * matters for heap consumption. By default, uses “weak” references (consume less heap). Using “hard” will make it 082 * much more memory aggressive and possibly faster (system and Java dependent). Supported values: "hard", "weak". 083 * 084 * @since 1.9.5 085 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} 086 * @configurationType {@link java.lang.String} 087 * @configurationDefaultValue {@link #HARD} 088 */ 089 public static final String CONFIG_PROP_COLLECTOR_POOL_DESCRIPTOR = CONFIG_PROPS_PREFIX + "descriptor"; 090 091 /** 092 * Flag controlling interning data pool type used by dependency lists collector for ArtifactDescriptor (POM) instances, 093 * matters for heap consumption. By default, uses “weak” references (consume less heap). Using “hard” will make it 094 * much more memory aggressive and possibly faster (system and Java dependent). Supported values: "hard", "weak". 095 * 096 * @since 1.9.22 097 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} 098 * @configurationType {@link java.lang.String} 099 * @configurationDefaultValue {@link #HARD} 100 */ 101 public static final String CONFIG_PROP_COLLECTOR_POOL_DEPENDENCY_LISTS = 102 "aether.dependencyCollector.pool.dependencyLists"; 103 104 /** 105 * Flag controlling interning artifact descriptor dependencies. 106 * 107 * @since 1.9.22 108 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} 109 * @configurationType {@link java.lang.Boolean} 110 * @configurationDefaultValue false 111 */ 112 public static final String CONFIG_PROP_COLLECTOR_POOL_INTERN_ARTIFACT_DESCRIPTOR_DEPENDENCIES = 113 "aether.dependencyCollector.pool.internArtifactDescriptorDependencies"; 114 115 /** 116 * Flag controlling interning artifact descriptor managed dependencies. 117 * 118 * @since 1.9.22 119 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} 120 * @configurationType {@link java.lang.Boolean} 121 * @configurationDefaultValue true 122 */ 123 public static final String CONFIG_PROP_COLLECTOR_POOL_INTERN_ARTIFACT_DESCRIPTOR_MANAGED_DEPENDENCIES = 124 "aether.dependencyCollector.pool.internArtifactDescriptorManagedDependencies"; 125 126 private static final Object ARTIFACT_POOL = Keys.of(DataPool.class, "artifact"); 127 128 private static final Object DEPENDENCY_POOL = Keys.of(DataPool.class, "dependency"); 129 130 private static final Object DESCRIPTORS = Keys.of(DataPool.class, "descriptors"); 131 132 private static final Object DEPENDENCY_LISTS_POOL = Keys.of(DataPool.class, "dependencyLists"); 133 134 public static final ArtifactDescriptorResult NO_DESCRIPTOR = 135 new ArtifactDescriptorResult(new ArtifactDescriptorRequest()); 136 137 /** 138 * Artifact interning pool, lives across session (if session carries non-null {@link RepositoryCache}). 139 */ 140 private final InternPool<Artifact, Artifact> artifacts; 141 142 /** 143 * Dependency interning pool, lives across session (if session carries non-null {@link RepositoryCache}). 144 */ 145 private final InternPool<Dependency, Dependency> dependencies; 146 147 /** 148 * Descriptor interning pool, lives across session (if session carries non-null {@link RepositoryCache}). 149 */ 150 private final InternPool<DescriptorKey, Descriptor> descriptors; 151 152 /** 153 * {@link Dependency} list interning pool, lives across session (if session carries non-null {@link RepositoryCache}). 154 */ 155 private final InternPool<List<Dependency>, List<Dependency>> dependencyLists; 156 157 /** 158 * Constraint cache, lives during single collection invocation (same as this DataPool instance). 159 */ 160 private final ConcurrentHashMap<Object, Constraint> constraints; 161 162 /** 163 * DependencyNode cache, lives during single collection invocation (same as this DataPool instance). 164 */ 165 private final ConcurrentHashMap<Object, List<DependencyNode>> nodes; 166 167 private final boolean internArtifactDescriptorDependencies; 168 169 private final boolean internArtifactDescriptorManagedDependencies; 170 171 @SuppressWarnings("unchecked") 172 public DataPool(RepositorySystemSession session) { 173 final RepositoryCache cache = session.getCache(); 174 175 internArtifactDescriptorDependencies = ConfigUtils.getBoolean( 176 session, false, CONFIG_PROP_COLLECTOR_POOL_INTERN_ARTIFACT_DESCRIPTOR_DEPENDENCIES); 177 internArtifactDescriptorManagedDependencies = ConfigUtils.getBoolean( 178 session, true, CONFIG_PROP_COLLECTOR_POOL_INTERN_ARTIFACT_DESCRIPTOR_MANAGED_DEPENDENCIES); 179 180 InternPool<Artifact, Artifact> artifactsPool; 181 InternPool<Dependency, Dependency> dependenciesPool; 182 InternPool<DescriptorKey, Descriptor> descriptorsPool; 183 InternPool<List<Dependency>, List<Dependency>> dependencyListsPool; 184 if (cache != null) { 185 artifactsPool = (InternPool<Artifact, Artifact>) cache.computeIfAbsent( 186 session, 187 ARTIFACT_POOL, 188 () -> createPool(ConfigUtils.getString(session, WEAK, CONFIG_PROP_COLLECTOR_POOL_ARTIFACT))); 189 dependenciesPool = (InternPool<Dependency, Dependency>) cache.computeIfAbsent( 190 session, 191 DEPENDENCY_POOL, 192 () -> createPool(ConfigUtils.getString(session, WEAK, CONFIG_PROP_COLLECTOR_POOL_DEPENDENCY))); 193 descriptorsPool = (InternPool<DescriptorKey, Descriptor>) cache.computeIfAbsent( 194 session, 195 DESCRIPTORS, 196 () -> createPool(ConfigUtils.getString(session, HARD, CONFIG_PROP_COLLECTOR_POOL_DESCRIPTOR))); 197 dependencyListsPool = (InternPool<List<Dependency>, List<Dependency>>) cache.computeIfAbsent( 198 session, 199 DEPENDENCY_LISTS_POOL, 200 () -> createPool( 201 ConfigUtils.getString(session, HARD, CONFIG_PROP_COLLECTOR_POOL_DEPENDENCY_LISTS))); 202 } else { 203 artifactsPool = createPool(ConfigUtils.getString(session, WEAK, CONFIG_PROP_COLLECTOR_POOL_ARTIFACT)); 204 dependenciesPool = createPool(ConfigUtils.getString(session, WEAK, CONFIG_PROP_COLLECTOR_POOL_DEPENDENCY)); 205 descriptorsPool = createPool(ConfigUtils.getString(session, HARD, CONFIG_PROP_COLLECTOR_POOL_DESCRIPTOR)); 206 dependencyListsPool = 207 createPool(ConfigUtils.getString(session, HARD, CONFIG_PROP_COLLECTOR_POOL_DEPENDENCY_LISTS)); 208 } 209 210 this.artifacts = artifactsPool; 211 this.dependencies = dependenciesPool; 212 this.descriptors = descriptorsPool; 213 this.dependencyLists = dependencyListsPool; 214 215 this.constraints = new ConcurrentHashMap<>(256); 216 this.nodes = new ConcurrentHashMap<>(256); 217 } 218 219 public Artifact intern(Artifact artifact) { 220 return artifacts.intern(artifact, artifact); 221 } 222 223 public Dependency intern(Dependency dependency) { 224 return dependencies.intern(dependency, dependency); 225 } 226 227 public DescriptorKey toKey(ArtifactDescriptorRequest request) { 228 return new DescriptorKey(request.getArtifact(), request.getRepositories()); 229 } 230 231 public ArtifactDescriptorResult getDescriptor(DescriptorKey key, ArtifactDescriptorRequest request) { 232 Descriptor descriptor = descriptors.get(key); 233 if (descriptor != null) { 234 return descriptor.toResult(request); 235 } 236 return null; 237 } 238 239 public void putDescriptor(DescriptorKey key, ArtifactDescriptorResult result) { 240 if (internArtifactDescriptorDependencies) { 241 result.setDependencies(intern(result.getDependencies())); 242 } 243 if (internArtifactDescriptorManagedDependencies) { 244 result.setManagedDependencies(intern(result.getManagedDependencies())); 245 } 246 descriptors.intern(key, new GoodDescriptor(result)); 247 } 248 249 public void putDescriptor(DescriptorKey key, ArtifactDescriptorException e) { 250 descriptors.intern(key, new BadDescriptor(e)); 251 } 252 253 /** 254 * Returns the failure reason of a previously cached bad descriptor, or {@code null} if the given key does not 255 * map to a cached failure. Only the exception message is retained (not the exception itself), to avoid pinning 256 * the originating request in the session-wide pool. 257 */ 258 public String getDescriptorFailure(DescriptorKey key) { 259 Descriptor descriptor = descriptors.get(key); 260 if (descriptor instanceof BadDescriptor) { 261 return ((BadDescriptor) descriptor).reason; 262 } 263 return null; 264 } 265 266 private List<Dependency> intern(List<Dependency> dependencies) { 267 return dependencyLists.intern(dependencies, dependencies); 268 } 269 270 public Object toKey(VersionRangeRequest request) { 271 return new ConstraintKey(request); 272 } 273 274 public VersionRangeResult getConstraint(Object key, VersionRangeRequest request) { 275 Constraint constraint = constraints.get(key); 276 if (constraint != null) { 277 return constraint.toResult(request); 278 } 279 return null; 280 } 281 282 public void putConstraint(Object key, VersionRangeResult result) { 283 constraints.put(key, new Constraint(result)); 284 } 285 286 public Object toKey( 287 Artifact artifact, 288 List<RemoteRepository> repositories, 289 DependencySelector selector, 290 DependencyManager manager, 291 DependencyTraverser traverser, 292 VersionFilter filter) { 293 return new GraphKey(artifact, repositories, selector, manager, traverser, filter); 294 } 295 296 public List<DependencyNode> getChildren(Object key) { 297 return nodes.get(key); 298 } 299 300 public void putChildren(Object key, List<DependencyNode> children) { 301 nodes.put(key, children); 302 } 303 304 public static final class DescriptorKey { 305 private final Artifact artifact; 306 private final List<RemoteRepository> repositories; 307 private final int hashCode; 308 309 private DescriptorKey(Artifact artifact, List<RemoteRepository> repositories) { 310 this.artifact = artifact; 311 this.repositories = repositories; 312 this.hashCode = Objects.hashCode(artifact); 313 } 314 315 @Override 316 public boolean equals(Object o) { 317 if (this == o) { 318 return true; 319 } 320 if (o == null || getClass() != o.getClass()) { 321 return false; 322 } 323 DescriptorKey that = (DescriptorKey) o; 324 return Objects.equals(artifact, that.artifact) && repositoriesEquals(repositories, that.repositories); 325 } 326 327 @Override 328 public int hashCode() { 329 return hashCode; 330 } 331 332 @Override 333 public String toString() { 334 return getClass().getSimpleName() + "{" + "artifact='" + artifact + '\'' + ", repositories='" + repositories 335 + '\'' + '}'; 336 } 337 } 338 339 abstract static class Descriptor { 340 public abstract ArtifactDescriptorResult toResult(ArtifactDescriptorRequest request); 341 } 342 343 static final class GoodDescriptor extends Descriptor { 344 345 final Artifact artifact; 346 347 final List<Artifact> relocations; 348 349 final Collection<Artifact> aliases; 350 351 final List<RemoteRepository> repositories; 352 353 final List<Dependency> dependencies; 354 355 final List<Dependency> managedDependencies; 356 357 GoodDescriptor(ArtifactDescriptorResult result) { 358 artifact = result.getArtifact(); 359 relocations = result.getRelocations(); 360 aliases = result.getAliases(); 361 dependencies = result.getDependencies(); 362 managedDependencies = result.getManagedDependencies(); 363 repositories = result.getRepositories(); 364 } 365 366 public ArtifactDescriptorResult toResult(ArtifactDescriptorRequest request) { 367 ArtifactDescriptorResult result = new ArtifactDescriptorResult(request); 368 result.setArtifact(artifact); 369 result.setRelocations(relocations); 370 result.setAliases(aliases); 371 result.setDependencies(dependencies); 372 result.setManagedDependencies(managedDependencies); 373 result.setRepositories(repositories); 374 return result; 375 } 376 } 377 378 static final class BadDescriptor extends Descriptor { 379 380 final String reason; 381 382 BadDescriptor(ArtifactDescriptorException exception) { 383 this.reason = exception != null ? exception.getMessage() : null; 384 } 385 386 public ArtifactDescriptorResult toResult(ArtifactDescriptorRequest request) { 387 return NO_DESCRIPTOR; 388 } 389 } 390 391 private static final class Constraint { 392 final VersionRepo[] repositories; 393 394 final VersionConstraint versionConstraint; 395 396 Constraint(VersionRangeResult result) { 397 versionConstraint = result.getVersionConstraint(); 398 List<Version> versions = result.getVersions(); 399 repositories = new VersionRepo[versions.size()]; 400 int i = 0; 401 for (Version version : versions) { 402 repositories[i++] = new VersionRepo(version, result.getRepository(version)); 403 } 404 } 405 406 VersionRangeResult toResult(VersionRangeRequest request) { 407 VersionRangeResult result = new VersionRangeResult(request); 408 for (VersionRepo vr : repositories) { 409 result.addVersion(vr.version); 410 result.setRepository(vr.version, vr.repo); 411 } 412 result.setVersionConstraint(versionConstraint); 413 return result; 414 } 415 416 static final class VersionRepo { 417 final Version version; 418 419 final ArtifactRepository repo; 420 421 VersionRepo(Version version, ArtifactRepository repo) { 422 this.version = version; 423 this.repo = repo; 424 } 425 } 426 } 427 428 static final class ConstraintKey { 429 private final Artifact artifact; 430 431 private final List<RemoteRepository> repositories; 432 433 private final int hashCode; 434 435 ConstraintKey(VersionRangeRequest request) { 436 artifact = request.getArtifact(); 437 repositories = request.getRepositories(); 438 hashCode = artifact.hashCode(); 439 } 440 441 @Override 442 public boolean equals(Object obj) { 443 if (obj == this) { 444 return true; 445 } else if (!(obj instanceof ConstraintKey)) { 446 return false; 447 } 448 ConstraintKey that = (ConstraintKey) obj; 449 return artifact.equals(that.artifact) && repositoriesEquals(repositories, that.repositories); 450 } 451 452 @Override 453 public int hashCode() { 454 return hashCode; 455 } 456 } 457 458 private static boolean repositoriesEquals(List<RemoteRepository> repos1, List<RemoteRepository> repos2) { 459 if (repos1.size() != repos2.size()) { 460 return false; 461 } 462 for (Iterator<RemoteRepository> it1 = repos1.iterator(), it2 = repos2.iterator(); 463 it1.hasNext() && it2.hasNext(); ) { 464 RemoteRepository repo1 = it1.next(); 465 RemoteRepository repo2 = it2.next(); 466 if (repo1.isRepositoryManager() != repo2.isRepositoryManager()) { 467 return false; 468 } 469 if (repo1.isRepositoryManager()) { 470 if (!repositoriesEquals(repo1.getMirroredRepositories(), repo2.getMirroredRepositories())) { 471 return false; 472 } 473 } else if (!repo1.getUrl().equals(repo2.getUrl())) { 474 return false; 475 } else if (repo1.getPolicy(true).isEnabled() 476 != repo2.getPolicy(true).isEnabled()) { 477 return false; 478 } else if (repo1.getPolicy(false).isEnabled() 479 != repo2.getPolicy(false).isEnabled()) { 480 return false; 481 } 482 } 483 return true; 484 } 485 486 static final class GraphKey { 487 private final Artifact artifact; 488 489 private final List<RemoteRepository> repositories; 490 491 private final DependencySelector selector; 492 493 private final DependencyManager manager; 494 495 private final DependencyTraverser traverser; 496 497 private final VersionFilter filter; 498 499 private final int hashCode; 500 501 GraphKey( 502 Artifact artifact, 503 List<RemoteRepository> repositories, 504 DependencySelector selector, 505 DependencyManager manager, 506 DependencyTraverser traverser, 507 VersionFilter filter) { 508 this.artifact = artifact; 509 this.repositories = repositories; 510 this.selector = selector; 511 this.manager = manager; 512 this.traverser = traverser; 513 this.filter = filter; 514 515 hashCode = Objects.hash(artifact, repositories, selector, manager, traverser, filter); 516 } 517 518 @Override 519 public boolean equals(Object obj) { 520 if (obj == this) { 521 return true; 522 } else if (!(obj instanceof GraphKey)) { 523 return false; 524 } 525 GraphKey that = (GraphKey) obj; 526 return Objects.equals(artifact, that.artifact) 527 && Objects.equals(repositories, that.repositories) 528 && Objects.equals(selector, that.selector) 529 && Objects.equals(manager, that.manager) 530 && Objects.equals(traverser, that.traverser) 531 && Objects.equals(filter, that.filter); 532 } 533 534 @Override 535 public int hashCode() { 536 return hashCode; 537 } 538 } 539 540 private static <K, V> InternPool<K, V> createPool(String type) { 541 if (HARD.equals(type)) { 542 return new HardInternPool<>(); 543 } else if (WEAK.equals(type)) { 544 return new WeakInternPool<>(); 545 } else { 546 throw new IllegalArgumentException("Unknown object pool type: '" + type + "'"); 547 } 548 } 549 550 public static final String HARD = "hard"; 551 552 public static final String WEAK = "weak"; 553 554 private interface InternPool<K, V> { 555 V get(K key); 556 557 V intern(K key, V value); 558 } 559 560 private static class HardInternPool<K, V> implements InternPool<K, V> { 561 private final ConcurrentHashMap<K, V> map = new ConcurrentHashMap<>(256); 562 563 @Override 564 public V get(K key) { 565 return map.get(key); 566 } 567 568 @Override 569 public V intern(K key, V value) { 570 return map.computeIfAbsent(key, k -> value); 571 } 572 } 573 574 /** 575 * Intern pool backed by ConcurrentWeakCache with weak keys and weak values. 576 * Lock-free reads (ConcurrentHashMap.get is a volatile read, zero allocation via 577 * ThreadLocal lookup key), lock-striped writes, weak keys and values allow GC of 578 * interned objects when no longer strongly referenced. 579 * Uses putIfAbsent to guarantee concurrent callers for the same key get the same instance. 580 */ 581 private static class WeakInternPool<K, V> implements InternPool<K, V> { 582 private final ConcurrentWeakCache<K, V> cache = new ConcurrentWeakCache<>(256); 583 584 @Override 585 public V get(K key) { 586 return cache.get(key); 587 } 588 589 @Override 590 public V intern(K key, V value) { 591 return cache.putIfAbsent(key, value); 592 } 593 } 594}