How to List the Files Inside a Jar File

How to list the files inside a JAR file?

CodeSource src = MyClass.class.getProtectionDomain().getCodeSource();
if (src != null) {
URL jar = src.getLocation();
ZipInputStream zip = new ZipInputStream(jar.openStream());
while(true) {
ZipEntry e = zip.getNextEntry();
if (e == null)
break;
String name = e.getName();
if (name.startsWith("path/to/your/dir/")) {
/* Do something with this entry. */
...
}
}
}
else {
/* Fail... */
}

Note that in Java 7, you can create a FileSystem from the JAR (zip) file, and then use NIO's directory walking and filtering mechanisms to search through it. This would make it easier to write code that handles JARs and "exploded" directories.

How to get names of classes inside a jar file?

Unfortunately, Java doesn't provide an easy way to list classes in the "native" JRE. That leaves you with a couple of options: (a) for any given JAR file, you can list the entries inside that JAR file, find the .class files, and then determine which Java class each .class file represents; or (b) you can use a library that does this for you.

Option (a): Scanning JAR files manually

In this option, we'll fill classNames with the list of all Java classes contained inside a jar file at /path/to/jar/file.jar.

List<String> classNames = new ArrayList<String>();
ZipInputStream zip = new ZipInputStream(new FileInputStream("/path/to/jar/file.jar"));
for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) {
if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
// This ZipEntry represents a class. Now, what class does it represent?
String className = entry.getName().replace('/', '.'); // including ".class"
classNames.add(className.substring(0, className.length() - ".class".length()));
}
}

Option (b): Using specialized reflections libraries

Guava

Guava has had ClassPath since at least 14.0, which I have used and liked. One nice thing about ClassPath is that it doesn't load the classes it finds, which is important when you're scanning for a large number of classes.

ClassPath cp=ClassPath.from(Thread.currentThread().getContextClassLoader());
for(ClassPath.ClassInfo info : cp.getTopLevelClassesRecurusive("my.package.name")) {
// Do stuff with classes here...
}

Reflections

I haven't personally used the Reflections library, but it seems well-liked. Some great examples are provided on the website like this quick way to load all the classes in a package provided by any JAR file, which may also be useful for your application.

Reflections reflections = new Reflections("my.project.prefix");

Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);

Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(SomeAnnotation.class);

How to list only folders inside jar using jar cmmand similar to jar -tvf will give all files and directory?

Command line(Linux);

jar tf JAR_PATH | grep ".*/$"

Java code to take directories in a jar file;

