--- old/src/share/classes/sun/tools/jar/Main.java 2011-10-26 12:29:27.000000000 -0700 +++ new/src/share/classes/sun/tools/jar/Main.java 2011-10-26 12:29:26.000000000 -0700 @@ -26,9 +26,10 @@ package sun.tools.jar; import java.io.*; -import java.nio.file.Path; -import java.nio.file.Files; +import java.nio.file.*; +import java.nio.file.attribute.*; import java.util.*; +import java.util.concurrent.*; import java.util.zip.*; import java.util.jar.*; import java.util.jar.Manifest; @@ -73,7 +74,9 @@ * Mflag: DO NOT generate a manifest file (just ZIP) * iflag: generate jar index */ - boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag; + boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag, mtflag; + + int threadNum; static final String MANIFEST_DIR = "META-INF/"; static final String VERSION = "1.0"; @@ -197,8 +200,8 @@ vflag = false; } } - expand(null, files, false); - create(new BufferedOutputStream(out, 4096), manifest); + //expand(null, files, false); + create(files, new BufferedOutputStream(out, 4096), manifest); if (in != null) { in.close(); } @@ -253,7 +256,10 @@ } } else if (xflag) { replaceFSC(files); - if (fname != null && files != null) { + + if (mtflag && fname != null) { + extract2(fname, files); + } else if (fname != null && files != null) { extract(fname, files); } else { InputStream in = (fname == null) @@ -361,6 +367,12 @@ case 'e': ename = args[count++]; break; + case 'T': + mtflag = true; + threadNum = flags.charAt(++i) - '0'; + if (threadNum <= 0) + threadNum = 1; + break; default: error(formatMsg("error.illegal.option", String.valueOf(flags.charAt(i)))); @@ -459,10 +471,99 @@ } } + //void expand2(File[] files, ArrayList elist, ExecutorService es) { + void expand2(File[] files, Queue elist, ExecutorService es) { + if (files == null) { + return; + } + for (File f :files) { + if (f.isFile()) { + if (entries.add(f)) { + if (f.length() > MAXBUFSIZE) { + elist.add(f); + } else { + DefWork job = new DefWork(f); + elist.add(es.submit(job, job)); + } + } + } else if (f.isDirectory()) { + if (entries.add(f)) { + elist.add(f); + expand2(f.listFiles(), elist, es); + } + } else { + error(formatMsg("error.nosuch.fileordir", String.valueOf(f))); + ok = false; + } + } + } + + private static int MAXBUFSIZE = 1024 * 1024 * 50; + private ThreadLocal tlDef = new ThreadLocal<>(); + private ThreadLocal tlCrc = new ThreadLocal<>(); + private ThreadLocal tlBuf = new ThreadLocal<>(); + + private class DefByteArrayOutputStream extends ByteArrayOutputStream { + DefByteArrayOutputStream(int size) { + super(size); + } + byte[] getByteArray() { + return buf; + } + } + + private class DefWork implements Runnable { + File f; + DefByteArrayOutputStream dbaos; + long size; + long csize; + long crc; + IOException x; + + DefWork(File f) { + this.f = f; + } + + public void run() { + long len = f.length(); + dbaos = new DefByteArrayOutputStream((int)len); + Deflater def = tlDef.get(); + if (def == null) + def = new Deflater(Deflater.DEFAULT_COMPRESSION, true); + CRC32 crc32 = tlCrc.get(); + if (crc32 == null) + crc32 = new CRC32(); + byte[] buf = tlBuf.get(); + if (buf == null) + buf = new byte[8192]; + DeflaterOutputStream dos = new DeflaterOutputStream(dbaos, def); + try (InputStream in = new FileInputStream(f)) { + int n; + while ((n = in.read(buf)) != -1) { + dos.write(buf, 0, n); + crc32.update(buf, 0, n); + } + dos.close(); + } catch (IOException x) { + x.printStackTrace(); + this.x = x; + } + size = def.getBytesRead(); + csize = def.getBytesWritten(); + crc = crc32.getValue(); + def.reset(); + tlDef.set(def); + crc32.reset(); + tlCrc.set(crc32); + tlBuf.set(buf); + } + } + + /** * Creates a new JAR file. */ - void create(OutputStream out, Manifest manifest) + void create(String[] files, OutputStream out, Manifest manifest) throws IOException { ZipOutputStream zos = new JarOutputStream(out); @@ -487,10 +588,137 @@ manifest.write(zos); zos.closeEntry(); } - for (File file: entries) { - addFile(zos, file); + + if (!mtflag || flag0) { + expand(null, files, false); + for (File file: entries) { + addFile(zos, file); + } + } else { +System.out.println("mtCreate: threadNum=" + threadNum + "..."); + final ExecutorService es = Executors.newFixedThreadPool(threadNum); + + final File[] fs = new File[files.length]; + for (int i = 0; i < files.length; i++) + fs[i] = new File(files[i]); + + + final Queue elist = new ConcurrentLinkedQueue(); + final Object LAST = new Object(); + es.submit(new Runnable() { + public void run() { + expand2(fs, elist, es); + elist.add(LAST); + } + }); + + /* + ArrayList elist = new ArrayList(); + expand2(fs, elist, es); + */ + + /* + expand(null, files, false); + ArrayList elist = new ArrayList(entries.size()); + for (File f: entries) { + if (f.isDirectory() || f.length() > MAXBUFSIZE) { + elist.add(f); + } else { + DefWork job = new DefWork(f); + elist.add(es.submit(job, job)); + } + } + */ + + //for (Object o : elist) { + + while(true) { + Object o = elist.poll(); + if (o == null) + continue; + if (o == LAST) + break; + + + + File file = null; + DefWork work = null; + if (o instanceof File) + file = (File)o; + else { + try { + work = (DefWork)((java.util.concurrent.Future)o).get(); + } catch (Exception x) { + x.printStackTrace(); + continue; + } + file = work.f; + } + String name = file.getPath(); + boolean isDir = file.isDirectory(); + if (isDir) { + name = name.endsWith(File.separator) ? name : + (name + File.separator); + } + name = entryName(name); + + if (name.equals("") || name.equals(".") || name.equals(zname)) { + return; + } else if ((name.equals(MANIFEST_DIR) || name.equals(MANIFEST_NAME)) + && !Mflag) { + if (vflag) { + output(formatMsg("out.ignore.entry", name)); + } + return; + } + long size = isDir ? 0 : file.length(); + if (vflag) { + //outprint(formatMsg("out.adding", name)); + System.out.println(formatMsg("out.adding", name)); + } + ZipEntry e = new ZipEntry(name); + e.setTime(file.lastModified()); + if (size == 0) { + e.setMethod(ZipEntry.STORED); + e.setSize(0); + e.setCrc(0); + } + if (work != null && work.dbaos != null) { + e.setMethod(ZipEntry.DEFLATED); + e.setSize(work.size); + e.setCompressedSize(work.csize); + e.setCrc(work.crc); + zos.writeNextEntry(e, work.dbaos.getByteArray(), 0, work.dbaos.size()); + work.dbaos = null; + } else { + zos.putNextEntry(e); + if (!isDir) { + copy(file, zos); + } + zos.closeEntry(); + } + /* report how much compression occurred. */ + if (vflag) { + size = e.getSize(); + long csize = e.getCompressedSize(); + //out.print(formatMsg2("out.size", String.valueOf(size), + System.out.println(formatMsg2("out.size", String.valueOf(size), + String.valueOf(csize))); + if (e.getMethod() == ZipEntry.DEFLATED) { + long ratio = 0; + if (size != 0) { + ratio = ((size - csize) * 100) / size; + } + output(formatMsg("out.deflated", String.valueOf(ratio))); + } else { + output(getMsg("out.stored")); + } + } + } + es.shutdown(); } zos.close(); + } private char toUpperCaseASCII(char c) { @@ -919,11 +1147,79 @@ updateLastModifiedTime(dirs); } + void extract2(String fname, String files[]) throws IOException { +System.out.println("mtExtract: threadNum=" + threadNum + "..."); + final ZipFile zf = new ZipFile(fname); + final Set dirs = newDirSet(); + Enumeration zes = zf.entries(); + + class ExtractWork implements Runnable { + IOException ioe; + ZipEntry e; + InputStream is; + ExtractWork(InputStream is, ZipEntry e) { + this.is = is; + this.e = e; + } + public void run() { + try { + dirs.add(extractFile(is, e)); + } catch (IOException ioe) { + this.ioe = ioe; + } + } + } + + final ExecutorService es = Executors.newFixedThreadPool(threadNum); + final Queue> works = new ConcurrentLinkedQueue<>(); + + while (zes.hasMoreElements()) { + ZipEntry e = zes.nextElement(); + InputStream is; + + if (files == null) { + if (e.isDirectory()) { + dirs.add(extractFile(null, e)); + } else { + ExtractWork work = new ExtractWork(zf.getInputStream(e), e); + works.add(es.submit(work, work)); + } + } else { + String name = e.getName(); + for (String file : files) { + if (name.startsWith(file)) { + if (e.isDirectory()) { + dirs.add(extractFile(null, e)); + } else { + ExtractWork work = new ExtractWork(zf.getInputStream(e), e); + works.add(es.submit(work, work)); + } + } + } + } + } + Future f; + while((f = works.poll()) != null) { + while (!f.isDone()) { + Thread.yield(); + } + try { + if (f.get().ioe != null) + throw f.get().ioe; + } catch (Exception ire) {} + } + zf.close(); + updateLastModifiedTime(dirs); + es.shutdown(); + } + + /** * 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(InputStream is, ZipEntry e) throws IOException { ZipEntry rc = null; String name = e.getName(); @@ -940,9 +1236,8 @@ f.getPath())); } else { rc = e; - } + } } - if (vflag) { output(formatMsg("out.create", name)); } --- old/src/share/classes/java/util/zip/ZipOutputStream.java 2011-10-26 12:29:27.000000000 -0700 +++ new/src/share/classes/java/util/zip/ZipOutputStream.java 2011-10-26 12:29:27.000000000 -0700 @@ -161,6 +161,38 @@ def.setLevel(level); } + public void writeNextEntry(ZipEntry e, byte[] b, int off, int len) + throws IOException + { + ensureOpen(); + if (current != null) { + closeEntry(); // close previous entry + } + if (e.time == -1) { + e.setTime(System.currentTimeMillis()); + } + if (e.method != DEFLATED) { + throw new ZipException("writeNextEntry: e.method must be DEFLATED"); + } + if (e.size == -1 || e.csize == -1 || e.crc == -1) { + throw new ZipException("writeNextEntry: e.size/csize/crc is -1"); + } + if (! names.add(e.name)) { + throw new ZipException("duplicate entry: " + e.name); + } + if (zc.isUTF8()) + e.flag |= EFS; + current = new XEntry(e, written); + xentries.add(current); + writeLOC(current); + out.write(b, off, len); + written += len; + if ((e.flag & 8) != 0) + writeEXT(e); + crc.reset(); + current = null; + } + /** * Begins writing a new ZIP file entry and positions the stream to the * start of the entry data. Closes the current entry if still active. @@ -191,6 +223,7 @@ if (e.size == -1 || e.csize == -1 || e.crc == -1) e.flag = 8; + break; case STORED: // compressed size, uncompressed size, and crc-32 must all be