So far until non-modularized java, you would simply put a file in src/main/java/resources
make sure it is in classpath and then load it with
file = getClass().getClassLoader().getResourceAsStream("myfilename");
from pretty much anywhere in the classpath.
Now with modules, the plot thickens.
My project setup is the following:
module playground.api {
requires java.base;
requires java.logging;
requires framework.core;
}
Config file is placed inside src/main/resources/config.yml
.
Project is run with
java -p target/classes:target/dependency -m framework.core/com.framework.Main
Since the main class does not reside in my own project, but in an external framework module, it can't see config.yml
. Now the question is, is there a way to somehow put my config file into the module or open it up? Do I have to change the way file is loaded by framework upstream?
I tried using "exports" or "opens" in module-info but it wants to have a package name, not a folder name.
How to achieve this in best practical way so it would work as in Java 8 and with as little changes as possible?
// to scan the module path
ClassLoader.getSystemResources(resourceName)
// if you know a class where the resource is
Class.forName(className).getResourceAsStream(resourceName)
// if you know the module containing the resource
ModuleLayer.boot().findModule(moduleName).getResourceAsStream(resourceName)
See a working example below.
Given:
.
├── FrameworkCore
│ └── src
│ └── FrameworkCore
│ ├── com
│ │ └── framework
│ │ └── Main.java
│ └── module-info.java
└── PlaygroundApi
└── src
└── PlaygroundApi
├── com
│ └── playground
│ └── api
│ └── App.java
├── config.yml
└── module-info.java
Main.java
could be
package com.framework;
import java.io.*;
import java.net.URL;
import java.util.Optional;
import java.util.stream.Collectors;
public class Main {
public static void main( String[] args )
{
// load from anywhere in the modulepath
try {
URL url = ClassLoader.getSystemResources("config.yml").nextElement();
InputStream is = url.openStream();
Main.read(is);
} catch (IOException e) {
throw new RuntimeException(e);
}
// load from the the module where a given class is
try {
InputStream is = Class.forName("com.playground.api.App").getResourceAsStream("/config.yml");
Main.read(is);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
// load from a specific module
Optional<Module> specificModule = ModuleLayer.boot().findModule("PlaygroundApi");
specificModule.ifPresent(module -> {
try {
InputStream is = module.getResourceAsStream("config.yml");
Main.read(is);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
private static void read(InputStream is) {
String s = new BufferedReader(new InputStreamReader(is)).lines().collect(Collectors.joining("\n"));
System.out.println("config.yml: " + s);
}
}
And you would launch with
java --module-path ./FrameworkCore/target/classes:./PlaygroundApi/target/classes \
--add-modules FrameworkCore,PlaygroundApi \
com.framework.Main
To clone this example: git clone https://github.com/j4n0/SO-46861589.git