【问题标题】:Can you use a Pack200 file without first unpacking it?您可以在不先解包的情况下使用 Pack200 文件吗?
【发布时间】:2018-04-10 06:43:10
【问题描述】:

是否可以从 Pack200 创建的 pack.gz 文件中即时(在内存中)加载类,而无需先将其解压缩回 jar 中?我能找到的所有示例都向我展示了如何将其解压缩到 .jar 文件中,然后从 .jar 文件中加载类。

【问题讨论】:

  • 是的,这通常使用 JWS 完成。

标签: java jar classloader


【解决方案1】:

是的,这是可能的。 Pack200.Unpacker.unpack 方法写入 JarOutputStream;如果您在后台线程中执行此操作,则可以使用管道将新的 JarOutputStream 连接到 JarInputStream,并从中读取。

public JarInputStream readPackFile(Path packGzFile)
throws IOException {

    PipedInputStream pipeIn = new PipedInputStream();
    PipedOutputStream pipeOut = new PipedOutputStream(pipeIn);

    ExecutorService executor = Executors.newSingleThreadExecutor();
    Callable<Void> unpacker = new Callable<Void>() {
        @Override
        public Void call()
        throws IOException {
            try (InputStream file =
                    new GZIPInputStream(
                        new BufferedInputStream(
                            Files.newInputStream(packGzFile)));
                 JarOutputStream jarOutput = new JarOutputStream(pipeOut)) {

                Pack200.newUnpacker().unpack(file, jarOutput);
                return null;
            } finally {
                executor.shutdown();
            }
        }
    };
    executor.submit(unpacker);

    return new JarInputStream(pipeIn);
}

应该就足够了。但是,仍然存在两个问题:

  • 如果解包操作出现错误,您将永远不会知道,因为 ExecutorServices 会抑制其任务的异常。
  • JarInputStream 和 JarOutputStream(更具体地说,它们的超类 InflaterInputStream 和 DeflaterOutputStream)不适用于管道。这是因为关闭 JarOutputStream 会强制调用其 finish() 方法,但管道另一端的 JarInputStream 永远不会读取该数据。这意味着 JarInputStream 几乎肯定会在调用 finish() 之前关闭,从而导致 finish() 由于管道损坏而生成 IOException。

为了解决第一个问题,我们可以重写 JarInputStream 的 close 方法来解决解包操作中的任何失败。要解决第二个问题,我们可以在 JarOutputStream 中覆盖 finish() 以不执行任何操作:

public JarInputStream readPackFile(Path packGzFile)
throws IOException {

    PipedInputStream pipeIn = new PipedInputStream();
    PipedOutputStream pipeOut = new PipedOutputStream(pipeIn);

    class NonFinishingJarOutputStream
    extends JarOutputStream {
        NonFinishingJarOutputStream(OutputStream out)
        throws IOException {
            super(out);
        }

        @Override
        public void finish()
        throws IOException {
            // Deliberately empty.
        }
    }

    ExecutorService executor = Executors.newSingleThreadExecutor();
    Callable<Void> unpacker = new Callable<Void>() {
        @Override
        public Void call()
        throws IOException {
            try (InputStream file =
                    new GZIPInputStream(
                        new BufferedInputStream(
                            Files.newInputStream(packGzFile)));
                 JarOutputStream jarOutput =
                    new NonFinishingJarOutputStream(pipeOut)) {

                Pack200.newUnpacker().unpack(file, jarOutput);
                return null;
            } finally {
                executor.shutdown();
            }
        }
    };
    Future<?> unpackerTask = executor.submit(unpacker);

    return new JarInputStream(pipeIn) {
        @Override
        public void close()
        throws IOException {
            super.close();
            try {
                // If the unpack generated an exception, propagate it here.
                unpackerTask.get();
            } catch (ExecutionException e) {
                throw new IOException(e);
            } catch (InterruptedException e) {
                InterruptedIOException iie = new InterruptedIOException();
                iie.initCause(e);
                throw iie;
            }
        }
    };
}

【讨论】:

  • 这给了我一个 JarInputStream,而不是一个 JarFile。 stackoverflow.com/questions/16602668/… 似乎表明实际上很难用 JarInputStream 做很多事情。
  • 所有 JarFile 构造函数都需要一个文件。我很确定没有一个就无法使用 JarFile 类。
猜你喜欢
  • 2018-09-16
  • 1970-01-01
  • 2011-01-06
  • 1970-01-01
  • 1970-01-01
  • 2013-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多