src/share/classes/sun/tools/jar/Main.java
Print this page
@@ -24,13 +24,14 @@
*/
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;
import java.text.MessageFormat;
import sun.misc.JarIndex;
@@ -71,11 +72,13 @@
* vflag: verbose
* flag0: no zip compression (store only)
* 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";
private static ResourceBundle rsrc;
@@ -195,12 +198,12 @@
// on stdout along with file data
// error("Warning: -v option ignored");
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();
}
out.close();
} else if (uflag) {
@@ -251,11 +254,14 @@
in.close();
}
}
} 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)
? new FileInputStream(FileDescriptor.in)
: new FileInputStream(fname);
@@ -359,10 +365,16 @@
iflag = true;
break;
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))));
usageError();
return false;
@@ -457,14 +469,103 @@
ok = false;
}
}
}
+ //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<Deflater> tlDef = new ThreadLocal<>();
+ private ThreadLocal<CRC32> tlCrc = new ThreadLocal<>();
+ private ThreadLocal<byte[]> 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);
if (flag0) {
zos.setMethod(ZipOutputStream.STORED);
@@ -485,14 +586,141 @@
}
zos.putNextEntry(e);
manifest.write(zos);
zos.closeEntry();
}
+
+ 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) {
return (c < 'a' || c > 'z') ? c : (char) (c + 'A' - 'a');
}
@@ -917,15 +1145,83 @@
}
zf.close();
updateLastModifiedTime(dirs);
}
+ void extract2(String fname, String files[]) throws IOException {
+System.out.println("mtExtract: threadNum=" + threadNum + "...");
+ final ZipFile zf = new ZipFile(fname);
+ final Set<ZipEntry> dirs = newDirSet();
+ Enumeration<? extends ZipEntry> 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<Future<ExtractWork>> 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<ExtractWork> 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();
File f = new File(e.getName().replace('/', File.separatorChar));
if (e.isDirectory()) {
@@ -940,11 +1236,10 @@
f.getPath()));
} else {
rc = e;
}
}
-
if (vflag) {
output(formatMsg("out.create", name));
}
} else {
if (f.getParent() != null) {