You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@commons.apache.org by "Matt Quigley (JIRA)" <ji...@apache.org> on 2013/03/05 23:42:13 UTC

[jira] [Commented] (COMPRESS-221) Compress cannot run without XZ included

    [ https://issues.apache.org/jira/browse/COMPRESS-221?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13594040#comment-13594040 ] 

Matt Quigley commented on COMPRESS-221:
---------------------------------------

Note that a possible solution is to have a try-catch on a static method which will fail when the class loader tries loading a class.


For example, {{XZUtils}} does not import any classes not in the Compress project, so we can add a static method {{isAvailable()}} which calls a class which does import classes not in the Compress project:

{code}
    /**
     * This is a helper method to see if the XZ library is available.
     * 
     * @return {@code true} if the XZ library is available.
     */
    public static boolean isAvailable() {
        try {
            XZCompressorInputStream.isAvailable();
            return true;
        } catch (Throwable t) {
            return false;
        }
    }
{code}

The following will always return {{true}} when the class loader can load the XZ library, but will throw a {{NoClassDefFoundError}} when its class cannot be loaded due to the imports problem.
{code}
    /**
     * This is a helper method to see if the XZ library is available. If the library is not available, then the class
     * loader will throw a {@code NoClassDefFoundError}.
     * 
     * @return {@code true} if the XZ library is available.
     */
    public static boolean isAvailable() {
        return true;
    }
{code}

And then before the XZ library is used, we can simply check to see if it is available:
{code}
            if (XZUtils.isAvailable()) {
                if (XZCompressorInputStream.matches(signature, signatureLength)) {
                    return new XZCompressorInputStream(in);
                }
            }
{code}

                
> Compress cannot run without XZ included
> ---------------------------------------
>
>                 Key: COMPRESS-221
>                 URL: https://issues.apache.org/jira/browse/COMPRESS-221
>             Project: Commons Compress
>          Issue Type: Bug
>    Affects Versions: 1.4.1
>            Reporter: Matt Quigley
>
> In previous versions of Compress, we used the Compress library in our application without requiring any other libraries.  Since the introduction of XZ, the library crashes because it does not dynamically try to find the XZ classes. Our application does not use or need XZ, and we _cannot_ add another ~100KB file to our application.
> To reproduce:
> {code}
>         try {
> // <properties><mimeTypeRepository resource="/etc/tika-mimetypes.xml" magic="true"/></properties>
>             tika = new TikaConfig(getClass().getClassLoader().getResourceAsStream("etc/tika-config.xml"));
>         } catch (Throwable t) {
>             t.printStackTrace();
>             throw new MimeTypeException("Cannot load tika-config.xml", t);
>         }
>         detector = tika.getDetector();
>         MediaType contentType = MediaType.OCTET_STREAM;
>         contentType = detector.detect(tika, metadata); // error here
> {code}
> Calling {{Detector.detect(InputStream, Metadata)}} causes a {{NoClassDefFoundError}} because it is trying to find the XZ files using a direct reference to the XZ classes.  {{Detector.detect:61}} uses {{ZipContainerDetector.detect}} to use {{CompressorInputStream createCompressorInputStream(InputStream)}}.
> The problem in {{public CompressorInputStream createCompressorInputStream(InputStream)}} is it directly calls static methods on the {{XZCompressorInputStream}} class which aren't in the classpath.
> {code}
>       if (BZip2CompressorInputStream.matches(signature, signatureLength)) {
>         return new BZip2CompressorInputStream(in);
>       }
>       if (GzipCompressorInputStream.matches(signature, signatureLength)) {
>         return new GzipCompressorInputStream(in);
>       }
>       if (XZCompressorInputStream.matches(signature, signatureLength)) {
>         return new XZCompressorInputStream(in);
>       }
>       if (Pack200CompressorInputStream.matches(signature, signatureLength))
>         return new Pack200CompressorInputStream(in);
> {code}
> Ass you can see, the {{XZCompressorInputStream.matches()}} method is a static method.  When the class loader loads this class, it tries to load the import statements at the top of {{XZCompressorInputStream}}:
> {code}
> import org.apache.commons.compress.compressors.CompressorInputStream;
> import org.tukaani.xz.SingleXZInputStream;
> import org.tukaani.xz.XZ;
> import org.tukaani.xz.XZInputStream;
> {code}
> And, since these {{org.tukaani.xz}} classes don't exist, a {{NoClassDefFoundError}} exception is thrown.
> {code}
> java.lang.NoClassDefFoundError: org/tukaani/xz/XZInputStream
> at org.apache.commons.compress.compressors.CompressorStreamFactory.createCompressorInputStream(CompressorStreamFactory.java:120)
> at org.apache.tika.parser.pkg.ZipContainerDetector.detectCompressorFormat(ZipContainerDetector.java:95)
> at org.apache.tika.parser.pkg.ZipContainerDetector.detect(ZipContainerDetector.java:81)
> at org.apache.tika.detect.CompositeDetector.detect(CompositeDetector.java:61)
> at com.lookout.mimetype.TikaResourceMetadataFactory.createMetadata(TikaResourceMetadataFactory.java:166)
> at com.lookout.mimetype.TikaResourceMetadataFactory.createMetadata(TikaResourceMetadataFactory.java:112)
> (...)
> Caused by: java.lang.ClassNotFoundException: org.tukaani.xz.XZInputStream
> at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
> at java.security.AccessController.doPrivileged(Native Method)
> at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
> at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
> at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
> at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
> {code}
> The appropriate approach is to see if the classes exist before trying to use them.  If they fail, then XZ is not supported and {{ZipContainerDetector.detect}} will not support XZ files.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira