JarInputStream: getNextJarEntry always returns null

守給你的承諾、 提交于 2019-12-24 11:13:15

问题


I have an I18n helper class that can find out the available Locales by looking at the name of the files inside the application's Jar.

private static void addLocalesFromJar(List<Locale> locales) throws IOException {
    ProtectionDomain domain = I18n.class.getProtectionDomain();
    CodeSource src = domain.getCodeSource();
    URL url = src.getLocation();
    JarInputStream jar = new JarInputStream(url.openStream());
    while (true) {
        JarEntry entry = jar.getNextJarEntry();
        if (entry == null) {
            break;
        }
        String name = entry.getName();
        // ...
    }
}

Currently, this isn't working - jar.getNextJarEntry() seems to always return null. I have no idea why that's happening, all I know is that url is set to rsrc:./. I have never seen that protocol, and couldn't find anything about it.

Curiously, this works:

class Main {
    public static void main(String[] args) {
        URL url = Main.class.getProtectionDomain().getCodeSource().getLocation();
        JarInputStream jar = new JarInputStream(url.openStream());
        while (true) {
            JarEntry entry = jar.getNextJarEntry();
            if (entry == null) {
                break;
            }
            System.out.println(entry.getName());
        }
    }
}

In this version, even though there is practically no difference between them, the url is correctly set to the path of the Jar file.

Why doesn't the first version work, and what is breaking it?

UPDATE:

The working example really only works if I don't use Eclipse to export it. It worked just fine in NetBeans, but in the Eclipse version the URL got set to rsrc:./ too.

Since I exported it with Package required libraries into generated JAR library handling, Eclipse put its jarinjarloader in my Jar so I can have all dependencies inside it. It works fine with the other settings, but is there any way to make this work independently of them?


Another question

At the moment, that class is part of my application, but I plan to put it in a separate library. In that case, how can I make sure it will work with separate Jars?


回答1:


The problem is the jarinjarloader ClassLoader that is being used by Eclipse. Apparently it is using its own custom rsrc: URL scheme to point to jar files stored inside the main jar file. This scheme is not understood by your URL stream handler factory, so the openStream() method returns null which causes the problem that you're seeing.

This answers the second part of your question about separate jars - not only will this work, it's the only way that it will work. You need to change your main application to use separate jars instead of bundling them all up inside the main jar. If you're building a web application, copy them into the WEB-INF/lib directory and you're fine. If you're building a desktop application, add a relative path reference in the META-INF/MANIFEST.MF to the other jars, and they will automatically be included as part of the classpath when you run the main jar.




回答2:


The code may or may not result into the jar file where I18n resides. Also getProtectionDomain can be null. It depends how the classloader is implemented.

ProtectionDomain domain = I18n.class.getProtectionDomain();
CodeSource src = domain.getCodeSource();
URL url = src.getLocation();

about the rsrc:./ protocol, the classloader is free to use whatever URL they please (or name it for that matter)

try this out, you might get lucky :)

URL url = getClass().getResource(getClass().getSimpleName()+".class");
java.net.JarURLConnection conn = (java.net.JarURLConnection) url.openConnection();
Enumeration<JarEntry> e = conn.getJarFile().entries();
...

and good luck!




回答3:


Eclipse's jarinjarloader loads everything using the system classloader and it never knows what jar file it was loaded from. That's why you can't get the jar URL for a rsrc: url.

I suggest storing the list of locales in a file in each application jar, e.g. META-INF/locales. Then you can use ClassLoader.getResources("META-INF/locales") to get the list of all the files with that name in the classpath and combine them to obtain the full list of locales.




回答4:


I use System.getProperty("java.class.path") for getting the location of the jar. I do not know if that makes a difference. I have not explored the ProtectDomain path so I cannot help you there, sorry. As for multiple jars, just iterate through those jar file also.



来源:https://stackoverflow.com/questions/5206619/jarinputstream-getnextjarentry-always-returns-null

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!