Reputation: 17020
I have an I18n
helper class that can find out the available Locale
s 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?
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?
Upvotes: 1
Views: 2737
Reputation: 7194
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.
Upvotes: 1
Reputation: 6816
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.
Upvotes: 2
Reputation: 12056
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!
Upvotes: 1
Reputation: 634
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.
Upvotes: 0