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.apache.maven.resolver.examples; 020 021import java.time.Duration; 022import java.time.Instant; 023import java.time.temporal.ChronoUnit; 024import java.util.List; 025import java.util.concurrent.CountDownLatch; 026import java.util.concurrent.atomic.AtomicInteger; 027 028import org.apache.maven.resolver.examples.util.Booter; 029import org.eclipse.aether.RepositorySystem; 030import org.eclipse.aether.RepositorySystemSession; 031import org.eclipse.aether.RepositorySystemSession.CloseableSession; 032import org.eclipse.aether.artifact.Artifact; 033import org.eclipse.aether.artifact.DefaultArtifact; 034import org.eclipse.aether.collection.CollectRequest; 035import org.eclipse.aether.graph.Dependency; 036import org.eclipse.aether.repository.RemoteRepository; 037import org.eclipse.aether.resolution.ArtifactResult; 038import org.eclipse.aether.resolution.DependencyRequest; 039import org.eclipse.aether.util.artifact.JavaScopes; 040 041/** 042 * Resolves the transitive (compile) LARGE dependencies of an imaginary artifact in parallel. 043 * This is the reproducer for locking issues: <a href="https://github.com/apache/maven-resolver/issues/1644">GH-1644</a>. 044 * This code does NOT run as part of build/tests, it is meant to be ad-hoc run from IDE or alike. 045 */ 046public class ResolveTransitiveDependenciesParallel { 047 048 /** 049 * Main. 050 * @param args 051 * @throws Exception 052 */ 053 public static void main(String[] args) throws Exception { 054 System.out.println("------------------------------------------------------------"); 055 System.out.println(ResolveTransitiveDependenciesParallel.class.getSimpleName()); 056 057 // note: these numbers below are not "universal", they stand for one given WS with one given network, 058 // and they may need change with time if anything changes in relation (even remote like Central!) 059 // 060 // cstamas (DK HW+net) 17. 11. 2025 (w/ empty local repo) 061 // One job is done in 20 sec 062 // BUT other must wait as they are serialized (time adds up) 063 // reproducer to succeed: 210 sec 064 // my run: 065 // DONE (21 sec): org.example:test:1: resolved 11; failed 0 066 // DONE (37 sec): org.example:test:6: resolved 11; failed 0 067 // DONE (60 sec): org.example:test:4: resolved 11; failed 0 068 // DONE (60 sec): org.example:test:5: resolved 11; failed 0 069 // DONE (170 sec): org.example:test:8: resolved 11; failed 0 070 // DONE (181 sec): org.example:test:3: resolved 11; failed 0 071 // DONE (181 sec): org.example:test:7: resolved 11; failed 0 072 // DONE (181 sec): org.example:test:2: resolved 11; failed 0 073 // ===== 074 // TOTAL success=8; fail=0 075 // 076 // Pattern: as said above, one job does in 20 sec, BUT subsequent one will do it in cca 40 sec (waiting 20 sec) 077 // and so on, the times are adding up. Each subsequent job with start by waiting for previous jobs to finish 078 // due (intentional) artifact overlaps. 079 080 try (RepositorySystem system = Booter.newRepositorySystem(Booter.selectFactory(args)); 081 CloseableSession session = Booter.newRepositorySystemSession(system, Booter.selectFs(args)) 082 .setTransferListener(null) 083 .setRepositoryListener(null) 084 .setConfigProperty("aether.syncContext.named.time", "210") 085 .build()) { 086 Artifact bigArtifact1 = new DefaultArtifact("org.bytedeco:llvm:jar:linux-arm64:16.0.4-1.5.9"); 087 Artifact bigArtifact2 = new DefaultArtifact("org.bytedeco:llvm:jar:linux-armhf:16.0.4-1.5.9"); 088 Artifact bigArtifact3 = new DefaultArtifact("org.bytedeco:llvm:jar:linux-ppc64le:16.0.4-1.5.9"); 089 Artifact bigArtifact4 = new DefaultArtifact("org.bytedeco:llvm:jar:linux-x86:16.0.4-1.5.9"); 090 Artifact bigArtifact5 = new DefaultArtifact("org.bytedeco:llvm:jar:linux-x86_64:16.0.4-1.5.9"); 091 Artifact bigArtifact6 = new DefaultArtifact("org.bytedeco:llvm:jar:macosx-arm64:16.0.4-1.5.9"); 092 Artifact bigArtifact7 = new DefaultArtifact("org.bytedeco:llvm:jar:macosx-x86_64:16.0.4-1.5.9"); 093 Artifact bigArtifact8 = new DefaultArtifact("org.bytedeco:llvm:jar:windows-x86:16.0.4-1.5.9"); 094 Artifact bigArtifact9 = new DefaultArtifact("org.bytedeco:llvm:jar:windows-x86_64:16.0.4-1.5.9"); 095 096 CountDownLatch latch = new CountDownLatch(8); 097 AtomicInteger success = new AtomicInteger(0); 098 AtomicInteger fail = new AtomicInteger(0); 099 100 Thread thread1 = new Thread(resolveWithDependencies( 101 latch, 102 success, 103 fail, 104 system, 105 session, 106 Booter.newRepositories(system, session), 107 "org.example:test:1", 108 bigArtifact1, 109 bigArtifact2)); 110 Thread thread2 = new Thread(resolveWithDependencies( 111 latch, 112 success, 113 fail, 114 system, 115 session, 116 Booter.newRepositories(system, session), 117 "org.example:test:2", 118 bigArtifact2, 119 bigArtifact3)); 120 Thread thread3 = new Thread(resolveWithDependencies( 121 latch, 122 success, 123 fail, 124 system, 125 session, 126 Booter.newRepositories(system, session), 127 "org.example:test:3", 128 bigArtifact3, 129 bigArtifact4)); 130 Thread thread4 = new Thread(resolveWithDependencies( 131 latch, 132 success, 133 fail, 134 system, 135 session, 136 Booter.newRepositories(system, session), 137 "org.example:test:4", 138 bigArtifact4, 139 bigArtifact5)); 140 Thread thread5 = new Thread(resolveWithDependencies( 141 latch, 142 success, 143 fail, 144 system, 145 session, 146 Booter.newRepositories(system, session), 147 "org.example:test:5", 148 bigArtifact5, 149 bigArtifact6)); 150 Thread thread6 = new Thread(resolveWithDependencies( 151 latch, 152 success, 153 fail, 154 system, 155 session, 156 Booter.newRepositories(system, session), 157 "org.example:test:6", 158 bigArtifact6, 159 bigArtifact7)); 160 Thread thread7 = new Thread(resolveWithDependencies( 161 latch, 162 success, 163 fail, 164 system, 165 session, 166 Booter.newRepositories(system, session), 167 "org.example:test:7", 168 bigArtifact7, 169 bigArtifact8)); 170 Thread thread8 = new Thread(resolveWithDependencies( 171 latch, 172 success, 173 fail, 174 system, 175 session, 176 Booter.newRepositories(system, session), 177 "org.example:test:8", 178 bigArtifact8, 179 bigArtifact9)); 180 181 thread1.start(); 182 thread2.start(); 183 thread3.start(); 184 thread4.start(); 185 thread5.start(); 186 thread6.start(); 187 thread7.start(); 188 thread8.start(); 189 190 latch.await(); 191 192 System.out.println("====="); 193 System.out.println("TOTAL success=" + success.get() + "; fail=" + fail.get()); 194 } 195 } 196 197 @SuppressWarnings("checkstyle:parameternumber") 198 private static Runnable resolveWithDependencies( 199 CountDownLatch latch, 200 AtomicInteger success, 201 AtomicInteger fail, 202 RepositorySystem system, 203 RepositorySystemSession session, 204 List<RemoteRepository> repositories, 205 String gav, 206 Artifact... deps) { 207 return () -> { 208 try { 209 Instant now = Instant.now(); 210 CollectRequest collectRequest = new CollectRequest(); 211 collectRequest.setRootArtifact(new DefaultArtifact(gav)); 212 for (Artifact dep : deps) { 213 collectRequest.addDependency(new Dependency(dep, JavaScopes.COMPILE)); 214 } 215 collectRequest.setRepositories(repositories); 216 DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, null); 217 List<ArtifactResult> artifactResults = 218 system.resolveDependencies(session, dependencyRequest).getArtifactResults(); 219 int resolved = 0; 220 int fails = 0; 221 for (ArtifactResult artifactResult : artifactResults) { 222 if (artifactResult.isResolved()) { 223 resolved++; 224 } else { 225 fails++; 226 } 227 } 228 String dur = Duration.between(now, Instant.now()).get(ChronoUnit.SECONDS) + " sec"; 229 System.out.println("DONE (" + dur + "): " + gav + ": resolved " + resolved + "; failed " + fails); 230 success.getAndIncrement(); 231 } catch (Exception e) { 232 System.out.println("FAILED " + gav + ": " + e.getMessage()); 233 fail.getAndIncrement(); 234 } finally { 235 latch.countDown(); 236 } 237 }; 238 } 239}