I'm stumped rigth now by some strange behavior by Spring's resource locations in my main project. I've made a smaller, simpler Spring projet to replicate my problem and I can't get it to work (ie: I've got the same problem as my main project).
Basically, I'm using a JAR file to stock all non-class files, which includes the spring xml bean configuration, and other files (text ones). I'm also using Eclipse as an IDE, if that could be the source of the problem.
Steps are this:
I got a bean class that is resource loader aware. It has two properties, one string and one Resource. It also got an init method once all properties are set. It should be able the get a resource from it's resourceLoader using the String property. However, Spring is not even able to create the Resource property using the file name I give it.
All files I try to get are located in the same JAR as the spring bean XML. The thing is, since Spring breaks during the bean instanciation, that should mean the JAR is accessible and that the files within it are accessible as well.
But, for all my tests, both files in the JAR (besides the XML) remain invisible to Spring.
Here is the source code for all classes:
The Spring bean configuration XML:
The JAR file "fichier.jar" is in the classpath during execution and contains the following files:
Here is an error message that I get when I try to execute the main class:
Any help would be appreciated. Thanks in advance.
(Edit: inserted line breaks in extremely long stack-trace lines to make post a reasonable width -- Paul C)
Perfection cannot be attained, but must be aimed for.
Hidden way over there in the far right of the error message, three screens to the right with the horizontal scroll bar, is this
cannot be resolved to absolute file path because it does not reside in the file system
And you're getting a FileNotFoundException. So something in the bean creation is under the mistaken impression that you
are giving it a file name. Perhaps that's because of line 17 in your bean class which calls a getFile() method? I'm just guessing,
I know very little about Spring.
Joined: Dec 07, 2009
I tried to put the text file location as this in the config.xml: "classpath:/test2.txt" (same for the other), but the error message is exactly the same (ie: the last part of the URL reads as fichiers.jar!/test2.txt, same as before).
I should have mentioned it clearly in the original post, but the three files are at the root of the "fichiers.jar" archive. In the original project that was not the case, but I quickly found with this test that it did not matter either way, as Spring did not manage to find it.
As for your question Paul, the stacktrace shows that the error occurs during Spring bean initialisation, and not in the class itself. Even then, the whole point of this little project is to act like a test case, it must not fail anywhere because it mimics the desired outcome, which is to have access to the files from within the class for further use, from resources given by Spring.
A few things I noticed since yesterday:
1- In the end of the output the last three lines show that the bean methods were called (#setResource, #init, etc...). I'll do more tests in those method to test the output, just in case the error message was a false positive.
2- Earlier tests (in the original project) showed that files in the root directory or in specific locations (file system) did work. However, this is not a desirable solution because the project is meant to be executed as an applet, and therefore must be totally contained within signed jars.
3- I was using an ApplicationContext before (ClassPathXmlApplicationContext to be exact), but the result was the same.
4- I'll try to find all three files using ClassPathResource instances before the BeanFactory initialization, to see how it goes.
I'll post new developments soon.
Joined: Dec 07, 2009
Ok, I got help from a fellow programmer and the problem was thus:
But first, an exerpt from the Spring documentation:
This class represents a resource which should be obtained from the classpath. This uses either the thread context class loader, a given class loader, or a given class for loading resources.
This Resource implementation supports resolution as java.io.File if the class path resource resides in the file system, but not for classpath resources which reside in a jar and have not been expanded (by the servlet engine, or whatever the environment is) to the filesystem. To address this the various Resource implementations always support resolution as a java.net.URL.
A ClassPathResource is created by Java code explicitly using the ClassPathResource constructor, but will often be created implicitly when you call an API method which takes a String argument which is meant to represent a path. For the latter case, a JavaBeans PropertyEditor will recognize the special prefix classpathn the string path, and create a ClassPathResource in that case.
In a nutshell, this means that you must NOT use a normal File(String) constructor with a Spring resource, because the underlying implementation will try to use some kind of absolute path. Therefore, to prevent this, you must use the getFile() method from a URL object (as the documentation says in a weird way). That URL object can be fetched from the Resource instance. The resulting path will be the correct one.
That means that Paul's hunch was correct: the error was not in the Spring loading sequence (despite the stacktrace) but in the bean class setters. I was fooled, but to my defense it really was counter-intuitive.
I corrected the code and it works. See below.
Also, I corrected another error in the code: the ResourceLoaderAware interface only calls the SetResourceLoader() method in a context. Therefore, we must use an ApplicationContext instead of a bean factory in the main class for it to work. It will throw an NullPointerException otherwise because the init() method will be called without the resourceLoader being initialized.
The other files have not been altered. (Edit: as a matter of fact, both file paths in the config.xml have been modified to the following form: "classpath:filename.txt")