Skip to content

Commit 7da52fc

Browse files
committed
Use download URL for transitive dependencies
This avoids duplicated lookups for snapshots transitive dependencies (one for every added repository). Also, this makes sure to use the correct repository to download transitive dependencies, since a transitive dependency could be in a repository only declared in the POM of some other transitive dependency.
1 parent d272889 commit 7da52fc

File tree

5 files changed

+99
-26
lines changed

5 files changed

+99
-26
lines changed

core/src/main/java/com/alessiodp/libby/Library.java

+4-8
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
import org.jetbrains.annotations.NotNull;
66
import org.jetbrains.annotations.Nullable;
77

8-
98
import java.util.Base64;
109
import java.util.Collection;
1110
import java.util.Collections;
1211
import java.util.LinkedList;
1312

13+
import static com.alessiodp.libby.Util.craftPartialPath;
14+
import static com.alessiodp.libby.Util.craftPath;
1415
import static com.alessiodp.libby.Util.hexStringToByteArray;
1516
import static com.alessiodp.libby.Util.replaceWithDots;
1617
import static java.util.Objects.requireNonNull;
@@ -146,13 +147,8 @@ private Library(@Nullable Collection<String> urls,
146147
this.checksum = checksum;
147148
this.relocations = relocations != null ? Collections.unmodifiableList(new LinkedList<>(relocations)) : Collections.emptyList();
148149

149-
this.partialPath = this.groupId.replace('.', '/') + '/' + this.artifactId + '/' + version + '/';
150-
String path = this.partialPath + this.artifactId + '-' + version;
151-
if (hasClassifier()) {
152-
path += '-' + classifier;
153-
}
154-
155-
this.path = path + ".jar";
150+
this.partialPath = craftPartialPath(this.artifactId, this.groupId, version);
151+
this.path = craftPath(this.partialPath, this.artifactId, this.version, this.classifier);
156152

157153
this.repositories = repositories != null ? Collections.unmodifiableList(new LinkedList<>(repositories)) : Collections.emptyList();
158154
relocatedPath = hasRelocations() ? path + "-relocated-" + Math.abs(this.relocations.hashCode()) + ".jar" : null;

core/src/main/java/com/alessiodp/libby/LibraryManager.java

+1-5
Original file line numberDiff line numberDiff line change
@@ -424,11 +424,7 @@ protected String getURLFromMetadata(@NotNull InputStream inputStream, @NotNull L
424424
version = version.substring(0, version.length() - "-SNAPSHOT".length());
425425
}
426426

427-
String url = library.getPartialPath() + library.getArtifactId() + '-' + version + '-' + timestamp + '-' + buildNumber;
428-
if (library.hasClassifier()) {
429-
url += '-' + library.getClassifier();
430-
}
431-
return url + ".jar";
427+
return Util.craftPath(library.getPartialPath(), library.getArtifactId(), version + '-' + timestamp + '-' + buildNumber, library.getClassifier());
432428
}
433429

