1 /* 2 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.tools.jlink.internal; 27 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.io.UncheckedIOException; 31 import java.nio.file.Path; 32 import java.util.Objects; 33 import java.util.jar.JarEntry; 34 import java.util.jar.JarFile; 35 import java.util.stream.Stream; 36 import java.util.zip.ZipFile; 37 import jdk.tools.jlink.internal.Archive.Entry.EntryType; 38 39 /** 40 * An Archive backed by a jar file. 41 */ 42 public abstract class JarArchive implements Archive { 43 44 /** 45 * An entry located in a jar file. 46 */ 47 private class JarFileEntry extends Entry { 48 49 private final long size; 50 private final JarEntry entry; 51 private final JarFile file; 52 53 JarFileEntry(String path, String name, EntryType type, JarFile file, JarEntry entry) { 54 super(JarArchive.this, path, name, type); 55 this.entry = Objects.requireNonNull(entry); 56 this.file = Objects.requireNonNull(file); 57 size = entry.getSize(); 58 } 59 60 /** 61 * Returns the number of uncompressed bytes for this entry. 62 */ 63 @Override 64 public long size() { 65 return size; 66 } 67 68 @Override 69 public InputStream stream() throws IOException { 70 return file.getInputStream(entry); 71 } 72 } 73 74 private static final String MODULE_INFO = "module-info.class"; 75 76 private final Path file; 77 private final String moduleName; 78 // currently processed JarFile 79 private JarFile jarFile; 80 private int prefixLength = 0; 81 82 protected JarArchive(String mn, Path file) { 83 Objects.requireNonNull(mn); 84 Objects.requireNonNull(file); 85 this.moduleName = mn; 86 this.file = file; 87 } 88 89 @Override 90 public String moduleName() { 91 return moduleName; 92 } 93 94 @Override 95 public Path getPath() { 96 return file; 97 } 98 99 @Override 100 public Stream<Entry> entries() { 101 try { 102 if (jarFile == null) { 103 open(); 104 } 105 } catch (IOException ioe) { 106 throw new UncheckedIOException(ioe); 107 } 108 Stream<JarEntry> jarEntries = jarFile.stream(); 109 if (jarFile.isMultiRelease()) { 110 int version = jarFile.getVersion().major(); 111 String name = "META-INF/versions/" + version + "/"; 112 prefixLength = name.length(); 113 jarEntries = jarEntries 114 .filter(je -> je.getName().startsWith(name)); 115 } 116 return jarEntries.map(this::toEntry).filter(n -> n != null); 117 } 118 119 abstract EntryType toEntryType(String entryName); 120 121 abstract String getFileName(String entryName); 122 123 private Entry toEntry(JarEntry ze) { 124 String name = ze.getName().substring(prefixLength); 125 String fn = getFileName(name); 126 127 if (ze.isDirectory() || fn.startsWith("_")) { 128 return null; 129 } 130 131 EntryType rt = toEntryType(name); 132 133 if (fn.equals(MODULE_INFO)) { 134 fn = moduleName + "/" + MODULE_INFO; 135 } 136 return new JarFileEntry(name, fn, rt, jarFile, ze); 137 } 138 139 @Override 140 public void close() throws IOException { 141 if (jarFile != null) { 142 jarFile.close(); 143 } 144 } 145 146 @Override 147 public void open() throws IOException { 148 if (jarFile != null) { 149 jarFile.close(); 150 } 151 jarFile = new JarFile(file.toFile(), true, ZipFile.OPEN_READ, JarFile.runtimeVersion()); 152 } 153 }