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