View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.resolver.examples;
20  
21  import java.time.Duration;
22  import java.time.Instant;
23  import java.time.temporal.ChronoUnit;
24  import java.util.List;
25  import java.util.concurrent.CountDownLatch;
26  import java.util.concurrent.atomic.AtomicInteger;
27  
28  import org.apache.maven.resolver.examples.util.Booter;
29  import org.eclipse.aether.RepositorySystem;
30  import org.eclipse.aether.RepositorySystemSession;
31  import org.eclipse.aether.RepositorySystemSession.CloseableSession;
32  import org.eclipse.aether.artifact.Artifact;
33  import org.eclipse.aether.artifact.DefaultArtifact;
34  import org.eclipse.aether.collection.CollectRequest;
35  import org.eclipse.aether.graph.Dependency;
36  import org.eclipse.aether.repository.RemoteRepository;
37  import org.eclipse.aether.resolution.ArtifactResult;
38  import org.eclipse.aether.resolution.DependencyRequest;
39  import org.eclipse.aether.util.artifact.JavaScopes;
40  
41  /**
42   * Resolves the transitive (compile) LARGE dependencies of an imaginary artifact in parallel.
43   * This is the reproducer for locking issues: <a href="https://github.com/apache/maven-resolver/issues/1644">GH-1644</a>.
44   * This code does NOT run as part of build/tests, it is meant to be ad-hoc run from IDE or alike.
45   */
46  public class ResolveTransitiveDependenciesParallel {
47  
48      /**
49       * Main.
50       * @param args
51       * @throws Exception
52       */
53      public static void main(String[] args) throws Exception {
54          System.out.println("------------------------------------------------------------");
55          System.out.println(ResolveTransitiveDependenciesParallel.class.getSimpleName());
56  
57          // note: these numbers below are not "universal", they stand for one given WS with one given network,
58          // and they may need change with time if anything changes in relation (even remote like Central!)
59          //
60          // cstamas (DK HW+net) 17. 11. 2025 (w/ empty local repo)
61          // One job is done in 20 sec
62          // BUT other must wait as they are serialized (time adds up)
63          // reproducer to succeed: 210 sec
64          // my run:
65          // DONE (21 sec): org.example:test:1: resolved 11; failed 0
66          // DONE (37 sec): org.example:test:6: resolved 11; failed 0
67          // DONE (60 sec): org.example:test:4: resolved 11; failed 0
68          // DONE (60 sec): org.example:test:5: resolved 11; failed 0
69          // DONE (170 sec): org.example:test:8: resolved 11; failed 0
70          // DONE (181 sec): org.example:test:3: resolved 11; failed 0
71          // DONE (181 sec): org.example:test:7: resolved 11; failed 0
72          // DONE (181 sec): org.example:test:2: resolved 11; failed 0
73          // =====
74          // TOTAL success=8; fail=0
75          //
76          // Pattern: as said above, one job does in 20 sec, BUT subsequent one will do it in cca 40 sec (waiting 20 sec)
77          // and so on, the times are adding up. Each subsequent job with start by waiting for previous jobs to finish
78          // due (intentional) artifact overlaps.
79  
80          try (RepositorySystem system = Booter.newRepositorySystem(Booter.selectFactory(args));
81                  CloseableSession session = Booter.newRepositorySystemSession(system, Booter.selectFs(args))
82                          .setTransferListener(null)
83                          .setRepositoryListener(null)
84                          .setConfigProperty("aether.syncContext.named.time", "210")
85                          .build()) {
86              Artifact bigArtifact1 = new DefaultArtifact("org.bytedeco:llvm:jar:linux-arm64:16.0.4-1.5.9");
87              Artifact bigArtifact2 = new DefaultArtifact("org.bytedeco:llvm:jar:linux-armhf:16.0.4-1.5.9");
88              Artifact bigArtifact3 = new DefaultArtifact("org.bytedeco:llvm:jar:linux-ppc64le:16.0.4-1.5.9");
89              Artifact bigArtifact4 = new DefaultArtifact("org.bytedeco:llvm:jar:linux-x86:16.0.4-1.5.9");
90              Artifact bigArtifact5 = new DefaultArtifact("org.bytedeco:llvm:jar:linux-x86_64:16.0.4-1.5.9");
91              Artifact bigArtifact6 = new DefaultArtifact("org.bytedeco:llvm:jar:macosx-arm64:16.0.4-1.5.9");
92              Artifact bigArtifact7 = new DefaultArtifact("org.bytedeco:llvm:jar:macosx-x86_64:16.0.4-1.5.9");
93              Artifact bigArtifact8 = new DefaultArtifact("org.bytedeco:llvm:jar:windows-x86:16.0.4-1.5.9");
94              Artifact bigArtifact9 = new DefaultArtifact("org.bytedeco:llvm:jar:windows-x86_64:16.0.4-1.5.9");
95  
96              CountDownLatch latch = new CountDownLatch(8);
97              AtomicInteger success = new AtomicInteger(0);
98              AtomicInteger fail = new AtomicInteger(0);
99  
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 }