Spring ResourceLoader doesn't find resources in JAR - java

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).

Related

Reach External Resource With ClassLoader.getResourceFromStream on WebSphere

I'm working with a local WebSphere server configured in IntelliJ Idea, and the application I'm working on is using a third-party library that loads a properties file with:
ThirdPartyClass.class.getClassLoader().getResourceAsStream(fileNameParameter);
It uses the default bootstrapClassLoader.
I've been instructed to make sure the properties file is in a config directory so that it can be edited without deploying a code change. My project looks something like this:
ProjectName
Configs
my.properties
src
java (sources root)
packages, .java files, etc
main (resources root)
schemas, web docs, etc
I have tried several of paths to make it work but it always returns null. Since I initially thought it was reaching from within the third party library package, I tried adding several ..\'s to the file path, but then I learned that this method loads from the classpath, so I pulled a
String test = System.getProperty("java.class.path");
and upon inspection, my classpath is all made up of websphere directories and jars within them:
C:\Users\me\Programs\IBM\AppServer\profiles\AppSrv01/properties
C:\Users\me\Programs\IBM\AppServer\AppSrv01/properties
and several jar files in C:\Users\me\Programs\IBM\AppServer/lib/
So just as a test I stuck the file in C:\Users\me\Programs\IBM\AppServer\AppSrv01/properties, then tried to grab it with just its file name (my.properties), but still couldn't reach it. I've also tried moving the file into the src directory and the main directory, but no matter what I do it just can't seem to find the file.
I'm aware that this method is typically used to grab resources from within a jar file, but from my understanding it seems like it should be possible to reach my file from outside of one as long as it's in a directory in the classpath... but apparently not since that didn't work.
I have the absolute path on my hard drive and will have said path on the server; is there a way to derive the path that ClassLoader.getResourceFromStream() wants with that info? Failing that, is there some obvious mistake I'm making with the resource url?
I think your fileNameParameter simply needs to start with / to indicate that it is in the root level of the classpath. Otherwise it will be searched relative to the class it is loaded from, i.e. the package of ThirdPartyClass in your example.

Spring resource resolver with different maven modules