434430
/**

core/src/main/java/com/alessiodp/libby/Util.java

+35-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.alessiodp.libby;
22

33
import org.jetbrains.annotations.NotNull;
4+
import org.jetbrains.annotations.Nullable;
45

56
/**
67
* Libby's utility class.
@@ -28,7 +29,7 @@ public static String replaceWithDots(@NotNull String str) {
2829
* @param string The string to convert
2930
* @return The byte array
3031
*/
31-
public static byte[] hexStringToByteArray(String string) {
32+
public static byte[] hexStringToByteArray(@NotNull String string) {
3233
int len = string.length();
3334
byte[] data = new byte[len / 2];
3435
for (int i = 0; i < len; i += 2) {
@@ -37,4 +38,37 @@ public static byte[] hexStringToByteArray(String string) {
3738
}
3839
return data;
3940
}
41+
42+
/**
43+
* Constructs the partial path of a {@link Library} given its artifactId, groupId and version.
44+
*
45+
* @param artifactId The artifactId of the library.
46+
* @param groupId The groupId of the library.
47+
* @param version The version of the library.
48+
* @return The partial path of the library.
49+
* @see Library#getPartialPath()
50+
*/
51+
@NotNull
52+
public static String craftPartialPath(@NotNull String artifactId, @NotNull String groupId, @NotNull String version) {
53+
return groupId.replace('.', '/') + '/' + artifactId + '/' + version + '/';
54+
}
55+
56+
/**
57+
* Constructs the path of a {@link Library} given its partialPath, artifactId, version and classifier.
58+
*
59+
* @param partialPath The partialPath of the library.
60+
* @param artifactId The artifactId of the library.
61+
* @param version The version of the library.
62+
* @param classifier The classifier of the library. May be null.
63+
* @return The path of the library.
64+
* @see Library#getPath()
65+
*/
66+
@NotNull
67+
public static String craftPath(@NotNull String partialPath, @NotNull String artifactId, @NotNull String version, @Nullable String classifier) {
68+
String path = partialPath + artifactId + '-' + version;
69+
if (classifier != null && !classifier.isEmpty()) {
70+
path += '-' + classifier;
71+
}
72+
return path + ".jar";
73+
}
4074
}

core/src/main/java/com/alessiodp/libby/transitive/TransitiveDependencyCollector.java

+20-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import org.eclipse.aether.artifact.DefaultArtifact;
99
import org.eclipse.aether.collection.CollectRequest;
1010
import org.eclipse.aether.graph.Dependency;
11+
import org.eclipse.aether.repository.ArtifactRepository;
1112
import org.eclipse.aether.repository.LocalRepository;
1213
import org.eclipse.aether.repository.RemoteRepository;
1314
import org.eclipse.aether.resolution.ArtifactResult;
@@ -18,12 +19,15 @@
1819
import org.eclipse.aether.util.artifact.JavaScopes;
1920
import org.eclipse.aether.util.filter.ScopeDependencyFilter;
2021
import org.jetbrains.annotations.NotNull;
22+
import org.jetbrains.annotations.Nullable;
2123

2224
import java.nio.file.Path;
25+
import java.util.AbstractMap.SimpleEntry;
2326
import java.util.Arrays;
2427
import java.util.Collection;
2528
import java.util.Collections;
2629
import java.util.List;
30+
import java.util.Map.Entry;
2731
import java.util.Properties;
2832
import java.util.concurrent.atomic.AtomicInteger;
2933
import java.util.stream.Collectors;
@@ -91,19 +95,30 @@ public static RemoteRepository newDefaultRepository(@NotNull String url) {
9195
* @param version Maven dependency version
9296
* @param classifier Maven artifact classifier. May be null
9397
* @param repositories Maven repositories that would be used for dependency resolution
94-
* @return Transitive dependencies, exception otherwise
98+
* @return Transitive dependencies paired with their repository url, exception otherwise
9599
* @throws DependencyResolutionException thrown if dependency doesn't exist on provided repositories
96100
*/
97101
@NotNull
98-
public Collection<Artifact> findTransitiveDependencies(@NotNull String groupId, @NotNull String artifactId, @NotNull String version, @NotNull String classifier, @NotNull List<RemoteRepository> repositories) throws DependencyResolutionException {
102+
public Collection<Entry<Artifact, @Nullable String>> findTransitiveDependencies(@NotNull String groupId, @NotNull String artifactId, @NotNull String version, @NotNull String classifier, @NotNull List<RemoteRepository> repositories) throws DependencyResolutionException {
99103
Artifact artifact = new DefaultArtifact(groupId, artifactId, classifier, "jar", version);
100104

101105
CollectRequest collectRequest = new CollectRequest(new Dependency(artifact, JavaScopes.COMPILE), repositories);
102106
DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, new ScopeDependencyFilter(Arrays.asList(JavaScopes.COMPILE, JavaScopes.RUNTIME), Collections.emptyList()));
103107

104108
DependencyResult dependencyResult = repositorySystem.resolveDependencies(repositorySystemSession, dependencyRequest);
105109

106-
return dependencyResult.getArtifactResults().stream().filter(ArtifactResult::isResolved).map(ArtifactResult::getArtifact).collect(Collectors.toList());
110+
return dependencyResult.getArtifactResults()
111+
.stream()
112+
.filter(ArtifactResult::isResolved)
113+
.map(artifactResult -> {
114+
ArtifactRepository repo = artifactResult.getRepository();
115+
String url = null;
116+
if (repo instanceof RemoteRepository) {
117+
url = ((RemoteRepository) repo).getUrl();
118+
}
119+
return new SimpleEntry<>(artifactResult.getArtifact(), url);
120+
})
121+
.collect(Collectors.toList());
107122
}
108123

109124
/**
@@ -114,12 +129,12 @@ public Collection<Artifact> findTransitiveDependencies(@NotNull String groupId,
114129
* @param version Maven artifact version
115130
* @param classifier Maven artifact classifier. May be null
116131
* @param repositories Maven repositories for transitive dependencies search
117-
* @return Transitive dependencies, exception otherwise
132+
* @return Transitive dependencies paired with their repository url, exception otherwise
118133
* @throws DependencyResolutionException thrown if dependency doesn't exist on provided repositories
119134
* @see #findTransitiveDependencies(String, String, String, String, List)
120135
*/
121136
@NotNull
122-
public Collection<Artifact> findTransitiveDependencies(@NotNull String groupId, @NotNull String artifactId, @NotNull String version, @NotNull String classifier, @NotNull Stream<String> repositories) throws DependencyResolutionException {
137+
public Collection<Entry<Artifact, @Nullable String>> findTransitiveDependencies(@NotNull String groupId, @NotNull String artifactId, @NotNull String version, @NotNull String classifier, @NotNull Stream<String> repositories) throws DependencyResolutionException {
123138
return findTransitiveDependencies(groupId, artifactId, version, classifier, repositories.map(TransitiveDependencyCollector::newDefaultRepository).collect(Collectors.toList()));
124139
}
125140

core/src/main/java/com/alessiodp/libby/transitive/TransitiveDependencyHelper.java

+39-7
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import com.alessiodp.libby.Library;
44
import com.alessiodp.libby.LibraryManager;
5+
import com.alessiodp.libby.Util;
56
import com.alessiodp.libby.classloader.IsolatedClassLoader;
67
import org.jetbrains.annotations.NotNull;
8+
import org.jetbrains.annotations.Nullable;
79

810
import java.io.IOException;
911
import java.lang.reflect.Constructor;
@@ -14,6 +16,7 @@
1416
import java.util.Collections;
1517
import java.util.HashSet;
1618
import java.util.List;
19+
import java.util.Map.Entry;
1720
import java.util.Set;
1821
import java.util.stream.Stream;
1922

@@ -46,7 +49,7 @@ public class TransitiveDependencyHelper {
4649
/**
4750
* Reflected getter methods of Artifact class
4851
*/
49-
private final Method artifactGetGroupIdMethod, artifactGetArtifactIdMethod, artifactGetBaseVersionMethod, artifactGetClassifierMethod;
52+
private final Method artifactGetGroupIdMethod, artifactGetArtifactIdMethod, artifactGetVersionMethod, artifactGetBaseVersionMethod, artifactGetClassifierMethod;
5053

5154
/**
5255
* LibraryManager instance, used in {@link #findTransitiveLibraries(Library)}
@@ -93,6 +96,8 @@ public TransitiveDependencyHelper(@NotNull LibraryManager libraryManager, @NotNu
9396
artifactGetGroupIdMethod = artifactClass.getMethod("getGroupId");
9497
// org.eclipse.aether.artifact.Artifact#getArtifactId()
9598
artifactGetArtifactIdMethod = artifactClass.getMethod("getArtifactId");
99+
// org.eclipse.aether.artifact.Artifact#getVersion()
100+
artifactGetVersionMethod = artifactClass.getMethod("getVersion");
96101
// org.eclipse.aether.artifact.Artifact#getBaseVersion()
97102
artifactGetBaseVersionMethod = artifactClass.getMethod("getBaseVersion");
98103
// org.eclipse.aether.artifact.Artifact#getClassifier()
@@ -111,7 +116,7 @@ public TransitiveDependencyHelper(@NotNull LibraryManager libraryManager, @NotNu
111116
* </p>
112117
* <p>
113118
* Note: The method merges the repositories from both the library manager and the given library
114-
* for dependency resolution. And clones all relocations into transitive libraries.
119+
* for dependency resolution. It also clones all relocations into transitive libraries.
115120
* </p>
116121
*
117122
* @param library The primary library for which transitive dependencies need to be found.
@@ -132,16 +137,20 @@ public Collection<Library> findTransitiveLibraries(@NotNull Library library) {
132137

133138
Stream<String> repositories = Stream.of(globalRepositories, libraryRepositories).flatMap(Collection::stream);
134139
try {
135-
Collection<?> artifacts = (Collection<?>) resolveTransitiveDependenciesMethod.invoke(transitiveDependencyCollectorObject,
140+
Collection<?> resolvedArtifacts = (Collection<?>) resolveTransitiveDependenciesMethod.invoke(transitiveDependencyCollectorObject,
136141
library.getGroupId(),
137142
library.getArtifactId(),
138143
library.getVersion(),
139144
library.getClassifier(),
140145
repositories);
141-
for (Object artifact : artifacts) {
146+
for (Object resolved : resolvedArtifacts) {
147+
Entry<?, ?> resolvedEntry = (Entry<?, ?>) resolved;
148+
Object artifact = resolvedEntry.getKey();
149+
@Nullable String repository = (String) resolvedEntry.getValue();
150+
142151
String groupId = (String) artifactGetGroupIdMethod.invoke(artifact);
143152
String artifactId = (String) artifactGetArtifactIdMethod.invoke(artifact);
144-
String version = (String) artifactGetBaseVersionMethod.invoke(artifact);
153+
String baseVersion = (String) artifactGetBaseVersionMethod.invoke(artifact);
145154
String classifier = (String) artifactGetClassifierMethod.invoke(artifact);
146155

147156
if (library.getGroupId().equals(groupId) && library.getArtifactId().equals(artifactId))
@@ -153,7 +162,7 @@ public Collection<Library> findTransitiveLibraries(@NotNull Library library) {
153162
Library.Builder libraryBuilder = Library.builder()
154163
.groupId(groupId)
155164
.artifactId(artifactId)
156-
.version(version)
165+
.version(baseVersion)
157166
.isolatedLoad(library.isIsolatedLoad())
158167
.loaderId(library.getLoaderId());
159168

@@ -162,7 +171,30 @@ public Collection<Library> findTransitiveLibraries(@NotNull Library library) {
162171
}
163172

164173
library.getRelocations().forEach(libraryBuilder::relocate);
165-
library.getRepositories().forEach(libraryBuilder::repository);
174+
175+
if (repository != null) {
176+
// Construct direct download URL
177+
178+
// Add ending "/" if missing
179+
if (!repository.endsWith("/")) {
180+
repository = repository + '/';
181+
}
182+
183+
// TODO Uncomment the line below once LibraryManager#resolveLibrary stops resolving snapshots
184+
// for every repository before trying direct URLs
185+
// Make sure the repository is added as fallback if the dependency isn't found at the constructed URL
186+
// libraryBuilder.repository(repository);
187+
188+
// For snapshots, getVersion() returns version-timestamp-buildNumber instead of version-SNAPSHOT
189+
String version = (String) artifactGetVersionMethod.invoke(artifact);
190+
191+
String partialPath = Util.craftPartialPath(artifactId, groupId, baseVersion);
192+
String path = Util.craftPath(partialPath, artifactId, version, classifier);
193+
194+
libraryBuilder.url(repository + path);
195+
} else {
196+
library.getRepositories().forEach(libraryBuilder::repository);
197+
}
166198

167199
transitiveLibraries.add(libraryBuilder.build());
168200
}

0 commit comments

Comments
 (0)