I have a plugin folder that contains some JARs, and I want to load all of their *.class. I see a lot of answers with JarFile
or ZipEntry
stuff. However, I would like to do this using java.nio package and not io.
What I can do:
- listing files in a folder (listing all my jars for instance) using
Files.list(Path)
- loading a .class that's inside a JAR, knowning its exact location in the said JAR.
What I cannot do yet:
- listing files in a JAR/ZIP using NIO.
Ultimately, I want:
- Walking through every class in several JARs and loading them to seek an annotation for a kind of plugin system
I should add that I do not need a working solution using APIs like Guava or Apache but I just want to know how it's done.
My current code:
import java.io.IOException;
import java.nio.file.*;
import java.security.SecureClassLoader;
import java.util.*;
public class PluginManager {
public final Path pluginFolder;
public PluginManager(Path pluginFolder) {
this.pluginFolder = pluginFolder;
}
public void load() {
final var pluginClassLoader = new SecureClassLoader(getClass().getClassLoader()){
FileSystem jar;
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String pluginClassName = jar.getSeparator() + name.replaceAll("\\.", jar.getSeparator()) + ".class";
try{
byte[] bytes = Files.readAllBytes(jar.getPath(pluginClassName));
return defineClass(name, bytes, 0, bytes.length);
}catch(IOException e){
e.printStackTrace();
}
throw new ClassNotFoundException("Failed to found "+name);
}
};
try(Stream<Path> files = Files.list(pluginFolder)){
files.filter((file) -> !Files.isDirectory(file) &&
(file.toString().endsWith(".jar") || file.toString().endsWith(".zip")))
.forEach(jar -> {
try(FileSystem fileSystem = FileSystems.newFileSystem(jar)){
pluginClassLoader.jar = fileSystem;
loadPlugin(fileSystem, pluginClassLoader);
}catch(IOException | ClassNotFoundException e){
e.printStackTrace();
}
});
}catch(IOException e){
e.printStackTrace();
throw new IllegalStateException("Failed to scan plugin folder. Cannot continue.");
}
}
private void loadPlugin(FileSystem jar, SecureClassLoader classLoader) throws IOException, ClassNotFoundException{
String className = ... // here, we retrieve the plugin class name from a manifest file using FileSystem.getPath().
// what I want is to get rid of that manifest and to load and test every class.
Class<?> clazz = classLoader.loadClass(className);
// do stuff with loaded class
}
I want to avoid java.io code like this: How to load Classes at runtime from a folder or JAR?