So for our test structure we currently have a base module in which we have some of our common configuration files etc (example: ds.properties). Now I'm currently running tests in a different module and I'm trying to load all .properties files (to get all the configurations) and I was using
(new PathMatchingResourcePatternResolver(getClass().getClassLoader())).getResources("classpath:*.properties")
Now this is only finding alpha.properties (the property file in my module). Is there a way to get the property files in all modules?
Some stuff I have already tried:
(new PathMatchingResourcePatternResolver(getClass().getClassLoader())).getResources("classpath:ds.properties")
Returns the ds.properties that I want but obviously not auth.properties.
(new PathMatchingResourcePatternResolver(getClass().getClassLoader())).getResources("classpath*:*.properties")
Again only alpha.properties
(new PathMatchingResourcePatternResolver(getClass().getClassLoader())).getResources("classpath*:**/*.properties")
Returns alpha.properties and a bunch of .properties files from the jre that I do not want.
I'm too lazy atm to find the reference in the documentation, but it's essentially this:
Top-level classpath-scan is not finding all resources matching a pattern. The reason is written in the docs.
Put your property files in a package (src/main/resources/somefolder for maven) and adapt your scan path to it and it should work as expected. (classpath*:somefolder/*.properties)
For completeness: From the docs
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’sClassLoader.getResources() method which only returns file system locations for a passed-in empty string (indicating potential roots to search).
Ant-style patterns with " classpath:" resources are not guaranteed to find matching resources if the root package to search is available in multiple class path locations. This is because a resource such as
com/mycompany/package1/service-context.xml
may be in only one location, but when a path such as
classpath:com/mycompany/**/service-context.xml
is used to try to resolve it, the resolver will work off the (first) URL returned by getResource("com/mycompany");. If this base package node exists in multiple classloader locations, the actual end resource may not be underneath. Therefore, preferably, use " classpath*:" with the same Ant-style pattern in such a case, which will search all class path locations that contain the root package.

Getting the current working resource directory in java maven project

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.

Setting the root for relative file paths in JUnit, AppEngine

I have some code that references a filename. On the server, this reference is relative to my war directory. When I'm running tests, though, the relative root doesn't seem to be set - only absolute paths, starting at the root of my local HD, actually find the files.
I'm testing in the context of an AppEngine LocalServiceTestHelper, which returns my war directory in its getAppDir method, but still the code can't understand the relative path.
How can I set the root for relative filenames in JUnit tests?
There are a number of options. In any case, I'd encapsulate the file access (or at least the file path resolution) in a separate class. That way, you could:
Mock that class in your tests to provide the correct file (path)
Pass an environment variable to your test class to resolve the correct file (path)
Provide a fallback implementation if the file is not found (as it is the case in your tests)
etc...
For my unit tests, I created a utility class called SupportFilePathResolver (see the code). It finds the file by looking in the classpath. This works nicely if the files you care about are in the classpath. If not in the classpath, then this won't help you.

How to get a resource in another jar

I have a jar embedded in a bundle that needs to fetch a resource packaged with it like so:
MyBundle
-\ src
-\lib
-\MyEmbeddedJar
-\src
-\SomeClass
-\someResource.xml
I am trying to access 'someResource.xml' from 'SomeClass' like so:
SomeClass.class.getResource( "someResource.xml" );
But I've had no luck. I've tried several variations with the CWD appended (eg: './someResource.xml') but I just can't get this resource to load.
I know that the "right" way is to use Activator to get hooks back to the proper classloader, but the embedded jar can be used in other projects, so I'd hate to have to add OSGi specific code to it just to get it to play nice with OSGi.
Is there any other way to load resources in OSGi agnostically of OSGi?
I Assume that SomeClass is inside the embedded jar (say, somejar.jar), and someResource.xml is in the outer jar, in a lib directory.
In this case, there is no way to get to that in a non-OSGi context. Let's look at both situations in isolation.
In OSGi
Your someResource.xml should very well be reachable using the regular (non-OSGi specific) resource loading mechanisms, provided that it is reachable from the Bundle-ClassPath. For instance, if you have the following manifest header,
Bundle-ClassPath: ., somejar.jar
you will be able to get to your resource using "lib/someResource.xml".
Notice the dot on the classpath: this means you can reach classes and resources from the root of the jar. If you forget that, you will only be able to get to classes and resources inside somejar.jar.
Not using OSGi
If you're not using OSGi, there is no (reasonably simple) way to get to classes and resources inside of the inner jar that I know of.
Your options
Depending on what you want your bundle to look like, you have two options now.
Is it really necessary that SomeClass is in an embedded jar? If so, you're at a loss, and you jar will only work using OSGi.
If you have the option to 'unpack' somejar.jar into your jar, you subvert the problem, and your jar can work in both situations.
Personally, I'd pick option 2.: unless you have resources that might overwrite each other when you 'merge' the jars, it is no problem at all to have a slight mess of resources inside your bundle.
My assumptions are that:
That I'm not quite sure I get how your description of the problem matches the diagram. Where is the *.jar file?
The bundle that is attempting to
access the embedded jar is the same
bundle that contains the embedded
jar.
Per the OSGi agnosticism, I am
assuming that the embedded jar is
not explicitly exposed as part of
the current bundle's classpath and
that it is not loaded as another
OSGi bundle.
If the jar in question is itself a resource of the current classloader, then you would first need to get the jar as a resource or as an InputStream, such as with MyBundleClass.class.getResourceAsStream("/pathToJar.jar"); then wrap it with a java.util.jar.JarInputStream. Then, continue to call getNextJarEntry() until you find the JarEntry object where "someResource.xml".equals(jarEntry.getName()).
I'm going to accept #Angelo's solution as it gave me the idea on how to work around this, though, I'd like to add more information in - thus my answer.
My work around was to add another constructor to SomeClass that takes in a java.net.URL instance. I also copied someResource.xml into the bundle's root.
I then updated the instantiation of SomeClass in the bundle like so:
new SomeClass( FileLocator.find( Activator.getDefault().getBundle(), new Path( "./someResource.xml" ), new HashMap< String, String >() ) );
This seems like a pretty big hack to me. What if I could not edit the contents of SomeClass ? I guess I would have to unpack it or I'd be forced to wrap it into it's own bundle?
I'm going to make the following assumption:
embedded.jar
src/SomeClass.class
someResource.xml
and the bundle contains embedded.jar and embedded.jar is on the Bundle-Classpath.
In this situation SomeClass.class.getResource("someResource.xml") will look for a resource on the classpath called src/someResource.xml because SomeClass is in the package src. In order to get someResource.xml in the root of the jar you need to do SomeClass.class.getResource("/someResource.xml") instead.
This isn't OSGi specific though, this is just how resource loading works in Java.

Categories

Resources