Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move TransitiveDependencyCollector to separate subproject #44

Merged
merged 5 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 71 additions & 4 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,25 +1,92 @@
import groovy.xml.MarkupBuilder
import java.security.MessageDigest
import java.util.Base64

plugins {
id("net.kyori.blossom") version "2.0.1"
}

dependencies {
compileOnly("org.apache.maven.resolver:maven-resolver-supplier:1.9.15")
compileOnly("org.apache.maven:maven-resolver-provider:3.9.4")
val libbyMavenResolverRepo = layout.buildDirectory.dir("libby-maven-resolver-repo").get()
val libbyMavenResolverJar = provider {}.flatMap { // Use a provider to not resolve shadowTask task too early
project(":libby-maven-resolver").tasks.named("shadowJar").flatMap { (it as Jar).archiveFile }
}

val deleteLibbyMavenResolver = tasks.register<Delete>("deleteLibbyMavenResolver") {
delete(libbyMavenResolverRepo)
isFollowSymlinks = false
}

val copyLibbyMavenResolver = tasks.register<Copy>("copyLibbyMavenResolver") {
dependsOn(":libby-maven-resolver:shadowJar")
dependsOn(deleteLibbyMavenResolver)
}
setupCopy()

tasks.test {
dependsOn(copyLibbyMavenResolver)
}

sourceSets {
main {
blossom {
javaSources {
property("version", project.version.toString())
property("libbyMavenResolverChecksum", provider {
val md = MessageDigest.getInstance("SHA-256")
val sha256 = md.digest(libbyMavenResolverJar.get().asFile.readBytes())
Base64.getEncoder().encodeToString(sha256)
})
}
}
}
test {
blossom {
javaSources {
property("buildDir", layout.buildDirectory.asFile.get().absolutePath.replace("\\", "\\\\"))
property("libbyMavenResolverRepo", libbyMavenResolverRepo.asFile.absolutePath.replace("\\", "\\\\"))
}
}
}
}

tasks.named("generateJavaTemplates") {
dependsOn(":libby-maven-resolver:shadowJar")
}

fun setupCopy() {
val version = project.version.toString()
val partialPath = "${project.group.toString().replace('.', '/')}/libby-maven-resolver/${version}"
val mainFolder = libbyMavenResolverRepo.dir(partialPath)

copyLibbyMavenResolver {
from(libbyMavenResolverJar)
into(mainFolder)
}

if (!version.endsWith("-SNAPSHOT")) {
return
}

// Generate snapshot's maven-metadata-local.xml
copyLibbyMavenResolver {
doFirst {
mainFolder.file("maven-metadata-local.xml").asFile.printWriter(Charsets.UTF_8).use {
val builder = MarkupBuilder(it)
builder.doubleQuotes = true
builder.mkp.xmlDeclaration(mapOf("version" to "1.0", "encoding" to "UTF-8"))
builder.withGroovyBuilder {
"metadata"("modelVersion" to "1.1.0") {
"groupId"(project.group.toString())
"artifactId"("libby-maven-resolver")
"version"(version)
"versioning"() {
"snapshot"() {
"localCopy"(true)
}
}
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,9 @@ public class LibbyProperties {
* User agent string to use when downloading libraries
*/
public static final String HTTP_USER_AGENT = "libby/" + VERSION;

/**
* Checksum of libby-maven-resolver jar
*/
public static final String LIBBY_MAVEN_RESOLVER_CHECKSUM = "{{ libbyMavenResolverChecksum }}";
}
62 changes: 34 additions & 28 deletions core/src/main/java/com/alessiodp/libby/LibraryManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,8 @@ public Collection<String> resolveLibrary(@NotNull Library library) {
*/
@Nullable
protected String resolveSnapshot(@NotNull String repository, @NotNull Library library) {
String url = requireNonNull(repository, "repository") + requireNonNull(library, "library").getPartialPath() + "maven-metadata.xml";
String mavenMetadata = repository.startsWith("file") ? "maven-metadata-local.xml" : "maven-metadata.xml";
String url = requireNonNull(repository, "repository") + requireNonNull(library, "library").getPartialPath() + mavenMetadata;
try {
URLConnection connection = new URL(requireNonNull(url, "url")).openConnection();

Expand Down Expand Up @@ -372,7 +373,7 @@ protected String getURLFromMetadata(@NotNull InputStream inputStream, @NotNull L
requireNonNull(inputStream, "inputStream");
requireNonNull(library, "library");

String timestamp, buildNumber;
String version = library.getVersion();
try {
// This reads the maven-metadata.xml file and gets the snapshot info from the <snapshot> tag.
// Example tag:
Expand All @@ -394,37 +395,42 @@ protected String getURLFromMetadata(@NotNull InputStream inputStream, @NotNull L
if (snapshot.getNodeType() != Node.ELEMENT_NODE) {
return null;
}
Node timestampNode = ((Element) snapshot).getElementsByTagName("timestamp").item(0);
if (timestampNode == null || timestampNode.getNodeType() != Node.ELEMENT_NODE) {
return null;
}
Node buildNumberNode = ((Element) snapshot).getElementsByTagName("buildNumber").item(0);
if (buildNumberNode == null || buildNumberNode.getNodeType() != Node.ELEMENT_NODE) {
return null;
}
Node timestampChild = timestampNode.getFirstChild();
if (timestampChild == null || timestampChild.getNodeType() != Node.TEXT_NODE) {
return null;
}
Node buildNumberChild = buildNumberNode.getFirstChild();
if (buildNumberChild == null || buildNumberChild.getNodeType() != Node.TEXT_NODE) {
return null;
Node localCopyNode = ((Element) snapshot).getElementsByTagName("localCopy").item(0);
if (localCopyNode == null || localCopyNode.getNodeType() != Node.ELEMENT_NODE) {
// Resolve with snapshot number instead of base name
Node timestampNode = ((Element) snapshot).getElementsByTagName("timestamp").item(0);
if (timestampNode == null || timestampNode.getNodeType() != Node.ELEMENT_NODE) {
return null;
}
Node buildNumberNode = ((Element) snapshot).getElementsByTagName("buildNumber").item(0);
if (buildNumberNode == null || buildNumberNode.getNodeType() != Node.ELEMENT_NODE) {
return null;
}
Node timestampChild = timestampNode.getFirstChild();
if (timestampChild == null || timestampChild.getNodeType() != Node.TEXT_NODE) {
return null;
}
Node buildNumberChild = buildNumberNode.getFirstChild();
if (buildNumberChild == null || buildNumberChild.getNodeType() != Node.TEXT_NODE) {
return null;
}
String timestamp = timestampChild.getNodeValue();
String buildNumber = buildNumberChild.getNodeValue();

version = library.getVersion();
// Call .substring(...) only on versions ending in "-SNAPSHOT".
// It should never happen that a snapshot version doesn't end in "-SNAPSHOT", but better be sure
if (version.endsWith("-SNAPSHOT")) {
version = version.substring(0, version.length() - "-SNAPSHOT".length());
}

version = version + '-' + timestamp + '-' + buildNumber;
}
timestamp = timestampChild.getNodeValue();
buildNumber = buildNumberChild.getNodeValue();
} catch (ParserConfigurationException | SAXException e) {
logger.debug("Invalid maven-metadata.xml", e);
return null;
}

String version = library.getVersion();
// Call .substring(...) only on versions ending in "-SNAPSHOT".
// It should never happen that a snapshot version doesn't end in "-SNAPSHOT", but better be sure
if (version.endsWith("-SNAPSHOT")) {
version = version.substring(0, version.length() - "-SNAPSHOT".length());
}

return Util.craftPath(library.getPartialPath(), library.getArtifactId(), version + '-' + timestamp + '-' + buildNumber, library.getClassifier());
return Util.craftPath(library.getPartialPath(), library.getArtifactId(), version, library.getClassifier());
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.alessiodp.libby.transitive;

import com.alessiodp.libby.LibbyProperties;
import com.alessiodp.libby.Library;
import com.alessiodp.libby.LibraryManager;
import com.alessiodp.libby.Util;
import com.alessiodp.libby.classloader.IsolatedClassLoader;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.nio.file.Path;
Expand All @@ -31,10 +31,15 @@
*/
public class TransitiveDependencyHelper {

/**
* com.alessiodp.libby.transitive.TransitiveDependencyCollector class name for reflections
*/
private static final String TRANSITIVE_DEPENDENCY_COLLECTOR_CLASS = replaceWithDots("com{}alessiodp{}libby{}transitive{}TransitiveDependencyCollector");

/**
* org.eclipse.aether.artifact.Artifact class name for reflections
*/
private static final String ARTIFACT_CLASS = replaceWithDots("org.eclipse.aether.artifact.Artifact");
private static final String ARTIFACT_CLASS = replaceWithDots("org{}eclipse{}aether{}artifact{}Artifact");

/**
* TransitiveDependencyCollector class instance, used in {@link #findTransitiveLibraries(Library)}
Expand Down Expand Up @@ -68,21 +73,17 @@ public TransitiveDependencyHelper(@NotNull LibraryManager libraryManager, @NotNu
this.libraryManager = libraryManager;

IsolatedClassLoader classLoader = new IsolatedClassLoader();
String collectorClassName = "com.alessiodp.libby.transitive.TransitiveDependencyCollector";
String collectorClassPath = '/' + collectorClassName.replace('.', '/') + ".class";

for (TransitiveLibraryResolutionDependency dependency : TransitiveLibraryResolutionDependency.values()) {
classLoader.addPath(libraryManager.downloadLibrary(dependency.toLibrary()));
}

final Class<?> transitiveDependencyCollectorClass;
try {
transitiveDependencyCollectorClass = classLoader.defineClass(collectorClassName, requireNonNull(getClass().getResourceAsStream(collectorClassPath), "resourceCollectorClass"));
} catch (IOException e) {
throw new RuntimeException(e);
}
classLoader.addPath(libraryManager.downloadLibrary(Library.builder()
.groupId("com{}alessiodp{}libby")
.artifactId("libby-maven-resolver")
.version(LibbyProperties.VERSION)
.checksumFromBase64(LibbyProperties.LIBBY_MAVEN_RESOLVER_CHECKSUM)
.build()
));

try {
Class<?> transitiveDependencyCollectorClass = classLoader.loadClass(TRANSITIVE_DEPENDENCY_COLLECTOR_CLASS);
Class<?> artifactClass = classLoader.loadClass(ARTIFACT_CLASS);

// com.alessiodp.libby.TransitiveDependencyCollector(Path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,9 @@ public class LibbyTestProperties {
* Build dir path
*/
public static final String BUILD_DIR = "{{ buildDir }}";

/**
* Local repo set up by gradle for libby-maven-resolver
*/
public static final String LIBBY_MAVEN_RESOLVER_REPO = "{{ libbyMavenResolverRepo }}";
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@
import com.alessiodp.libby.Repositories;
import org.jetbrains.annotations.NotNull;

/**
* Represents the libraries required for Maven transitive dependency resolution and related operations.
* <p>
* This class bundles Maven Resolver Supplier, Maven Resolver Provider and their transitive dependencies.
*/
enum TransitiveLibraryResolutionDependency {
enum MavenResolverDependencies {
MAVEN_RESOLVER_SUPPLIER("org{}apache{}maven{}resolver", "maven-resolver-supplier", "1.9.15", "BNncFRDRqBBHWixE0DYfFydh7h9bc3mhC/MBr5WD448"),
MAVEN_RESOLVER_API("org{}apache{}maven{}resolver", "maven-resolver-api", "1.9.15", "1Ugp1gooJ7uXYxK9u/YnlR5hMpJ2LuFGFAxRqn3OYL4"),
MAVEN_RESOLVER_UTIL("org{}apache{}maven{}resolver", "maven-resolver-util", "1.9.15", "z9Gnzg0gpWHtZrI18lpvwa5A/f8Zr6k1GrCpZNTlVm8"),
Expand Down Expand Up @@ -38,7 +33,7 @@ enum TransitiveLibraryResolutionDependency {

private final String groupId, artifactId, version, checksum;

TransitiveLibraryResolutionDependency(@NotNull String groupId, @NotNull String artifactId, @NotNull String version, @NotNull String checksum) {
MavenResolverDependencies(@NotNull String groupId, @NotNull String artifactId, @NotNull String version, @NotNull String checksum) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
Expand All @@ -53,11 +48,6 @@ public Library toLibrary() {
.version(version)
.checksumFromBase64(checksum)
.repository(Repositories.MAVEN_CENTRAL)
// Relocate all packages used in Libby to avoid conflicts
.relocate("org{}eclipse{}aether{}util", "org.eclipse.aether.util") // maven-resolver-util
.relocate("org{}eclipse{}aether", "org.eclipse.aether") // maven-resolver-api
.relocate("org{}apache{}maven{}repository{}internal", "org.apache.maven.repository.internal") // maven-resolver-provider

.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.alessiodp.libby.transitive;

import com.alessiodp.libby.LibbyTestProperties;
import com.alessiodp.libby.Library;
import com.alessiodp.libby.LibraryManagerMock;
import com.alessiodp.libby.TestUtils;
Expand All @@ -8,6 +9,7 @@
import org.junit.jupiter.api.Test;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

import static org.junit.jupiter.api.Assertions.*;
Expand All @@ -20,7 +22,7 @@ public class TransitiveDownloadingTest {
.version("1.9.15")
.resolveTransitiveDependencies(true)
.build();
private static final Library EXCLUDED_LIBRARY = TransitiveLibraryResolutionDependency.MAVEN_RESOLVER_API.toLibrary();
private static final Library EXCLUDED_LIBRARY = MavenResolverDependencies.MAVEN_RESOLVER_API.toLibrary();
private static final Library MAVEN_RESOLVER_SUPPLIER_WITH_EXCLUDED = Library.builder()
.groupId(MAVEN_RESOLVER_SUPPLIER.getGroupId())
.artifactId(MAVEN_RESOLVER_SUPPLIER.getArtifactId())
Expand All @@ -33,6 +35,7 @@ public class TransitiveDownloadingTest {
.artifactId("bungeecord-api")
.version("1.20-R0.2-SNAPSHOT")
.repository("https://oss.sonatype.org/content/repositories/snapshots")
.repository("https://hub.spigotmc.org/nexus/content/groups/public/")
.isolatedLoad(true)
.loaderId("bungeecord")
.resolveTransitiveDependencies(true)
Expand All @@ -43,6 +46,8 @@ public class TransitiveDownloadingTest {
@BeforeEach
public void setUp() throws Exception {
libraryManager = new LibraryManagerMock();
// Local repo set up by gradle for libby-maven-resolver
libraryManager.addRepository(Paths.get(LibbyTestProperties.LIBBY_MAVEN_RESOLVER_REPO).toUri().toString());
libraryManager.addMavenCentral();
}

Expand All @@ -69,29 +74,29 @@ public void snapshotLibraryTransitiveLoad() throws Exception {
public void transitiveWithExcludedLoad() {
libraryManager.loadLibrary(MAVEN_RESOLVER_SUPPLIER_WITH_EXCLUDED);

checkDownloadedDependencies(TransitiveLibraryResolutionDependency.MAVEN_RESOLVER_API);
checkDownloadedDependencies(MavenResolverDependencies.MAVEN_RESOLVER_API);
}

/**
* Compares the libraries required by maven-resolver-supplier with the ones declared in {@link TransitiveLibraryResolutionDependency}.
* Compares the libraries required by maven-resolver-supplier with the ones declared in {@link MavenResolverDependencies}.
*
* @param excludedDependencies Optionally excluded more dependencies
*/
private void checkDownloadedDependencies(TransitiveLibraryResolutionDependency... excludedDependencies) {
Set<TransitiveLibraryResolutionDependency> excluded = new HashSet<>();
private void checkDownloadedDependencies(MavenResolverDependencies... excludedDependencies) {
Set<MavenResolverDependencies> excluded = new HashSet<>();
// Always exclude javax.inject since it is excluded by maven-resolver-supplier
excluded.add(TransitiveLibraryResolutionDependency.JAVAX_INJECT);
excluded.add(MavenResolverDependencies.JAVAX_INJECT);
// Always exclude slf4j-nop since it is not included in maven-resolver-supplier
excluded.add(TransitiveLibraryResolutionDependency.SLF4J_NOP);
excluded.add(MavenResolverDependencies.SLF4J_NOP);
excluded.addAll(Arrays.asList(excludedDependencies));

List<String> loaded = libraryManager.getLoaded();
// Assert that the correct amount of libraries has been loaded
assertEquals(TransitiveLibraryResolutionDependency.values().length - excluded.size(), loaded.size());
assertEquals(MavenResolverDependencies.values().length - excluded.size(), loaded.size());

Arrays.stream(TransitiveLibraryResolutionDependency.values())
Arrays.stream(MavenResolverDependencies.values())
.filter(dep -> !excluded.contains(dep))
.map(TransitiveLibraryResolutionDependency::toLibrary)
.map(MavenResolverDependencies::toLibrary)
.forEach(dep -> {
Path path = libraryManager.getSaveDirectory().resolve(dep.getPath()).toAbsolutePath();
assertTrue(loaded.contains(path.toString()));
Expand Down
Loading
Loading