planetiler/planetiler-core/src/main/java/com/onthegomap/planetiler/util/DuplicateClassLoader.java

52 wiersze
1.7 KiB
Java

package com.onthegomap.planetiler.util;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Objects;
import java.util.function.Predicate;
/**
* A class loader that loads certain classes even if they are already loaded by a parent classloader.
*
* This makes it possible to get multiple copies of the same class, so for example you could invoke a method
* synchronized on a static variable from different classes without contention.
*/
public class DuplicateClassLoader extends ClassLoader {
private final Predicate<String> shouldDuplicate;
private DuplicateClassLoader(Predicate<String> shouldDuplicate) {
this.shouldDuplicate = shouldDuplicate;
}
/**
* Returns a {@link ClassLoader} that loads every class with a name starting with {@code prefix} even if the parent
* has already loaded it.
*/
public static DuplicateClassLoader duplicateClassesWithPrefix(String prefix) {
return new DuplicateClassLoader(name -> name.startsWith(prefix));
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (shouldDuplicate.test(name)) {
Class<?> c = findLoadedClass(name);
if (c == null) {
byte[] b = loadClassFromFile(name);
return defineClass(name, b, 0, b.length);
}
}
return super.loadClass(name);
}
private byte[] loadClassFromFile(String fileName) {
String classFileName = fileName.replace('.', '/') + ".class";
try (var inputStream = getClass().getClassLoader().getResourceAsStream(classFileName)) {
return Objects.requireNonNull(inputStream, "Could not load " + fileName + " (" + classFileName + ")")
.readAllBytes();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}