Miscellaneous improvements to "jar".
Martin Buchholz
martinrb at google.com
Sat Jun 27 16:56:35 UTC 2009
On Fri, Jun 26, 2009 at 10:33, Xueming Shen <Xueming.Shen at sun.com> wrote:
> The latest version looks good. 2 nit comments
>
> (1) it's reasonable to have createTempFileInSamDirectoryAs separate out,
> but I would
> keep the directoryOf within it. Yes, it's "clearer":-)
>
Done.
}
/**
- * A variant of File.getParentFile that always returns a valid
- * directory (i.e. returns new File(".") where File.getParentFile
- * would return null).
- */
- private static File directoryOf(File file) {
- File dir = file.getParentFile();
- return (dir != null) ? dir : new File(".");
- }
-
- /**
* 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 {
- return File.createTempFile("jartmp", null, directoryOf(file));
+ File dir = file.getParentFile();
+ if (dir == null)
+ dir = new File(".");
+ return File.createTempFile("jartmp", null, dir);
}
private boolean ok;
>
> (2)the "updateOK" in dumpIndex really serves nobody but "clearer":-)
>
I tried to improve dumpIndex by
- removing updateOk variable.
- using Path.moveTo is more likely to be atomic, so that
a concurrent process is less likely to find the jar being updated missing.
(Alan: is there a better way to do the common task of replacing a file
with its transformed output?)
*/
void dumpIndex(String rootjar, JarIndex index) throws IOException {
File jarFile = new File(rootjar);
- File tmpFile = createTempFileInSameDirectoryAs(jarFile);
+ Path jarPath = jarFile.toPath();
+ Path tmpPath = createTempFileInSameDirectoryAs(jarFile).toPath();
try {
- boolean updateOk = update(new FileInputStream(jarFile),
- new FileOutputStream(tmpFile),
- null, index);
- if (updateOk) {
- jarFile.delete();
- if (!tmpFile.renameTo(jarFile)) {
- throw new IOException(getMsg("error.write.file"));
+ if (update(jarPath.newInputStream(),
+ tmpPath.newOutputStream(),
+ null, index)) {
+ try {
+ tmpPath.moveTo(jarPath, REPLACE_EXISTING);
+ } catch (IOException e) {
+ throw new IOException(getMsg("error.write.file"), e);
}
}
} finally {
- tmpFile.delete();
+ tmpPath.delete(false);
}
}
webrev updated.
Martin
>
> sherman
>
> btw, while you are here:-) do you have time to make the update() NOT to put
> jarIndex the
> first one when there is a manifest present? The JarInputStream assumes the
> manifest is always
> the first entry (or the second if the manifest dir is the first), which
> causes problem...I'm not saying
> to mix with this one:-) just in case you have time and interested while you
> are here:-)
>
>
> Martin Buchholz wrote:
>
>> I did some benchmarking,
>> and found that my changes "only" make jar 10-20% faster.
>> Disappointing - we expect an order of magnitude for every commit!
>>
>> While benchmarking, I discovered to my horror that the simple
>>
>> jar cf /tmp/t1 ...
>> jar i /tmp/t1
>>
>> fails, because it tries to create the replacement jar in "." and then
>> rename() it into place. Oops... Better refactor out the code that
>> puts the replacement temp file in the same directory.
>> Better write some tests for that, too.
>>
>> /**
>> * A variant of File.getParentFile that always returns a valid
>> * directory (i.e. returns new File(".") where File.getParentFile
>> * would return null).
>> */
>> private static File directoryOf(File file) {
>> File dir = file.getParentFile();
>> return (dir != null) ? dir : new File(".");
>> }
>>
>> /**
>> * 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 {
>> return File.createTempFile("jartmp", null, directoryOf(file));
>> }
>>
>> ---
>>
>> While we're here, let's remove the double buffering on file copy
>> operations.
>> And the repeated allocations of big buffers.
>>
>> /**
>> * 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();
>> }
>> }
>>
>> ----
>>
>> On the other hand, allocating a CRC32 is very cheap, so let's make that
>> private to CRC32OutputStream.
>> + private static class CRC32OutputStream extends java.io.OutputStream {
>> + final CRC32 crc = new CRC32();
>>
>> ----
>>
>> We should add a try { finally } to delete the tempfile for jar i
>> + try {
>> boolean updateOk = update(new FileInputStream(jarFile),
>> - new FileOutputStream(scratchFile),
>>
>> + new FileOutputStream(tmpFile),
>> null, index);
>> + if (updateOk) {
>> jarFile.delete();
>>
>> - if (!scratchFile.renameTo(jarFile)) {
>> - scratchFile.delete();
>> + if (!tmpFile.renameTo(jarFile)) {
>>
>> throw new IOException(getMsg("error.write.file"));
>> }
>> - scratchFile.delete();
>> + }
>> + } finally {
>>
>> + tmpFile.delete();
>> + }
>> }
>>
>> ----
>>
>> Webrev updated.
>> http://cr.openjdk.java.net/~martin/jar-misc/<http://cr.openjdk.java.net/%7Emartin/jar-misc/><
>> http://cr.openjdk.java.net/%7Emartin/jar-misc/>
>>
>> There are now many small changes combined in this fix. Sorry about that.
>> I'd be more inclined to separate them if creating new bugs was easier.
>>
>> I'm not planning on making any more changes. Really.
>>
>> Martin
>>
>> On Thu, Jun 25, 2009 at 12:00, Martin Buchholz <martinrb at google.com<mailto:
>> martinrb at google.com>> wrote:
>>
>> I have an updated version of this fix, with these changes:
>>
>> - Documented the turkish i problem
>>
>> /**
>> * 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) {
>>
>> - Refactored code so that updateEntry now also sets the method to
>> STORED.
>>
>> /**
>> * 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());
>> }
>>
>> - addIndex was never updating the size in the ZipEntry (as required),
>> which was not previously noticed because closeEntry was never
>> called.
>>
>> private void addIndex(JarIndex index, ZipOutputStream zos)
>> throws IOException
>> {
>> ZipEntry e = new ZipEntry(INDEX_NAME);
>> e.setTime(System.currentTimeMillis());
>> if (flag0) {
>> CRC32OutputStream os = new CRC32OutputStream(crc32);
>> index.write(os);
>> os.updateEntry(e);
>> }
>> zos.putNextEntry(e);
>> index.write(zos);
>> zos.closeEntry();
>>
>> }
>>
>> http://cr.openjdk.java.net/~martin/jar-misc/<http://cr.openjdk.java.net/%7Emartin/jar-misc/>
>> <http://cr.openjdk.java.net/%7Emartin/jar-misc/>
>> Previous webrev:
>> http://cr.openjdk.java.net/~martin/jar-misc.0/<http://cr.openjdk.java.net/%7Emartin/jar-misc.0/>
>> <http://cr.openjdk.java.net/%7Emartin/jar-misc.0/>
>>
>> Martin
>>
>>
>>
>> On Wed, Jun 24, 2009 at 19:34, Martin Buchholz
>> <martinrb at google.com <mailto:martinrb at google.com>> wrote:
>>
>> Hi jar team,
>>
>> I have a bunch of minor improvements to
>> src/share/classes/sun/tools/jar/Main.java
>>
>> Toby and Xueming, please review.
>>
>> Warning: the index code has not been maintained for many years.
>>
>> Xueming, please file a bug.
>>
>> Synopsis: Miscellaneous improvements to "jar".
>> Description:
>> - Use standard jdk coding style for javadoc
>> - Don't create a temp file for jar index in STORED mode.
>> - Don't use synchronized collections.
>> - Fix javac warnings.
>> - Don't define new names for things like INDEX_NAME;
>> use static import instead.
>> - more efficiently compare special file names in update mode.
>> Update mode should be measurably faster.
>> - make CRC32OutputStream a nested class.
>> refactor crc32.reset and updating entry into CRC32OutputStream.
>> - Fix apparently benign bug updating n in
>> CRC32OutputStream.write(byte[], int, int)
>>
>> Evaluation: Yep.
>>
>> http://cr.openjdk.java.net/~martin/jar-misc/<http://cr.openjdk.java.net/%7Emartin/jar-misc/>
>> <http://cr.openjdk.java.net/%7Emartin/jar-misc/>
>>
>> Thanks,
>>
>> Martin
>>
>>
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/core-libs-dev/attachments/20090627/e4bd1858/attachment.html>
More information about the core-libs-dev
mailing list