Accessing resource files from external modules
While you are using the java
command to launch an application as follows:-
java -p target/classes:target/dependency -m framework.core/com.framework.Main
you are specifying the modulepath using the option
-p
aternate for--module-path
which would look up into target/classes and target/dependency for your modules.Alongside, using
-m
alternate for--module
specifies the initial module to resolve with the nameframework.core
and constructs the module graph with the main class to execute explicitly listed ascom.framework.Main
.
Now, the problem here seems to be that the module framework.core
doesn't requires
or read playground.api
module because of which the module graph doesn't include the desired module consisting of the actual resource config.yml
.
As suggested by @Alan, a good way to list out the module resolution output during startup is to make use of the --show-module-resolution
option.
I just naively tried to opens src/main/resources, doesn't compile ofc
Since the resource in your module is at the root level, it is, therefore, not encapsulated and does not need to be opened or exported to any other module.
In your case, you just need to make sure that the module playground.api
ends up in the module graph and then the resource would be accessible to the application. To specify root modules to resolve in addition to the initial module, you can make use of the --add-modules
option.
Hence the overall solution to work for you along with some debugging shall be :
java --module-path target/classes:target/dependency
--module framework.core/com.framework.Main
--add-modules playground.api
--show-module-resolution
Getting The URL of Resource in Another Module
I was able to solve the problem using ClassGraph
library with some help of how to properly make the URL from VGR
The following bit of code solved all (Java 8 and Java 9+ with modular system):
public static Resource getRescoure(String urlStr) {
try (ScanResult scan = new ClassGraph().scan()) {
if (urlStr.startsWith("/"))
urlStr = urlStr.substring(1);
ResourceList rl = scan.getResourcesWithPath(urlStr);
if (rl.size() > 0)
return rl.get(0);
return null;
}
}
How to access common resource files from multiple projects
You wrote
... but since I use it from different projects the path is not correct - it is relative to current project that is being built/tested, not the one where utility class and resources are
...
Relative paths are not the problem here. You use an absolute path in your example, but even if you would use a relative one, this would refer to the package or directory structure. getResourceAsStream
would pick them up as long as the classpath is correct. The real problem is that you are referring to test resources in another project. But test resources and classes are not contained in the project artifact, so they are not accessible from modules that include this as a dependency. If you need these resources for tests in several projects, I would suggest that you create a new project (let's say "projectxyz-testresources") with these resouces contained in src/main/resources
and add this as a dependency with scope "test" where relevant.
EDITED TO ADD:
If you don't want to use a separate project for test resources, you can create a test-jar containing test classes and resources using goal jar:test-jar
and include this as a test dependency. You may want to configure the jar plugin in your pom to execute this goal on regular builds.
How to retrieve some resources from external JAR file using reflections
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.
Getting resource file from inside jar
Maven uses something called the Standard Directory Layout. If you don't follow this layout then the plugins can't do their job correctly. Technically, you can configure Maven to use different directories but 99.999% of the time this is not necessary.
One of the features of this layout is that production files go in:
<project-dir>/src/main/java
- All
*.java
files
- All
<project-dir>/src/main/resources
- All non-
*.java
files (that are meant to be resources)
- All non-
When you build your project the Java source files are compiled and the *.class
files are put into the target/classes
directory; this is done by the maven-compiler-plugin
. Meanwhile, the resource files are copied from src/main/resources
into target/classes
as well; the maven-resources-plugin
is responsible for this.
Note: See Introduction to the Build Lifecycle for more information about phases and which plugins are executed by which phase. This Stack Overflow question may also be useful.
When you launch your application from the IDE (possibly via the exec-maven-plugin
) the target/classes
directory is put on the classpath. This means all the compiled classes from src/main/java
and all the copied resources from src/main/resources
are available to use via the classpath.
Then, when you package your application in a JAR file, all the files in target/classes
are added to the resulting JAR file (handled by the maven-jar-plugin
). This includes the resources copied from src/main/resources
. When you launch the application using this JAR file the resources are still available to use via the classpath, because they're embedded in the JAR file.
To make resource.txt
available on the classpath, just move:
<project-dir>/resource.txt
To:
<project-dir>/src/main/resources/resource.txt.
Then you can use Class#getResource
with /resource.txt
as the path and everything should work out for you. The URL
returned by getResource
will be different depending on if you're executing against target/classes
or against the JAR file.
When executing against target/classes
you'll get something like:
file:///.../<project-dir>/target/classes/resource.txt
When executing against the JAR file you'll get something like:
jar:file:///.../<project-dir>/target/projectname-version.jar!/resource.txt
Note: This all assumes resource.txt
is actually supposed to be a resource and not an external file. Resources are typically read-only once deployed in a JAR file; if you need a writable file then it's up to you to use a designated location for the file (e.g. a folder in the user's home directory). One typically accesses external files via either java.io.File
or java.nio.file.*
. Remember, resources are not the same thing as normal files.
Now, if you were to put resource.txt
directly under <project-dir>
that would mean nothing to Maven. It would not be copied to target/classes
or end up in the JAR file which means the resource is never available on the classpath. So just to reiterate, all resources go under src/main/resources
.
Check out the Javadoc of java.lang.Class#getResource(String)
for more information about the path, such as when to use a leading /
and when not to. The link points to the Javadoc for Java 12 which includes information about resources and modules (JPMS/Jigsaw modules, not Maven modules); if you aren't using modules you can ignore that part of the documentation.
Related Topics
Eventlisteners Using Hibernate 4.0 with Spring 3.1.0.Release
Java Try-Finally Return Design Question
Auto Resizing the Jtable Column Widths
Why Aren't Integers Cached in Java
Spring - How to Use Multiple Transaction Managers in the Same Application
How to Specify the Default Jvm Arguments for Programs I Run from Eclipse
How to Concatenate Characters in Java
Include External Jar When Running Java -Jar
Do Not Use System.Out.Println in Server Side Code
Eclipse Error ... Cannot Be Resolved to a Type
How to Convert a Java Object to Xml with Open Source APIs
Spring Scheduling Task - Run Only Once