Hi Alan, If you would like to improve the performance a little bit more, you can make additional changes (see the patch) to JmodTask and maybe Claes can rerun his tests to see if it helps.
The patch makes following changes: - reads module info in a little bit more efficient way. - avoids duplicate searching for module info when hashing of modules is required. - uses String#lastIndexOf(int) in toPackageName(ZipEntry) instead of String#lastIndexOf(String). - avoids checking for every directory entry in a JarFile if it ends with “module-info.class”. Best regards, Andrej Golovnin
diff -r 7210b5dbd92f src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java --- a/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java Sat Apr 30 16:41:08 2016 -0700 +++ b/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java Sun May 01 15:43:24 2016 +0200 @@ -373,7 +373,6 @@ final String osArch = options.osArch; final String osVersion = options.osVersion; final List<PathMatcher> excludes = options.excludes; - final Hasher hasher = hasher(); JmodFileWriter() { } @@ -400,31 +399,34 @@ * on the class path of directories and JAR files. */ Supplier<InputStream> newModuleInfoSupplier() throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] bytes = null; for (Path e: classpath) { if (Files.isDirectory(e)) { Path mi = e.resolve(MODULE_INFO); if (Files.isRegularFile(mi)) { - Files.copy(mi, baos); + bytes = Files.readAllBytes(mi); break; } } else if (Files.isRegularFile(e) && e.toString().endsWith(".jar")) { try (JarFile jf = new JarFile(e.toFile())) { ZipEntry entry = jf.getEntry(MODULE_INFO); if (entry != null) { - jf.getInputStream(entry).transferTo(baos); - break; + byte[] tmp = new byte[(int) entry.getSize()]; + if (jf.getInputStream(entry).read(tmp) == tmp.length) { + bytes = tmp; + break; + } } } catch (ZipException x) { // Skip. Do nothing. No packages will be added. } } } - if (baos.size() == 0) { + if (bytes == null) { return null; } else { - byte[] bytes = baos.toByteArray(); - return () -> new ByteArrayInputStream(bytes); + byte[] tmp = bytes; + return () -> new ByteArrayInputStream(tmp); } } @@ -450,7 +452,6 @@ try (InputStream in = miSupplier.get()) { descriptor = ModuleDescriptor.read(in); } - // copy the module-info.class into the jmod with the additional // attributes for the version, main class and other meta data try (InputStream in = miSupplier.get()) { @@ -479,6 +480,7 @@ if (moduleVersion != null) extender.version(moduleVersion); + Hasher hasher = hasher(descriptor); if (hasher != null) { ModuleHashes moduleHashes = hasher.computeHashes(descriptor.name()); if (moduleHashes != null) { @@ -504,50 +506,36 @@ * The jmod file is being created and does not exist in the * given modulepath. */ - private Hasher hasher() { + private Hasher hasher(ModuleDescriptor descriptor) { if (options.modulesToHash == null) return null; - try { - Supplier<InputStream> miSupplier = newModuleInfoSupplier(); - if (miSupplier == null) { - throw new IOException(MODULE_INFO + " not found"); + URI uri = options.jmodFile.toUri(); + ModuleReference mref = new ModuleReference(descriptor, uri, new Supplier<>() { + @Override + public ModuleReader get() { + throw new UnsupportedOperationException(); } + }); - ModuleDescriptor descriptor; - try (InputStream in = miSupplier.get()) { - descriptor = ModuleDescriptor.read(in); - } + // compose a module finder with the module path and also + // a module finder that can find the jmod file being created + ModuleFinder finder = ModuleFinder.compose(options.moduleFinder, + new ModuleFinder() { + @Override + public Optional<ModuleReference> find(String name) { + if (descriptor.name().equals(name)) + return Optional.of(mref); + else return Optional.empty(); + } - URI uri = options.jmodFile.toUri(); - ModuleReference mref = new ModuleReference(descriptor, uri, new Supplier<>() { @Override - public ModuleReader get() { - throw new UnsupportedOperationException(); + public Set<ModuleReference> findAll() { + return Collections.singleton(mref); } }); - // compose a module finder with the module path and also - // a module finder that can find the jmod file being created - ModuleFinder finder = ModuleFinder.compose(options.moduleFinder, - new ModuleFinder() { - @Override - public Optional<ModuleReference> find(String name) { - if (descriptor.name().equals(name)) - return Optional.of(mref); - else return Optional.empty(); - } - - @Override - public Set<ModuleReference> findAll() { - return Collections.singleton(mref); - } - }); - - return new Hasher(finder); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + return new Hasher(finder); } /** @@ -617,7 +605,7 @@ String toPackageName(ZipEntry entry) { String name = entry.getName(); assert name.endsWith(".class"); - int index = name.lastIndexOf("/"); + int index = name.lastIndexOf('/'); if (index != -1) return name.substring(0, index).replace('/', '.'); else @@ -714,7 +702,7 @@ public boolean test(JarEntry je) { String name = je.getName(); // ## no support for excludes. Is it really needed? - return !name.endsWith(MODULE_INFO) && !je.isDirectory(); + return !je.isDirectory() && !name.endsWith(MODULE_INFO); } } }