利用 ShrinkWrap 创建可部署的归档文件

Author: Andrew Lee Rubinger Translator: Hantsy Bai Language:
Tags: shrinkwrap, arquillian Last Update:Sep 21, 2017

ShrinkWrap 是 Java 中最简单的创建归档文件的方法,它为 Arquillian 部署机制提供了动力。本教程作为一个快速课程会教你创建这些代表部署的对象。包括:

  • 相对传统的基于文件的归档方式,ShrinkWrap 的目的和好处
  • 从头创建一个新的归档文件
  • 不同的添加内容的机制
  • 从现有的文件结构导入归档

声明

最初,ShrinkWrap 的诞生是出于更简单了测试 Java 企业部署的需要。传统的基于普通文件归档源于 ZIP 标准,这样必需引入一些构建步骤,对所有应用资源进行打包。 一次构建过程要花费不少时间:

$ mvn clean install
... terrifying output trace ...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1:13.492s
[INFO] ------------------------------------------------------------------------

但作为开发人员,我们使用的是编码环境。基于这种想法去运行一次构建实在是浪费。

所以我们不禁要问: “如果在 Java 中我们能够声明一个对象代表归档,结果会有什么不同?"

结果是一个 Java API 模拟 “jar” 工具,一种具有直观语法的虚拟文件系统。

创建一个 ShrinkWrap 归档文件
JavaArchive archive = ShrinkWrap.create(JavaArchive.class,"myarchive.jar") 
   .addClasses(MyClass.class, MyOtherClass.class)
   .addResource("mystuff.properties");

结果是利用 IDE 增量编译的特性,允许我们跳过构建过程。

结果是一种从 IDE 中直接运行测试的方法。

结果是 ShrinkWrap 诞生了。

入门

第一步是让你了解一下 ShrinkWrap 体系结构,核心由三部分组成:

Name Maven Coordinates
API org.jboss.shrinkwrap:shrinkwrap-api
SPI org.jboss.shrinkwrap:shrinkwrap-spi
Implementation org.jboss.shrinkwrap:shrinkwrap-impl-base

只有 API 是必须包含在编译的 Classpath 中, SPI 和 Implementation 仅在运行时需要。这样可以很好的将直接使用和项目内部类进行了分离。

在 Maven 中, 利用传递性依赖的 POM, 这些依赖可以放在不同的 Scope 中,从 Maven Central 资源库获取:

项目的 pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="
  http://maven.apache.org/POM/4.0.0
  http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <!-- snip -->
  
  <dependency>
    <groupId>org.jboss.shrinkwrap</groupId>
    <artifactId>shrinkwrap-depchain</artifactId>
    <version>${version.shrinkwrap}</version>
    <type>pom</type>
  </dependency>

  <!-- snip -->
</project>

为了顾及那些还没有使用 Maven 资源库系统的项目,ShrinkWrap 发布包将所有的模块放在一个下载文件中,你可以根据自己的需要手动设置依赖。

前提条件

  • JRE5+ 运行环境
  • 无需额外的依赖

ShrinkWrap 可以运行任何 Java 5 或者更高的运行环境,但是编译时最少需要 JDK6。

API 文档

每个发布版本的 JavaDoc 在 这里

开放编码

Fork 我们,并参与 开发

归档文件的创建

ShrinkWrap 库的主要入口是 org.jboss.shrinkwrap.api.ShrinkWrap 类。你可以调用 create 方法来创建一个新的 Archive , 虚拟文件系统的通用视图允许添加名为 Asset 的内容到一个位置,称之为 ArchivePath 。下表显示了更多 ShrinkWrap 名称中的通用术语。

Common Term ShrinkWrap Class Description
Archive org.jboss.shrinkwrap.api.Archive A collection of resources, essentially a virtual filesystem
File org.jboss.shrinkwrap.api.Node An entry in an Archive; may represent content or a directory
Path org.jboss.shrinkwrap.api.ArchivePath Location in an Archive under which a Node lives
Asset org.jboss.shrinkwrap.api.Asset Byte-based content within a Node

另外, Archive 包括多个视图,通常情况下,你不会直接处理 Archive 类。相反,ShrinkWrap 提供了一些 Archive 扩展,自带了一些辅助方法来操作类型相关的内容。

Archive Type Description
org.jboss.shrinkwrap.api.GenericArchive Simplest type of concrete user-view of an Archive; supports generic operations
org.jboss.shrinkwrap.api.spec.JavaArchive JAR type; allows addition of Class es, Package s, and Manifest operations
org.jboss.shrinkwrap.api.spec.EnterpriseArchive Java EE EAR type; supports Manifest and related spec operations
org.jboss.shrinkwrap.api.spec.WebArchive Java EE WAR type; supports operations common to web application deployments
org.jboss.shrinkwrap.api.spec.ResourceAdaptorArchive Java EE RAR type; supports operations common to resource adaptor deployments

要创建一个 Archive, 很简单, 只要选择你要创建的归档类型,根据需要,还可以添加一个名称到 ShrinkWrap:create 静态方法中:

GenericArchive myArchive = ShrinkWrap.create(GenericArchive.class,"myArchive.jar");

就这样,你创建了第一个 ShrinkWrap 归档文档!

添加内容

当然一个代表空归档的对象是毫无意义的。所以我们试着看添加一些内容。正如前面所说的那样,内容由 Asset 进行构造,所以我们先看 ShrinkWrap 提供的一些 Asset 实现:

