1

Ok, I know that similar questions have been already posted here, and in fact, my solution is feeding from them.

The idea I have is to have some modules (a module is a set of XML files in a folder inside a JAR file) that can be added to an application by the user. That means that the user can put some jars in a predefined folder, and all resources from this JARs must be available for the application. For this purpose, I need to load all JARs from an external folder, and retrieve the XMLs as resources. The JAR files, usually have not any class file, but maybe at the future can have it.

Also the application has a "default" module that is already inside its resources folder. This module is working fine and I can list all XMLs inside it using Reflections.

My code from retrieving all XML files using Reflection is very simple:

    final Set<String> resources = new Reflections(CustomPath.MODULES_FOLDER, new ResourcesScanner()).getResources(Pattern
            .compile(".*\\.xml"));

that returns a Set of strings similar to modules_folder/module_name/file1.xml.

And the code to load a Jar file is using a URLClassLoader:

Method method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[] { URL.class });
        method.setAccessible(true); // promote the method to public access.
        method.invoke(loader, new Object[] { url });

As far as I have understood, the resources are available from the URLClassLoader. In fact, I can do something like:

URLClassLoader.getSystemResource("modules_folder/module_name/file1.xml").

And the file is loaded and If I put an invalid path, an error is thrown. Also I need to know the 'module_name' that is not predefined. Then is not a solution.

If I run again the method to retrieve the resources using Reflection, it is only retrieving the files inside the resources folder of the project. No resource from the JAR is retrieved.

Then, I cannot understand why reflections is unable to get the files from the JAR files if are already loaded. I am not sure if Reflections is unable to access to the URLCLassLoader for any reason, or that must be used in a different way.

The question is... Why Reflections is not able to access to the resources from a JAR loaded at runtime? And if it is a limitation, what is the best approach to do it without any Java classes inside the external JAR file?

King Midas
  • 1,442
  • 4
  • 29
  • 50
  • Why .jar? Can't you just load and "index" (just put to some map by name) all zip files in given folder? I don't see a point in using **Class** loader here. – GotoFinal Aug 23 '19 at 15:47
  • What is inside `CustomPath.MODULES_FOLDER`? you are probably scanning in wrong place, as `Reflections` is expecting package filter here – GotoFinal Aug 23 '19 at 15:57
  • CustomPath.Modules_FOLDER has only a string with the name of the folder of the project. Here, as far as I know, I can put here some prefix for the search, not the package filter. – King Midas Aug 23 '19 at 20:12
  • Regarding to the Class loader. Is the way I have found to try to load the resources, but I am opened to any other way to load them due to I have not success. I prefer to have everything in a single file (jar, zip) due to is easier to add or remove. – King Midas Aug 23 '19 at 20:15
  • the code for String parameter is: `builder.addUrls(ClasspathHelper.forPackage((String)param, classLoaders));`, `filter.includePackage(new String[]{(String)param});` – GotoFinal Aug 23 '19 at 20:59
  • Ok, the point in your comment is not the String parameter, that as far as I have understood I can use `Reflections(String prefix, @Nullable Scanner... scanners)` for this purpose [Documentation](https://static.javadoc.io/org.reflections/reflections/0.9.10/org/reflections/Reflections.html). But the clue you have give to me is that I can define the classLoaders using the ConfigurationBuilder.. Here I can define not only the URLClassLoader but also the default ones. – King Midas Aug 24 '19 at 07:33
  • Thanks for the help, it would be good if it is post as an answer that I can accept. – King Midas Aug 24 '19 at 07:41

1 Answers1

0

Ok, then puts the final result as a future reference. My problem is that the default constructor of new Reflections(CustomPath.MODULES_FOLDER, new ResourcesScanner()) is not accessing the URLClassLoader. Then is impossible that it can obtains the resources I am looking for. A solution is to use the ConfigurationBuilder and include all ClassLoaders I need.

final ConfigurationBuilder builder = new ConfigurationBuilder();
builder.addUrls(ClasspathHelper.forPackage(PathManager.MODULES_FOLDER, ClassLoader.getSystemClassLoader(),
            ClasspathHelper.contextClassLoader(), ClasspathHelper.staticClassLoader()));
builder.addScanners(new ResourcesScanner());
final Reflections reflections = new Reflections(builder);
final Set<String> resources = reflections.getResources(Pattern.compile(".*\\.xml"));

As can been seen in the previous code, I add the ClassLoader.getSystemClassLoader() to the builder, and this loader is the one I am using for loading the Jar files.

King Midas
  • 1,442
  • 4
  • 29
  • 50