Skip to content
Permalink
Browse files

Use custom classloader (compatible with Java 8 & 9)

  • Loading branch information
mastercoms committed Apr 10, 2017
1 parent d917c6c commit 354640b5a78f842317ededed9aa304cc5185a707
Showing with 74 additions and 19 deletions.
  1. +1 −1 pom.xml
  2. +3 −0 src/main/java/net/glowstone/GlowServer.java
  3. +70 −18 src/main/java/net/glowstone/util/LibraryManager.java
@@ -7,7 +7,7 @@
<name>Glowstone</name>
<groupId>net.glowstone</groupId>
<artifactId>glowstone</artifactId>
<version>2017.4.0.1-SNAPSHOT</version>
<version>2017.4.0.2-SNAPSHOT</version>
<packaging>jar</packaging>
<inceptionYear>2011</inceptionYear>
<url>https://www.glowstone.net</url>
@@ -301,6 +301,7 @@ public GlowServer(ServerConfig config) {
*/
public static void main(String... args) {
try {
Thread.currentThread().setContextClassLoader(LibraryManager.getLibraryClassLoader());
GlowServer server = createFromArguments(args);

// we don't want to run a server when called with --version
@@ -309,6 +310,8 @@ public static void main(String... args) {
}

server.run();
} catch (SecurityException e) {
logger.log(Level.WARNING, "Error loading classpath!", e);
} catch (Throwable t) {
// general server startup crash
logger.log(Level.SEVERE, "Error during server startup.", t);
@@ -6,12 +6,17 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@@ -25,21 +30,48 @@
/**
* The Maven repository to download from.
*/
private final String repository;
final String repository;

/**
* The directory to store downloads in.
*/
private final File directory;
final File directory;

private final ExecutorService downloaderService = Executors.newCachedThreadPool();

private static final LibraryClassLoader loader = new LibraryClassLoader();

public LibraryManager() {
// todo: allow configuration of repository, libraries, and directory
repository = "https://repo.glowstone.net/service/local/repositories/central/content/";
directory = new File("lib");
}

public static void addToClasspath(String... paths) {
try {
for (String path : Objects.requireNonNull(paths)) {
String trimmedPath;
if (path != null && !(trimmedPath = path.trim()).isEmpty()) {
loader.addURL(Paths.get(trimmedPath).toUri().toURL());
}
}
} catch (IllegalArgumentException | MalformedURLException e) {
RuntimeException re = new RuntimeException(e);
re.setStackTrace(e.getStackTrace());
throw re;
}
}

public static ClassLoader getLibraryClassLoader() {
return loader;
}

public static void addToLibraryPath(String... paths) {
for (String path : Objects.requireNonNull(paths)) {
loader.addLibPath(path);
}
}

public void run() {
if (!directory.isDirectory() && !directory.mkdirs()) {
GlowServer.logger.log(Level.SEVERE, "Could not create libraries directory: " + directory);
@@ -63,7 +95,7 @@ public void run() {
private final String version;
private String checksum;

private LibraryDownloader(String group, String library, String version, String checksum) {
LibraryDownloader(String group, String library, String version, String checksum) {
this.group = group;
this.library = library;
this.version = version;
@@ -73,40 +105,60 @@ private LibraryDownloader(String group, String library, String version, String c
@Override
public void run() {
// check if we already have it
File file = new File(directory, library + "-" + version + ".jar");
File file = new File(directory, library + '-' + version + ".jar");
if (!file.exists() && checksum(file, checksum)) {
// download it
GlowServer.logger.info("Downloading " + library + " " + version + "...");
GlowServer.logger.info("Downloading " + library + ' ' + version + "...");
try {
URL downloadUrl = new URL(repository + group.replace('.', '/') + "/" + library + "/" + version + "/" + library + "-" + version + ".jar");
URL downloadUrl = new URL(repository + group.replace('.', '/') + '/' + library + '/' + version + '/' + library + '-' + version + ".jar");
HttpsURLConnection connection = (HttpsURLConnection) downloadUrl.openConnection();
connection.setRequestProperty("User-Agent", "Mozilla/5.0");

try (ReadableByteChannel input = Channels.newChannel(connection.getInputStream());
FileOutputStream output = new FileOutputStream(file)) {
output.getChannel().transferFrom(input, 0, Long.MAX_VALUE);
GlowServer.logger.info("Downloaded " + library + " " + version + ".");
GlowServer.logger.info("Downloaded " + library + ' ' + version + '.');
}
} catch (IOException e) {
GlowServer.logger.log(Level.WARNING, "Failed to download: " + library + " " + version, e);
GlowServer.logger.log(Level.WARNING, "Failed to download: " + library + ' ' + version, e);
return;
}
}

// hack it onto the classpath
URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
try {
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(sysLoader, file.toURI().toURL());
} catch (ReflectiveOperationException | MalformedURLException e) {
GlowServer.logger.log(Level.WARNING, "Failed to add to classpath: " + library + " " + version, e);
}
addToClasspath(file.getAbsolutePath());
}

public boolean checksum(File file, String checksum) {
// TODO: actually check checksum
return true;
}
}

private static class LibraryClassLoader extends URLClassLoader {

static {
ClassLoader.registerAsParallelCapable();
}

private final Set<Path> libPaths = new CopyOnWriteArraySet<>();

private LibraryClassLoader() {
super(new URL[0]);
}

@Override
protected void addURL(URL url) {
super.addURL(url);
}

protected void addLibPath(String path) {
libPaths.add(Paths.get(path).toAbsolutePath());
}

@Override
protected String findLibrary(String libname) {
String nativeName = System.mapLibraryName(libname);
return libPaths.stream().map(path -> path.resolve(nativeName)).filter(Files::exists).map(Path::toString).findFirst().orElse(super.findLibrary(libname));
}
}
}

0 comments on commit 354640b

Please sign in to comment.
You can’t perform that action at this time.