Asset Represents
org.jboss.shrinkwrap.api.asset.ArchiveAsset Nested Archive content
org.jboss.shrinkwrap.api.asset.ByteArrayAsset byte[] or InputStream content
org.jboss.shrinkwrap.api.asset.ClassAsset Java Class content
org.jboss.shrinkwrap.api.asset.ClassLoaderAsset A resource which can be loaded by an optionally-specified ClassLoader
org.jboss.shrinkwrap.api.asset.FileAsset File content
org.jboss.shrinkwrap.api.asset.StringAsset String content
org.jboss.shrinkwrap.api.asset.UrlAsset Content located at a given URL
org.jboss.shrinkwrap.api.asset.EmptyAsset Empty (0-byte) content

另外,因为 Asset 是一个接口,你可以提供你自己的实现,来处理任何代表 InputStream 基于字节流的内容。例如,下面的代码片断演示如何将一个 Activation Framework DataSource 转换成 Asset :

final DataSource dataSource = null; // Assume you have this
  Asset asset = new Asset() {
  @Override
  public InputStream openStream() {
    try {
      return dataSource.getInputStream();
    } catch (final IOException e) {
      throw new RuntimeException(e);
    }
  }
};

Archive:add 方法允许我们传入一些 Asset 内容,并加入到 ArchivePath 下面。

myArchive.add(myAsset,"path/to/content");
System.out.println(myArchive.toString(true));

传递一个 true 标志给 ArchivetoString 方法来递归打印 "ls -l" 样式的输出:

myArchive.jar:
/path/
/path/to/
/path/to/content

根据你要操作的不同内容的类型, 我们先前提到的 Archive 视图非常有用。例如,一个标准的 JAR 文件一般包含 .class 文件和其它资源,所以你可以用 JavaArchive 类型来添加这些内容。

ShrinkWrap 支持一种简单的机制,允许你的归档文件在不同的视图间切换, 由 org.jboss.shrinkwrap.api.Assignable 接口的 as 方法提供,每种视图都是从 Assignable 继承。所以要让你的归档用上 JavaArchive 视图,要添加 Class 资源, 代码可以简化为:

myArchive.as(JavaArchive.class).addClasses(String.class, Integer.class);
System.out.println(myArchive.toString(true));
archive.jar:
/java/
/java/lang/
/java/lang/String.class
/java/lang/Integer.class

使用这种机制的好处是保持 ShrinkWrap 用法简洁和直观,同时提供了多重继承语言的灵活性。

操作文件内容

因为 ShrinkWrap 扎根于 Java EE 并与 Arquillian 测试平台密不可分,但不局限这些领域。事实上,ShrinkWrap 更偏向扮演一个归档文件的虚拟系统的角色。因此,它提供了一种简单机制来操作普通文件结构。

从上面的代码可以看出,也许我们想用 ShrinkWrap 对包中所有的 .class 文件进行打包,并输出到一个标准的 JAR 或者 ZIP 格式。代码很简单:

JavaArchive archive = ShrinkWrap.create(JavaArchive.class,
  "myPackage.jar").addPackage(this.getClass().getPackage());
  System.out.println(archive.toString(true));
  archive.as(ZipExporter.class).exportTo(
    new File("/home/alr/Desktop/myPackage.jar"), true);
javalang.jar:
/org/
/org/alr/
/org/alr/test/
/org/alr/test/TestClass.class

简单的分析一下。首先,我们创建了一个 JavaArchive ,加入当前的 Class 所在 Package 的所有内容。为了验证包含了哪些内容,然后我们将它输出到控制台。最后一行,我们再次利用 JavaArchive 中从 Assignable 派生的特性切换到新视图:输出为 ZIP 格式。本例中,我们使用相应的 ZipExporter ,允许输出到一个 File, OutputStream, 甚至将内容转化为 InputStream ,这样我们还可以进行字节流操作。

ShrinkWrap 自带三种 exporters:

Exporter Output Format
org.jboss.shrinkwrap.api.exporter.TarExporter TAR
org.jboss.shrinkwrap.api.exporter.TarGzExporter TAR.GZ
org.jboss.shrinkwrap.api.exporter.ZipExporter ZIP

当然,我们也可以依葫芦画瓢使用下面标准的 importers 之一从普通文件来创建一个 ShrinkWrap 归档:

Importer Output Format
org.jboss.shrinkwrap.api.importer.TarImporter TAR
org.jboss.shrinkwrap.api.importer.TarGzImporter TAR.GZ
org.jboss.shrinkwrap.api.importer.ZipImporter ZIP

前面代码改成使用导入的方法可能看起来像这样:

JavaArchive roundtrip = ShrinkWrap
  .create(ZipImporter.class, "myPackageRoundtrip.jar")
  .importFrom(new File("/home/alr/Desktop/myPackage.jar"))
  .as(JavaArchive.class);

注意我们是如何将 ZipImporter 传递给 ShrinkWrap.create 方法的,它也是一个 Assignable ! 开始注意到这个把戏了吗?

这里我们简短的介绍了如何 ShrinkWrap 操作归档文件内容。 我们希望你能够深入研究 API,它更直观,更一致,欢迎到我们社区。

Share the Knowledge

Find this guide useful?

There’s a lot more about Arquillian to cover. If you’re ready to learn more, check out the other available guides.

Feedback

Find a bug in the guide? Something missing? You can fix it by forking this website, making the correction and sending a pull request. If you’re just plain stuck, feel free to ask a question in the user discussion forum.

Recent Changelog

  • Sep 21, 2017: Fix(scripts) timeout for waiting for timestamp available on pages is by Matous Jobanek

See full history »