I'm trying to read the contents of my own JAR.
With getResourceAsStream(path)
I get a sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream
. But this stream seems empty, when the path is a directory.
This is my test code:
package de.CoSoCo.testzone;
import java.io.*;
/**
*
* @author Ulf Zibis <Ulf.Zibis@CoSoCo.de>
* @version
*/
public class ResourceAsStream {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
String packagePath = ResourceAsStream.class.getPackageName().replace('.', '/');
String path;
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class c = ResourceAsStream.class;
InputStream in;
byte [] bytes = new byte[16];
try {
System.out.println("path="+(
path = packagePath));
System.out.println("Class="+(
in = cl.getResourceAsStream(path)));
BufferedReader r = new BufferedReader(new InputStreamReader(in));
System.out.println("entries=");
for (String line; (line = r.readLine()) != null; )
System.out.println(" "+line);
in = cl.getResourceAsStream(path);
System.out.println("read bytes="+in.read(bytes));
} catch (Exception ex) { System.out.println(ex); }
try {
System.out.println("path="+(
path = packagePath+'/'+"A_Picture.png"));
System.out.println("Class="+(
in = cl.getResourceAsStream(path)));
System.out.println("read bytes="+in.read(bytes));
} catch (Exception ex) { System.out.println(ex); }
try {
System.out.println("path="+(
path = ""));
System.out.println("Class="+(
in = c.getResourceAsStream(path)));
BufferedReader r = new BufferedReader(new InputStreamReader(in));
System.out.println("entries=");
for (String line; (line = r.readLine()) != null; )
System.out.println(" "+line);
in = c.getResourceAsStream(path);
System.out.println("read bytes="+in.read(bytes));
} catch (Exception ex) { System.out.println(ex); }
try {
System.out.println("path="+(
path = "A_Picture.png"));
System.out.println("Class="+(
in = c.getResourceAsStream(path)));
System.out.println("read bytes="+in.read(bytes));
} catch (Exception ex) { System.out.println(ex); }
}
}
When I run this with separate class files – e.g. inside my NetBeans IDE – I get the expected directory entries:
path=de/CoSoCo/testzone
Class=java.io.ByteArrayInputStream@4e50df2e
entries=
A_Picture.png
ClassFinder.class
ClassFinder$1.class
FileChooserDemo.class
FileTimesFromCalendar.class
ResourceAsStream.class
read bytes=16
path=de/CoSoCo/testzone/A_Picture.png
Class=java.io.BufferedInputStream@3941a79c
read bytes=16
path=
Class=java.io.ByteArrayInputStream@506e1b77
entries=
A_Picture.png
ClassFinder.class
ClassFinder$1.class
FileChooserDemo.class
FileTimesFromCalendar.class
ResourceAsStream.class
read bytes=16
path=A_Picture.png
Class=java.io.BufferedInputStream@4fca772d
read bytes=16
But when I run it from the JAR it fails:
$ java -jar TestZone.jar
path=de/CoSoCo/testzone
Class=sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@3d4eac69
entries=
read bytes=-1
path=de/CoSoCo/testzone/A_Picture.png
Class=sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@135fbaa4
read bytes=16
path=
Class=sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@330bedb4
entries=
read bytes=-1
path=A_Picture.png
Class=sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@7ea987ac
read bytes=16
To me this looks like a bug, but maybe I'm doing wrong.
My goal is to read the directory entries.
Here is a solution using the StandardJavaFileManager
, which finds all resources as well as all classes from a package and it's sub-packages.
It works on file system files and JAR packaged as well mixed.
package de.CoSoCo.testzone;
import java.io.*;
import java.util.*;
import javax.tools.*;
import static javax.tools.JavaFileObject.Kind.*;
/**
*
* @author Ulf Zibis <Ulf.Zibis@CoSoCo.de>
* @version
*/
public class ResourceList {
// A sorted set of resource names of this package:
protected static final Set<String> RESOURCES = new TreeSet<>();
protected static final Set<Class> CLASSES // A sorted set of subclasses of this package
= new TreeSet<>((Class o1, Class o2) -> o1.getName().compareTo(o2.getName()));
/**
* Scans all classes accessible from the context class loader which belong to
* the current package and subpackages.
*/
static {
String packageName = ResourceList.class.getPackageName();
StandardJavaFileManager fm = ToolProvider.getSystemJavaCompiler().getStandardFileManager(null, null, null);
try {
for (JavaFileObject file : fm.list(StandardLocation.CLASS_PATH, packageName,
new TreeSet<>(Arrays.asList(SOURCE, CLASS, HTML, OTHER)), true)) {
RESOURCES.add(file.getName()
.replaceAll(".*("+packageName.replace('.', File.separatorChar)+".*?)\\)*$", "$1"));
}
for (JavaFileObject file : fm.list(StandardLocation.CLASS_PATH, packageName,
Collections.singleton(CLASS), true)) {
try {
CLASSES.add(Class.forName(file.getName().replace(File.separatorChar, '.')
.replaceAll(".*("+packageName+".*)\\.class.*", "$1")));
} catch (ClassNotFoundException | NoClassDefFoundError ex) { System.err.println(ex); }
}
} catch (Exception ex) { ex.printStackTrace(); }
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
for (String str : RESOURCES)
System.out.println("resource name "+str);
for (Class clazz : CLASSES)
System.out.println(clazz);
}
}