QuantumVertex
QuantumVertex

Reputation: 27

Java - Append files to Jar File Programatically

I've searched high and low for a way to do this, finding all manner of different ways to approach this problem, and yet none of them have worked for me.

I simply need a way to append files to a Jar File, overwriting if they already exist, and I want the fastest way to do this too. I've tried converting it to a Zip File and adding the files using Zip File System but I run into errors such as "zip END header not found", other methods work but are painfully slow (6 MB of files took 3 minutes).

What is the fastest way to append files to a Jar File?

Edit: Yeah go ahead, downvote instead of answering, very productive.

Upvotes: 0

Views: 1919

Answers (2)

Ibrahim.H
Ibrahim.H

Reputation: 1195

Here is the best way I could found:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;

public class JarUtil {

    public static void updateJarFile(File srcJarFile, boolean update, File ...filesToAdd) throws IOException {

        File tmpJarFile = File.createTempFile("tempJar", ".tmp");
        JarFile jarFile = new JarFile(srcJarFile);
        boolean jarUpdated = false;
        List<String> fileNames = new ArrayList<String>();

        try {
            JarOutputStream tempJarOutputStream = new JarOutputStream(new FileOutputStream(tmpJarFile));
            try {
                // Added the new files to the jar.
                for (int i = 0; i < filesToAdd.length; i++) {
                    File file = filesToAdd[i];
                    FileInputStream fis = new FileInputStream(file);
                    try {
                        byte[] buffer = new byte[1024];
                        int bytesRead = 0;
                        JarEntry entry = new JarEntry(file.getName());
                        fileNames.add(entry.getName());
                        tempJarOutputStream.putNextEntry(entry);
                        while ((bytesRead = fis.read(buffer)) != -1) {
                            tempJarOutputStream.write(buffer, 0, bytesRead);
                        }

                        // System.out.println(entry.getName() + " added.");
                    } finally {
                        fis.close();
                    }
                }

                // Copy original jar file to the temporary one.
                Enumeration<?> jarEntries = jarFile.entries();
                while (jarEntries.hasMoreElements()) {
                    JarEntry entry = (JarEntry) jarEntries.nextElement();
                    /*
                     * Ignore classes from the original jar which are being
                     * replaced
                     */
                    String[] fileNameArray = (String[]) fileNames
                            .toArray(new String[0]);
                    Arrays.sort(fileNameArray);// required for binary search
                    if (Arrays.binarySearch(fileNameArray, entry.getName()) < 0) {
                        InputStream entryInputStream = jarFile
                                .getInputStream(entry);
                        tempJarOutputStream.putNextEntry(entry);
                        byte[] buffer = new byte[1024];
                        int bytesRead = 0;
                        while ((bytesRead = entryInputStream.read(buffer)) != -1) {
                            tempJarOutputStream.write(buffer, 0, bytesRead);
                        }
                    } else if (!update) {
                        throw new IOException(
                                "Jar Update Aborted: Entry "
                                        + entry.getName()
                                        + " could not be added to the jar"
                                        + " file because it already exists and the update parameter was false");
                    }
                }

                jarUpdated = true;
            } catch (Exception ex) {
                System.err.println("Unable to update jar file");
                tempJarOutputStream.putNextEntry(new JarEntry("stub"));
            } finally {
                tempJarOutputStream.close();
            }

        } finally {
            jarFile.close();
            // System.out.println(srcJarFile.getAbsolutePath() + " closed.");

            if (!jarUpdated) {
                tmpJarFile.delete();
            }
        }

        if (jarUpdated) {
            srcJarFile.delete();
            tmpJarFile.renameTo(srcJarFile);
            // System.out.println(srcJarFile.getAbsolutePath() + " updated.");
        }
    }
}

Original Source (modified): https://subversivebytes.wordpress.com/2012/10/11/java-programmatically-update-jar-file/ Hope this helps.

Upvotes: 0

Andreas
Andreas

Reputation: 159165

You'd do this the same way you update a text file:

  • Open an input stream from current file.
  • Open an output stream to new (temporary) file.
  • Read from input stream:
    • If no change needed, write to output stream.
    • If content should be removed, don't write it.
    • If content should be changed, change it, then write it.
    • If new content should be added at this point, write it now.
    • Repeat until all content processed.
  • Close both streams.
  • Delete/rename old file and rename new file to old file.

In your case, that means:

  • Start with list of files to be added.
  • Open ZipInputStream on current Jar file.
  • Open ZipOutputStream on new (temporary) Jar file.
  • Copy all existing entries (files), except files that will be replaced.
  • Insert all new files.
  • Close both streams.
  • Delete/rename old Jar file and rename new file to old Jar file.

You can of course flip the order.

  • Open ZipOutputStream on new (temporary) Jar file.
  • Insert all new files, and remember their names.
  • Open ZipInputStream on current Jar file.
  • Copy all existing entries (files), except files already added.
  • Close both streams.
  • Delete/rename old Jar file and rename new file to old Jar file.

Upvotes: 2

Related Questions