< prev index next >

src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JarArchive.java

Print this page
rev 15454 : 8156499: Update jlink to support creating images with modules that are packaged as multi-release JARs
Reviewed-by:
Contributed-by: steve.drach@oracle.com

*** 27,39 **** import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.nio.file.Path; import java.util.Objects; import java.util.stream.Stream; - import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import jdk.tools.jlink.internal.Archive.Entry.EntryType; /** * An Archive backed by a jar file. --- 27,43 ---- import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.nio.file.Path; + import java.util.Comparator; + import java.util.Map; import java.util.Objects; + import java.util.jar.JarEntry; + import java.util.jar.JarFile; + import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.zip.ZipFile; import jdk.tools.jlink.internal.Archive.Entry.EntryType; /** * An Archive backed by a jar file.
*** 41,57 **** public abstract class JarArchive implements Archive { /** * An entry located in a jar file. */ ! private class JarEntry extends Entry { private final long size; ! private final ZipEntry entry; ! private final ZipFile file; ! JarEntry(String path, String name, EntryType type, ZipFile file, ZipEntry entry) { super(JarArchive.this, path, name, type); this.entry = Objects.requireNonNull(entry); this.file = Objects.requireNonNull(file); size = entry.getSize(); } --- 45,61 ---- public abstract class JarArchive implements Archive { /** * An entry located in a jar file. */ ! private class JarFileEntry extends Entry { private final long size; ! private final JarEntry entry; ! private final JarFile file; ! JarFileEntry(String path, String name, EntryType type, JarFile file, JarEntry entry) { super(JarArchive.this, path, name, type); this.entry = Objects.requireNonNull(entry); this.file = Objects.requireNonNull(file); size = entry.getSize(); }
*** 68,83 **** public InputStream stream() throws IOException { return file.getInputStream(entry); } } private static final String MODULE_INFO = "module-info.class"; private final Path file; private final String moduleName; ! // currently processed ZipFile ! private ZipFile zipFile; protected JarArchive(String mn, Path file) { Objects.requireNonNull(mn); Objects.requireNonNull(file); this.moduleName = mn; --- 72,92 ---- public InputStream stream() throws IOException { return file.getInputStream(entry); } } + private static final String MANIFEST = "META-INF/MANIFEST.MF"; private static final String MODULE_INFO = "module-info.class"; + private static final String VERSIONS_DIR = "META-INF/versions/"; + private static final int VERSIONS_DIR_LEN = VERSIONS_DIR.length(); private final Path file; private final String moduleName; ! // currently processed JarFile ! private JarFile jarFile; ! private int version; ! private int offset; protected JarArchive(String mn, Path file) { Objects.requireNonNull(mn); Objects.requireNonNull(file); this.moduleName = mn;
*** 95,143 **** } @Override public Stream<Entry> entries() { try { ! if (zipFile == null) { open(); } } catch (IOException ioe) { throw new UncheckedIOException(ioe); } ! return zipFile.stream().map(this::toEntry).filter(n -> n != null); } abstract EntryType toEntryType(String entryName); abstract String getFileName(String entryName); ! private Entry toEntry(ZipEntry ze) { ! String name = ze.getName(); String fn = getFileName(name); ! if (ze.isDirectory() || fn.startsWith("_")) { return null; } EntryType rt = toEntryType(name); if (fn.equals(MODULE_INFO)) { fn = moduleName + "/" + MODULE_INFO; } ! return new JarEntry(ze.getName(), fn, rt, zipFile, ze); } @Override public void close() throws IOException { ! if (zipFile != null) { ! zipFile.close(); } } @Override public void open() throws IOException { ! if (zipFile != null) { ! zipFile.close(); } ! zipFile = new ZipFile(file.toFile()); } } --- 104,236 ---- } @Override public Stream<Entry> entries() { try { ! if (jarFile == null) { open(); } } catch (IOException ioe) { throw new UncheckedIOException(ioe); } ! if (jarFile.isMultiRelease()) { ! // sort entries in ascending version order, extract the base name ! // and add them to a map, then return the Entries as with regular jar ! return jarFile.stream() ! .filter(je -> !je.isDirectory()) ! .filter(je -> !je.getName().equals(MANIFEST)) ! .filter(this::versionAcceptable) ! .sorted(entryComparator) ! .collect(Collectors.toMap(this::extractBaseName, je -> je, (v1, v2) -> v2)) ! .entrySet() ! .stream().map(this::toEntry).filter(n -> n != null); ! } ! return jarFile.stream().map(this::toEntry).filter(n -> n != null); } abstract EntryType toEntryType(String entryName); abstract String getFileName(String entryName); ! // sort base entries before versioned entries ! private Comparator<JarEntry> entryComparator = (je1, je2) -> { ! String s1 = je1.getName(); ! String s2 = je2.getName(); ! if (s1.equals(s2)) return 0; ! boolean b1 = s1.startsWith(VERSIONS_DIR); ! boolean b2 = s2.startsWith(VERSIONS_DIR); ! if (b1 && !b2) return 1; ! if (!b1 && b2) return -1; ! int n = 0; // starting char for String compare ! if (b1 && b2) { ! // normally strings would be sorted so "10" goes before "9", but ! // version number strings need to be sorted numerically ! n = VERSIONS_DIR.length(); // skip the common prefix ! int i1 = s1.indexOf('/', n); ! int i2 = s1.indexOf('/', n); ! if (i1 == -1) throw new RuntimeException(s1); // fixme, better message ! if (i2 == -1) throw new RuntimeException(s2); // fixme, better message ! // shorter version numbers go first ! if (i1 != i2) return i1 - i2; ! // otherwise, handle equal length numbers below ! } ! int l1 = s1.length(); ! int l2 = s2.length(); ! int lim = Math.min(l1, l2); ! for (int k = n; k < lim; k++) { ! char c1 = s1.charAt(k); ! char c2 = s2.charAt(k); ! if (c1 != c2) { ! return c1 - c2; ! } ! } ! return l1 - l2; ! }; ! ! // must be invoked after versionAcceptable ! private String extractBaseName(JarEntry je) { ! String name = je.getName(); ! if (name.startsWith(VERSIONS_DIR)) { ! return name.substring(VERSIONS_DIR_LEN + offset + 1); ! } ! return name; ! } ! ! private Entry toEntry(JarEntry je) { ! String name = je.getName(); ! return toEntry(name, je); ! } ! ! private Entry toEntry(Map.Entry<String,JarEntry> entry) { ! String name = entry.getKey(); ! JarEntry je = entry.getValue(); ! return toEntry(name, je); ! } ! ! private Entry toEntry(String name, JarEntry je) { String fn = getFileName(name); ! if (je.isDirectory() || fn.startsWith("_")) { return null; } EntryType rt = toEntryType(name); if (fn.equals(MODULE_INFO)) { fn = moduleName + "/" + MODULE_INFO; } ! return new JarFileEntry(name, fn, rt, jarFile, je); ! } ! ! // must be invoked before extractBaseName ! private boolean versionAcceptable(JarEntry je) { ! String name = je.getName(); ! if (name.startsWith(VERSIONS_DIR)) { ! name = name.substring(VERSIONS_DIR_LEN); ! offset = name.indexOf('/'); ! if (offset == -1) ! throw new RuntimeException("");// fixme, better message ! if (Integer.parseInt(name.substring(0, offset)) <= version) { ! return true; ! } ! return false; ! } ! return true; } @Override public void close() throws IOException { ! if (jarFile != null) { ! jarFile.close(); } } @Override public void open() throws IOException { ! if (jarFile != null) { ! jarFile.close(); } ! // open this way to determine if it's a multi-release jar ! jarFile = new JarFile(file.toFile(), true, ZipFile.OPEN_READ, JarFile.runtimeVersion()); ! version = jarFile.getVersion().major(); } }
< prev index next >