On Fri, Jun 26, 2009 at 10:33, Xueming Shen <xueming.s...@sun.com
<mailto:xueming.s...@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
<marti...@google.com <mailto:marti...@google.com>
<mailto:marti...@google.com <mailto:marti...@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
<marti...@google.com <mailto:marti...@google.com>
<mailto:marti...@google.com <mailto:marti...@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