Skip to content

Commit

Permalink
Optimize listJar() method
Browse files Browse the repository at this point in the history
listJar() is a method that returns a list of files
contained within a Jar file according to a glob pattern.

It used to rely on a ZipInputStream that needs to read
the entire file to discover its content.

Now switched to a ZipFileSystem that is able to use the
Central Directory at the end of the file directly to list
contained files, therefore it is by order of magnitude
faster.

Change-Id: Idecd9c512fc878d13e04f60d9500173b26220db0
  • Loading branch information
TheItivitist committed Aug 8, 2023
1 parent 77153fb commit 4765b39
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 50 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ subprojects {
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'

group = "co.paralleluniverse"
version = "1.0.4-ullink5"
version = "1.0.4-ullink6"
status = "integration"
description = "Simple Java deployment"
ext.url = "https://github.com/puniverse/capsule"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
*/
package co.paralleluniverse.capsule;

import com.google.common.jimfs.Jimfs;
import static com.google.common.truth.Truth.assert_;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.io.InputStream;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -22,7 +24,7 @@
import java.util.Properties;
import org.junit.After;
import org.junit.Test;
import static org.junit.Assert.*;
import com.google.common.jimfs.Jimfs;

/**
*
Expand Down Expand Up @@ -50,7 +52,7 @@ public void testSimpleExtract() throws Exception {
.addEntry("lib/a.jar", emptyInputStream())
.addEntry("lib/b.class", emptyInputStream())
.addEntry("q/w/x.txt", emptyInputStream())
.addEntry("d\\f\\y.txt", emptyInputStream()) // test with Windows path
.addEntry("d/f/y.txt", emptyInputStream())
.addEntry("META-INF/x.txt", emptyInputStream());

List<String> args = list("hi", "there");
Expand Down
68 changes: 48 additions & 20 deletions capsule/src/main/java/Capsule.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@
* http://www.eclipse.org/legal/epl-v10.html
*/

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableSet;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.EOFException;
Expand Down Expand Up @@ -59,40 +69,39 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Properties;
import java.util.RandomAccess;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicReference;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.Properties;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.RandomAccess;
import java.util.concurrent.atomic.AtomicReference;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

import static java.util.Collections.*;
import static java.util.Arrays.asList;

/**
* An application capsule.
* <p>
Expand Down Expand Up @@ -3693,20 +3702,39 @@ private static boolean shouldExtractFile(String fileName) {
return true;
}

private List<Path> listJar(Path jar, String glob, boolean regular) {
final long start = clock();
final List<Path> res = new ArrayList<>();
final Pattern p = Pattern.compile(globToRegex(glob));
try (ZipInputStream zis = openJarInputStream(jar)) {
for (ZipEntry entry; (entry = zis.getNextEntry()) != null;) {
if ((!regular || !entry.isDirectory()) && p.matcher(entry.getName()).matches())
res.add(path(entry.getName())); // new URL("jar", "", jar + "!/" + entry.getName())
}
} catch (IOException e) {
private List<Path> listJar(Path jar, String glob, boolean regular)
{
long start = clock();
try (FileSystem fileSystem = FileSystems.newFileSystem(jar, null))
{
Matcher matcher = Pattern.compile(globToRegex(glob)).matcher("");
return StreamSupport.stream(fileSystem.getRootDirectories().spliterator(), false)
.flatMap(rootDir -> {
try
{
return Files.walk(rootDir);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
})
.filter(p -> !regular || Files.isRegularFile(p))
.map(Path::toString)
.map(p -> p.startsWith("/") ? p.substring(1) : p)
.filter(p -> matcher.reset(p).matches())
.map(this::path)
.sorted()
.collect(Collectors.toList());
}
catch (IOException e)
{
throw rethrow(e);
}
time("listJar", start);
return res;
finally
{
time("listJar", start);
}
}

private Path mergeCapsule(Path wrapperCapsule, Path wrappedCapsule, Path outCapsule) throws IOException {
Expand Down
19 changes: 9 additions & 10 deletions capsule/src/test/java/CapsuleAgentTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,22 @@
* of the Eclipse Public License v1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/
import capsule.test.Pair;
import co.paralleluniverse.capsule.Jar;
import co.paralleluniverse.capsule.test.CapsuleTestUtils;
import co.paralleluniverse.common.FlexibleClassLoader;
import co.paralleluniverse.common.JarClassLoader;
import co.paralleluniverse.common.PathClassLoader;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import capsule.test.Pair;
import co.paralleluniverse.capsule.Jar;
import co.paralleluniverse.capsule.test.CapsuleTestUtils;
import co.paralleluniverse.common.FlexibleClassLoader;
import co.paralleluniverse.common.JarClassLoader;
import co.paralleluniverse.common.PathClassLoader;

/**
* @author circlespainter
Expand Down
40 changes: 25 additions & 15 deletions capsule/src/test/java/CapsuleTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,29 @@
* http://www.eclipse.org/legal/epl-v10.html
*/

import co.paralleluniverse.capsule.Jar;
import co.paralleluniverse.capsule.test.CapsuleTestUtils;
import co.paralleluniverse.capsule.test.CapsuleTestUtils.StringPrintStream;
import static co.paralleluniverse.capsule.test.CapsuleTestUtils.*;
import static co.paralleluniverse.capsule.test.CapsuleTestUtils.DEVNULL;
import static co.paralleluniverse.capsule.test.CapsuleTestUtils.isCI;
import static co.paralleluniverse.capsule.test.CapsuleTestUtils.resetOutputStreams;
import static co.paralleluniverse.capsule.test.CapsuleTestUtils.rethrow;
import static co.paralleluniverse.capsule.test.CapsuleTestUtils.setCacheDir;
import static co.paralleluniverse.capsule.test.CapsuleTestUtils.setProperties;
import static co.paralleluniverse.capsule.test.CapsuleTestUtils.setSTDERR;
import static co.paralleluniverse.capsule.test.CapsuleTestUtils.setSTDOUT;
import static com.google.common.truth.Truth.assert_;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;

import co.paralleluniverse.common.ZipFS;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import java.io.IOException;
import java.io.InputStream;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.nio.file.FileSystem;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
Expand All @@ -35,14 +43,16 @@
import java.util.Properties;
import java.util.Set;
import java.util.jar.JarInputStream;
import org.joor.Reflect;
import org.junit.After;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
import org.junit.Before;
import static com.google.common.truth.Truth.*;
import java.nio.file.Paths;
import org.joor.Reflect;
import org.junit.Test;
import co.paralleluniverse.capsule.Jar;
import co.paralleluniverse.capsule.test.CapsuleTestUtils;
import co.paralleluniverse.capsule.test.CapsuleTestUtils.StringPrintStream;
import co.paralleluniverse.common.ZipFS;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
//import static org.mockito.Mockito.*;

public class CapsuleTest {
Expand Down Expand Up @@ -86,7 +96,7 @@ public void testSimpleExtract() throws Exception {
.addEntry("lib/a.jar", emptyInputStream())
.addEntry("lib/b.class", emptyInputStream())
.addEntry("q/w/x.txt", emptyInputStream())
.addEntry("d\\f\\y.txt", emptyInputStream()) // test with Windows path
.addEntry("d/f/y.txt", emptyInputStream()) // test with Windows path
.addEntry("META-INF/x.txt", emptyInputStream());

List<String> args = list("hi", "there");
Expand Down

0 comments on commit 4765b39

Please sign in to comment.