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()); 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, BadDescriptor.INSTANCE); 251 } 252 253 private List<Dependency> intern(List<Dependency> dependencies) { 254 return dependencyLists.intern(dependencies, dependencies); 255 } 256 257 public Object toKey(VersionRangeRequest request) { 258 return new ConstraintKey(request); 259 } 260 261 public VersionRangeResult getConstraint(Object key, VersionRangeRequest request) { 262 Constraint constraint = constraints.get(key); 263 if (constraint != null) { 264 return constraint.toResult(request); 265 } 266 return null; 267 } 268 269 public void putConstraint(Object key, VersionRangeResult result) { 270 constraints.put(key, new Constraint(result)); 271 } 272 273 public Object toKey( 274 Artifact artifact, 275 List<RemoteRepository> repositories, 276 DependencySelector selector, 277 DependencyManager manager, 278 DependencyTraverser traverser, 279 VersionFilter filter) { 280 return new GraphKey(artifact, repositories, selector, manager, traverser, filter); 281 } 282 283 public List<DependencyNode> getChildren(Object key) { 284 return nodes.get(key); 285 } 286 287 public void putChildren(Object key, List<DependencyNode> children) { 288 nodes.put(key, children); 289 } 290 291 public static final class DescriptorKey { 292 private final Artifact artifact; 293 private final int hashCode; 294 295 private DescriptorKey(Artifact artifact) { 296 this.artifact = artifact; 297 this.hashCode = Objects.hashCode(artifact); 298 } 299 300 @Override 301 public boolean equals(Object o) { 302 if (this == o) { 303 return true; 304 } 305 if (o == null || getClass() != o.getClass()) { 306 return false; 307 } 308 DescriptorKey that = (DescriptorKey) o; 309 return Objects.equals(artifact, that.artifact); 310 } 311 312 @Override 313 public int hashCode() { 314 return hashCode; 315 } 316 317 @Override 318 public String toString() { 319 return getClass().getSimpleName() + "{" + "artifact='" + artifact + '\'' + '}'; 320 } 321 } 322 323 abstract static class Descriptor { 324 public abstract ArtifactDescriptorResult toResult(ArtifactDescriptorRequest request); 325 } 326 327 static final class GoodDescriptor extends Descriptor { 328 329 final Artifact artifact; 330 331 final List<Artifact> relocations; 332 333 final Collection<Artifact> aliases; 334 335 final List<RemoteRepository> repositories; 336 337 final List<Dependency> dependencies; 338 339 final List<Dependency> managedDependencies; 340 341 GoodDescriptor(ArtifactDescriptorResult result) { 342 artifact = result.getArtifact(); 343 relocations = result.getRelocations(); 344 aliases = result.getAliases(); 345 dependencies = result.getDependencies(); 346 managedDependencies = result.getManagedDependencies(); 347 repositories = result.getRepositories(); 348 } 349 350 public ArtifactDescriptorResult toResult(ArtifactDescriptorRequest request) { 351 ArtifactDescriptorResult result = new ArtifactDescriptorResult(request); 352 result.setArtifact(artifact); 353 result.setRelocations(relocations); 354 result.setAliases(aliases); 355 result.setDependencies(dependencies); 356 result.setManagedDependencies(managedDependencies); 357 result.setRepositories(repositories); 358 return result; 359 } 360 } 361 362 static final class BadDescriptor extends Descriptor { 363 364 static final BadDescriptor INSTANCE = new BadDescriptor(); 365 366 public ArtifactDescriptorResult toResult(ArtifactDescriptorRequest request) { 367 return NO_DESCRIPTOR; 368 } 369 } 370 371 private static final class Constraint { 372 final VersionRepo[] repositories; 373 374 final VersionConstraint versionConstraint; 375 376 Constraint(VersionRangeResult result) { 377 versionConstraint = result.getVersionConstraint(); 378 List<Version> versions = result.getVersions(); 379 repositories = new VersionRepo[versions.size()]; 380 int i = 0; 381 for (Version version : versions) { 382 repositories[i++] = new VersionRepo(version, result.getRepository(version)); 383 } 384 } 385 386 VersionRangeResult toResult(VersionRangeRequest request) { 387 VersionRangeResult result = new VersionRangeResult(request); 388 for (VersionRepo vr : repositories) { 389 result.addVersion(vr.version); 390 result.setRepository(vr.version, vr.repo); 391 } 392 result.setVersionConstraint(versionConstraint); 393 return result; 394 } 395 396 static final class VersionRepo { 397 final Version version; 398 399 final ArtifactRepository repo; 400 401 VersionRepo(Version version, ArtifactRepository repo) { 402 this.version = version; 403 this.repo = repo; 404 } 405 } 406 } 407 408 static final class ConstraintKey { 409 private final Artifact artifact; 410 411 private final List<RemoteRepository> repositories; 412 413 private final int hashCode; 414 415 ConstraintKey(VersionRangeRequest request) { 416 artifact = request.getArtifact(); 417 repositories = request.getRepositories(); 418 hashCode = artifact.hashCode(); 419 } 420 421 @Override 422 public boolean equals(Object obj) { 423 if (obj == this) { 424 return true; 425 } else if (!(obj instanceof ConstraintKey)) { 426 return false; 427 } 428 ConstraintKey that = (ConstraintKey) obj; 429 return artifact.equals(that.artifact) && equals(repositories, that.repositories); 430 } 431 432 private static boolean equals(List<RemoteRepository> repos1, List<RemoteRepository> repos2) { 433 if (repos1.size() != repos2.size()) { 434 return false; 435 } 436 for (Iterator<RemoteRepository> it1 = repos1.iterator(), it2 = repos2.iterator(); 437 it1.hasNext() && it2.hasNext(); ) { 438 RemoteRepository repo1 = it1.next(); 439 RemoteRepository repo2 = it2.next(); 440 if (repo1.isRepositoryManager() != repo2.isRepositoryManager()) { 441 return false; 442 } 443 if (repo1.isRepositoryManager()) { 444 if (!equals(repo1.getMirroredRepositories(), repo2.getMirroredRepositories())) { 445 return false; 446 } 447 } else if (!repo1.getUrl().equals(repo2.getUrl())) { 448 return false; 449 } else if (repo1.getPolicy(true).isEnabled() 450 != repo2.getPolicy(true).isEnabled()) { 451 return false; 452 } else if (repo1.getPolicy(false).isEnabled() 453 != repo2.getPolicy(false).isEnabled()) { 454 return false; 455 } 456 } 457 return true; 458 } 459 460 @Override 461 public int hashCode() { 462 return hashCode; 463 } 464 } 465 466 static final class GraphKey { 467 private final Artifact artifact; 468 469 private final List<RemoteRepository> repositories; 470 471 private final DependencySelector selector; 472 473 private final DependencyManager manager; 474 475 private final DependencyTraverser traverser; 476 477 private final VersionFilter filter; 478 479 private final int hashCode; 480 481 GraphKey( 482 Artifact artifact, 483 List<RemoteRepository> repositories, 484 DependencySelector selector, 485 DependencyManager manager, 486 DependencyTraverser traverser, 487 VersionFilter filter) { 488 this.artifact = artifact; 489 this.repositories = repositories; 490 this.selector = selector; 491 this.manager = manager; 492 this.traverser = traverser; 493 this.filter = filter; 494 495 hashCode = Objects.hash(artifact, repositories, selector, manager, traverser, filter); 496 } 497 498 @Override 499 public boolean equals(Object obj) { 500 if (obj == this) { 501 return true; 502 } else if (!(obj instanceof GraphKey)) { 503 return false; 504 } 505 GraphKey that = (GraphKey) obj; 506 return Objects.equals(artifact, that.artifact) 507 && Objects.equals(repositories, that.repositories) 508 && Objects.equals(selector, that.selector) 509 && Objects.equals(manager, that.manager) 510 && Objects.equals(traverser, that.traverser) 511 && Objects.equals(filter, that.filter); 512 } 513 514 @Override 515 public int hashCode() { 516 return hashCode; 517 } 518 } 519 520 private static <K, V> InternPool<K, V> createPool(String type) { 521 if (HARD.equals(type)) { 522 return new HardInternPool<>(); 523 } else if (WEAK.equals(type)) { 524 return new WeakInternPool<>(); 525 } else { 526 throw new IllegalArgumentException("Unknown object pool type: '" + type + "'"); 527 } 528 } 529 530 public static final String HARD = "hard"; 531 532 public static final String WEAK = "weak"; 533 534 private interface InternPool<K, V> { 535 V get(K key); 536 537 V intern(K key, V value); 538 } 539 540 private static class HardInternPool<K, V> implements InternPool<K, V> { 541 private final ConcurrentHashMap<K, V> map = new ConcurrentHashMap<>(256); 542 543 @Override 544 public V get(K key) { 545 return map.get(key); 546 } 547 548 @Override 549 public V intern(K key, V value) { 550 return map.computeIfAbsent(key, k -> value); 551 } 552 } 553 554 /** 555 * Intern pool backed by ConcurrentWeakCache with weak keys and weak values. 556 * Lock-free reads (ConcurrentHashMap.get is a volatile read, zero allocation via 557 * ThreadLocal lookup key), lock-striped writes, weak keys and values allow GC of 558 * interned objects when no longer strongly referenced. 559 * Uses putIfAbsent to guarantee concurrent callers for the same key get the same instance. 560 */ 561 private static class WeakInternPool<K, V> implements InternPool<K, V> { 562 private final ConcurrentWeakCache<K, V> cache = new ConcurrentWeakCache<>(256); 563 564 @Override 565 public V get(K key) { 566 return cache.get(key); 567 } 568 569 @Override 570 public V intern(K key, V value) { 571 return cache.putIfAbsent(key, value); 572 } 573 } 574}