I am creating a JUnit TestCase for a project which needs to load a configuration file during initialization.
This configuration file is inside the project in src/main/resources/config folder and during the build maven places it into /config folder, inside the JAR.
The initialization class, reads the file from there using this statement:
ClassLoader classloader = this.getClass().getClassLoader();
BufferedReader xmlSource = new BufferedReader(new InputStreamReader(classLoader.getResourceAsStream("/config/config.xml")));
The problem I have is that when I deploy and execute this jar into the application server it works as expected, however, whenever I run it in a JUnit TestCase within Eclipse, the getResrouceAsStream method returns null.
Considering that the class is my.package.MyClassTest.java, and that it lives in src/test/java/my/package/MyClassTest.java, I already tried placing a copy of the config.xml file into the following folders without success:
- src/test/resources/config
- src/test/resources/my/package/config
- src/test/java/my/package/config
I know that similar questions have been asked many times here in StackOverflow, but all the responses I found refer to changing the way the file is loaded and, although changing the code may be an option, I would prefer to just find the right place for the file so I do not need to modify things which already work in the production environment.
So, where should I place this file to be able to use it in my JUnit test?
UPDATE
I just came up with the solution with a small change in the code:
Instead of using the ClassLoader to get the resource, I directly used the class:
Class clazz = this.getClass();
BufferedReader xmlSource = new BufferedReader(new InputStreamReader(clazz.getResourceAsStream("/config/config.xml")));
And it reads the file successfully from src/test/resources/config/config.xml.
However, there's is something very weird here:
The Class.getResourceAsStream method is:
public InputStream getResourceAsStream(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader0();
if (cl==null) {
// A system class.
return ClassLoader.getSystemResourceAsStream(name);
}
return cl.getResourceAsStream(name);
}
And if I debug it, I can clearly see that this getClassLoader0() returns exactly the same object (same id) than the previous call, this.getClass().getResourceAsStream() (which I maintained, just to compare the values)!!!
What's going on here?!
Why does calling the method directly not work, while inserting a new method call in between works?
Honestly, I'm really astonished in front of this.
BTW, I am using JUnit version 4.10. May it be tampering the getClassLoader call in some way?
Many thanks,
Carles
Replying as to your question
And if I debug it, I can clearly see that this getClassLoader0() returns exactly the same object (same id) than the previous call, this.getClass().getResourceAsStream() (which I maintained, just to compare the values)!!!
What's going on here?!
Why does calling the method directly not work, while inserting a new method call in between works?
The difference between calling
this.getClass().getClassLoader().getResourceAsStream("/config/config.xml");
and calling
this.getClass().getResourceAsStream("/config/config.xml");
Lies in the exact source that you were showing from Class:
public InputStream getResourceAsStream(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader0();
if (cl==null) {
// A system class.
return ClassLoader.getSystemResourceAsStream(name);
}
return cl.getResourceAsStream(name);
}
But the problem is not with what getClassLoader0() returns. It returns the same thing in both cases. The difference is actually in resolveName(name). This is a private method in the Class class.
private String resolveName(String name) {
if (name == null) {
return name;
}
if (!name.startsWith("/")) {
Class<?> c = this;
while (c.isArray()) {
c = c.getComponentType();
}
String baseName = c.getName();
int index = baseName.lastIndexOf('.');
if (index != -1) {
name = baseName.substring(0, index).replace('.', '/')
+"/"+name;
}
} else {
name = name.substring(1);
}
return name;
}
So you see, before actually calling the classLoader's getResourceAsStream(), it actually removes the starting slash from the path.
In general, it will try to get the resource relative to this when it doesn't have a slash, and pass it on to the classLoader if it does have a slash at the beginning.
The classLoader's getResourceAsStream() method is actually intended to be used for relative paths (otherwise you would just use a FileInputStream).
So when you used this.getClass().getClassLoader().getResourceAsStream("/config/config.xml");, you were actually passing it the path with a slash in the beginning, which failed. When you used this.getClass().getResourceAsStream("/config/config.xml"); it was kind enough to remove it for you.
getResourceAsStream function from ClassLoader object will not remove slash prepended with your search string since the search will be done relative to the classpath. i.e searches the resource where search path used to load classes.
Say for example if your class yourpackage/Test.class, which is located under /a/b/c/d/yourpackage/Test.class, loaded by system classloader(i.e default classloader) and your classpath must point to /a/b/c/d in order to load the class. Search will be done on to this path.
getResourceAsStream function from Class Object does removes the slash prepended with your search string since the search will be done relative to the class where it resides. i.e searches the resource where your class being loaded from.
Say for example if yourpackage/Test.class loaded from /a/b/c/d/yourpackage/Test.class then the resource path will be /a/b/c/d/yourpackage/config/config.xml
You can test this using following code snipet since both getResource and getResourceAsStream used to follow the same algorithm for search.
System.out.println(Test.class.getClassLoader().getResource("config/config.xml"));
System.out.println(Test.class.getResource("config/config.xml"));
I'm not sure about this, but you could try putting your resources folder in the src/main tree instead of the src/test tree. In some configurations, at least, eclipse copies 'resource' files from src to classes, but not from test to classes. It's worth a shot...
Related
In a Tomcat 7 I have a pretty standar jar file on WEB-INF/lib. Inside this jar I have this class called Parser, and next to it (on the same dir) I have another one called AutomaticLocalLoader. Compilation gives no problem at all. In run time the AutomaticLoader class is found, and when It needs the Parser class, I get a NoClassDefFoundError
The Parser and AutomaticLoader class have been working without this problem for 15 years!! in many diferent vers of java and tomact; and now out of the blue, I am getting this NoClassDefFoundError, only for the Parser class. I already put a copy on a directory inside the WEB-INF/classes path and still got the same error. I already created my own ClassLoader to see if I get some error loading the class from the WEB-INF/classes directory by myself, but I can load it without problems.
log.info("Leer " + aFlInstructions[i].getAbsolutePath());
LoaderTest A = new LoaderTest();
A.test("com.hds.resolve.model.aguila.AutomaticLocalLoader");
LoaderTest B = new LoaderTest();
B.test("com.hds.resolve.model.aguila.Parser");
if(!bOverrideInputDir)
Psr = new Parser(aFlInstructions[i]);
else
Psr = new Parser(aFlInstructions[i], new String[] { StrLocalDirectory } );
The LoaderTest class, try to create the Class Object for the given name using Class.forName. If NoClassDefFoundError, then try to load the class using my own classloader and then create the class.
For the AutomaticLoader, it succed at the first try. For the Parser class if fails, then successfully load it with the custom classloader. Of course when the code reach the "new Parser" part, the old webclassloader still fails and throws the NoClassDefFoundError.
Both Parser and AutomaticLocalLoader belong to the same package and are stored on the same jar inside WEB-LIB.
Funny enough, the error does always happen on production... but never in my machine. I do not use customs classloaders except for doing this debug. Also, trying an old version of the software seems to fix the error. No idea why.
I think I can hack a solution messing with the tomcat's webclassloader, but I really would prefer to understand what is going wrong with this code.
I'm trying to scan my classpath for config files matching a certain pattern. I'm using corn-cps.
The file I'm looking for is packaged in a jar and I can find it using java's default
MyClass.class.getClassLoader().getResource("jmulticonfig.3.properties")
returns
jar:file:/tmp/testjar.jar!/jmulticonfig.3.properties
I would like to find all jmulticonfig.*.properties so my corn-cps code is
List<URL> resources = CPScanner.scanResources(
new ResourceFilter()
.packageName("*")
.resourceName("jmulticonfig.*.properties")
);
An empty List is returned when I run this.
Anyone with corn-cps experience can help or suggest some other way?
Edit: To take the good suggestion of #approxiblue, the code can be found at https://github.com/kanesee/jmulticonfig.
Please make sure to add src/main/resources/jmulticonfig-3.jar to your classpath. It contains jmulticonfig.3.properties, the file which I'm trying to read using corn-cps
I think you've found some kind of bug in the corn-cps library because if you debug their classes running your code, it seems that there is a point where the resource name gets lost.
Anyway the following JmultiConfig.getConfig() implementation finds all the resources you're searching with a regular expression:
public static synchronized Properties getConfig() {
System.out.println("---");
if (s_props == null) {
List<URL> resources = CPScanner.scanResources(new ResourceFilter().archiveName("*"));
for (URL resource : resources) {
if (resource.getFile().matches(".*jmulticonfig\\..*\\.properties$")) {
System.out.println("*** -> " + resource);
}
}
}
return s_props;
}
The regular expression can be modified to match anything you need.
You are trying to obtain resources from the default package "".
But corn-cps has a bug reading resources from that package (the condition in line 63 of net.sf.corn.cps.RootedUrl fails for these resources).
For a workaround you could place your resources in another package, e.g. package res and filter for that package:
List<URL> resources = CPScanner.scanResources(
new ResourceFilter().packageName("res").resourceName("jmulticonfig.*.properties")
);
Also a filter .packageName(*) finds resources in all packages, except the default package.
The solution a Max also works, but comes at the price to construct a URL object for every resource in the classpath (except rt.jar which is not covered by corn-cps).
Hello I have a problem with importing an image from my resources folder. I have looked all over google (or so I think) and I have no idea what I am doing wrong.
All help is appreciated thanks
Here is the picture of my java project:
Here is my Game Code:
public Game(){
handler = new Handler();
this.addKeyListener(new KeyInput(handler));
new Window(WIDTH, HEIGHT, "Testing", this);
BufferedImageLoader loader = new BufferedImageLoader();
level = loader.loadImage("/level.png");
hud = new HUD();
spawn = new Spawn(handler, hud);
r = new Random();
walls = new WallsMap(handler);
cam = new Camera(0, 0);
walls.render();
handler.addObject(new Player(WIDTH/2-32, HEIGHT/2-32, ID.Player, handler));
}
Finally here is my BufferedImageLoader class:
public class BufferedImageLoader {
private BufferedImage image;
public BufferedImage loadImage(String path){
try {
image = ImageIO.read(getClass().getClassLoader().getResourceAsStream(path));
} catch (IOException e) {
e.printStackTrace();
}
return image;
}
}
To solve your particular problem, either of these two options should work so that the resource can be properly located:
Remove the call to getClassLoader() from your loadImage method so that the call is just getClass().getResourceAsStream(path) -OR-
Remove the slash from "/level.png" so that your call to loadImage looks like loader.loadImage("level.png")
However, in general, I agree with mastercork889, that it is better practice to organize your resources into packages as well.
EDIT:
In response to mastercork889's comment saying that my answer should be AND instead of OR, I thought I should elaborate on why it is indeed exclusive OR. I would have just commented, but I'm still too new to stack overflow to be allowed to comment, and this is quite a bit of information anyway :)
Removing the call to getClassLoader() works because then you're using the getResourceAsStream() method from the Class class, which does extra work before delegation to the getResourceAsStream() method from the ClassLoader class.
From the Java API:
Before delegation, an absolute resource name is constructed from the given resource name using this algorithm:
If the name begins with a '/' ('\u002f'), then the absolute name of the resource is the portion of the name following the '/'.
Since your resource isn't in a package, and is at the top level of a source folder (it looks like res is an "Eclipse Source Folder"), the level.png portion following the slash will adequately identify the location of the resource in an absolute way.
OR
Removing the slash from "/level.png" also works because then when the object's class loader (which should be the system class loader) looks for the resource, it will attempt to resolve your resource in an absolute way. The special handling of the slash at the beginning is behavior that is specific to the getResourceAsStream() method in Class, not ClassLoader.
The reason that AND won't work, is because if you call the getResourceAsStream() method from Class and remove the slash, then it will attempt to locate the resource in the same package that the associated class is located in. If you choose to go with the AND option, then you would need to move level.png into the com.plat.gfx package.
As a final point, I have built a small test program that followed the same format as your example, and once I followed one of the two suggestions, it worked for me.
The subtleties of resources, classes, and class loaders can be quite tricky. Good luck with your project!
Don't worry about putting images in a specific folder. Instead put them also in the src folder under a specific package: com.plat.res
I find that putting images in a specific package makes the package hierarchy look much more efficient, and less spaghetti-like.
Also a note on package conventions: domain-extension.domain.main-program.etc. My package hierarchy looks like this:
com.brennytizer.jumg
com.brennytizer.jumg.res
com.brennytizer.jumg.engine
com.brennytizer.jumg.level
com.brennytizer.jumg.level.maps
If you don't have a domain, write in what you think your domain would be (if you were to buy it in the future), or just use your (backwards) name:
My name is Jarod Brennfleck, writing program foobar, my package would be: brennfleck.jarod.foobar.
Once in there use the ImageIO class: ImageIO.read(Game.class.getResourceAsStream("/com/plat/res/leve.png"));
Let's say for instance I have this scenario
C:\Users\Name\Documents\Workspace\Project\Src\Com\Name\Foo.java
Public class Foo {
public Foo() {
Bar b = new Bar();
b.method();
}
}
Then lets say that class Bar is in a .JAR file that's being used as a library, is it possible to figure out where the class that called method() was from? (in this case, the Foo class)
I've done a little looking around Google and can't find anything, and this code would definately simplify my library quite a bit.
If you need to get path of the caller class file from inside the method Bar#method then you can use something like this:
StackTraceElement[] stackTrace = new Throwable().getStackTrace();
String callerFilePath = getClass().getClassLoader().getResource(stackTrace[1].getClassName().replace('.', '/') + ".class"));
Yes, you can get the path from where the file is being executed. For example, you have the file :
C:\Users\Name\Documents\Workspace\Project\Src\Com\Name\Foo.java
After compiling, it will change to :
C:\Users\Name\Documents\Workspace\Project\Src\Com\Name\Foo.class
You can use this to get the directory of the file:
System.getProperty("user.dir");
and then you can add this String to it:
String cPath = System.getProperty("user.dir")+"\\Foo.class";
Thus, cPath would be the complete path to the file.
You can use Foo.class.getResource("Foo.class") to get the location of the compiled class file. The question is, how will this help you simplify your library?
I am working on a command line app that loads user specified text translators at runtime (path to class files/jar provided via command line arg). Basically I am taking that argument and using it to create a URLClassLoader. Then I need to find all classes available to the URLClassloader that implement the Transable interface.
Right now I am only allowing this command line arg to be a directory with class files in it. Making the solution fairly simple (code below). But honestly I don't like the solution as it breaks down for jar files, directory of jar files, etc... Also, this obviously breaks down for any classes with a defined package, as loadClass needs the full name including the package. Anyone have a better method?
File d = new File(path);
if(d.isDirectory()) {
URL url = d.toURI().toURL();
ClassLoader cl = new URLClassLoader(new URL[]{url});
FilenameFilter filter = new FilenameFilter() {
#Override
public boolean accept(File dir, String name) {
return name.endsWith(".class");
}
};
for(File f : d.listFiles(filter)) {
String name = f.getName().substring(0, f.getName().indexOf("."));
String key = "";
if(name.endsWith("Translator")) {
key = name.substring(0, name.indexOf("Translator"));
}
else if(name.endsWith("translator")) {
key = name.substring(0, name.indexOf("translator"));
}
else
key = name;
Class c = cl.loadClass(name);
if(Transable.class.isAssignableFrom(c)) {
Transable t = (Transable)c.newInstance();
env.registerTranslator(key, t);
}
else {
System.out.println("[ClassLoader] "+c.getCanonicalName()+" will not be loaded. It is not a translator class");
}
}
}
else {
throw new Error("NOT IMPLEMENTED");
}
You may just have to brute force it if you continue down this road. To my knowledge the default class loader will not even load a class into the JVM unless it is referenced somehow. Which means, some of your classes will be basically invisible unless you know their fully qualified class name to load them.
You may want to reconsider your requirements. As it would be far easier to load a set of Translators that you have been given the class names for.
Instead of searching for implementors explicitly, you should uss Java's Service Provider Interface (SPI) and the ServiceLoader class (introduced in Java 6). SPI is a pretty much a standard way to do what you are describing in Java.
Please see the official Java tutorial on how to create a service provider and how to use it, at runtime, with ServiceLoader.
This may fit the bill. If not, you ought to be able to look through their source to get an idea of what will work for you. http://code.google.com/p/reflections/
In principle this can't work for arbitrary classloaders, as they may use any way imaginable to actually load the classes, and not have any "directory listening" function at all.
A classloader might even generate classes (i.e. the bytecode for the classes) on the fly whenever a loadClass comes, and imagine a TransableClassloader where each such automatically defined class would implement your interface - your program would never end.
That said, for an URLClassloader you can use getURLs(), and for the jar: and file: URL you can use the JarFile or File api to get the list of filenames (and thus Classnames) to try. As you have the root of your package hierarchy given in the URL, finding the right package name is not difficult, too.
(If you need more details, say it.)
Edit: For the package names, they correspond to the directory names inside of your hierarchy. So, when you have a base URL which corresponds to (say) dir/classes, and find a class-file named dir/classes/com/company/gui/SimpleTranslator.class, it corresponds to class com.company.gui.SimpleTranslator.
So, remove the base prefix and replace / by . (and cut of the .class). (In a JarFile you don't have to cut a prefix off.)
Actually, if you use a recursive method to traverse your File hierarchy, you can build up your package-name with the same method, simply by appending Strings (give them as a parameter to the next recursive Invocation):
public void searchClassesInDir(File dir, String packagePrefix) {
if(dir.isDirectory()) {
String prefix = packagePrefix + dir.getName() + ".";
for(File f : dir.listFiles()) {
searchClasses(f, prefix);
}
}
else {
String fileName = dir.getName();
if(! fileName.endsWith(".class"))
return;
String className = packagePrefix + fileName.substring(0, fileName.length()-".class".length());
// now do the rest of your processing
}
}
searchClasses(new File(url.toURI()), "");
Would this help?
Since Class c = cl.loadClass(name);
Class method getInterfaces() returns an array of classes
Check each class name for match to translator class name.