refactor: Slight refactoring of DependencyLoader class

I am too scared on touching too much logic and breaking something as I didn't work on this class
or with that library much yet.

So I'm just refactoring it a bit not changing a lot of logic
This commit is contained in:
Christian Koop 2024-02-02 18:54:34 +01:00
parent 193af915e9
commit fcb613e3d6
No known key found for this signature in database
GPG Key ID: 89A8181384E010A3
4 changed files with 75 additions and 102 deletions

View File

@ -122,14 +122,14 @@ public abstract class SongodaPlugin extends JavaPlugin {
dependencies.add(new Dependency("https://repo1.maven.org/maven2", "com;zaxxer", "HikariCP", "4.0.3")); dependencies.add(new Dependency("https://repo1.maven.org/maven2", "com;zaxxer", "HikariCP", "4.0.3"));
dependencies.add(new Dependency("https://repo1.maven.org/maven2", "org;reactivestreams", "reactive-streams", "1.0.2", true)); dependencies.add(new Dependency("https://repo1.maven.org/maven2", "org;reactivestreams", "reactive-streams", "1.0.2", true));
dependencies.add(new Dependency("https://repo1.maven.org/maven2", "org;jooq", "jooq", "3.14.16", true, dependencies.add(new Dependency("https://repo1.maven.org/maven2", "org;jooq", "jooq", "3.14.16", true,
new Relocation("org;reactivestreams", "com.craftaro.third_party.org.reactivestreams")) //Relocate reactive-streams to avoid conflicts new Relocation("org;reactivestreams", "com.craftaro.third_party.org.reactivestreams")) // Relocate reactive-streams to avoid conflicts
); );
dependencies.add(new Dependency("https://repo1.maven.org/maven2", "org;mariadb;jdbc", "mariadb-java-client", "3.2.0")); dependencies.add(new Dependency("https://repo1.maven.org/maven2", "org;mariadb;jdbc", "mariadb-java-client", "3.2.0"));
dependencies.add(new Dependency("https://repo1.maven.org/maven2", "com;h2database", "h2", "1.4.200", false, dependencies.add(new Dependency("https://repo1.maven.org/maven2", "com;h2database", "h2", "1.4.200", false,
new Relocation("org;h2", "com;craftaro;third_party;org;h2")) //Custom relocation if the package names not match with the groupId new Relocation("org;h2", "com;craftaro;third_party;org;h2")) // Custom relocation if the package names not match with the groupId
); );
dependencies.add(new Dependency("https://repo1.maven.org/maven2", "com;github;cryptomorin", "XSeries", "9.8.0", false, dependencies.add(new Dependency("https://repo1.maven.org/maven2", "com;github;cryptomorin", "XSeries", "9.8.0", false,
new Relocation("com;cryptomorin;xseries", "com;craftaro;third_party;com;cryptomorin;xseries")) //Custom relocation if the package names not match with the groupId new Relocation("com;cryptomorin;xseries", "com;craftaro;third_party;com;cryptomorin;xseries")) // Custom relocation if the package names not match with the groupId
); );
//Load plugin dependencies //Load plugin dependencies

View File

@ -1,13 +1,8 @@
package com.craftaro.core.dependency; package com.craftaro.core.dependency;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
public class Dependency { public class Dependency {
private final String repositoryUrl; private final String repositoryUrl;
@ -18,35 +13,32 @@ public class Dependency {
private final List<Relocation> relocations; private final List<Relocation> relocations;
/** /**
*
* @param repositoryUrl The repository url to download the dependency from. * @param repositoryUrl The repository url to download the dependency from.
* @param groupId The groupId of the dependency. * @param groupId The groupId of the dependency.
* @param artifactId The artifactId of the dependency. * @param artifactId The artifactId of the dependency.
* @param version The version of the dependency. * @param version The version of the dependency.
*/ */
public Dependency(String repositoryUrl, String groupId, String artifactId, String version) { public Dependency(String repositoryUrl, String groupId, String artifactId, String version) {
this(repositoryUrl, groupId, artifactId, version, true); this(repositoryUrl, groupId, artifactId, version, true);
} }
/** /**
*
* @param repositoryUrl The repository url to download the dependency from. * @param repositoryUrl The repository url to download the dependency from.
* @param groupId The groupId of the dependency. * @param groupId The groupId of the dependency.
* @param artifactId The artifactId of the dependency. * @param artifactId The artifactId of the dependency.
* @param version The version of the dependency. * @param version The version of the dependency.
* @param baseRelocate If the dependency should be relocated to com.craftaro.third_party. * @param baseRelocate If the dependency should be relocated to com.craftaro.third_party.
*/ */
public Dependency(String repositoryUrl, String groupId, String artifactId, String version, boolean baseRelocate) { public Dependency(String repositoryUrl, String groupId, String artifactId, String version, boolean baseRelocate) {
this(repositoryUrl, groupId, artifactId, version, baseRelocate, new Relocation[0]); this(repositoryUrl, groupId, artifactId, version, baseRelocate, new Relocation[0]);
} }
/** /**
* * @param repositoryUrl The repository url to download the dependency from.
* @param repositoryUrl The repository url to download the dependency from. * @param groupId The groupId of the dependency.
* @param groupId The groupId of the dependency. * @param artifactId The artifactId of the dependency.
* @param artifactId The artifactId of the dependency. * @param version The version of the dependency.
* @param version The version of the dependency. * @param baseRelocate If the dependency should be relocated to com.craftaro.third_party.
* @param baseRelocate If the dependency should be relocated to com.craftaro.third_party.
* @param extraRelocations Extra relocations to apply to the dependency. * @param extraRelocations Extra relocations to apply to the dependency.
*/ */
public Dependency(String repositoryUrl, String groupId, String artifactId, String version, boolean baseRelocate, Relocation... extraRelocations) { public Dependency(String repositoryUrl, String groupId, String artifactId, String version, boolean baseRelocate, Relocation... extraRelocations) {
@ -85,6 +77,14 @@ public class Dependency {
} }
public boolean shouldRelocate() { public boolean shouldRelocate() {
return this.relocate || !relocations.isEmpty(); return this.relocate || !this.relocations.isEmpty();
}
public String buildArtifactUrl() {
return this.repositoryUrl + "/" +
this.groupId.replace('.', '/') + "/" +
this.artifactId + "/" +
this.version + "/" +
this.artifactId + "-" + this.version + ".jar";
} }
} }

View File

@ -10,11 +10,10 @@ import me.lucko.jarrelocator.Relocation;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.nio.file.Files;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Enumeration; import java.util.Enumeration;
@ -42,135 +41,109 @@ public class DependencyLoader {
} }
public void loadDependency(Dependency dependency) throws IOException { public void loadDependency(Dependency dependency) throws IOException {
String repositoryUrl = dependency.getRepositoryUrl();
String groupId = dependency.getGroupId();
String artifactId = dependency.getArtifactId();
String version = dependency.getVersion();
//Download dependency from the repositoryUrl
//Check if we have the dependency downloaded already
String name = dependency.getArtifactId() + "-" + dependency.getVersion(); String name = dependency.getArtifactId() + "-" + dependency.getVersion();
File outputFile = new File(this.libraryLoader.getLibFolder(), dependency.getGroupId().replace(".", File.separator) + File.separator + dependency.getArtifactId().replace(".", File.separator) + File.separator + dependency.getVersion() + File.separator + "raw-" + name + ".jar"); File outputFile = new File(this.libraryLoader.getLibFolder(), dependency.getGroupId().replace(".", File.separator) + File.separator + dependency.getArtifactId().replace(".", File.separator) + File.separator + dependency.getVersion() + File.separator + "raw-" + name + ".jar");
File relocatedFile = new File(outputFile.getParentFile(), name.replace("raw-", "") + ".jar"); File relocatedFile = new File(outputFile.getParentFile(), name.replace("raw-", "") + ".jar");
if (relocatedFile.exists()) { if (relocatedFile.exists()) {
//Check if the file is already loaded to the classpath
if (isLoaded(relocatedFile)) { if (isLoaded(relocatedFile)) {
return; return;
} }
//Load dependency into the classpath
loadJarIntoClasspath(relocatedFile, dependency); loadJarIntoClasspath(relocatedFile, dependency);
return; return;
} }
SongodaCore.getLogger().info("Downloading dependency " + groupId + ":" + artifactId + ":" + version + " from " + repositoryUrl); SongodaCore.getLogger().info(String.format("Downloading dependency %s:%s:%s from %s", dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(), dependency.getRepositoryUrl()));
// Construct the URL for the artifact in the Maven repository Files.createDirectories(outputFile.getParentFile().toPath());
String artifactUrl = repositoryUrl + "/" + try (InputStream is = new URL(dependency.buildArtifactUrl()).openStream()) {
groupId.replace('.', '/') + "/" + Files.copy(is, outputFile.toPath());
artifactId + "/" +
version + "/" +
artifactId + "-" + version + ".jar";
URL url = new URL(artifactUrl);
URLConnection connection = url.openConnection();
InputStream in = connection.getInputStream();
// Define the output file
outputFile.getParentFile().mkdirs();
FileOutputStream out = new FileOutputStream(outputFile);
// Read from the input stream and write to the output stream
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) != -1) {
out.write(buffer, 0, length);
} }
// Close both streams
in.close();
out.close();
//Load dependency into the classpath
SongodaCore.getLogger().info("Downloaded dependency " + groupId + ":" + artifactId + ":" + version);
loadJarIntoClasspath(outputFile, dependency); loadJarIntoClasspath(outputFile, dependency);
} }
public void loadJarIntoClasspath(File file, Dependency dependency) throws IOException { public void loadJarIntoClasspath(File file, Dependency dependency) throws IOException {
if (!isRelocated(file) && dependency.shouldRelocate()) { if (!isRelocated(file) && dependency.shouldRelocate()) {
SongodaCore.getLogger().info("Loading dependency for relocation " + file); SongodaCore.getLogger().info("Loading dependency for relocation " + file);
//relocate package to com.craftaro.core.third_party to avoid conflicts // relocate package to com.craftaro.core.third_party to avoid conflicts
List<Relocation> relocations = new ArrayList<>(); List<Relocation> relocations = new ArrayList<>();
for (com.craftaro.core.dependency.Relocation r : dependency.getRelocations()) { for (com.craftaro.core.dependency.Relocation r : dependency.getRelocations()) {
relocations.add(new Relocation(r.getFrom(), r.getTo())); relocations.add(new Relocation(r.getFrom(), r.getTo()));
} }
//Relocate the classes // Relocate the classes
File finalJar = new File(file.getParentFile(), file.getName().replace("raw-", "")); File finalJar = new File(file.getParentFile(), file.getName().replace("raw-", ""));
JarRelocator relocator = new JarRelocator(file, finalJar, relocations); JarRelocator relocator = new JarRelocator(file, finalJar, relocations);
try { try {
relocator.run(); relocator.run();
SongodaCore.getLogger().info("Relocated dependency " + file); SongodaCore.getLogger().info("Relocated dependency " + file);
//Delete the old jar
file.delete();
} catch (Exception e) {
SongodaCore.getLogger().severe("Failed to relocate dependency1 " + file);
if (e.getMessage().contains("zip file is empty")) {
SongodaCore.getLogger().severe("Try deleting the 'server root/craftaro' folder and restarting the server");
}
//Delete the new jar cuz it's probably corrupted
finalJar.delete();
// Delete the old jar
Files.deleteIfExists(file.toPath());
} catch (Exception e) {
SongodaCore.getLogger().severe("Failed to relocate dependency " + file);
if (e.getMessage().contains("zip file is empty")) {
SongodaCore.getLogger().severe("Try deleting '" + this.libraryLoader.getLibFolder().getParent() + "' and restarting the server");
}
// Delete the new jar cuz it's probably corrupted
Files.deleteIfExists(finalJar.toPath());
throw e; throw e;
} }
} }
try { try {
this.libraryLoader.load(new LibraryLoader.Dependency(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(), dependency.getRepositoryUrl()), true); this.libraryLoader.load(new LibraryLoader.Dependency(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(), dependency.getRepositoryUrl()), true);
} catch (InvalidDependencyException ignored) { } catch (InvalidDependencyException ignored) {
//already loaded // Already loaded
} catch (UnknownDependencyException ex) { } catch (UnknownDependencyException ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
SongodaCore.getLogger().info("----------------------------"); SongodaCore.getLogger().info("----------------------------");
} }
private boolean isRelocated(File file) throws IOException { private boolean isRelocated(File jarFile) throws IOException {
try (ZipFile zipFile = new ZipFile(file)) { try (ZipFile zipFile = new ZipFile(jarFile)) {
Enumeration<? extends ZipEntry> entries = zipFile.entries(); Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
while (entries.hasMoreElements()) { while (zipEntries.hasMoreElements()) {
ZipEntry entry = entries.nextElement(); ZipEntry entry = zipEntries.nextElement();
if (entry.getName().startsWith("com/craftaro/third_party")) { if (entry.getName().startsWith("com/craftaro/third_party")) {
return true; return true;
} }
} }
} }
return false; return false;
} }
private boolean isLoaded(File file) throws IOException { /**
//Find the first class file in the jar and try Class.forName * Finds the first .class file in the jar and check if it's loaded
try (ZipFile zipFile = new ZipFile(file)) { */
Enumeration<? extends ZipEntry> entries = zipFile.entries(); private boolean isLoaded(File jarFile) throws IOException {
while (entries.hasMoreElements()) { try (ZipFile zipFile = new ZipFile(jarFile)) {
ZipEntry entry = entries.nextElement(); Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
if (entry.getName().startsWith("META-INF")) { while (zipEntries.hasMoreElements()) {
ZipEntry zipEntry = zipEntries.nextElement();
if (zipEntry.getName().startsWith("META-INF")) {
continue;
}
if (!zipEntry.getName().endsWith(".class")) {
continue; continue;
} }
if (entry.getName().endsWith(".class")) { String className = zipEntry.getName()
String className = entry.getName().replace("/", ".").replace(".class", ""); .substring(0, zipEntry.getName().length() - ".class".length())
try { .replace("/", ".");
Class.forName(className); try {
return true; Class.forName(className);
} catch (Exception | Error e) { return true;
return false; } catch (Throwable th) {
} return false;
} }
} }
} }
return false; return false;
} }

View File

@ -1,26 +1,26 @@
package com.craftaro.core.dependency; package com.craftaro.core.dependency;
public class Relocation { public class Relocation {
private final String from; private final String from;
private final String to; private final String to;
public Relocation(String from, String to) { public Relocation(String from, String to) {
if (from == null) { if (from == null) {
throw new IllegalArgumentException("from cannot be null"); throw new IllegalArgumentException("'from' cannot be null");
} }
if (to == null) { if (to == null) {
throw new IllegalArgumentException("to cannot be null"); throw new IllegalArgumentException("'to' cannot be null");
} }
this.from = from.replaceAll(";", "."); this.from = from.replaceAll(";", ".");
this.to = to.replaceAll(";", "."); this.to = to.replaceAll(";", ".");
} }
public String getFrom() { public String getFrom() {
return from; return this.from;
} }
public String getTo() { public String getTo() {
return to; return this.to;
} }
} }