Getting the current working resource directory in java maven project - java

I am currently working on a JUnit test that checks functionality responsible for loading/saving a process configuration from/to some file. Given that a particular configuration file is present in resources, the functionality loads parameters from the file. Otherwise the functionality attempts to create new configuration file and persist a default configuration coded in the class. Right now I am using .class.getResource() method to check if configuration file exists, and to retrieve the necessary information. This approach is proven to be working fine from both maven's "test-class" and "class" directories. However, I am having problems while attempting to save default configuration when the file does not exist, namely the .class.getResource() method returns null, as the resource does not yet exist. This stops me from building the target resource directory (context-dependent) where the file should be saved.
Is there a way to code my functionality to evaluate whether particular object is being executed as a test or in production? More precisely, how can I build a relative path to my resource files to point to either production resources (../classes/...) or test resources (../test-classes/..) depending on the execution mode in which the project currently is?
My question is somewhat similar to the following How should I discover test-resource files in a Maven-managed Java project? but I think it is different enough to warrant new thread.

If I understand you right, essentially your issue is that you have a Maven project, which reads a particular file (normally, and during unit tests), that determines the application's behaviour. If that file doesn't exist, your application creates it.
The problem with ClassLoader.getSystemResource(...), is that it's not actually scanning a single directory. Instead it's looking at Java's classpath to determine the location of that particular resource. If there's multiple directories on the classpath, it'll have a number of areas that the file could potentially be located in.
In a sense then, .getSystemResource(...) is one way. You're able to look-up the location of a file, but not get the appropriate location to place it.
*So what about when you need to put the file in the correct location?*
You have two options essentially:
Hard-code the location of the file: Noone likes doing that.
The locations that are scanned on the classpath are passed into the classloader. You could use, for example, the first one and create the file there.
The second option isn't actually a bad one; have a look at this sample code.
final Enumeration<URL> urls = ClassLoader.getSystemClassLoader().getResources("");
if(! urls.hasMoreElements()) {
LOG.error("No entries exist on the class path!");
System.exit(1);
}
final File configFile = new File(urls.nextElement().getFile(), "config.xml");
configFile.createNewFile();
LOG.info("Create a new configuration file: " + configFile.getPath());
System.exit(0);
This resolved the configuration file to be within my target folder: ..\target\classes\config.xml
Up to you what you do; happy to provide more tips & advice if you feel more is required.

