Niekonsekwentna obsługa manifestu przez JarFile i JarInputStream

Napisałem niedawno program narzędziowy który przeszukiwał bundle OSGi w poszukiwaniu dynamicznych importów i deklaracji buddy class loading. Kod wczytujący manifest z pliku JAR wyglądał mniej więcej tak

import java.util.jar.*

def withJarInputStream(InputStream inputStream) {
    def jarIn
    try {
        jarIn = new JarInputStream(inputStream)
        closure(jarIn)
    } finally {
        jarIn?.close()
    }
}

def readManifest(File jar) {
    jar.withInputStream { inputStream ->
        withJarInputStream(inputStream) { jarIn ->
            Manifest mf = jarIn.getManifest()
            return mf
        }
    }
}

Okazało się jednak, że w niektórych plikach JAR nie potrafi on odnaleźć istniejącego manifestu podczas gdy konstrukcja

import java.util.jar.*

def withJar(File file) {
    def jarFile
    try {
        jarFile = new JarFile(file)
        closure(jarIn)
    } finally {
        jarFile?.close()
    }
}

def readManifest(File file) {
    withJar(file) { jarFile ->
        Manifest mf = jarFile.getManifest()
        return mf
    }
}

działała poprawnie! Zaciekawiony postanowiłem udać się do źródeł i sytuacja stała się jasna. Klasa java.util.jar.JarInputStream poszukuje pliku JarFile.MANIFEST_NAME tylko w dwóch pierwszych rekordach strumienia ZipInputStream

   // java.util.jar.JarInputStream.JarInputStream(InputStream, boolean)
   public JarInputStream(InputStream in, boolean verify) throws IOException {
        super(in);
        this.doVerify = verify;

        // This implementation assumes the META-INF/MANIFEST.MF entry
        // should be either the first or the second entry (when preceded
        // by the dir META-INF/). It skips the META-INF/ and then
        // "consumes" the MANIFEST.MF to initialize the Manifest object.
        JarEntry e = (JarEntry)super.getNextEntry();
        if (e != null && e.getName().equalsIgnoreCase("META-INF/"))
            e = (JarEntry)super.getNextEntry();
        first = checkManifest(e);
    }

    private JarEntry checkManifest(JarEntry e)
        throws IOException
    {
        if (e != null && JarFile.MANIFEST_NAME.equalsIgnoreCase(e.getName())) {
            man = new Manifest();
            byte bytes[] = getBytes(new BufferedInputStream(this));
            man.read(new ByteArrayInputStream(bytes));
            closeEntry();
            if (doVerify) {
                jv = new JarVerifier(bytes);
                mev = new ManifestEntryVerifier(man);
            }
            return (JarEntry)super.getNextEntry();
        }
        return e;
    }

podczas gdy java.util.jar.JarFile przeszukuje całą zawartość archiwum

    // java.util.jar.JarFile.getManEntry()
    private JarEntry getManEntry() {
        if (manEntry == null) {
            // First look up manifest entry using standard name
            manEntry = getJarEntry(MANIFEST_NAME);
            if (manEntry == null) {
                // If not found, then iterate through all the "META-INF/"
                // entries to find a match.
                String[] names = getMetaInfEntryNames();
                if (names != null) {
                    for (int i = 0; i < names.length; i++) {
                        if (MANIFEST_NAME.equals(
                                                 names[i].toUpperCase(Locale.ENGLISH))) {
                            manEntry = getJarEntry(names[i]);
                            break;
                        }
                    }
                }
            }
        }
        return manEntry;
    }

Inną kwestią jest pochodzenie takich nietypowych plików JAR; przypuszczam że powstały w wyniku ręcznej modyfikacji manifestu archiwum JAR za pomocą narzędzi typu 7-Zip.

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s