try {
JarFile jarFile = new JarFile(JAR_PATH);
Enumeration<JarEntry> paths = jarFile.entries();
while (paths.hasMoreElements()) {
JarEntry path = paths.nextElement();
if (path.isDirectory()) {
System.out.println(path.getName());
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

java - How to list directories in resources in jar

I finally ended up with this (using spring):

public class ResourceScanner {
private PathMatchingResourcePatternResolver resourcePatternResolver;

public ResourceScanner() {
this.resourcePatternResolver = new PathMatchingResourcePatternResolver();
}

public Resource getResource(String path) {
path = path.replace("\\", "/");
return resourcePatternResolver.getResource(path);
}

public Resource[] getResources(String path) throws IOException {
path = path.replace("\\", "/");
return resourcePatternResolver.getResources(path);
}

public Resource[] getResourcesIn(String path) throws IOException {
// Get root dir URI
Resource root = getResource(path);
String rootUri = root.getURI().toString();

// Search all resources under the root dir
path = (path.endsWith("/")) ? path + "**" : path + "/**";

// Filter only direct children
return Arrays.stream(getResources(path)).filter(resource -> {
try {
String uri = resource.getURI().toString();

boolean isChild = uri.length() > rootUri.length() && !uri.equals(rootUri + "/");
if (isChild) {
boolean isDirInside = uri.indexOf("/", rootUri.length() + 1) == uri.length() - 1;
boolean isFileInside = uri.indexOf("/", rootUri.length() + 1) == -1;
return isDirInside || isFileInside;
}

return false;
} catch (IOException e) {
return false;
}
}).toArray(Resource[]::new);
}

public String[] getResourcesNamesIn(String path) throws IOException {
// Get root dir URI
Resource root = getResource(path);
String rootUri = URLDecoder.decode(root.getURI().toString().endsWith("/") ? root.getURI().toString() : root.getURI().toString() + "/", "UTF-8");

// Get direct children names
return Arrays.stream(getResourcesIn(path)).map(resource -> {
try {
String uri = URLDecoder.decode(resource.getURI().toString(), "UTF-8");

boolean isFile = uri.indexOf("/", rootUri.length()) == -1;
if (isFile) {
return uri.substring(rootUri.length());
} else {
return uri.substring(rootUri.length(), uri.indexOf("/", rootUri.length() + 1));
}
} catch (IOException e) {
return null;
}
}).toArray(String[]::new);
}
}

Listing the files in a directory of the current JAR file

Old java1.4 code, but that would give you the idea:

private static List getClassesFromJARFile(String jar, String packageName) throws Error
{
final List classes = new ArrayList();
JarInputStream jarFile = null;
try
{
jarFile = new JarInputStream(new FileInputStream(jar));
JarEntry jarEntry;
do
{
try
{
jarEntry = jarFile.getNextJarEntry();
}
catch(IOException ioe)
{
throw new CCException.Error("Unable to get next jar entry from jar file '"+jar+"'", ioe);
}
if (jarEntry != null)
{
extractClassFromJar(jar, packageName, classes, jarEntry);
}
} while (jarEntry != null);
closeJarFile(jarFile);
}
catch(IOException ioe)
{
throw new CCException.Error("Unable to get Jar input stream from '"+jar+"'", ioe);
}
finally
{
closeJarFile(jarFile);
}
return classes;
}
private static void extractClassFromJar(final String jar, final String packageName, final List classes, JarEntry jarEntry) throws Error
{
String className = jarEntry.getName();
if (className.endsWith(".class"))
{
className = className.substring(0, className.length() - ".class".length());
if (className.startsWith(packageName))
{
try
{
classes.add(Class.forName(className.replace('/', '.')));
} catch (ClassNotFoundException cnfe)
{
throw new CCException.Error("unable to find class named " + className.replace('/', '.') + "' within jar '" + jar + "'", cnfe);
}
}
}
}
private static void closeJarFile(final JarInputStream jarFile)
{
if(jarFile != null)
{
try
{
jarFile.close();
}
catch(IOException ioe)
{
mockAction();
}
}
}

Reading Directory Contents From a JAR file

  1. getClass() is the wrong approach for jobs like this; it breaks if anybody subclasses. The proper way is to use MyClassName.class instead.

  2. getClassLoader().getResource() is also the wrong approach; this breaks in exotic but possible cases where getClassLoader() returns null. Just use getResource and slightly change the path (add a leading slash, or, write the path relative to your class file).

  3. You're turning the string file:\H:\apache-tomcat-9.0.27\lib\myConfigurationFiles.jar!\META-INF\Maintenance\xmlFiles\secondary into a filename and then asking if it is a directory. Of course it isn't; that isn't even a file. You need to do some string manipulation to extract the actual file out of it: You want just H:\apache-tomcat-9.0.27\lib\myConfigurationFiles.jar, feed that to the java.nio.file API, and then use that to ask if it is a file (it will never be a directory; jars are not directories).

  4. Note that this will not work if the resource you're reading from isn't a jar. Note that the class loading API is abstracted: You could find yourself in the scenario where source files are generated from scratch or loaded out of a DB, with more exotic URLs being produced by the getResource method to boot. Thus, this kind of code simply won't work then. Make sure that's okay first.

Thus:

String urlAsString = MyClassName.class.getResource("MyClassName.class").toString(); // produces a link to yourself.

int start = urlAsString.startsWith("file:jar:") ? 8 : urlAsString.startsWith("file:") ? 4 : 0;
int end = urlAsString.lastIndexOf('!');
String jarFileLoc = urlAsString.substring(start, end);

if you want this to apply to actual directories (class files and such can come from dirs instead of files), you could do:

var map = new HashMap<String, String>();

Path root = Paths.get(jarFileLoc);

Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
String content = new String(Files.readAllBytes(file), StandardCharsets.UTF_8);
map.put(root.relativize(file), content);
}
});

for a jar, which is really just a zip, it'll be more like:

var map = new HashMap<String, String>();
Path root = Paths.get(jarFileLoc);
try (var fileIn = Files.newInputStream(root)) {
ZipInputStream zip = new ZipInputStream(fileIn);
for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) {
String content = new String(zip.readAllBytes(), StandardCharsets.UTF_8);
map.put(entry.getName(), content);
}
}

Make sure you know what charsets are and that UTF_8 is correct here.

How can I access a folder inside of a resource folder from inside my jar File?

Finally, I found the solution:

final String path = "sample/folder";
final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath());

if(jarFile.isFile()) { // Run with JAR file
final JarFile jar = new JarFile(jarFile);
final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
while(entries.hasMoreElements()) {
final String name = entries.nextElement().getName();
if (name.startsWith(path + "/")) { //filter according to the path
System.out.println(name);
}
}
jar.close();
} else { // Run with IDE
final URL url = Launcher.class.getResource("/" + path);
if (url != null) {
try {
final File apps = new File(url.toURI());
for (File app : apps.listFiles()) {
System.out.println(app);
}
} catch (URISyntaxException ex) {
// never happens
}
}
}

The second block just work when you run the application on IDE (not with jar file), You can remove it if you don't like that.



Related Topics



Leave a reply



Submit