new-1.0
This commit is contained in:
parent
dde365448e
commit
706de6f042
5
pom.xml
5
pom.xml
|
@ -113,28 +113,33 @@
|
|||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>1.26.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.luben</groupId>
|
||||
<artifactId>zstd-jni</artifactId>
|
||||
<version>1.5.6-3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<version>4.0.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.46.0.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<version>5.1.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
@ -3,21 +3,47 @@ package com.io.yutian.elementoriginlib;
|
|||
import com.io.yutian.elementoriginlib.lang.Lang;
|
||||
import com.io.yutian.elementoriginlib.listener.GuiHandlerListener;
|
||||
import com.io.yutian.elementoriginlib.listener.PlayerChatInputListener;
|
||||
import com.io.yutian.elementoriginlib.logger.Logger;
|
||||
import com.io.yutian.elementoriginlib.redis.RedisIO;
|
||||
import net.byteflux.libby.*;
|
||||
import net.byteflux.libby.logging.LogLevel;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import redis.clients.jedis.Jedis;
|
||||
|
||||
public final class ElementOriginLib extends JavaPlugin {
|
||||
|
||||
private static final org.slf4j.Logger log = LoggerFactory.getLogger(ElementOriginLib.class);
|
||||
private static ElementOriginLib instance;
|
||||
|
||||
private static Logger logger = Logger.getLogger(ElementOriginLib.class);
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
|
||||
loadLibraries();
|
||||
|
||||
Lang.registerLangFile(this);
|
||||
Lang.reload();
|
||||
|
||||
new GuiHandlerListener(this);
|
||||
new PlayerChatInputListener(this);
|
||||
|
||||
logger.info("Successfully load ElementOriginLib v"+getPluginMeta().getVersion());
|
||||
}
|
||||
|
||||
private void loadLibraries() {
|
||||
logger.info("Loading libraries...");
|
||||
LibraryManager libraryManager = new BukkitLibraryManager(this);
|
||||
libraryManager.addMavenCentral();
|
||||
libraryManager.setLogLevel(LogLevel.WARN);
|
||||
libraryManager.loadLibrary(Library.builder().groupId("org{}apache{}commons").artifactId("commons-compress").version("1.26.2").build());
|
||||
libraryManager.loadLibrary(Library.builder().groupId("com{}github{}luben").artifactId("zstd-jni").version("1.5.6-3").build());
|
||||
libraryManager.loadLibrary(Library.builder().groupId("com{}zaxxer").artifactId("HikariCP").version("4.0.3").build());
|
||||
libraryManager.loadLibrary(Library.builder().groupId("org{}xerial").artifactId("sqlite-jdbc").version("3.46.0.0").build());
|
||||
libraryManager.loadLibrary(Library.builder().groupId("redis{}clients").id("jedis").artifactId("jedis").version("5.1.3").build());
|
||||
logger.info("Successfully loaded libraries.");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
package com.io.yutian.elementoriginlib.logger;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class Logger {
|
||||
|
||||
private Class clazz;
|
||||
protected String formatClassName;
|
||||
|
||||
private Logger(Class clazz) {
|
||||
this.clazz = clazz;
|
||||
this.formatClassName = getFormatClassName(clazz, 2);
|
||||
}
|
||||
|
||||
public String getFormatClassName() {
|
||||
return formatClassName;
|
||||
}
|
||||
|
||||
protected String getFormatClassName(Class clazz, int keepChars) {
|
||||
String className = clazz.getName();
|
||||
String[] parts = className.split("\\.");
|
||||
StringBuilder abbreviated = new StringBuilder();
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
if (i < parts.length - 1) {
|
||||
abbreviated.append(parts[i].substring(0, Math.min(keepChars, parts[i].length()))).append(".");
|
||||
} else {
|
||||
abbreviated.append(parts[i]);
|
||||
}
|
||||
}
|
||||
return abbreviated.toString();
|
||||
}
|
||||
|
||||
private String getPrefix(String type) {
|
||||
return "[" + clazz.getSimpleName() +"] ";
|
||||
}
|
||||
|
||||
public void info(String msg) {
|
||||
Bukkit.getLogger().info(getPrefix("INFO")+ msg);
|
||||
}
|
||||
|
||||
public void warn(String msg) {
|
||||
Bukkit.getLogger().warning(getPrefix("WARN") + msg);
|
||||
}
|
||||
|
||||
public void error(String msg) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, getPrefix("ERROR") + msg);
|
||||
}
|
||||
|
||||
public void fatal(String msg) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "[!!!] "+getPrefix("FATAL") + msg);
|
||||
}
|
||||
|
||||
public void debug(String msg) {
|
||||
Bukkit.getLogger().finer(getPrefix("DEBUG") + msg);
|
||||
}
|
||||
|
||||
|
||||
public static Logger getLogger(Class clazz) {
|
||||
return new Logger(clazz);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package com.io.yutian.elementoriginlib.nbt;
|
||||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import com.io.yutian.elementoriginlib.exception.SerializeException;
|
|||
import com.io.yutian.elementoriginlib.serialize.serializers.ItemStackSerializer;
|
||||
import com.io.yutian.elementoriginlib.serialize.serializers.UUIDSerializer;
|
||||
import com.io.yutian.elementoriginlib.util.ReflectionUtil;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
|
|
@ -3,7 +3,7 @@ package com.io.yutian.elementoriginlib.serialize.serializers;
|
|||
import com.io.yutian.elementoriginlib.serialize.Serializer;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.minecraft.nbt.*;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
@ -52,7 +52,7 @@ public class ItemStackSerializer implements Serializer<ItemStack> {
|
|||
return tagByteArray.e();
|
||||
case 8:
|
||||
NBTTagString nbtTagString = (NBTTagString) nbtBase;
|
||||
return nbtTagString.m_();
|
||||
return nbtTagString.t_();
|
||||
case 9:
|
||||
NBTTagList nbtTagList = (NBTTagList) nbtBase;
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
|
|
50
src/main/java/net/byteflux/libby/BukkitLibraryManager.java
Normal file
50
src/main/java/net/byteflux/libby/BukkitLibraryManager.java
Normal file
|
@ -0,0 +1,50 @@
|
|||
package net.byteflux.libby;
|
||||
|
||||
import net.byteflux.libby.classloader.URLClassLoaderHelper;
|
||||
import net.byteflux.libby.logging.adapters.JDKLogAdapter;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* A runtime dependency manager for Bukkit plugins.
|
||||
*/
|
||||
public class BukkitLibraryManager extends LibraryManager {
|
||||
/**
|
||||
* Plugin classpath helper
|
||||
*/
|
||||
private final URLClassLoaderHelper classLoader;
|
||||
|
||||
/**
|
||||
* Creates a new Bukkit library manager.
|
||||
*
|
||||
* @param plugin the plugin to manage
|
||||
*/
|
||||
public BukkitLibraryManager(Plugin plugin) {
|
||||
this(plugin, "lib");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Bukkit library manager.
|
||||
*
|
||||
* @param plugin the plugin to manage
|
||||
* @param directoryName download directory name
|
||||
*/
|
||||
public BukkitLibraryManager(Plugin plugin, String directoryName) {
|
||||
super(new JDKLogAdapter(requireNonNull(plugin, "plugin").getLogger()), plugin.getDataFolder().toPath(), directoryName);
|
||||
classLoader = new URLClassLoaderHelper((URLClassLoader) plugin.getClass().getClassLoader(), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a file to the Bukkit plugin's classpath.
|
||||
*
|
||||
* @param file the file to add
|
||||
*/
|
||||
@Override
|
||||
protected void addToClasspath(Path file) {
|
||||
classLoader.addToClasspath(file);
|
||||
}
|
||||
}
|
16
src/main/java/net/byteflux/libby/LibbyProperties.java
Normal file
16
src/main/java/net/byteflux/libby/LibbyProperties.java
Normal file
|
@ -0,0 +1,16 @@
|
|||
package net.byteflux.libby;
|
||||
|
||||
/**
|
||||
* Filtered Maven properties and other related constants.
|
||||
*/
|
||||
public class LibbyProperties {
|
||||
/**
|
||||
* Project version
|
||||
*/
|
||||
public static final String VERSION = "${project.version}";
|
||||
|
||||
/**
|
||||
* User agent string to use when downloading libraries
|
||||
*/
|
||||
public static final String HTTP_USER_AGENT = "libby/" + VERSION;
|
||||
}
|
533
src/main/java/net/byteflux/libby/Library.java
Normal file
533
src/main/java/net/byteflux/libby/Library.java
Normal file
|
@ -0,0 +1,533 @@
|
|||
package net.byteflux.libby;
|
||||
|
||||
import net.byteflux.libby.relocation.Relocation;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.UUID;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* An immutable representation of a Maven artifact that can be downloaded,
|
||||
* relocated and then loaded into a plugin's classpath at runtime.
|
||||
*
|
||||
* @see #builder()
|
||||
*/
|
||||
public class Library {
|
||||
/**
|
||||
* Direct download URLs for this library
|
||||
*/
|
||||
private final Collection<String> urls;
|
||||
|
||||
/**
|
||||
* Repository URLs for this library
|
||||
*/
|
||||
private final Collection<String> repositories;
|
||||
|
||||
/**
|
||||
* Library id (used by Isolated Class Loaders)
|
||||
*/
|
||||
private final String id;
|
||||
|
||||
/**
|
||||
* Maven group ID
|
||||
*/
|
||||
private final String groupId;
|
||||
|
||||
/**
|
||||
* Maven artifact ID
|
||||
*/
|
||||
private final String artifactId;
|
||||
|
||||
/**
|
||||
* Artifact version
|
||||
*/
|
||||
private final String version;
|
||||
|
||||
/**
|
||||
* Artifact classifier
|
||||
*/
|
||||
private final String classifier;
|
||||
|
||||
/**
|
||||
* Binary SHA-256 checksum for this library's jar file
|
||||
*/
|
||||
private final byte[] checksum;
|
||||
|
||||
/**
|
||||
* Jar relocations to apply
|
||||
*/
|
||||
private final Collection<Relocation> relocations;
|
||||
|
||||
/**
|
||||
* Relative Maven path to this library's artifact
|
||||
*/
|
||||
private final String path;
|
||||
|
||||
/**
|
||||
* Relative partial Maven path to this library
|
||||
*/
|
||||
private final String partialPath;
|
||||
|
||||
/**
|
||||
* Relative path to this library's relocated jar
|
||||
*/
|
||||
private final String relocatedPath;
|
||||
|
||||
/**
|
||||
* Should this library be loaded in an isolated class loader?
|
||||
*/
|
||||
private final boolean isolatedLoad;
|
||||
|
||||
/**
|
||||
* Creates a new library.
|
||||
*
|
||||
* @param urls direct download URLs
|
||||
* @param id library ID
|
||||
* @param groupId Maven group ID
|
||||
* @param artifactId Maven artifact ID
|
||||
* @param version artifact version
|
||||
* @param classifier artifact classifier or null
|
||||
* @param checksum binary SHA-256 checksum or null
|
||||
* @param relocations jar relocations or null
|
||||
* @param isolatedLoad isolated load for this library
|
||||
*/
|
||||
private Library(Collection<String> urls,
|
||||
String id,
|
||||
String groupId,
|
||||
String artifactId,
|
||||
String version,
|
||||
String classifier,
|
||||
byte[] checksum,
|
||||
Collection<Relocation> relocations,
|
||||
boolean isolatedLoad) {
|
||||
|
||||
this(urls, null, id, groupId, artifactId, version, classifier, checksum, relocations, isolatedLoad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new library.
|
||||
*
|
||||
* @param urls direct download URLs
|
||||
* @param repositories repository URLs
|
||||
* @param id library ID
|
||||
* @param groupId Maven group ID
|
||||
* @param artifactId Maven artifact ID
|
||||
* @param version artifact version
|
||||
* @param classifier artifact classifier or null
|
||||
* @param checksum binary SHA-256 checksum or null
|
||||
* @param relocations jar relocations or null
|
||||
* @param isolatedLoad isolated load for this library
|
||||
*/
|
||||
private Library(Collection<String> urls,
|
||||
Collection<String> repositories,
|
||||
String id,
|
||||
String groupId,
|
||||
String artifactId,
|
||||
String version,
|
||||
String classifier,
|
||||
byte[] checksum,
|
||||
Collection<Relocation> relocations,
|
||||
boolean isolatedLoad) {
|
||||
|
||||
this.urls = urls != null ? Collections.unmodifiableList(new LinkedList<>(urls)) : Collections.emptyList();
|
||||
this.id = id != null ? id : UUID.randomUUID().toString();
|
||||
this.groupId = requireNonNull(groupId, "groupId").replace("{}", ".");
|
||||
this.artifactId = requireNonNull(artifactId, "artifactId");
|
||||
this.version = requireNonNull(version, "version");
|
||||
this.classifier = classifier;
|
||||
this.checksum = checksum;
|
||||
this.relocations = relocations != null ? Collections.unmodifiableList(new LinkedList<>(relocations)) : Collections.emptyList();
|
||||
|
||||
this.partialPath = this.groupId.replace('.', '/') + '/' + artifactId + '/' + version + '/';
|
||||
String path = this.partialPath + artifactId + '-' + version;
|
||||
if (hasClassifier()) {
|
||||
path += '-' + classifier;
|
||||
}
|
||||
|
||||
this.path = path + ".jar";
|
||||
|
||||
this.repositories = repositories != null ? Collections.unmodifiableList(new LinkedList<>(repositories)) : Collections.emptyList();
|
||||
relocatedPath = hasRelocations() ? path + "-relocated.jar" : null;
|
||||
this.isolatedLoad = isolatedLoad;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the direct download URLs for this library.
|
||||
*
|
||||
* @return direct download URLs
|
||||
*/
|
||||
public Collection<String> getUrls() {
|
||||
return urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the repositories URLs for this library.
|
||||
*
|
||||
* @return repositories URLs
|
||||
*/
|
||||
public Collection<String> getRepositories() {
|
||||
return repositories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the library ID
|
||||
*
|
||||
* @return the library id
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Maven group ID for this library.
|
||||
*
|
||||
* @return Maven group ID
|
||||
*/
|
||||
public String getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Maven artifact ID for this library.
|
||||
*
|
||||
* @return Maven artifact ID
|
||||
*/
|
||||
public String getArtifactId() {
|
||||
return artifactId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the artifact version for this library.
|
||||
*
|
||||
* @return artifact version
|
||||
*/
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the artifact classifier for this library.
|
||||
*
|
||||
* @return artifact classifier or null
|
||||
*/
|
||||
public String getClassifier() {
|
||||
return classifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this library has an artifact classifier.
|
||||
*
|
||||
* @return true if library has classifier, false otherwise
|
||||
*/
|
||||
public boolean hasClassifier() {
|
||||
return classifier != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the binary SHA-256 checksum of this library's jar file.
|
||||
*
|
||||
* @return checksum or null
|
||||
*/
|
||||
public byte[] getChecksum() {
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this library has a checksum.
|
||||
*
|
||||
* @return true if library has checksum, false otherwise
|
||||
*/
|
||||
public boolean hasChecksum() {
|
||||
return checksum != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the jar relocations to apply to this library.
|
||||
*
|
||||
* @return jar relocations to apply
|
||||
*/
|
||||
public Collection<Relocation> getRelocations() {
|
||||
return relocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this library has any jar relocations.
|
||||
*
|
||||
* @return true if library has relocations, false otherwise
|
||||
*/
|
||||
public boolean hasRelocations() {
|
||||
return !relocations.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the relative Maven path to this library's artifact.
|
||||
*
|
||||
* @return relative Maven path for this library
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the relative partial Maven path to this library.
|
||||
*
|
||||
* @return relative partial Maven path for this library
|
||||
*/
|
||||
public String getPartialPath() {
|
||||
return partialPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the relative path to this library's relocated jar.
|
||||
*
|
||||
* @return path to relocated artifact or null if has no relocations
|
||||
*/
|
||||
public String getRelocatedPath() {
|
||||
return relocatedPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the library loaded isolated?
|
||||
*
|
||||
* @return true if the library is loaded isolated
|
||||
*/
|
||||
public boolean isIsolatedLoad() {
|
||||
return isolatedLoad;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the library is a snapshot.
|
||||
*
|
||||
* @return whether the library is a snapshot.
|
||||
*/
|
||||
public boolean isSnapshot() {
|
||||
return version.endsWith("-SNAPSHOT");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a concise, human-readable string representation of this library.
|
||||
*
|
||||
* @return string representation
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
String name = groupId + ':' + artifactId + ':' + version;
|
||||
if (hasClassifier()) {
|
||||
name += ':' + classifier;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new library builder.
|
||||
*
|
||||
* @return new library builder
|
||||
*/
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Due to the constructor complexity of an immutable {@link Library},
|
||||
* instead this fluent builder is used to configure and then construct
|
||||
* a new library.
|
||||
*/
|
||||
public static class Builder {
|
||||
/**
|
||||
* Direct download URLs for this library
|
||||
*/
|
||||
private final Collection<String> urls = new LinkedList<>();
|
||||
|
||||
/**
|
||||
* Repository URLs for this library
|
||||
*/
|
||||
private final Collection<String> repositories = new LinkedList<>();
|
||||
|
||||
/**
|
||||
* The library ID
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* Maven group ID
|
||||
*/
|
||||
private String groupId;
|
||||
|
||||
/**
|
||||
* Maven artifact ID
|
||||
*/
|
||||
private String artifactId;
|
||||
|
||||
/**
|
||||
* Artifact version
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* Artifact classifier
|
||||
*/
|
||||
private String classifier;
|
||||
|
||||
/**
|
||||
* Binary SHA-256 checksum for this library's jar file
|
||||
*/
|
||||
private byte[] checksum;
|
||||
|
||||
/**
|
||||
* Isolated load
|
||||
*/
|
||||
private boolean isolatedLoad;
|
||||
|
||||
/**
|
||||
* Jar relocations to apply
|
||||
*/
|
||||
private final Collection<Relocation> relocations = new LinkedList<>();
|
||||
|
||||
/**
|
||||
* Adds a direct download URL for this library.
|
||||
*
|
||||
* @param url direct download URL
|
||||
* @return this builder
|
||||
*/
|
||||
public Builder url(String url) {
|
||||
urls.add(requireNonNull(url, "url"));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a repository URL for this library.
|
||||
* <p>Most common repositories can be found in {@link Repositories} class as constants.
|
||||
* <p>Note that repositories should be preferably added to the {@link LibraryManager} via {@link LibraryManager#addRepository(String)}.
|
||||
*
|
||||
* @param url repository URL
|
||||
* @return this builder
|
||||
*/
|
||||
public Builder repository(String url) {
|
||||
repositories.add(requireNonNull(url, "repository").endsWith("/") ? url : url + '/');
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the id for this library.
|
||||
*
|
||||
* @param id the ID
|
||||
* @return this builder
|
||||
*/
|
||||
public Builder id(String id) {
|
||||
this.id = id != null ? id : UUID.randomUUID().toString();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Maven group ID for this library.
|
||||
*
|
||||
* @param groupId Maven group ID
|
||||
* @return this builder
|
||||
*/
|
||||
public Builder groupId(String groupId) {
|
||||
this.groupId = requireNonNull(groupId, "groupId");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Maven artifact ID for this library.
|
||||
*
|
||||
* @param artifactId Maven artifact ID
|
||||
* @return this builder
|
||||
*/
|
||||
public Builder artifactId(String artifactId) {
|
||||
this.artifactId = requireNonNull(artifactId, "artifactId");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the artifact version for this library.
|
||||
*
|
||||
* @param version artifact version
|
||||
* @return this builder
|
||||
*/
|
||||
public Builder version(String version) {
|
||||
this.version = requireNonNull(version, "version");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the artifact classifier for this library.
|
||||
*
|
||||
* @param classifier artifact classifier
|
||||
* @return this builder
|
||||
*/
|
||||
public Builder classifier(String classifier) {
|
||||
this.classifier = requireNonNull(classifier, "classifier");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the binary SHA-256 checksum for this library.
|
||||
*
|
||||
* @param checksum binary SHA-256 checksum
|
||||
* @return this builder
|
||||
*/
|
||||
public Builder checksum(byte[] checksum) {
|
||||
this.checksum = requireNonNull(checksum, "checksum");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Base64-encoded SHA-256 checksum for this library.
|
||||
*
|
||||
* @param checksum Base64-encoded SHA-256 checksum
|
||||
* @return this builder
|
||||
*/
|
||||
public Builder checksum(String checksum) {
|
||||
return checksum(Base64.getDecoder().decode(requireNonNull(checksum, "checksum")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the isolated load for this library.
|
||||
*
|
||||
* @param isolatedLoad the isolated load boolean
|
||||
* @return this builder
|
||||
*/
|
||||
public Builder isolatedLoad(boolean isolatedLoad) {
|
||||
this.isolatedLoad = isolatedLoad;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a jar relocation to apply to this library.
|
||||
*
|
||||
* @param relocation jar relocation to apply
|
||||
* @return this builder
|
||||
*/
|
||||
public Builder relocate(Relocation relocation) {
|
||||
relocations.add(requireNonNull(relocation, "relocation"));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a jar relocation to apply to this library.
|
||||
*
|
||||
* @param pattern search pattern
|
||||
* @param relocatedPattern replacement pattern
|
||||
* @return this builder
|
||||
*/
|
||||
public Builder relocate(String pattern, String relocatedPattern) {
|
||||
return relocate(new Relocation(pattern, relocatedPattern));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new library using this builder's configuration.
|
||||
*
|
||||
* @return new library
|
||||
*/
|
||||
public Library build() {
|
||||
return new Library(urls, repositories, id, groupId, artifactId, version, classifier, checksum, relocations, isolatedLoad);
|
||||
}
|
||||
}
|
||||
}
|
596
src/main/java/net/byteflux/libby/LibraryManager.java
Normal file
596
src/main/java/net/byteflux/libby/LibraryManager.java
Normal file
|
@ -0,0 +1,596 @@
|
|||
package net.byteflux.libby;
|
||||
|
||||
import net.byteflux.libby.classloader.IsolatedClassLoader;
|
||||
import net.byteflux.libby.logging.LogLevel;
|
||||
import net.byteflux.libby.logging.Logger;
|
||||
import net.byteflux.libby.logging.adapters.LogAdapter;
|
||||
import net.byteflux.libby.relocation.Relocation;
|
||||
import net.byteflux.libby.relocation.RelocationHelper;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* A runtime dependency manager for plugins.
|
||||
* <p>
|
||||
* The library manager can resolve a dependency jar through the configured
|
||||
* Maven repositories, download it into a local cache, relocate it and then
|
||||
* load it into the plugin's classpath.
|
||||
* <p>
|
||||
* Transitive dependencies for a library aren't downloaded automatically and
|
||||
* must be explicitly loaded like every other library.
|
||||
* <p>
|
||||
* It's recommended that libraries are relocated to prevent any namespace
|
||||
* conflicts with different versions of the same library bundled with other
|
||||
* plugins or maybe even bundled with the server itself.
|
||||
*
|
||||
* @see Library
|
||||
*/
|
||||
public abstract class LibraryManager {
|
||||
/**
|
||||
* Wrapped plugin logger
|
||||
*/
|
||||
protected final Logger logger;
|
||||
|
||||
/**
|
||||
* Directory where downloaded library jars are saved to
|
||||
*/
|
||||
protected final Path saveDirectory;
|
||||
|
||||
/**
|
||||
* Maven repositories used to resolve artifacts
|
||||
*/
|
||||
private final Set<String> repositories = new LinkedHashSet<>();
|
||||
|
||||
/**
|
||||
* Lazily-initialized relocation helper that uses reflection to call into
|
||||
* Luck's Jar Relocator
|
||||
*/
|
||||
private RelocationHelper relocator;
|
||||
|
||||
/**
|
||||
* Map of isolated class loaders and theirs id
|
||||
*/
|
||||
private final Map<String, IsolatedClassLoader> isolatedLibraries = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Creates a new library manager.
|
||||
*
|
||||
* @param logAdapter plugin logging adapter
|
||||
* @param dataDirectory plugin's data directory
|
||||
*
|
||||
* @deprecated Use {@link LibraryManager#LibraryManager(LogAdapter, Path, String)}
|
||||
*/
|
||||
@Deprecated
|
||||
protected LibraryManager(LogAdapter logAdapter, Path dataDirectory) {
|
||||
logger = new Logger(requireNonNull(logAdapter, "logAdapter"));
|
||||
saveDirectory = requireNonNull(dataDirectory, "dataDirectory").toAbsolutePath().resolve("lib");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new library manager.
|
||||
*
|
||||
* @param logAdapter plugin logging adapter
|
||||
* @param dataDirectory plugin's data directory
|
||||
* @param directoryName download directory name
|
||||
*/
|
||||
protected LibraryManager(LogAdapter logAdapter, Path dataDirectory, String directoryName) {
|
||||
logger = new Logger(requireNonNull(logAdapter, "logAdapter"));
|
||||
saveDirectory = requireNonNull(dataDirectory, "dataDirectory").toAbsolutePath().resolve(requireNonNull(directoryName, "directoryName"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a file to the plugin's classpath.
|
||||
*
|
||||
* @param file the file to add
|
||||
*/
|
||||
protected abstract void addToClasspath(Path file);
|
||||
|
||||
/**
|
||||
* Adds a file to the isolated class loader
|
||||
*
|
||||
* @param library the library to add
|
||||
* @param file the file to add
|
||||
*/
|
||||
protected void addToIsolatedClasspath(Library library, Path file) {
|
||||
IsolatedClassLoader classLoader;
|
||||
String id = library.getId();
|
||||
if (id != null) {
|
||||
classLoader = isolatedLibraries.computeIfAbsent(id, s -> new IsolatedClassLoader());
|
||||
} else {
|
||||
classLoader = new IsolatedClassLoader();
|
||||
}
|
||||
classLoader.addPath(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the isolated class loader of the library
|
||||
*
|
||||
* @param libraryId the id of the library
|
||||
*/
|
||||
public IsolatedClassLoader getIsolatedClassLoaderOf(String libraryId) {
|
||||
return isolatedLibraries.get(libraryId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the logging level for this library manager.
|
||||
*
|
||||
* @return log level
|
||||
*/
|
||||
public LogLevel getLogLevel() {
|
||||
return logger.getLevel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the logging level for this library manager.
|
||||
* <p>
|
||||
* By setting this value, the library manager's logger will not log any
|
||||
* messages with a level less severe than the configured level. This can be
|
||||
* useful for silencing the download and relocation logging.
|
||||
* <p>
|
||||
* Setting this value to {@link LogLevel#WARN} would silence informational
|
||||
* logging but still print important things like invalid checksum warnings.
|
||||
*
|
||||
* @param level the log level to set
|
||||
*/
|
||||
public void setLogLevel(LogLevel level) {
|
||||
logger.setLevel(level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently added repositories used to resolve artifacts.
|
||||
* <p>
|
||||
* For each library this list is traversed to download artifacts after the
|
||||
* direct download URLs have been attempted.
|
||||
*
|
||||
* @return current repositories
|
||||
*/
|
||||
public Collection<String> getRepositories() {
|
||||
List<String> urls;
|
||||
synchronized (repositories) {
|
||||
urls = new LinkedList<>(repositories);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(urls);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a repository URL to this library manager.
|
||||
* <p>
|
||||
* Artifacts will be resolved using this repository when attempts to locate
|
||||
* the artifact through previously added repositories are all unsuccessful.
|
||||
*
|
||||
* @param url repository URL to add
|
||||
*/
|
||||
public void addRepository(String url) {
|
||||
String repo = requireNonNull(url, "url").endsWith("/") ? url : url + '/';
|
||||
synchronized (repositories) {
|
||||
repositories.add(repo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the current user's local Maven repository.
|
||||
*/
|
||||
public void addMavenLocal() {
|
||||
addRepository(Paths.get(System.getProperty("user.home")).resolve(".m2/repository").toUri().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Maven Central repository.
|
||||
*/
|
||||
public void addMavenCentral() {
|
||||
addRepository(Repositories.MAVEN_CENTRAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Sonatype OSS repository.
|
||||
*/
|
||||
public void addSonatype() {
|
||||
addRepository(Repositories.SONATYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Bintray JCenter repository.
|
||||
*/
|
||||
public void addJCenter() {
|
||||
addRepository(Repositories.JCENTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the JitPack repository.
|
||||
*/
|
||||
public void addJitPack() {
|
||||
addRepository(Repositories.JITPACK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all of the possible download URLs for this library. Entries are
|
||||
* ordered by direct download URLs first and then repository download URLs.
|
||||
* <br>This method also resolves SNAPSHOT artifacts URLs.
|
||||
*
|
||||
* @param library the library to resolve
|
||||
* @return download URLs
|
||||
*/
|
||||
public Collection<String> resolveLibrary(Library library) {
|
||||
Set<String> urls = new LinkedHashSet<>(requireNonNull(library, "library").getUrls());
|
||||
boolean snapshot = library.isSnapshot();
|
||||
|
||||
// Try from library-declared repos first
|
||||
for (String repository : library.getRepositories()) {
|
||||
if (snapshot) {
|
||||
String url = resolveSnapshot(repository, library);
|
||||
if (url != null)
|
||||
urls.add(repository + url);
|
||||
} else {
|
||||
urls.add(repository + library.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
for (String repository : getRepositories()) {
|
||||
if (snapshot) {
|
||||
String url = resolveSnapshot(repository, library);
|
||||
if (url != null)
|
||||
urls.add(repository + url);
|
||||
} else {
|
||||
urls.add(repository + library.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableSet(urls);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the URL of the artifact of a snapshot library.
|
||||
*
|
||||
* @param repository The repository to query for snapshot information
|
||||
* @param library The library
|
||||
* @return The URl of the artifact of a snapshot library or null if no information could be gathered from the
|
||||
* provided repository
|
||||
*/
|
||||
private String resolveSnapshot(String repository, Library library) {
|
||||
String url = requireNonNull(repository, "repository") + requireNonNull(library, "library").getPartialPath() + "maven-metadata.xml";
|
||||
try {
|
||||
URLConnection connection = new URL(requireNonNull(url, "url")).openConnection();
|
||||
|
||||
connection.setConnectTimeout(5000);
|
||||
connection.setReadTimeout(5000);
|
||||
connection.setRequestProperty("User-Agent", LibbyProperties.HTTP_USER_AGENT);
|
||||
|
||||
try (InputStream in = connection.getInputStream()) {
|
||||
return getURLFromMetadata(in, library);
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
} catch (IOException e) {
|
||||
if (e instanceof FileNotFoundException) {
|
||||
logger.debug("File not found: " + url);
|
||||
} else if (e instanceof SocketTimeoutException) {
|
||||
logger.debug("Connect timed out: " + url);
|
||||
} else if (e instanceof UnknownHostException) {
|
||||
logger.debug("Unknown host: " + url);
|
||||
} else {
|
||||
logger.debug("Unexpected IOException", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL of the artifact of a snapshot library from the provided InputStream, which should be opened to the
|
||||
* library's maven-metadata.xml.
|
||||
*
|
||||
* @param inputStream The InputStream opened to the library's maven-metadata.xml
|
||||
* @param library The library
|
||||
* @return The URl of the artifact of a snapshot library or null if no information could be gathered from the
|
||||
* provided inputStream
|
||||
* @throws IOException If any IO errors occur
|
||||
*/
|
||||
private String getURLFromMetadata(InputStream inputStream, Library library) throws IOException {
|
||||
requireNonNull(inputStream, "inputStream");
|
||||
requireNonNull(library, "library");
|
||||
|
||||
String timestamp, buildNumber;
|
||||
try {
|
||||
// This reads the maven-metadata.xml file and gets the snapshot info from the <snapshot> tag.
|
||||
// Example tag:
|
||||
// <snapshot>
|
||||
// <timestamp>20220617.013635</timestamp>
|
||||
// <buildNumber>12</buildNumber>
|
||||
// </snapshot>
|
||||
|
||||
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
|
||||
Document doc = dBuilder.parse(inputStream);
|
||||
doc.getDocumentElement().normalize();
|
||||
|
||||
NodeList nodes = doc.getElementsByTagName("snapshot");
|
||||
if (nodes.getLength() == 0) {
|
||||
return null;
|
||||
}
|
||||
Node snapshot = nodes.item(0);
|
||||
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;
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
String url = library.getPartialPath() + library.getArtifactId() + '-' + version + '-' + timestamp + '-' + buildNumber;
|
||||
if (library.hasClassifier()) {
|
||||
url += '-' + library.getClassifier();
|
||||
}
|
||||
return url + ".jar";
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a library jar and returns the contents as a byte array.
|
||||
*
|
||||
* @param url the URL to the library jar
|
||||
* @return downloaded jar as byte array or null if nothing was downloaded
|
||||
*/
|
||||
private byte[] downloadLibrary(String url) {
|
||||
try {
|
||||
URLConnection connection = new URL(requireNonNull(url, "url")).openConnection();
|
||||
|
||||
connection.setConnectTimeout(5000);
|
||||
connection.setReadTimeout(5000);
|
||||
connection.setRequestProperty("User-Agent", LibbyProperties.HTTP_USER_AGENT);
|
||||
|
||||
try (InputStream in = connection.getInputStream()) {
|
||||
int len;
|
||||
byte[] buf = new byte[8192];
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
while ((len = in.read(buf)) != -1) {
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
} catch (SocketTimeoutException e) {
|
||||
logger.warn("Download timed out: " + connection.getURL());
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.info("Downloaded library " + connection.getURL());
|
||||
return out.toByteArray();
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
} catch (IOException e) {
|
||||
if (e instanceof FileNotFoundException) {
|
||||
logger.debug("File not found: " + url);
|
||||
} else if (e instanceof SocketTimeoutException) {
|
||||
logger.debug("Connect timed out: " + url);
|
||||
} else if (e instanceof UnknownHostException) {
|
||||
logger.debug("Unknown host: " + url);
|
||||
} else {
|
||||
logger.debug("Unexpected IOException", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a library jar to the save directory if it doesn't already
|
||||
* exist (snapshot libraries are always re-downloaded) and returns
|
||||
* the local file path.
|
||||
* <p>
|
||||
* If the library has a checksum, it will be compared against the
|
||||
* downloaded jar's checksum to verify the integrity of the download. If
|
||||
* the checksums don't match, a warning is generated and the next download
|
||||
* URL is attempted.
|
||||
* <p>
|
||||
* Checksum comparison is ignored if the library doesn't have a checksum
|
||||
* or if the library jar already exists in the save directory.
|
||||
* <p>
|
||||
* Most of the time it is advised to use {@link #loadLibrary(Library)}
|
||||
* instead of this method because this one is only concerned with
|
||||
* downloading the jar and returning the local path. It's usually more
|
||||
* desirable to download the jar and add it to the plugin's classpath in
|
||||
* one operation.
|
||||
*
|
||||
* @param library the library to download
|
||||
* @return local file path to library
|
||||
* @see #loadLibrary(Library)
|
||||
*/
|
||||
public Path downloadLibrary(Library library) {
|
||||
Path file = saveDirectory.resolve(requireNonNull(library, "library").getPath());
|
||||
if (Files.exists(file)) {
|
||||
// Early return only if library isn't a snapshot, since snapshot libraries are always re-downloaded
|
||||
if (!library.isSnapshot()) {
|
||||
return file;
|
||||
}
|
||||
|
||||
// Delete the file since the Files.move call down below will fail if it exists
|
||||
try {
|
||||
Files.delete(file);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
Collection<String> urls = resolveLibrary(library);
|
||||
if (urls.isEmpty()) {
|
||||
throw new RuntimeException("Library '" + library + "' couldn't be resolved, add a repository");
|
||||
}
|
||||
|
||||
MessageDigest md = null;
|
||||
if (library.hasChecksum()) {
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA-256");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
Path out = file.resolveSibling(file.getFileName() + ".tmp");
|
||||
out.toFile().deleteOnExit();
|
||||
|
||||
try {
|
||||
Files.createDirectories(file.getParent());
|
||||
|
||||
for (String url : urls) {
|
||||
byte[] bytes = downloadLibrary(url);
|
||||
if (bytes == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (md != null) {
|
||||
byte[] checksum = md.digest(bytes);
|
||||
if (!Arrays.equals(checksum, library.getChecksum())) {
|
||||
logger.warn("*** INVALID CHECKSUM ***");
|
||||
logger.warn(" Library : " + library);
|
||||
logger.warn(" URL : " + url);
|
||||
logger.warn(" Expected : " + Base64.getEncoder().encodeToString(library.getChecksum()));
|
||||
logger.warn(" Actual : " + Base64.getEncoder().encodeToString(checksum));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Files.write(out, bytes);
|
||||
Files.move(out, file);
|
||||
|
||||
return file;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
} finally {
|
||||
try {
|
||||
Files.deleteIfExists(out);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException("Failed to download library '" + library + "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the input jar and generates an output jar with the provided
|
||||
* relocation rules applied, then returns the path to the relocated jar.
|
||||
*
|
||||