0

I have an image inside my jar which I am trying to load, but getResourceAsStream() always returns null.

Directory structure:

com/thesimpzone/watch_pugs/watch_pugs/{all my class files}
META-INF/MANIFEST.MF
resources/generic/mouse.png

Content.java:

public abstract class Content {

    protected Map<String, BufferedImage> images = new HashMap<String, BufferedImage>();

    protected String prefix;

    public Content(String prefix){
        this.prefix = prefix;
    }

    protected void loadImage(String name){
        System.out.println(name);
        System.out.println(prefix);
        String path = (prefix + name);
        System.out.println(path);
        String identifier = name.substring(0, name.lastIndexOf("."));
        try{
            InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
            images.put(identifier, ImageIO.read(in));
        }catch(IOException | ClassCastException e){
            throw new RuntimeException("Image " + identifier + " at " + path + " could not be loaded.");
        }
    }
    [...]
}

GenericContent.java:

public class GenericContent extends Content {

    public GenericContent(){
        super("resources/generic/");
        this.loadContent();
    }

    @Override
    public void loadContent() {
        loadImage("mouse.png");
    }

}

StackTrace:

mouse.png
resources/generic/
resources/generic/mouse.png
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: input == null!
    at javax.imageio.ImageIO.read(ImageIO.java:1348)
    at com.thesimpzone.watch_pugs.watch_pugs.content.Content.loadImage(Content.java:29)
    at com.thesimpzone.watch_pugs.watch_pugs.content.GenericContent.loadContent(GenericContent.java:17)
    at com.thesimpzone.watch_pugs.watch_pugs.content.GenericContent.<init>(GenericContent.java:12)
    at com.thesimpzone.watch_pugs.watch_pugs.Canvas.<init>(Canvas.java:45)
    at com.thesimpzone.watch_pugs.watch_pugs.Framework.<init>(Framework.java:75)
    at com.thesimpzone.watch_pugs.watch_pugs.Window.<init>(Window.java:50)
    at com.thesimpzone.watch_pugs.watch_pugs.Window.<init>(Window.java:26)
    at com.thesimpzone.watch_pugs.watch_pugs.Window$1.run(Window.java:60)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:744)
    at java.awt.EventQueue.access$400(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:697)
    at java.awt.EventQueue$3.run(EventQueue.java:691)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:714)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

I have no idea why the classloader can't find the image. I have looked in the compiled jar and the file is there, and opens fine in paint so the file is OK. I have tried various ClassLoader variations, including getSystemClassLoader(), getClassLoader(), and Content.class.getClassLoader(); and also getResourceAsStream(path) instead of getResource(path).openStream(). I have tried with and without the leading '/' on the prefix, so I'm all out of ideas and Google isn't helping. Also, it seems like what I'm doing to define 'prefix' is very awkward and so if there is a better way I would be glad if someone showed me how.

Thanks.

Oscar S
  • 23
  • 1
  • 7

2 Answers2

3

It is all about relative vs absolute packages when calling getResourceAsStream(), you looking for something relative to whatever package Content is at as the root.

there are no "directories" on the classpath, especially inside .jar files, only packages

The best thing to use is Thread.currentThread().getContextClassloader().getResourceAsStream() with a fully qualified package without the leading /.

The reason this is best is because inside of application containers, they usually have multiple classloaders and this way you don't have to care which one your resource is loaded from.

In your case:

Thread.currentThread().getContextClassloader().getResourceAsStream("resources/generic/mouse.png");

If you still get an error with this approach your .jar isn't built like you think it is or if you are getting this from inside an IDE you probably aren't copying the contents resource/generic/ into the classpath.

Personally I always use the form:

Thread.currentThread().getContextClassLoader().getResourceAsStream("path/to/resource/file.ext"); as it always works from where ever you call it and whatever classloader you are in, and it is explicit about where it is looking to find what it is looking for.

  • Hmm still gives the exact same error (updated OP to show current code) I have also tried using the literal rather than the variable but it still shows the exact same IllegalArgumentException because of the null InputStream. – Oscar S Oct 12 '14 at 17:02
  • Then your .jar isn't built like you think it is, if you are getting this from inside an IDE you probably aren't copying `resource/generic` into the classpath. This is a configuration issue on your part then. –  Oct 12 '14 at 17:03
  • My directory structure is taken from extracting the exported .jar, and so the image is there. – Oscar S Oct 12 '14 at 17:04
  • Um OK I get the error in eclipse but apparently it loads perfectly when I run the exported (standalone) jar now? – Oscar S Oct 12 '14 at 17:06
0

Try with Content.class.getClassLoader().getResourceAsStream(path);

SMilos
  • 261
  • 2
  • 6