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 another solution without using the StandardJavaFileManager, which finds all resources as well as all classes from a package and it's sub-packages recursivly. It works on file system files and JAR packaged as well mixed.
To avoid the Files.isDirectory()
check use Files.walkFileTree()
.
package de.CoSoCo.testzone;
import java.io.*;
import java.net.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
/**
*
* @author Ulf Zibis <Ulf.Zibis@CoSoCo.de>
* @version
*/
public class ResourceList {
private static final String PACKAGE = ResourceList.class.getPackageName();
// A sorted set of resource names of this package:
protected static final Set<String> RESOURCES = new TreeSet<>();
// A sorted set of subclasses of this package
protected static final Set<Class> CLASSES = new TreeSet<>(Comparator.comparing(Class::getName));
/**
* Scans all resources accessible from the context class loader which belong to
* the current package and subpackages.
*/
static {
try {
for(Enumeration<URL> res=ResourceList.class.getClassLoader().getResources(PACKAGE.replace('.','/'));
res.hasMoreElements(); ) {
URI uri = res.nextElement().toURI();
try {
collectResources(Paths.get(uri));
} catch (FileSystemNotFoundException fe) {
try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap())) {
collectResources(Paths.get(uri));
}
}
}
} catch (Exception ex) { ex.printStackTrace(); }
}
private static void collectResources(Path path) throws IOException {
try (var stream = Files.walk(path, FileVisitOption.FOLLOW_LINKS)) {
stream.forEach(p -> {
if (Files.isDirectory(p, (LinkOption)null)) return;
RESOURCES.add(p.toString());
try {
String name = p.toString().replace(File.separatorChar, '.').replaceAll(".*(" + PACKAGE + ".*)", "$1");
if (name.endsWith(".class")) {
CLASSES.add(Class.forName(name.substring(0, name.length() - ".class".length())));
}
} catch (ClassNotFoundException | NoClassDefFoundError ex) { System.err.println(ex); }
});
}
}
// Alternative:
private static void collectResources2(Path path) throws IOException {
Files.walkFileTree(path, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
RESOURCES.add(file.toString());
String name = file.toString().replace(File.separatorChar, '.').replaceAll(".*(" + PACKAGE + ".*)", "$1");
if (name.endsWith(".class")) try {
CLASSES.add(Class.forName(name.substring(0, name.length() - ".class".length())));
} catch (ClassNotFoundException | NoClassDefFoundError ex) { System.err.println(ex); }
return FileVisitResult.CONTINUE;
}
});
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
for (String str : RESOURCES)
System.out.println("resource path "+str);
for (Class clazz : CLASSES)
System.out.println(clazz);
}
}