3

Good day everyone.

I am using spock framework for testing in my groovy project(IDE - Intellij Idea 12.6). My spock specification class pass filename to groovy object for processing (that file is in classpath for sure), but when i try to get that file this way

    def resource = getClass().getClassloader().getResourceAsStream(filepath)
    assert resource != null : "No input stream found for path ${filepath}"
    def rootNode = new XmlParser().parse(resource)

Then resource == null.

I tried debugging and in Expression Evaluation windows this code getClass().getResource(fileName) returns resource.

I tried to check which classloader used in first case (in class with the code) and in second case (Expression Evaluation window).

In first case classloader was sun.misc.Launcher$AppClassLoader@18dabf1, but in Expression Evaluation window classloader was groovy.lang.GroovyClassLoader$InnerLoader@1e69757 I suppose that's the reason my resource was null.

Can someone guide me about what I am doing wrong and how can I load that resource file ?

UPDATE:

Changed the way resource file was parsed. When filepath - full path to file this works, but if filepath is just file name and that file in classpath then resource == null

UPDATE2:

Change the way resource file loaded, clean up dependencies bit and all is working, I guess yesterday just wasn't my day.

artjomka
  • 1,229
  • 3
  • 15
  • 31
  • 2
    Passing just the filename only works when the resource is in the same package as the class referenced by `getClass()`, and only when `Class#getResourceStream` is used (rather than `ClassLoader#getResourceStream`). – Peter Niederwieser Nov 26 '13 at 12:57

3 Answers3

8

The problem is very likely unrelated to Spock. It's hard to say from a distance what's causing it, but the safest way to read a resource is getClass().getClassLoader().getResourceAsStream() or Thread.currentThread().getContextClassLoader().getResourceAsStream(), depending on the environment.

Not sure what Groovy does when you do new File(resource), as there is no File(URL) constructor (only a File(URI) constructor). In any case, getting a File from a class path should be avoided whenever possible.

Peter Niederwieser
  • 121,412
  • 21
  • 324
  • 259
  • Was trying both getClass().getClassLoader().getResourceAsStream() and Thread.currentThread().getContextClassLoader().getResourceAsStream() approach, result is same resource was null. Can you elaborate why should getting File from class path should be avoided ? In my case I need to read xml file and then process it – artjomka Nov 26 '13 at 10:55
  • 1
    Note that the argument expected by these methods is different from what `getClass().getResource()` expects (check the documentation). For the problems you can get yourself into when getting a `File` from a class path, see https://weblogs.java.net/blog/kohsuke/archive/2007/04/how_to_convert.html#comment-825410 (comment and main article). For processing an XML file, an `InputStream` typically works fine. – Peter Niederwieser Nov 26 '13 at 11:26
  • Yes I suppose Spock is unrelated to this problem, must be something with classloaders... – artjomka Nov 26 '13 at 11:47
  • Hi, I think the solution is right. Try something like it : URI filePath = this.class.getResource('/path_in_classpath').toURI() File file = new File(filePath) – bouquetf Nov 26 '13 at 14:56
6

This is likely due to the fact that Groovy may interpret the class of the object differently that what you think is happening. See the following other StackOverflow item:

Why does groovy .class return a different value than .getClass()

When the class is wrong, then the ClassLoader may well by the bootstrap loader and getClassLoader returns null.

So instead of using a statement like

def resource = getClass().getClassloader().getResourceAsStream(filepath)

specify the actual class using a statement like

def resource = MyClass.class.getClassLoader().getResourceAsStream(filePath)

worked for me in nearly identical circumstances.

Community
  • 1
  • 1
Peter N. Steinmetz
  • 1,252
  • 1
  • 15
  • 23
1
def resource = MyClass.class.getResourceAsStream(fileName)

or if you want the content of the file as String:

def str = new String(MyClass.class.getResourceAsStream(fileName).readAllBytes(), StandardCharsets.UTF_8)

Please note:

  1. MyClass is used, not this.getClass();
  2. In resources you must create the same directory structure as the package of your class, and put the files there;
  3. fileName is just simply the name of the file, without any path;
  4. You must clean and rebuild your project.
Roland
  • 758
  • 1
  • 7
  • 13