It sounds like you want to do the following:
When your code runs, it tries to load the configuration file. When the configuration file is not found you want to create the configuration file. The twist is that
if you are executing the code in "production mode" (I presume using something like the exec-maven-plugin or jetty-maven-plugin depending on the nature of your code) you want the configuration file to be placed in ${project.build.outputDirectory}
if you are executing the code in "test mode" (e.g. via surefire or failsafe) you want the configuration file to be placed in ${project.build.testOutputDirectory}
What I would do is use the ServiceLoader pattern.
You create a ConfigFileStore interface that is responsible for storing your configuration.
The ConfigFileStoreFactory enumerates all the services implementing that interface (using the ServiceLoader API by getting all the /META-INF/services/com.yourpackage.ConfigFileStore resources and extracting the class names from those. If there are no implementations registered then it will instantiate a default implementation that stores the file in the path based on getClass() (i.e. working backwards to get to the ${project.build.outputDirectory} note that it should handle the case where the classes get bundled up into a JAR, and I would presume in such a case the config file might get stored adjacent to the JAR)
Note: The default implementation will not be registered in /META-INF/services
Then in src/test/java you extend the default implementation and register that extended implementation in src/test/resources/META-INF/services/com.yourpackage.ConfigFileStore
Now when running code that has the test code on the classpath, the test version will be found, that will pick up the getClass() for a class from ${project.build.testOutputDirectory} because it is from the test classpath's /META-INF/services.
When running code that does not have the test code on the classpath, the default implementation will pick up the getClass() for a class from ${project.build.outputDirectory}
Should do what you want.

Related

Load files dynamically in multiple environments

so I am in the process of making a small application.
Right now, the project works fine. I am running it through an IDE. The problem comes about when trying to run the project as a jar - which is the end result. Right now, it fails to properly load the required files (classes and simple ASCII files).
The method I am using is one based off of:
final Enumeration<URL> paths = CLASS_LOADER.getResources("");
Where CLASS_LOADER is an instance of class.getClassLoader().
This works great when not inside a jar. Inside a jar though, it seems to fail horribly. For example, in the code above, paths would be empty.
I am assuming that the fault is that the files are all within a jar - the same jar to be precise.
The class path for the manifest file is blank at the moment.
If it helps, I have two tasks that require loading files.
I need to create a list of all files that are a subclass of
another class.
I need to load a list of language files (all of
which are in the same directory).
If you need anything else to help debug this problem or provide a solution - let me know. Thanks for reading this!
For ClassLoader.getResources() to work you need to feed a path relative to the jar root. If you want to search the jar then ClassLoader public API won't help you. You have to use custom code based on java.util.jar.JarFile, like the one here.

How do I access the resources directory for a calling program in Java

I have a library which is used by a program. This library loads a special directory in the resources folder.
in the library I have the method
public class DataRegistry{
public static File getSpecialDirectory(){
String resourceName = Thread.currentThread().getContextClassLoader().getResource("data").getFile().replace("%20", " ");
File file = new File(resourceName);
return file;
}
}
in my program I have the main method
public static void main(String args[]){
System.out.println(Data.getSpecialDirectory());
}
When I execute the getSpecialDirectory() in a junit test within the data program, the resource is fetched and all is well.
When I execute the getSpecialDirectory() in the main method outside the data program (imported jar) I get the jars data directory and not the directory the program executing the thread is expecting.
I figured getting the parent class loader would have solved this issue... I believe I may have a fundamental issue in my understanding here.
For clarity:
(library)
Line 15 of this file: https://gist.github.com/AnthonyClink/11275442
(Usage)
Line 31 of this file:
https://gist.github.com/AnthonyClink/11275661
My poms may have something to do with it, so sharing them is probably important:\
(Library)
https://github.com/Clinkworks/Neptical/blob/master/pom.xml
(Usage)
https://github.com/Clinkworks/Neptical-Maven-Plugin/blob/master/pom.xml
Maven uses the target/classes and target/test-classes directories to compose the classpath used to run unit tests. The content of your src/main/resources and src/test/resources gets copied to these locations together with the compiled java sources (the .class files).
java.lang.ClassLoader.getResource returns a java.net.URL.
In the unit test situation, ClassLoader.getResource returns a file:// schemed URL, and your code works as expected.
When your code is executed when packaged in a jar file, ClassLoader.getResource no longer returns a file:// schemed URL. It can't because the resource is no longer a separate entity in a file system - it's buried inside the jar file. What you actually get is a jar:file:// schemed URL.
jar: URLs cannot be accessed through a [java.io.File] object.
If you only need to read the content of the resource you should use java.lang.ClassLoader.getResourceAsStream(...) and read the content from that.
Note that it is not possible to update the content of class loader resources.
Do you want the directory where the Java program was loaded from ? Can try
File f = new File(".").getCanconicalFile();
Or if you want all the places could try parsing the classpath from System.env or use the default class loader
If you want a directory relative to the jar from where your classes were loaded: Quoting from http://www.nakov.com/blog/2008/06/11/finding-the-directory-where-your-java-class-file-is-loaded-from/
URL classesRootDir = getClass().getProtectionDomain().getCodeSource().getLocation();
The above returns the base directory used when loading your .class
files. It does not contain the package. It contains the classpath
directory only. For example, in a console application the above
returns someting like this:
file:/C:/Documents%20and%20Settings/Administrator/workspace/TestPrj/bin/
In a Web application it returns someting like this:
file:/C:/Tomcat/webapps/MyApp/WEB-INF/classes/
FYI This might apply if the user id running program cannot read all folders: http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getProtectionDomain%28%29
or pass a .class object of the calling class as a parameter and get its classloader
There is no guarantee that you have a directory for your resources as far as I know. Java provide methods to get a resource as a InputStream, but nothing that can easily get the File for a resource, although it is possible to get the URL for a given resource.
If you need a special working directory in your application, I recommend having that passed as a system property, a program parameter, a web application parameter, or something else that lets you know where your application files are. You can even populate this directory with a ZIP file that you include as a resource in your application, as the ZIP libraries in Java do accept input streams as parameters.
While user #tgkprog is right and his answer correct, I can hardly imagine that you really want what you seem to have asked for and what he correctly answered in (3). Please edit your question and explain
what the output should look like if resourceName would be printed to the console,
if you really want the JAR file's location (in my test case that would be something like file:/C:/Users/Alexander/.m2/repository/com/clinkworks/neptical/0.0.1-SNAPSHOT/data, which does not make sense because you do not want to read from or even write to the local Maven cache),
or if you maybe rather want to read a resource directly from the JAR file or any other location such as the current working directory etc.
Otherwise I am afraid there is no good way to answer the question.

Spring ResourceLoader doesn't find resources in JAR

I am looking to create a hierarchy based on the Spring configurations. The simplest form is several "core" libraries and several "custom" projects that are able to override the beans in the "core" libraries.
When running very simply unit tests via Maven the "core" configuration isn't able to found causing the test to fail.
final Resource[] resources = applicatonContext.getResources("classpath*:core-*spring.xml");
Returns nothing. It isn't able to find the expected core-one-spring.xml or core-two-spring.xml that are located in my custom projects core dependencies.
Isn't it default behavior of Spring to look into the JARs on the classpath as well? Or is there something special I have to do?
When I run in my IDE (IntelliJ) the tests pass perfectly because the entire project is loaded and they are just files that Spring can find.
UPDATE
Spring is able to find the files if I add them explicitly without wildcards.
#ContextConfiguration({"classpath:core-one-spring.xml", "classpath:core-two-spring.xml", "classpath:custom-spring.xml", "classpath:test-spring.xml"})
or
final Resource[] resources = custom.getResources("classpath:core-one-spring.xml");
From the manual
Please note that " classpath*:" when combined with Ant-style patterns will only work reliably with at least one root directory before the pattern starts, unless the actual target files reside in the file system. This means that a pattern like " classpath*:*.xml" will not retrieve files from the root of jar files but rather only from the root of expanded directories. This originates from a limitation in the JDK’s ClassLoader.getResources() method which only returns file system locations for a passed-in empty string (indicating potential roots to search).

Where to place a file if it is getting accessed using ClassLoader.getSystemResource in WebApplication

I am using one third party jar in my code. In the jar file , in one of the classes, when I opened the class using de-compiler, the code below is written:
java.net.URL fileURL = ClassLoader.getSystemResource("SOAPConfig.xml");
Now I am using this in my webapplication, where should I place this SOAPConfig.xml so that it will find the fileURL.
Note: I have tried putting this XML in WEB-INF/classes folder. But it is not working. Your help will be appreciated.
In Addition: In the explaination you have given, It is telling me not to use this code snippet inside the third party jar in this way...What is the exact usage of this statement
ClassLoader.getSystemResource will load the resource from the system classloader, which uses the classpath of the application as started from the command line. Any classloaders created by the application at runtime (i.e. the one that looks in WEB-INF/classes) are not on the system classpath.
You need to
Look through the script that starts your server, find out which directories are on the classpath there, and put your SOAPConfig.xml in one of those. If necessary, change the classpath in the script to look in a separate directory that's just used for your config file.
Track down the person who used ClassLoader.getSystemResource in the library, kick them squarely in the nuts, and tell them never to do that again.

Class.getResourceAsStream() issue

I have a JAR-archive with java classes. One of them uses some resource that is embedded into the same JAR. In order to load that resource I use
MyClass.class.getResourceAsStream(myResourceName);
One thing that bothers me though is whether it is guaranteed that required resource will be loaded from within the same JAR. The documentation for "getResourceAsStream()" method (and corresponding ClassLoader's method) is not really clear to me.
What would happen if there's a resource with the same name located somewhere in JVM classpath before my JAR? Will that resource be loaded instead of the one embedded in my JAR? Is there any other way to substitute resource embedded in JAR?
Yes. The first matching resource found on the class path is returned, just like an executable search path. This is why resources are often "namespaced" by putting them in directories that mirror the package structure of the library or application.
This behavior may be slightly different in the presence of custom classloaders (say in OSGi), but for vanilla Java apps, it is the case.
It works much the same way as for finding class files. So first try the parent class loader (recursively) then do whatever the class loader implementation does to find files.
There is no checking of the immediate caller class loader (as ResourceBundle does - see section 6.3 of the Java Secure Coding Guidelines). However, you do need permissions to open the URL, as ClassLoader.getResourceAsStream just calls URL.openStream in the default implementation.
Specify the package. Assuming you use com.yourcompany.file it SHOULD be unique. (Unless someone WANTS to override your config file via the classpath.)
If you want to read the file only from a specific JAR you can open the JarFile and read it directly.

Categories

Resources