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