Steve Hostettler

JEE and test automation

Programmatically Build Web Archives Using ShrinkWrap

| Comments

While doing integration tests on web applications, it is important to restrict the application to the strict minimum necessary for any given test. It helps to improve the startup time as well as to reduce unexpected interactions. To that end, I use ShrinkWrap from JBoss that is a very nice a simple API to build Java archives (e.g., JAR WAR, EAR). This post describes how to programmatically build a WAR file that can be deployed on an embedded server for integration testing. For more details on how to use this web archive in JUnit fixtures please refer to this article. The following snippet gets the ShrinkWrap package from the JBoss repository. As I only use it for test purposes, I restricted its use to the test scope.

ShrinkWrap is available as a Maven dependency:

Maven dependencies for the ShrinkWrap project
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<repositories>
...
  <repository>
      <id>repository.jboss.org</id>
      <name>JBoss Repository</name>
      <url>http://repository.jboss.org/nexus/content/groups/public-jboss/</url>
  </repository>
  ...
</repositories>
...
<dependencies>
  ...
  <dependency>
      <groupId>org.jboss.shrinkwrap</groupId>
      <artifactId>shrinkwrap-api</artifactId>
      <version>1.0.0-alpha-12</version>
      <scope>test</scope>
  </dependency>
  <dependency>
      <groupId>org.jboss.shrinkwrap</groupId>
      <artifactId>shrinkwrap-impl</artifactId>
      <version>1.0.0-alpha-12</version>
      <scope>test</scope>
  </dependency>
...
<dependencies>

Building the archive

The first step is to crate a web archive a.k.a. a WAR file. As we want to produce a WAR file, we use the WebArchive.class parameter to the ShrinkWrap.create method. It is also possible to produce JAR (JavaArchive.class) and EAR (EnterpriseArchive.class).

Creation of a Web Archive
1
WebArchive archive = ShrinkWrap.create(WebArchive.class, "test.war");

The next step to build a WAR is to provide the web.xml for you project. Again this can a be a simplified version of the web descriptor to improve startup time or limit dependencies. In the following example, the files are taken from a maven directory structure.

Add a web descriptor to the archive
1
2
3
private static final String WEBAPP_SRC = "src/main/webapp";

archive.setWebXML(new File(WEBAPP_SRC, "WEB-INF/web.xml"))

Now that we have a web descriptor, let’s add some classes that are required to run the application. To not having to add each single class individually, it is possible to a complete package to the archive.

add a package to the runtime classes
1
archive.addPackage(java.lang.Package.getPackage("ch.demo.web"))

It is also possible to add individual classes:

add a class to the runtime classes
1
archive.addClasses(Student.class, Address.class);

For JSF applications, we add add the faces-config.xml.

1
archive.addAsWebInfResource(new File(WEBAPP_SRC, "WEB-INF/faces-config.xml"))

And similarly, let’s add the CDI descriptor into WEB-INF. As we do not need to create a specific `beans.xml, we add an empty descriptor:

1
archive.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")

Let’s finally add standard web resources such as xhtml files or static files:

1
archive.addAsWebResource(new File(WEBAPP_SRC, "login.xhtml"));

As ShrinkWrap uses Method Chaining, it is possible to chain add class to fill the archive.

Build a simple web application
1
2
3
4
5
6
7
archive.setWebXML(new File(WEBAPP_SRC, "WEB-INF/web.xml"))
  .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
  .addAsWebInfResource(new File(WEBAPP_SRC, "WEB-INF/faces-config.xml"))
  .addPackage(java.lang.Package.getPackage("ch.demo.web"))
  .addAsWebResource(new File(WEBAPP_SRC, "login.xhtml"))
  .addAsWebResource(new File(WEBAPP_SRC, "index.jsp"))
  .addAsWebResource(new File(WEBAPP_SRC, "xhtml/listStudents.xhtml"), "xhtml/listStudents.xhtml");

Please note that when using maven to execute the tests, most of the required libraries are already on the classpath. Therefore, we often do not need to add any library or jars to the web archive.

Using the archive

After having built the package, we need to export it either as a war or as an exploded directory. The following snippet produces a WAR file named after the archive. The second parameter states whether a existing archive can be overwritten.

Produce the actual war
1
new ZipExporterImpl(archive).exportTo(new File(archive.getName()), true);

Alternatively it is possible to produce an exploded archive:

Produce an exploded archive
1
new ExplodedExporterImpl(archive).exportExploded(new File(archive.getName()));

Conclusion

We have seen how to use ShrinkWrap to programmatically build Web archives. This is especially useful to test smaller version of your web application during integration testing. It helps to test webapps with mock services and to improve the startup time. The examples used in this blog are to be found in the JEE-6-Demo project on Google code hosting.

Comments