Mercurial > hg > release > icedtea6-1.6
changeset 1685:da4268686a33
2009-09-10 Andrew Haley <aph@redhat.com>
* Makefile.am, patches/icedtea-jar-misc.patch: Import patch from
upstream OpenJDK 6.
http://hg.openjdk.java.net/jdk6/jdk6/jdk/rev/b35f1e5075a4
author | aph |
---|---|
date | Thu, 10 Sep 2009 13:52:13 +0100 |
parents | d6379c90d5fe |
children | 76a4bf70b153 |
files | ChangeLog Makefile.am patches/icedtea-jar-misc.patch |
diffstat | 3 files changed, 1134 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Wed Sep 09 16:44:37 2009 +0100 +++ b/ChangeLog Thu Sep 10 13:52:13 2009 +0100 @@ -1,3 +1,9 @@ +2009-09-10 Andrew Haley <aph@redhat.com> + + * Makefile.am, patches/icedtea-jar-misc.patch: Import patch from + upstream OpenJDK 6. + http://hg.openjdk.java.net/jdk6/jdk6/jdk/rev/b35f1e5075a4 + 2009-09-09 Andrew Haley <aph@redhat.com> * NEWS: Update for 1.6.
--- a/Makefile.am Wed Sep 09 16:44:37 2009 +0100 +++ b/Makefile.am Thu Sep 10 13:52:13 2009 +0100 @@ -622,7 +622,8 @@ patches/security/icedtea-6824440.patch \ patches/security/icedtea-6830335.patch \ patches/security/icedtea-6845701.patch \ - patches/security/icedtea-6813167.patch + patches/security/icedtea-6813167.patch \ + patches/icedtea-jar-misc.patch if WITH_ALT_HSBUILD ICEDTEA_PATCHES += \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/icedtea-jar-misc.patch Thu Sep 10 13:52:13 2009 +0100 @@ -0,0 +1,1126 @@ + +# HG changeset patch +# User martin +# Date 1246913604 25200 +# Node ID b35f1e5075a42030e8624459b13daa45ffc834f8 +# Parent 1661d42b1c09135b77f039e604878f0985d24255 +6854795: Miscellaneous improvements to "jar" +6834805: Improve jar -C performance +6332094: "jar t" and "jar x" should use ZipFile, not ZipInputStream +6496274: jar seems to use more CPU than it should +Summary: backport jdk7 jar command (remove use of nio2) +Reviewed-by: sherman, darcy + +--- openjdk/jdk/src/share/classes/sun/tools/jar/Main.java Thu Jun 25 17:15:18 2009 -0400 ++++ openjdk/jdk/src/share/classes/sun/tools/jar/Main.java Mon Jul 06 13:53:24 2009 -0700 +@@ -32,6 +32,8 @@ import java.util.jar.Manifest; + import java.util.jar.Manifest; + import java.text.MessageFormat; + import sun.misc.JarIndex; ++import static sun.misc.JarIndex.INDEX_NAME; ++import static java.util.jar.JarFile.MANIFEST_NAME; + + /** + * This class implements a simple utility for creating files in the JAR +@@ -46,10 +48,18 @@ class Main { + String zname = ""; + String[] files; + String rootjar = null; +- Hashtable filesTable = new Hashtable(); +- Vector paths = new Vector(); +- Vector v; +- CRC32 crc32 = new CRC32(); ++ ++ // An entryName(path)->File map generated during "expand", it helps to ++ // decide whether or not an existing entry in a jar file needs to be ++ // replaced, during the "update" operation. ++ Map<String, File> entryMap = new HashMap<String, File>(); ++ ++ // All files need to be added/updated. ++ Set<File> entries = new LinkedHashSet<File>(); ++ ++ // Directories specified by "-C" operation. ++ Set<String> paths = new HashSet<String>(); ++ + /* + * cflag: create + * uflag: update +@@ -62,11 +72,8 @@ class Main { + */ + boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag; + +- static final String MANIFEST = JarFile.MANIFEST_NAME; + static final String MANIFEST_DIR = "META-INF/"; + static final String VERSION = "1.0"; +- static final char SEPARATOR = File.separatorChar; +- static final String INDEX = JarIndex.INDEX_NAME; + + private static ResourceBundle rsrc; + +@@ -118,9 +125,21 @@ class Main { + this.program = program; + } + ++ /** ++ * Creates a new empty temporary file in the same directory as the ++ * specified file. A variant of File.createTempFile. ++ */ ++ private static File createTempFileInSameDirectoryAs(File file) ++ throws IOException { ++ File dir = file.getParentFile(); ++ if (dir == null) ++ dir = new File("."); ++ return File.createTempFile("jartmp", null, dir); ++ } ++ + private boolean ok; + +- /* ++ /** + * Starts main program with the specified arguments. + */ + public synchronized boolean run(String args[]) { +@@ -153,7 +172,7 @@ class Main { + } + addVersion(manifest); + addCreatedBy(manifest); +- if (isAmbigousMainClass(manifest)) { ++ if (isAmbiguousMainClass(manifest)) { + if (in != null) { + in.close(); + } +@@ -175,7 +194,8 @@ class Main { + vflag = false; + } + } +- create(new BufferedOutputStream(out), expand(files), manifest); ++ expand(null, files, false); ++ create(new BufferedOutputStream(out, 4096), manifest); + if (in != null) { + in.close(); + } +@@ -186,9 +206,7 @@ class Main { + FileOutputStream out; + if (fname != null) { + inputFile = new File(fname); +- String path = inputFile.getParent(); +- tmpFile = File.createTempFile("tmp", null, +- new File((path == null) ? "." : path)); ++ tmpFile = createTempFileInSameDirectoryAs(inputFile); + in = new FileInputStream(inputFile); + out = new FileOutputStream(tmpFile); + } else { +@@ -198,8 +216,9 @@ class Main { + } + InputStream manifest = (!Mflag && (mname != null)) ? + (new FileInputStream(mname)) : null; +- expand(files); +- boolean updateOk = update(in, new BufferedOutputStream(out), manifest); ++ expand(null, files, true); ++ boolean updateOk = update(in, new BufferedOutputStream(out), ++ manifest, null); + if (ok) { + ok = updateOk; + } +@@ -217,19 +236,32 @@ class Main { + } + tmpFile.delete(); + } +- } else if (xflag || tflag) { +- InputStream in; ++ } else if (tflag) { ++ replaceFSC(files); + if (fname != null) { +- in = new FileInputStream(fname); ++ list(fname, files); + } else { +- in = new FileInputStream(FileDescriptor.in); +- } +- if (xflag) { +- extract(new BufferedInputStream(in), files); ++ InputStream in = new FileInputStream(FileDescriptor.in); ++ try{ ++ list(new BufferedInputStream(in), files); ++ } finally { ++ in.close(); ++ } ++ } ++ } else if (xflag) { ++ replaceFSC(files); ++ if (fname != null && files != null) { ++ extract(fname, files); + } else { +- list(new BufferedInputStream(in), files); +- } +- in.close(); ++ InputStream in = (fname == null) ++ ? new FileInputStream(FileDescriptor.in) ++ : new FileInputStream(fname); ++ try { ++ extract(new BufferedInputStream(in), files); ++ } finally { ++ in.close(); ++ } ++ } + } else if (iflag) { + genIndex(rootjar, files); + } +@@ -248,8 +280,8 @@ class Main { + return ok; + } + +- /* +- * Parse command line arguments. ++ /** ++ * Parses command line arguments. + */ + boolean parseArgs(String args[]) { + /* Preprocess and expand @file arguments */ +@@ -354,7 +386,7 @@ class Main { + while (dir.indexOf("//") > -1) { + dir = dir.replace("//", "/"); + } +- paths.addElement(dir.replace(File.separatorChar, '/')); ++ paths.add(dir.replace(File.separatorChar, '/')); + nameBuf[k++] = dir + args[++i]; + } else { + nameBuf[k++] = args[i]; +@@ -383,21 +415,11 @@ class Main { + return true; + } + +- /* ++ /** + * Expands list of files to process into full list of all files that + * can be found by recursively descending directories. + */ +- String[] expand(String[] files) { +- v = new Vector(); +- expand(null, files, v, filesTable); +- files = new String[v.size()]; +- for (int i = 0; i < files.length; i++) { +- files[i] = ((File)v.elementAt(i)).getPath(); +- } +- return files; +- } +- +- void expand(File dir, String[] files, Vector v, Hashtable t) { ++ void expand(File dir, String[] files, boolean isUpdate) { + if (files == null) { + return; + } +@@ -409,17 +431,20 @@ class Main { + f = new File(dir, files[i]); + } + if (f.isFile()) { +- if (!t.contains(f)) { +- t.put(entryName(f.getPath()), f); +- v.addElement(f); ++ if (entries.add(f)) { ++ if (isUpdate) ++ entryMap.put(entryName(f.getPath()), f); + } + } else if (f.isDirectory()) { +- String dirPath = f.getPath(); +- dirPath = (dirPath.endsWith(File.separator)) ? dirPath : +- (dirPath + File.separator); +- t.put(entryName(dirPath), f); +- v.addElement(f); +- expand(f, f.list(), v, t); ++ if (entries.add(f)) { ++ if (isUpdate) { ++ String dirPath = f.getPath(); ++ dirPath = (dirPath.endsWith(File.separator)) ? dirPath : ++ (dirPath + File.separator); ++ entryMap.put(entryName(dirPath), f); ++ } ++ expand(f, f.list(), isUpdate); ++ } + } else { + error(formatMsg("error.nosuch.fileordir", String.valueOf(f))); + ok = false; +@@ -427,10 +452,10 @@ class Main { + } + } + +- /* ++ /** + * Creates a new JAR file. + */ +- void create(OutputStream out, String[] files, Manifest manifest) ++ void create(OutputStream out, Manifest manifest) + throws IOException + { + ZipOutputStream zos = new JarOutputStream(out); +@@ -446,7 +471,7 @@ class Main { + e.setSize(0); + e.setCrc(0); + zos.putNextEntry(e); +- e = new ZipEntry(MANIFEST); ++ e = new ZipEntry(MANIFEST_NAME); + e.setTime(System.currentTimeMillis()); + if (flag0) { + crc32Manifest(e, manifest); +@@ -455,42 +480,61 @@ class Main { + manifest.write(zos); + zos.closeEntry(); + } +- for (int i = 0; i < files.length; i++) { +- addFile(zos, new File(files[i])); ++ for (File file: entries) { ++ addFile(zos, file); + } + zos.close(); + } + +- /* +- * update an existing jar file. ++ private char toUpperCaseASCII(char c) { ++ return (c < 'a' || c > 'z') ? c : (char) (c + 'A' - 'a'); ++ } ++ ++ /** ++ * Compares two strings for equality, ignoring case. The second ++ * argument must contain only upper-case ASCII characters. ++ * We don't want case comparison to be locale-dependent (else we ++ * have the notorious "turkish i bug"). ++ */ ++ private boolean equalsIgnoreCase(String s, String upper) { ++ assert upper.toUpperCase(java.util.Locale.ENGLISH).equals(upper); ++ int len; ++ if ((len = s.length()) != upper.length()) ++ return false; ++ for (int i = 0; i < len; i++) { ++ char c1 = s.charAt(i); ++ char c2 = upper.charAt(i); ++ if (c1 != c2 && toUpperCaseASCII(c1) != c2) ++ return false; ++ } ++ return true; ++ } ++ ++ /** ++ * Updates an existing jar file. + */ + boolean update(InputStream in, OutputStream out, +- InputStream newManifest) throws IOException ++ InputStream newManifest, ++ JarIndex jarIndex) throws IOException + { +- Hashtable t = filesTable; +- Vector v = this.v; + ZipInputStream zis = new ZipInputStream(in); + ZipOutputStream zos = new JarOutputStream(out); + ZipEntry e = null; + boolean foundManifest = false; +- byte[] buf = new byte[1024]; +- int n = 0; + boolean updateOk = true; + +- if (t.containsKey(INDEX)) { +- addIndex((JarIndex)t.get(INDEX), zos); ++ if (jarIndex != null) { ++ addIndex(jarIndex, zos); + } + + // put the old entries first, replace if necessary + while ((e = zis.getNextEntry()) != null) { + String name = e.getName(); + +- boolean isManifestEntry = name.toUpperCase( +- java.util.Locale.ENGLISH). +- equals(MANIFEST); +- if ((name.toUpperCase().equals(INDEX) +- && t.containsKey(INDEX)) +- || (Mflag && isManifestEntry)) { ++ boolean isManifestEntry = equalsIgnoreCase(name, MANIFEST_NAME); ++ ++ if ((jarIndex != null && equalsIgnoreCase(name, INDEX_NAME)) ++ || (Mflag && isManifestEntry)) { + continue; + } else if (isManifestEntry && ((newManifest != null) || + (ename != null))) { +@@ -500,9 +544,9 @@ class Main { + // might need it below, and we can't re-read the same data + // twice. + FileInputStream fis = new FileInputStream(mname); +- boolean ambigous = isAmbigousMainClass(new Manifest(fis)); ++ boolean ambiguous = isAmbiguousMainClass(new Manifest(fis)); + fis.close(); +- if (ambigous) { ++ if (ambiguous) { + return false; + } + } +@@ -514,8 +558,7 @@ class Main { + } + updateManifest(old, zos); + } else { +- if (!t.containsKey(name)) { // copy the old stuff +- ++ if (!entryMap.containsKey(name)) { // copy the old stuff + // do our own compression + ZipEntry e2 = new ZipEntry(name); + e2.setMethod(e.getMethod()); +@@ -527,30 +570,24 @@ class Main { + e2.setCrc(e.getCrc()); + } + zos.putNextEntry(e2); +- while ((n = zis.read(buf, 0, buf.length)) != -1) { +- zos.write(buf, 0, n); +- } ++ copy(zis, zos); + } else { // replace with the new files +- addFile(zos, (File)(t.get(name))); +- t.remove(name); +- } +- } +- } +- t.remove(INDEX); ++ File f = entryMap.get(name); ++ addFile(zos, f); ++ entryMap.remove(name); ++ entries.remove(f); ++ } ++ } ++ } + + // add the remaining new files +- if (!t.isEmpty()) { +- for (int i = 0; i < v.size(); i++) { +- File f = (File)v.elementAt(i); +- if (t.containsValue(f)) { +- addFile(zos, f); +- } +- } ++ for (File f: entries) { ++ addFile(zos, f); + } + if (!foundManifest) { + if (newManifest != null) { + Manifest m = new Manifest(newManifest); +- updateOk = !isAmbigousMainClass(m); ++ updateOk = !isAmbiguousMainClass(m); + if (updateOk) { + updateManifest(m, zos); + } +@@ -567,23 +604,16 @@ class Main { + private void addIndex(JarIndex index, ZipOutputStream zos) + throws IOException + { +- ZipEntry e = new ZipEntry(INDEX); ++ ZipEntry e = new ZipEntry(INDEX_NAME); + e.setTime(System.currentTimeMillis()); + if (flag0) { +- e.setMethod(ZipEntry.STORED); +- File ifile = File.createTempFile("index", null, new File(".")); +- BufferedOutputStream bos = new BufferedOutputStream +- (new FileOutputStream(ifile)); +- index.write(bos); +- crc32File(e, ifile); +- bos.close(); +- ifile.delete(); ++ CRC32OutputStream os = new CRC32OutputStream(); ++ index.write(os); ++ os.updateEntry(e); + } + zos.putNextEntry(e); + index.write(zos); +- if (vflag) { +- // output(getMsg("out.update.manifest")); +- } ++ zos.closeEntry(); + } + + private void updateManifest(Manifest m, ZipOutputStream zos) +@@ -594,10 +624,9 @@ class Main { + if (ename != null) { + addMainClass(m, ename); + } +- ZipEntry e = new ZipEntry(MANIFEST); ++ ZipEntry e = new ZipEntry(MANIFEST_NAME); + e.setTime(System.currentTimeMillis()); + if (flag0) { +- e.setMethod(ZipEntry.STORED); + crc32Manifest(e, m); + } + zos.putNextEntry(e); +@@ -611,9 +640,9 @@ class Main { + private String entryName(String name) { + name = name.replace(File.separatorChar, '/'); + String matchPath = ""; +- for (int i = 0; i < paths.size(); i++) { +- String path = (String)paths.elementAt(i); +- if (name.startsWith(path) && (path.length() > matchPath.length())) { ++ for (String path : paths) { ++ if (name.startsWith(path) ++ && (path.length() > matchPath.length())) { + matchPath = path; + } + } +@@ -651,7 +680,7 @@ class Main { + global.put(Attributes.Name.MAIN_CLASS, mainApp); + } + +- private boolean isAmbigousMainClass(Manifest m) { ++ private boolean isAmbiguousMainClass(Manifest m) { + if (ename != null) { + Attributes global = m.getMainAttributes(); + if ((global.get(Attributes.Name.MAIN_CLASS) != null)) { +@@ -663,13 +692,12 @@ class Main { + return false; + } + +- /* ++ /** + * Adds a new file entry to the ZIP output stream. + */ + void addFile(ZipOutputStream zos, File file) throws IOException { + String name = file.getPath(); + boolean isDir = file.isDirectory(); +- + if (isDir) { + name = name.endsWith(File.separator) ? name : + (name + File.separator); +@@ -678,7 +706,7 @@ class Main { + + if (name.equals("") || name.equals(".") || name.equals(zname)) { + return; +- } else if ((name.equals(MANIFEST_DIR) || name.equals(MANIFEST)) ++ } else if ((name.equals(MANIFEST_DIR) || name.equals(MANIFEST_NAME)) + && !Mflag) { + if (vflag) { + output(formatMsg("out.ignore.entry", name)); +@@ -698,19 +726,11 @@ class Main { + e.setSize(0); + e.setCrc(0); + } else if (flag0) { +- e.setSize(size); +- e.setMethod(ZipEntry.STORED); + crc32File(e, file); + } + zos.putNextEntry(e); + if (!isDir) { +- byte[] buf = new byte[1024]; +- int len; +- InputStream is = new BufferedInputStream(new FileInputStream(file)); +- while ((len = is.read(buf, 0, buf.length)) != -1) { +- zos.write(buf, 0, len); +- } +- is.close(); ++ copy(file, zos); + } + zos.closeEntry(); + /* report how much compression occurred. */ +@@ -731,62 +751,126 @@ class Main { + } + } + +- /* +- * compute the crc32 of a file. This is necessary when the ZipOutputStream +- * is in STORED mode. ++ /** ++ * A buffer for use only by copy(InputStream, OutputStream). ++ * Not as clean as allocating a new buffer as needed by copy, ++ * but significantly more efficient. ++ */ ++ private byte[] copyBuf = new byte[8192]; ++ ++ /** ++ * Copies all bytes from the input stream to the output stream. ++ * Does not close or flush either stream. ++ * ++ * @param from the input stream to read from ++ * @param to the output stream to write to ++ * @throws IOException if an I/O error occurs ++ */ ++ private void copy(InputStream from, OutputStream to) throws IOException { ++ int n; ++ while ((n = from.read(copyBuf)) != -1) ++ to.write(copyBuf, 0, n); ++ } ++ ++ /** ++ * Copies all bytes from the input file to the output stream. ++ * Does not close or flush the output stream. ++ * ++ * @param from the input file to read from ++ * @param to the output stream to write to ++ * @throws IOException if an I/O error occurs ++ */ ++ private void copy(File from, OutputStream to) throws IOException { ++ InputStream in = new FileInputStream(from); ++ try { ++ copy(in, to); ++ } finally { ++ in.close(); ++ } ++ } ++ ++ /** ++ * Copies all bytes from the input stream to the output file. ++ * Does not close the input stream. ++ * ++ * @param from the input stream to read from ++ * @param to the output file to write to ++ * @throws IOException if an I/O error occurs ++ */ ++ private void copy(InputStream from, File to) throws IOException { ++ OutputStream out = new FileOutputStream(to); ++ try { ++ copy(from, out); ++ } finally { ++ out.close(); ++ } ++ } ++ ++ /** ++ * Computes the crc32 of a Manifest. This is necessary when the ++ * ZipOutputStream is in STORED mode. + */ + private void crc32Manifest(ZipEntry e, Manifest m) throws IOException { +- crc32.reset(); +- CRC32OutputStream os = new CRC32OutputStream(crc32); ++ CRC32OutputStream os = new CRC32OutputStream(); + m.write(os); +- e.setSize((long) os.n); +- e.setCrc(crc32.getValue()); +- } +- +- /* +- * compute the crc32 of a file. This is necessary when the ZipOutputStream +- * is in STORED mode. ++ os.updateEntry(e); ++ } ++ ++ /** ++ * Computes the crc32 of a File. This is necessary when the ++ * ZipOutputStream is in STORED mode. + */ + private void crc32File(ZipEntry e, File f) throws IOException { +- InputStream is = new BufferedInputStream(new FileInputStream(f)); +- byte[] buf = new byte[1024]; +- crc32.reset(); +- int r = 0; +- int nread = 0; +- long len = f.length(); +- while ((r = is.read(buf)) != -1) { +- nread += r; +- crc32.update(buf, 0, r); +- } +- is.close(); +- if (nread != (int) len) { ++ CRC32OutputStream os = new CRC32OutputStream(); ++ copy(f, os); ++ if (os.n != f.length()) { + throw new JarException(formatMsg( + "error.incorrect.length", f.getPath())); + } +- e.setCrc(crc32.getValue()); +- } +- +- /* ++ os.updateEntry(e); ++ } ++ ++ void replaceFSC(String files[]) { ++ if (files != null) { ++ for (String file : files) { ++ file = file.replace(File.separatorChar, '/'); ++ } ++ } ++ } ++ ++ @SuppressWarnings("serial") ++ Set<ZipEntry> newDirSet() { ++ return new HashSet<ZipEntry>() { ++ public boolean add(ZipEntry e) { ++ return ((e == null || useExtractionTime) ? false : super.add(e)); ++ }}; ++ } ++ ++ void updateLastModifiedTime(Set<ZipEntry> zes) throws IOException { ++ for (ZipEntry ze : zes) { ++ long lastModified = ze.getTime(); ++ if (lastModified != -1) { ++ File f = new File(ze.getName().replace('/', File.separatorChar)); ++ f.setLastModified(lastModified); ++ } ++ } ++ } ++ ++ /** + * Extracts specified entries from JAR file. + */ + void extract(InputStream in, String files[]) throws IOException { + ZipInputStream zis = new ZipInputStream(in); + ZipEntry e; +- // Set of all directory entries specified in archive. Dissallows ++ // Set of all directory entries specified in archive. Disallows + // null entries. Disallows all entries if using pre-6.0 behavior. +- Set<ZipEntry> dirs = new HashSet<ZipEntry>() { +- public boolean add(ZipEntry e) { +- return ((e == null || useExtractionTime) ? false : super.add(e)); +- }}; +- ++ Set<ZipEntry> dirs = newDirSet(); + while ((e = zis.getNextEntry()) != null) { + if (files == null) { + dirs.add(extractFile(zis, e)); +- + } else { + String name = e.getName(); +- for (int i = 0; i < files.length; i++) { +- String file = files[i].replace(File.separatorChar, '/'); ++ for (String file : files) { + if (name.startsWith(file)) { + dirs.add(extractFile(zis, e)); + break; +@@ -799,21 +883,41 @@ class Main { + // timestamps as given in the archive. We do this after extraction, + // instead of during, because creating a file in a directory changes + // that directory's timestamp. +- for (ZipEntry dirEntry : dirs) { +- long lastModified = dirEntry.getTime(); +- if (lastModified != -1) { +- File dir = new File(dirEntry.getName().replace('/', File.separatorChar)); +- dir.setLastModified(lastModified); +- } +- } +- } +- +- /* ++ updateLastModifiedTime(dirs); ++ } ++ ++ /** ++ * Extracts specified entries from JAR file, via ZipFile. ++ */ ++ void extract(String fname, String files[]) throws IOException { ++ ZipFile zf = new ZipFile(fname); ++ Set<ZipEntry> dirs = newDirSet(); ++ Enumeration<? extends ZipEntry> zes = zf.entries(); ++ while (zes.hasMoreElements()) { ++ ZipEntry e = zes.nextElement(); ++ InputStream is; ++ if (files == null) { ++ dirs.add(extractFile(zf.getInputStream(e), e)); ++ } else { ++ String name = e.getName(); ++ for (String file : files) { ++ if (name.startsWith(file)) { ++ dirs.add(extractFile(zf.getInputStream(e), e)); ++ break; ++ } ++ } ++ } ++ } ++ zf.close(); ++ updateLastModifiedTime(dirs); ++ } ++ ++ /** + * Extracts next entry from JAR file, creating directories as needed. If + * the entry is for a directory which doesn't exist prior to this + * invocation, returns that entry, otherwise returns null. + */ +- ZipEntry extractFile(ZipInputStream zis, ZipEntry e) throws IOException { ++ ZipEntry extractFile(InputStream is, ZipEntry e) throws IOException { + ZipEntry rc = null; + String name = e.getName(); + File f = new File(e.getName().replace('/', File.separatorChar)); +@@ -843,14 +947,14 @@ class Main { + "error.create.dir", d.getPath())); + } + } +- OutputStream os = new FileOutputStream(f); +- byte[] b = new byte[512]; +- int len; +- while ((len = zis.read(b, 0, b.length)) != -1) { +- os.write(b, 0, len); +- } +- zis.closeEntry(); +- os.close(); ++ try { ++ copy(is, f); ++ } finally { ++ if (is instanceof ZipInputStream) ++ ((ZipInputStream)is).closeEntry(); ++ else ++ is.close(); ++ } + if (vflag) { + if (e.getMethod() == ZipEntry.DEFLATED) { + output(formatMsg("out.inflated", name)); +@@ -868,14 +972,13 @@ class Main { + return rc; + } + +- /* ++ /** + * Lists contents of JAR file. + */ + void list(InputStream in, String files[]) throws IOException { + ZipInputStream zis = new ZipInputStream(in); + ZipEntry e; + while ((e = zis.getNextEntry()) != null) { +- String name = e.getName(); + /* + * In the case of a compressed (deflated) entry, the entry size + * is stored immediately following the entry data and cannot be +@@ -883,48 +986,52 @@ class Main { + * the entry first before printing out its attributes. + */ + zis.closeEntry(); +- if (files == null) { +- printEntry(e); +- } else { +- for (int i = 0; i < files.length; i++) { +- String file = files[i].replace(File.separatorChar, '/'); +- if (name.startsWith(file)) { +- printEntry(e); +- break; +- } +- } +- } +- } +- } +- +- +- /** +- * Output the class index table to the INDEX.LIST file of the ++ printEntry(e, files); ++ } ++ } ++ ++ /** ++ * Lists contents of JAR file, via ZipFile. ++ */ ++ void list(String fname, String files[]) throws IOException { ++ ZipFile zf = new ZipFile(fname); ++ Enumeration<? extends ZipEntry> zes = zf.entries(); ++ while (zes.hasMoreElements()) { ++ printEntry(zes.nextElement(), files); ++ } ++ zf.close(); ++ } ++ ++ /** ++ * Outputs the class index table to the INDEX.LIST file of the + * root jar file. + */ + void dumpIndex(String rootjar, JarIndex index) throws IOException { +- filesTable.put(INDEX, index); +- File scratchFile = File.createTempFile("scratch", null, new File(".")); + File jarFile = new File(rootjar); +- boolean updateOk = update(new FileInputStream(jarFile), +- new FileOutputStream(scratchFile), null); +- jarFile.delete(); +- if (!scratchFile.renameTo(jarFile)) { +- scratchFile.delete(); +- throw new IOException(getMsg("error.write.file")); +- } +- scratchFile.delete(); +- } +- +- private Hashtable jarTable = new Hashtable(); +- /* +- * Generate the transitive closure of the Class-Path attribute for ++ File tmpFile = createTempFileInSameDirectoryAs(jarFile); ++ try { ++ if (update(new FileInputStream(jarFile), ++ new FileOutputStream(tmpFile), ++ null, index)) { ++ jarFile.delete(); ++ if (! tmpFile.renameTo(jarFile)) ++ throw new IOException(getMsg("error.write.file")); ++ } ++ } finally { ++ tmpFile.delete(); ++ } ++ } ++ ++ private HashSet<String> jarPaths = new HashSet<String>(); ++ ++ /** ++ * Generates the transitive closure of the Class-Path attribute for + * the specified jar file. + */ +- Vector getJarPath(String jar) throws IOException { +- Vector files = new Vector(); ++ List<String> getJarPath(String jar) throws IOException { ++ List<String> files = new ArrayList<String>(); + files.add(jar); +- jarTable.put(jar, jar); ++ jarPaths.add(jar); + + // take out the current path + String path = jar.substring(0, Math.max(0, jar.lastIndexOf('/') + 1)); +@@ -947,7 +1054,7 @@ class Main { + if (!ajar.endsWith("/")) { // it is a jar file + ajar = path.concat(ajar); + /* check on cyclic dependency */ +- if (jarTable.get(ajar) == null) { ++ if (! jarPaths.contains(ajar)) { + files.addAll(getJarPath(ajar)); + } + } +@@ -961,10 +1068,10 @@ class Main { + } + + /** +- * Generate class index file for the specified root jar file. ++ * Generates class index file for the specified root jar file. + */ + void genIndex(String rootjar, String[] files) throws IOException { +- Vector jars = getJarPath(rootjar); ++ List<String> jars = getJarPath(rootjar); + int njars = jars.size(); + String[] jarfiles; + +@@ -976,18 +1083,34 @@ class Main { + } + njars = jars.size(); + } +- jarfiles = (String[])jars.toArray(new String[njars]); ++ jarfiles = jars.toArray(new String[njars]); + JarIndex index = new JarIndex(jarfiles); + dumpIndex(rootjar, index); + } + +- +- /* ++ /** ++ * Prints entry information, if requested. ++ */ ++ void printEntry(ZipEntry e, String[] files) throws IOException { ++ if (files == null) { ++ printEntry(e); ++ } else { ++ String name = e.getName(); ++ for (String file : files) { ++ if (name.startsWith(file)) { ++ printEntry(e); ++ return; ++ } ++ } ++ } ++ } ++ ++ /** + * Prints entry information. + */ + void printEntry(ZipEntry e) throws IOException { + if (vflag) { +- StringBuffer sb = new StringBuffer(); ++ StringBuilder sb = new StringBuilder(); + String s = Long.toString(e.getSize()); + for (int i = 6 - s.length(); i > 0; --i) { + sb.append(' '); +@@ -1000,21 +1123,21 @@ class Main { + } + } + +- /* +- * Print usage message and die. ++ /** ++ * Prints usage message. + */ + void usageError() { + error(getMsg("usage")); + } + +- /* ++ /** + * A fatal exception has been caught. No recovery possible + */ + void fatalError(Exception e) { + e.printStackTrace(); + } + +- /* ++ /** + * A fatal condition has been detected; message is "s". + * No recovery possible + */ +@@ -1036,39 +1159,43 @@ class Main { + err.println(s); + } + +- /* ++ /** + * Main routine to start program. + */ + public static void main(String args[]) { + Main jartool = new Main(System.out, System.err, "jar"); + System.exit(jartool.run(args) ? 0 : 1); + } ++ ++ /** ++ * An OutputStream that doesn't send its output anywhere, (but could). ++ * It's here to find the CRC32 of an input file, necessary for STORED ++ * mode in ZIP. ++ */ ++ private static class CRC32OutputStream extends java.io.OutputStream { ++ final CRC32 crc = new CRC32(); ++ long n = 0; ++ ++ CRC32OutputStream() {} ++ ++ public void write(int r) throws IOException { ++ crc.update(r); ++ n++; ++ } ++ ++ public void write(byte[] b, int off, int len) throws IOException { ++ crc.update(b, off, len); ++ n += len; ++ } ++ ++ /** ++ * Updates a ZipEntry which describes the data read by this ++ * output stream, in STORED mode. ++ */ ++ public void updateEntry(ZipEntry e) { ++ e.setMethod(ZipEntry.STORED); ++ e.setSize(n); ++ e.setCrc(crc.getValue()); ++ } ++ } + } +- +-/* +- * an OutputStream that doesn't send its output anywhere, (but could). +- * It's here to find the CRC32 of a manifest, necessary for STORED only +- * mode in ZIP. +- */ +-final class CRC32OutputStream extends java.io.OutputStream { +- CRC32 crc; +- int n = 0; +- CRC32OutputStream(CRC32 crc) { +- this.crc = crc; +- } +- +- public void write(int r) throws IOException { +- crc.update(r); +- n++; +- } +- +- public void write(byte[] b) throws IOException { +- crc.update(b, 0, b.length); +- n += b.length; +- } +- +- public void write(byte[] b, int off, int len) throws IOException { +- crc.update(b, off, len); +- n += len - off; +- } +-} +--- openjdk/jdk/test/tools/jar/index/MetaInf.java Thu Jun 25 17:15:18 2009 -0400 ++++ openjdk/jdk/test/tools/jar/index/MetaInf.java Mon Jul 06 13:53:24 2009 -0700 +@@ -23,13 +23,15 @@ + + /* + * @test +- * @bug 4408526 ++ * @bug 4408526 6854795 + * @summary Index the non-meta files in META-INF, such as META-INF/services. + */ + + import java.io.*; ++import java.util.Arrays; + import java.util.jar.*; + import sun.tools.jar.Main; ++import java.util.zip.ZipFile; + + public class MetaInf { + +@@ -39,29 +41,51 @@ public class MetaInf { + static String contents = + System.getProperty("test.src") + File.separatorChar + "jarcontents"; + +- // Options passed to "jar" command. +- static String[] jarArgs1 = new String[] { +- "cf", jarName, "-C", contents, SERVICES +- }; +- static String[] jarArgs2 = new String[] { +- "i", jarName +- }; ++ static void run(String ... args) { ++ if (! new Main(System.out, System.err, "jar").run(args)) ++ throw new Error("jar failed: args=" + Arrays.toString(args)); ++ } + +- public static void main(String[] args) throws IOException { ++ static void copy(File from, File to) throws IOException { ++ FileInputStream in = new FileInputStream(from); ++ FileOutputStream out = new FileOutputStream(to); ++ try { ++ byte[] buf = new byte[8192]; ++ int n; ++ while ((n = in.read(buf)) != -1) ++ out.write(buf, 0, n); ++ } finally { ++ in.close(); ++ out.close(); ++ } ++ } ++ ++ static boolean contains(File jarFile, String entryName) ++ throws IOException { ++ return new ZipFile(jarFile).getEntry(entryName) != null; ++ } ++ ++ static void checkContains(File jarFile, String entryName) ++ throws IOException { ++ if (! contains(jarFile, entryName)) ++ throw new Error(String.format("expected jar %s to contain %s", ++ jarFile, entryName)); ++ } ++ ++ static void testIndex(String jarName) throws IOException { ++ System.err.printf("jarName=%s%n", jarName); ++ ++ File jar = new File(jarName); + + // Create a jar to be indexed. +- Main jarTool = new Main(System.out, System.err, "jar"); +- if (!jarTool.run(jarArgs1)) { +- throw new Error("Could not create jar file."); ++ run("cf", jarName, "-C", contents, SERVICES); ++ ++ for (int i = 0; i < 2; i++) { ++ run("i", jarName); ++ checkContains(jar, INDEX); ++ checkContains(jar, SERVICES); + } + +- // Index the jar. +- jarTool = new Main(System.out, System.err, "jar"); +- if (!jarTool.run(jarArgs2)) { +- throw new Error("Could not index jar file."); +- } +- +- // Read the index. Verify that META-INF/services is indexed. + JarFile f = new JarFile(jarName); + BufferedReader index = + new BufferedReader( +@@ -75,4 +99,17 @@ public class MetaInf { + } + throw new Error(SERVICES + " not indexed."); + } ++ ++ public static void main(String[] args) throws IOException { ++ testIndex("a.jar"); // a path with parent == null ++ testIndex("./a.zip"); // a path with parent != null ++ ++ // Try indexing a jar in the default temp directory. ++ File tmpFile = File.createTempFile("MetaInf", null, null); ++ try { ++ testIndex(tmpFile.getPath()); ++ } finally { ++ tmpFile.delete(); ++ } ++ } + } +