From 0b65231175a20e236b9c37f8b414e0332b4ecb2d Mon Sep 17 00:00:00 2001 From: JFronny Date: Fri, 4 Nov 2022 12:41:41 +0100 Subject: [PATCH] Fix maven downloads with -SNAPSHOT --- .../jfronny/inceptum/common/Updater.java | 45 +++--- .../jfronny/inceptum/common/api/MavenApi.java | 143 ++++++++---------- .../common/model/maven/ArtifactMeta.java | 65 ++++++++ .../common/model/maven/DependencyNode.java | 5 +- .../inceptum/launcher/api/FabricMetaApi.java | 3 +- .../system/launch/InstanceLauncher.java | 8 +- .../setup/steps/DownloadLibrariesStep.java | 20 ++- 7 files changed, 170 insertions(+), 119 deletions(-) create mode 100644 common/src/main/java/io/gitlab/jfronny/inceptum/common/model/maven/ArtifactMeta.java diff --git a/common/src/main/java/io/gitlab/jfronny/inceptum/common/Updater.java b/common/src/main/java/io/gitlab/jfronny/inceptum/common/Updater.java index 2ae4f4e..ca365fc 100644 --- a/common/src/main/java/io/gitlab/jfronny/inceptum/common/Updater.java +++ b/common/src/main/java/io/gitlab/jfronny/inceptum/common/Updater.java @@ -8,8 +8,7 @@ import org.jetbrains.annotations.Nullable; import org.xml.sax.SAXException; import javax.xml.stream.XMLStreamException; -import java.io.File; -import java.io.IOException; +import java.io.*; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; @@ -19,9 +18,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; public class Updater { + public static final String PROJECT_MAVEN = "https://maven.frohnmeyer-wds.de/artifacts/"; private static final String ARTIFACTS_URL = "https://pages.frohnmeyer-wds.de/JfMods/Inceptum/artifacts/"; private static final String STABLE_URL = "https://pages.frohnmeyer-wds.de/JfMods/Inceptum/stable/"; - public static final String PROJECT_MAVEN = "https://maven.frohnmeyer-wds.de/artifacts/"; public static UpdateMetadata getUpdate() { return Updater.check(InceptumConfig.channel, true, channel -> { @@ -88,14 +87,14 @@ public class Updater { boolean configChanged = false; for (String lib : libs) { - Path p = artifactToPath(lib); + Path p = ArtifactMeta.parse(lib).getLocalPath(); if (!Files.exists(p)) { configChanged = true; downloadLibrary(wrapperConfig.repositories, lib, libs); } } for (String lib : natives) { - Path p = artifactToPath(lib); + Path p = ArtifactMeta.parse(lib).getLocalPath(); if (!Files.exists(p)) { configChanged = true; downloadLibrary(wrapperConfig.repositories, lib, natives); @@ -108,18 +107,28 @@ public class Updater { } private static Stream buildClasspath(Stream libraries) { - return libraries.map(Updater::artifactToPath); + return libraries.map(ArtifactMeta::parse).map(ArtifactMeta::getLocalPath); } private static DependencyNode downloadLibrary(Set repositories, final String artifact, Set libraries) throws IOException, URISyntaxException { - List exceptions = new LinkedList<>(); + List suppressed = new LinkedList<>(); for (String repository : Stream.concat(Stream.of(PROJECT_MAVEN), repositories.stream()).toList()) { + ArtifactMeta meta; + try { + meta = MavenApi.getMetadata(repository, artifact); + } catch (FileNotFoundException ignored) { + meta = ArtifactMeta.parse(artifact); + } catch (IOException | URISyntaxException | SAXException e) { + throw new IOException("Could not download artifact from " + repository, e); + } Pom pom; try { - pom = MavenApi.getPom(repository, artifact); - } catch (IOException | URISyntaxException | XMLStreamException | SAXException e) { - exceptions.add(new Exception("Could not download artifact from " + repository, e)); + pom = MavenApi.getPom(repository, meta); + } catch (FileNotFoundException notFound) { + suppressed.add(notFound); continue; + } catch (IOException | URISyntaxException | XMLStreamException | SAXException e) { + throw new IOException("Could not download artifact " + meta.getMavenNotation() + " from " + repository, e); } Set dependencies = new LinkedHashSet<>(); if (pom.dependencies != null) { @@ -128,19 +137,13 @@ public class Updater { dependencies.add(downloadLibrary(repositories, mvnName, libraries)); } } - MavenApi.downloadLibrary(repository, pom); + MavenApi.downloadLibrary(repository, meta); libraries.add(artifact); - return new DependencyNode(pom, dependencies); + return new DependencyNode(artifact, dependencies); } - IOException exception = new IOException("Could not find any repository containing the artifact " + artifact + " (searched: " + String.join(", ", repositories) + ")"); - for (Exception e : exceptions) { - exception.addSuppressed(e); - } - throw exception; - } - - private static Path artifactToPath(String artifact) { - return MetaHolder.LIBRARIES_DIR.resolve(MavenApi.mavenNotationToJarPath(artifact)).toAbsolutePath(); + IOException e = new IOException("Could not find any repository containing the artifact " + artifact + " (searched: " + String.join(", ", repositories) + ")"); + for (FileNotFoundException ex : suppressed) e.addSuppressed(ex); + throw e; } public static @Nullable UpdateMetadata check(UpdateChannel channel, boolean versionCompare, Consumer channelInvalid) { diff --git a/common/src/main/java/io/gitlab/jfronny/inceptum/common/api/MavenApi.java b/common/src/main/java/io/gitlab/jfronny/inceptum/common/api/MavenApi.java index f885a05..2a05355 100644 --- a/common/src/main/java/io/gitlab/jfronny/inceptum/common/api/MavenApi.java +++ b/common/src/main/java/io/gitlab/jfronny/inceptum/common/api/MavenApi.java @@ -2,8 +2,7 @@ package io.gitlab.jfronny.inceptum.common.api; import io.gitlab.jfronny.commons.HttpUtils; import io.gitlab.jfronny.inceptum.common.*; -import io.gitlab.jfronny.inceptum.common.model.maven.MavenDependency; -import io.gitlab.jfronny.inceptum.common.model.maven.Pom; +import io.gitlab.jfronny.inceptum.common.model.maven.*; import org.jetbrains.annotations.Nullable; import org.w3c.dom.*; import org.xml.sax.SAXException; @@ -28,25 +27,14 @@ public class MavenApi { } } - public static Path downloadLibrary(String repo, Pom pom) throws IOException, URISyntaxException { - String artifact = pom.groupId + ':' + pom.artifactId + ':' + pom.version; - String rawArtifact = artifact; - if (pom.classifier != null || pom.snapshotVersion != null) { - artifact += ':'; - if (pom.snapshotVersion != null) artifact += pom.snapshotVersion; - if (pom.classifier != null) { - if (pom.snapshotVersion != null) artifact += '-'; - artifact += pom.classifier; - } - } - if (pom.classifier != null) rawArtifact += ':' + pom.classifier; - Path res = MetaHolder.LIBRARIES_DIR.resolve(mavenNotationToJarPath(rawArtifact)); - Net.downloadFile(Utils.join("/", repo, mavenNotationToJarPath(artifact)), res); + public static Path downloadLibrary(String repo, ArtifactMeta meta) throws IOException, URISyntaxException { + Path res = meta.getLocalPath(); + Net.downloadFile(Utils.join("/", repo, meta.getJarPath(true)), res); return res; } - public static Pom getPom(String repo, String artifact) throws IOException, SAXException, URISyntaxException, XMLStreamException { - try (InputStream is = HttpUtils.get(Utils.join("/", repo, mavenNotationToPomPath(artifact))).sendInputStream()) { + public static Pom getPom(String repo, ArtifactMeta meta) throws IOException, SAXException, URISyntaxException, XMLStreamException { + try (InputStream is = HttpUtils.get(Utils.join("/", repo, meta.getPomPath())).sendInputStream()) { Document doc = FACTORY.parse(is); doc.getDocumentElement().normalize(); Pom result = new Pom(); @@ -105,24 +93,6 @@ public class MavenApi { } } case "classifier" -> result.classifier = node.getTextContent(); - case "versioning" -> { - for (Node node1 : iterable(node.getChildNodes())) { - if (node1.getNodeName().equals("snapshot")) { - String timestamp = null; - String buildNumber = null; - for (Node node2 : iterable(node1.getChildNodes())) { - switch (node2.getNodeName()) { - case "timestamp" -> timestamp = node2.getTextContent(); - case "buildNumber" -> buildNumber = node2.getTextContent(); - default -> {} - } - } - if (timestamp == null) throw new IOException("Pom snapshots lack timestamp"); - if (buildNumber == null) throw new IOException("Pom snapshots lack buildNumber"); - result.snapshotVersion = timestamp + '-' + buildNumber; - } - } - } default -> {} } } @@ -178,10 +148,57 @@ public class MavenApi { return result; } - private static boolean isWhitespace(Node node) { - if (node.getNodeType() == Node.TEXT_NODE && node.getTextContent().isBlank()) return true; - if (node.getNodeType() == Node.COMMENT_NODE) return true; - return false; + public static ArtifactMeta getMetadata(String repo, String artifact) throws IOException, SAXException, URISyntaxException { + ArtifactMeta sourceMeta = ArtifactMeta.parse(artifact); + try (InputStream is = HttpUtils.get(Utils.join("/", repo, sourceMeta.getMetadataPath())).sendInputStream()) { + Document doc = FACTORY.parse(is); + doc.getDocumentElement().normalize(); + ArtifactMeta result = new ArtifactMeta(); + if (!"metadata".equals(doc.getDocumentElement().getNodeName())) throw new IOException("Illegal document name"); + boolean hasGroupId = false; + boolean hasArtifactId = false; + boolean hasVersion = false; + for (Node node : iterable(doc.getDocumentElement().getChildNodes())) { + switch (node.getNodeName()) { + case "groupId" -> { + hasGroupId = true; + result.groupId = node.getTextContent(); + } + case "artifactId" -> { + hasArtifactId = true; + result.artifactId = node.getTextContent(); + } + case "version" -> { + hasVersion = true; + result.version = node.getTextContent(); + } + case "versioning" -> { + for (Node node1 : iterable(node.getChildNodes())) { + if (node1.getNodeName().equals("snapshot")) { + String timestamp = null; + String buildNumber = null; + for (Node node2 : iterable(node1.getChildNodes())) { + switch (node2.getNodeName()) { + case "timestamp" -> timestamp = node2.getTextContent(); + case "buildNumber" -> buildNumber = node2.getTextContent(); + default -> {} + } + } + if (timestamp == null) throw new IOException("Pom snapshots lack timestamp"); + if (buildNumber == null) throw new IOException("Pom snapshots lack buildNumber"); + result.snapshotVersion = timestamp + '-' + buildNumber; + } + } + } + default -> {} + } + } + if (!hasGroupId) throw new IOException("Pom lacks groupId"); + if (!hasArtifactId) throw new IOException("Pom lacks artifactId"); + if (!hasVersion) throw new IOException("Pom lacks version"); + result.classifier = sourceMeta.classifier; + return result; + } } private static Iterable iterable(NodeList list) { @@ -203,47 +220,9 @@ public class MavenApi { }; } - /** - * Converts an artifact in maven notation to a jar file path. The following are supported: - * - some.base.path:artifact:version -> some/base/path/artifact/version/artifact-version.jar - * - some.base.path:artifact:version:classifier -> some/base/path/artifact/version/artifact-version-classifier.jar - * @param mavenNotation An artifact in maven notation - * @return A file path - */ - public static String mavenNotationToJarPath(String mavenNotation) { - if (Objects.requireNonNull(mavenNotation).isEmpty()) throw new IllegalArgumentException("The notation is empty"); - String[] lib = mavenNotation.split(":"); - if (lib.length <= 1) throw new IllegalArgumentException("Not in maven notation"); - if (lib.length == 2) throw new IllegalArgumentException("Skipping versions is not supported"); - if (lib.length >= 5) throw new IllegalArgumentException("Unkown elements in maven notation"); - String path = lib[0].replace('.', '/') + '/'; // Base - path += lib[1] + '/'; // Artifact name - path += lib[2] + '/'; // Version - if (lib.length == 3) { // artifact-version.jar - path += lib[1] + '-' + lib[2]; - } else { // artifact-version-classifier.jar - path += lib[1] + '-' + lib[2] + "-" + lib[3]; - } - return path + ".jar"; - } - - /** - * Converts an artifact in maven notation to a pom file path. The following are supported: - * - some.base.path:artifact:version -> some/base/path/artifact/version/artifact-version.pom - * - some.base.path:artifact:version:classifier -> some/base/path/artifact/version/artifact-version.pom - * @param mavenNotation An artifact in maven notation - * @return A file path - */ - public static String mavenNotationToPomPath(String mavenNotation) { - if (Objects.requireNonNull(mavenNotation).isEmpty()) throw new IllegalArgumentException("The notation is empty"); - String[] lib = mavenNotation.split(":"); - if (lib.length <= 1) throw new IllegalArgumentException("Not in maven notation"); - if (lib.length == 2) throw new IllegalArgumentException("Skipping versions is not supported"); - if (lib.length >= 5) throw new IllegalArgumentException("Unkown elements in maven notation"); - String path = lib[0].replace('.', '/') + '/'; // Base - path += lib[1] + '/'; // Artifact name - path += lib[2] + '/'; // Version - path += lib[1] + '-' + lib[2]; // artifact-version - return path + ".pom"; + private static boolean isWhitespace(Node node) { + if (node.getNodeType() == Node.TEXT_NODE && node.getTextContent().isBlank()) return true; + if (node.getNodeType() == Node.COMMENT_NODE) return true; + return false; } } diff --git a/common/src/main/java/io/gitlab/jfronny/inceptum/common/model/maven/ArtifactMeta.java b/common/src/main/java/io/gitlab/jfronny/inceptum/common/model/maven/ArtifactMeta.java new file mode 100644 index 0000000..f7dc056 --- /dev/null +++ b/common/src/main/java/io/gitlab/jfronny/inceptum/common/model/maven/ArtifactMeta.java @@ -0,0 +1,65 @@ +package io.gitlab.jfronny.inceptum.common.model.maven; + +import io.gitlab.jfronny.inceptum.common.MetaHolder; + +import java.nio.file.Path; +import java.util.Objects; +import java.util.regex.Pattern; + +public class ArtifactMeta { + public static ArtifactMeta parse(String mavenNotation) { + if (Objects.requireNonNull(mavenNotation).isEmpty()) throw new IllegalArgumentException("The notation is empty"); + String[] lib = mavenNotation.split(":"); + if (lib.length <= 1) throw new IllegalArgumentException("Not in maven notation"); + if (lib.length == 2) throw new IllegalArgumentException("Skipping versions is not supported"); + if (lib.length >= 5) throw new IllegalArgumentException("Unkown elements in maven notation"); + ArtifactMeta meta = new ArtifactMeta(); + meta.groupId = lib[0]; + meta.artifactId = lib[1]; + meta.version = lib[2]; + if (lib.length > 3) meta.classifier = lib[3]; + return meta; + } + + public String groupId; + public String artifactId; + public String version; + + public String classifier; + public String snapshotVersion; + + public String getPomPath() { + String path = groupId.replace('.', '/') + '/'; + path += artifactId + '/'; + path += version + '/'; + path += artifactId + '-'; + if (snapshotVersion != null) path += version.replace("SNAPSHOT", snapshotVersion); + else path += version; + return path + ".pom"; + } + + public String getJarPath(boolean respectSnapshotVersion) { + String path = groupId.replace('.', '/') + '/'; + path += artifactId + '/'; + path += version + '/'; + path += artifactId + '-'; + if (snapshotVersion != null && respectSnapshotVersion) path += version.replace("SNAPSHOT", snapshotVersion); + else path += version; + if (classifier != null) path += '-' + classifier; + return path + ".jar"; + } + + public String getMavenNotation() { + String notation = groupId + ':' + artifactId + ':' + version; + if (classifier != null) notation += ':' + classifier; + return notation; + } + + public String getMetadataPath() { + return groupId.replace('.', '/') + '/' + artifactId + '/' + version + "/maven-metadata.xml"; + } + + public Path getLocalPath() { + return MetaHolder.LIBRARIES_DIR.resolve(getJarPath(false)); + } +} diff --git a/common/src/main/java/io/gitlab/jfronny/inceptum/common/model/maven/DependencyNode.java b/common/src/main/java/io/gitlab/jfronny/inceptum/common/model/maven/DependencyNode.java index 992888a..d54858b 100644 --- a/common/src/main/java/io/gitlab/jfronny/inceptum/common/model/maven/DependencyNode.java +++ b/common/src/main/java/io/gitlab/jfronny/inceptum/common/model/maven/DependencyNode.java @@ -6,9 +6,8 @@ public class DependencyNode { private final String name; private final Set dependencies; - public DependencyNode(Pom pom, Set dependencies) { - Objects.requireNonNull(pom); - this.name = pom.groupId + ":" + pom.artifactId + ":" + pom.version; + public DependencyNode(String name, Set dependencies) { + this.name = Objects.requireNonNull(name); this.dependencies = Objects.requireNonNull(dependencies); } diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/api/FabricMetaApi.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/api/FabricMetaApi.java index b97c965..200e8cc 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/api/FabricMetaApi.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/api/FabricMetaApi.java @@ -4,6 +4,7 @@ import io.gitlab.jfronny.gson.compile.util.GList; import io.gitlab.jfronny.gson.stream.JsonReader; import io.gitlab.jfronny.inceptum.common.Net; import io.gitlab.jfronny.inceptum.common.api.MavenApi; +import io.gitlab.jfronny.inceptum.common.model.maven.ArtifactMeta; import io.gitlab.jfronny.inceptum.launcher.model.fabric.*; import io.gitlab.jfronny.inceptum.launcher.model.mojang.*; import io.gitlab.jfronny.inceptum.launcher.util.GameVersionParser; @@ -69,7 +70,7 @@ public class FabricMetaApi { res.downloads = new VersionInfo.Library.Downloads(); res.downloads.classifiers = null; res.downloads.artifact = new VersionInfo.Library.Downloads.Artifact(); - res.downloads.artifact.path = MavenApi.mavenNotationToJarPath(library.name); + res.downloads.artifact.path = ArtifactMeta.parse(library.name).getJarPath(true); res.downloads.artifact.size = -1; res.downloads.artifact.sha1 = null; res.downloads.artifact.url = library.url + res.downloads.artifact.path; diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/launch/InstanceLauncher.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/launch/InstanceLauncher.java index 8f41faa..0e050c1 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/launch/InstanceLauncher.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/launch/InstanceLauncher.java @@ -80,13 +80,13 @@ public class InstanceLauncher { // Java classpath StringBuilder classPath = new StringBuilder(); for (ArtifactInfo artifact : VersionInfoLibraryResolver.getRelevant(versionInfo)) { - classPath.append(MetaHolder.LIBRARIES_DIR.resolve(artifact.path).toAbsolutePath()); + classPath.append(MetaHolder.LIBRARIES_DIR.resolve(artifact.path)); classPath.append(File.pathSeparatorChar); } Path gameJar = MetaHolder.LIBRARIES_DIR.resolve("net/minecraft/" + launchType.name).resolve(versionDataSimple.id + ".jar"); classPath.append(gameJar); classPath.append(File.pathSeparatorChar); - classPath.append(MetaHolder.LIBRARIES_DIR.resolve(MavenApi.mavenNotationToJarPath(DownloadLibrariesStep.getLaunchWrapperArtifact()))); + classPath.append(DownloadLibrariesStep.getLaunchWrapperArtifact().getLocalPath()); // JVM arguments if (launchType == LaunchType.Client && versionInfo.arguments != null) args.addAll(parse(versionInfo.arguments.jvm, versionInfo, instance, classPath.toString(), authInfo)); @@ -213,7 +213,7 @@ public class InstanceLauncher { .replace("${auth_player_name}", authInfo.name()) .replace("${version_name}", instance.getGameVersion()) .replace("${game_directory}", instance.path().toString()) - .replace("${assets_root}", MetaHolder.ASSETS_DIR.toAbsolutePath().toString()) + .replace("${assets_root}", MetaHolder.ASSETS_DIR.toString()) .replace("${assets_index_name}", info.assets) .replace("${auth_uuid}", authInfo.uuid()) .replace("${auth_access_token}", authInfo.accessToken()) @@ -222,7 +222,7 @@ public class InstanceLauncher { .replace("${resolution_width}", "1920") //TODO has_custom_resolution .replace("${resolution_height}", "1080") //TODO has_custom_resolution // jvm args - .replace("${natives_directory}", MetaHolder.NATIVES_DIR.resolve(instance.getGameVersion()).toAbsolutePath().toString()) + .replace("${natives_directory}", MetaHolder.NATIVES_DIR.resolve(instance.getGameVersion()).toString()) .replace("${launcher_name}", "Inceptum") .replace("${launcher_version}", BuildMetadata.VERSION.toString()) .replace("${classpath}", classPath) diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/setup/steps/DownloadLibrariesStep.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/setup/steps/DownloadLibrariesStep.java index 1d1381f..9a25ed9 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/setup/steps/DownloadLibrariesStep.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/setup/steps/DownloadLibrariesStep.java @@ -3,14 +3,14 @@ package io.gitlab.jfronny.inceptum.launcher.system.setup.steps; import io.gitlab.jfronny.commons.io.JFiles; import io.gitlab.jfronny.inceptum.common.*; import io.gitlab.jfronny.inceptum.common.api.MavenApi; +import io.gitlab.jfronny.inceptum.common.model.maven.ArtifactMeta; import io.gitlab.jfronny.inceptum.launcher.model.inceptum.ArtifactInfo; import io.gitlab.jfronny.inceptum.launcher.model.mojang.VersionInfo; +import io.gitlab.jfronny.inceptum.launcher.system.setup.SetupStepInfo; import io.gitlab.jfronny.inceptum.launcher.system.setup.Step; import io.gitlab.jfronny.inceptum.launcher.util.*; -import io.gitlab.jfronny.inceptum.launcher.system.setup.SetupStepInfo; import org.xml.sax.SAXException; -import javax.xml.stream.XMLStreamException; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.*; @@ -50,17 +50,21 @@ public class DownloadLibrariesStep implements Step { } } - String artifact = getLaunchWrapperArtifact(); - if (!Files.exists(MetaHolder.LIBRARIES_DIR.resolve(MavenApi.mavenNotationToJarPath(artifact)))) { + ArtifactMeta artifact = getLaunchWrapperArtifact(); + if (!Files.exists(artifact.getLocalPath())) { try { - MavenApi.downloadLibrary(Updater.PROJECT_MAVEN, MavenApi.getPom(Updater.PROJECT_MAVEN, artifact)); - } catch (URISyntaxException | XMLStreamException | SAXException e) { + MavenApi.downloadLibrary(Updater.PROJECT_MAVEN, MavenApi.getMetadata(Updater.PROJECT_MAVEN, artifact.getMavenNotation())); + } catch (URISyntaxException | SAXException e) { throw new IOException("Could not download launchwrapper", e); } } } - public static String getLaunchWrapperArtifact() { - return "io.gitlab.jfronny.inceptum:launchwrapper:" + (BuildMetadata.IS_PUBLIC ? BuildMetadata.VERSION : Updater.getUpdate().version); + public static ArtifactMeta getLaunchWrapperArtifact() { + ArtifactMeta meta = new ArtifactMeta(); + meta.groupId = "io.gitlab.jfronny.inceptum"; + meta.artifactId = "launchwrapper"; + meta.version = BuildMetadata.IS_PUBLIC ? BuildMetadata.VERSION : Updater.getUpdate().version; + return meta; } }