Saturday, February 12, 2005

A more useful way to write JAR files

The Java java.util.jar package has an ancient pedigree. Unfortunately, it shows that the early Java engineers did not all understand object-oriented design. Consider writing a file into a JAR:

// Write foo.jar!bar/qux.txt
JarOutputStream jos
        = new JarOutputStream(new File("foo.jar"));
jos.putNextEntry(new JarEntry("bar/qux.txt"));
writeContentsToOutputStream(jos);
jos.closeEntry();
jos.close();

Contrast with a more modern design:

NewerJarOutputStream newerJos
        = new NewerJarOutputStream(jarFile);
JarEntryOutputStream entryJos= newerJos.openEntry("bar/qux.txt");
writeContentsToOutputStream(entryJos);
entryJos.close();
newerJos.close();

The whole business with putNextEntry/closeEntry is lame, and managing a separate JarEntry object is annoying. What would NewerJarOutputStream and JarEntryOutputStream look like?

class NewerJarOutputStream extends JarOutputStream {
    public JarEntryOutputStream openEntry(final String name) {
        return new JarEntryOutputStream(this, name);
    }
}

class JarEntryOutputStream extends OutputStream {
    private final NewerJarOutputStream jos;

    public JarEntryOutputStream(final NewerJarOutputStream jos,
            final String name) {
        this.jos = jos;

        jos.putNextEntry(new JarEntry(name));
    }

    public void write(final int b) {
        jos.write(b);
    }

    @Override
    public void close() {
        jos.closeEntry();
    }
}

What I have actually written is a SignedJarOutputStream to provide programmatic JAR signing and the attendent classes such as SignedJarFile, et al. Unfortunately, the literature is a bit dowdy, and Sun provides very little help. But the task was interesting, and the JAR signing specification is elegant and well-considered even if over-sparse. Factum est.

1 comment:

Anonymous said...

Hey, dont you think the newer design you proposed seems to convey a wrong picture. You provide two separate references each of NewerJarOutputStream and JarEntryOutputStream. This can mislead anyone who does not see the code of these classes to think that they can create one output stream for each entry and write to them separately (Say in different threads)? Original design is more clear about the fact that it is a single stream and not separate independently usable ones. Just MHO - Maruthi