0

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:

  1. listing files in a folder (listing all my jars for instance) using Files.list(Path)
  2. loading a .class that's inside a JAR, knowning its exact location in the said JAR.

What I cannot do yet:

  1. listing files in a JAR/ZIP using NIO.

Ultimately, I want:

  1. 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?

  • Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. – Community Mar 18 '22 at 16:12
  • You don't need any of this. Just create a `URLClassLoader` with a list of the URLs to the JAR files, which you can get from a walker, and then set it as the context class loader. – user207421 Apr 01 '22 at 00:55

1 Answers1

0

I stumbled across this List of files inside a zip folder and its subfolder. What I initially missed with this approach was not to call Files.walkFileTree(Path, FileVisitor) with the jar path from the folder, but rather use the root path of the FileSystem created (using FileSystem.getPath("/")). This works perfectly for me.