Deploybare Archive with ShrinkWrap Erstellen

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

ShrinkWrap ist der einfachste Weg um Archive in Java zu erstellen. Es ist die Kraft hinter dem Arquillian Deployment Mechanismus. Dieser Guide fungiert als Crash-Kurs im Erstellen von Objekten, welche Deployments repräsentieren. Er behandelt:

  • Die Motivation und Vorteile von ShrinkWrap im Gegensatz zu traditionellen datei-basierten Archiven
  • Die Erstellung eines neuen Archives von Beginn an
  • Diverse Mechanismen um Inhalte hinzuzufügen
  • Importieren von bestehenden Dateistrukturen in Archive

Motivation

Von Anfang an war ShrinkWrap davon getrieben die Testbarkeit von Enterprise Deployments zu vereinfachen. Traditionell werden diese im ZIP Format erstellt, was zuerst einen Build-Schritt zur Erstellung eines geeigneten Archives erfordert. Dieser Schritt aber kostet Zeit:

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

Als Entwickler sind wir in unseren Entwicklungsumgebungen zuhause. Diese Gedankenwelt zu verlassen, um einen Build auszuführen ist Verschwendung.

Darum haben wir uns gefragt: “Was wäre, wenn wir ein Objekt in Java deklarieren können, welches ein Archiv repräsentiert?”

Das Ergebnis war eine Java API analog zum “jar” tool mit einem virtuellen Dateisystem und einer intuitiven Syntax.

Creating a ShrinkWrap Archive
JavaArchive archive = ShrinkWrap.create(JavaArchive.class,"myarchive.jar") 
   .addClasses(MyClass.class, MyOtherClass.class)
   .addResource("mystuff.properties");

Damit können die Vorteile der inkrementellen Kompilierung der IDE genutzt werden und der Build-Schritt übersprungen werden.

Nun können Tests direkt aus der IDE heraus ausgeführt werden.

Das Gesamtergebnis nennt sich ShrinkWrap.

Der Einstieg

Zuerst benötigen wir die ShrinkWrap Binaries. Der Kern besteht aus drei Teilen:

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

Nur die API muss zur Kompile-Zeit im Klassenpfad sein. SPI und Implementierung werden zur Laufzeit benötigt. Das führt zu einer guten Trennung von Klassen, welche zur direkten Verwendung vorgesehen sind und den Interna des ShrinkWrap Projektes.

Via Maven werden diese unter dem geeigneten Scope mithilfe der ShrinkWrap Dependency Chain POM eingebunden. ShrinkWrap ist über Maven Central verfügbar:

Your Project’s 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>

Projekte außerhalb des Maven Repository Systems müssen die ShrinkWrap Distribution herunterladen und die Abhängigkeiten manuell definieren.

Voraussetzungen

  • JRE (Java Runtime Environment) 5.0 oder größer

ShrinkWrap kann auf jeder Java 5 basierten Laufzeit ausgeführt werden. Zum Kompilieren benötigt es mindestens das JDK (Java Development Kit) 6.0

API Dokumentation

Die JavaDoc für jedes Release befindet sich auf docs.jboss.org .

Open Source Entwicklung

Fork uns auf github und helfe bei der Entwicklung!

Archive Erstellen

Der primäre Einstiegspunkt von ShrinkWrap ist die Klasse org.jboss.shrinkwrap.api.ShrinkWrap. Von hier aus kann die create Methode aufgerufen werden um ein neues Archive zu erstellen. Dieses liefert eine generische Sicht auf das zugrundeliegende virtuelle Dateisystem und ermöglicht das Hinzufügen von Inhalten, welche Asset s genannt werden, zu einem definierten Ort, welcher sich ArchivePath nennt. Die folgende Tabelle stellt die allgemeingültigen Begriffe dem ShrinkWrap Benennungssystem gegenüber:

Allgemeiner Begriff ShrinkWrap Klasse Beschreibung
Archiv org.jboss.shrinkwrap.api.Archive Eine Sammlung von Ressourcen, im Wesentlichen ein virtuelles Dateisystem
Datei org.jboss.shrinkwrap.api.Node Der Einstieg in ein Archive; Kann Inhalt oder ein Verzeichnis sein
Pfad org.jboss.shrinkwrap.api.ArchivePath Ort in einem Archive an dem ein Node exisitert
Element org.jboss.shrinkwrap.api.Asset Byte-basierter Inhalt eines Node

Zusätzlich haben Archive mehrere Sichten. Diese verstehen sich als Erweiterungen um vereinfachte Wege zur Arbeit mit den jeweils relevanten Inhalten zu ermöglichen. Die direkte Arbeit mit der Archive Klasse ist vielfach nicht notwendig.

Archiv Typ Beschreibung
org.jboss.shrinkwrap.api.GenericArchive Einfachster Typ einer Nutzer-Sicht auf ein Archiv; Unterstützt generische Operationen
org.jboss.shrinkwrap.api.spec.JavaArchive JAR Typ; Ermöglicht das Hinzufügen von Class es, Package s, und Manifest Operationen
org.jboss.shrinkwrap.api.spec.EnterpriseArchive Java EE EAR Typ; Untersützt Manifest und weitere Spezifikations Operationen
org.jboss.shrinkwrap.api.spec.WebArchive Java EE WAR Typ; Unterstützung für Web Application Deployments
org.jboss.shrinkwrap.api.spec.ResourceAdaptorArchive Java EE RAR Typ; Unterstützung für Resource Adaptor deployments

Um ein Archive zu erstellen wird einfach der gewünschte Archiv Typ ausgewählt. Option al kann der statischen ShrinkWrap:create Methode noch ein Name übergeben werden:

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

Das war es schon! Das erste ShrinkWrap Archiv ist erstellt!

Inhalte Hinzufügen

Natürlich ist ein Objekt welches ein leeres Archiv repräsentiert nicht hilfreich. Als nächstes muss Inhalt hinzugefügt werden. Wie bereits beschrieben wird Inhalt von der Asset Klasse modelliert. Auch für sie bringt ShrinkWrap bereits einige Spezialisierungen mit:

Asset Repräsentiert
org.jboss.shrinkwrap.api.asset.ArchiveAsset Verschachtelten Archive Inhalt
org.jboss.shrinkwrap.api.asset.ByteArrayAsset byte[] oder InputStream
org.jboss.shrinkwrap.api.asset.ClassAsset Eine Java Class
org.jboss.shrinkwrap.api.asset.ClassLoaderAsset Eine Ressource, welche durch einen optional spezifizierten ClassLoader geladen werden kann.
org.jboss.shrinkwrap.api.asset.FileAsset Ein File
org.jboss.shrinkwrap.api.asset.StringAsset Ein String
org.jboss.shrinkwrap.api.asset.UrlAsset Inhalt von einer spezifischen URL
org.jboss.shrinkwrap.api.asset.EmptyAsset Leerer (0-byte) Inhalt

Da Asset ein Interface ist, kann auch eine eigene Implementierung bereitgestellt werden um das arbeiten mit jedem byte-basierten Inhalt zu ermöglichen, welcher als InputStream dargestellt werden kann. Der folgende Code-Schnippsel zeigt beispielhaft die Abbilung einer Activation Framework DataSource als Asset :

final DataSource dataSource = null; // Angenommen, dass ist schon da.
  Asset asset = new Asset() {
  @Override
  public InputStream openStream() {
    try {
      return dataSource.getInputStream();
    } catch (final IOException e) {
      throw new RuntimeException(e);
    }
  }
};

Die Archive:add Methode erlaubt uns das Übergeben und Ablegen eines Asset Inhalts unter einem ArchivePath.

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

Durch das Übergeben eines true Flags in die toString Methode eines Archive s erzeugt eine rekursive "ls -l" -style Ausgabe:

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

Alle ShrinkWrap Archive haben auch gleichzeitige einen zugehörige Sicht. Mit deren Hilfe wird die Arbeit mit den eigentlichen Inhalten deutlich vereinfacht. So enthält eine Standard JAR Datei typischerweise .class Dateien und andere, Java bezogene Ressourcen, welche mit der zum JavaArchive Typ gehörenden Sicht einfach hinzugefügt werden können. Mithilfe eines einfachen Mechanismus kann die Sicht eines Archivs gewechselt werden. Durch die as Methode des org.jboss.shrinkwrap.api.Assignable Interfaces kann an jedem Archiv über die Methoden einer weiteren Archiv Sicht gearbeitet werden. Am konkreten Beispiel gibt es am GenericArchive keine Möglichkeit Java Ressourcen hinzuzufügen. Um dennoch in den Genuss der JavaArchive:addClasses Methode zu kommen wird diese Sicht einfach temporär verwendet:

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

Mithilfe dieses zentralen Mechanismus wird die Verwendung von ShrinkWrap klar und intuitiv gehalten und ermöglicht dennoch große Flexibilität.

Arbeiten mit Dateien

Obwohl ShrinkWrap seine Wurzeln im Java EE Umfeld hat und eine enge Verbindung zur Arquillian Test Plattform besitzt, ist es nicht auf diese Bereiche limitiert. Tatsächlich ist ShrinkWrap absichtlich auf die Funktionen einen virtuellen Dateisystems für Archive begrenzt. Als solches bietet es einfache und universell einsetzbare Mechanismen um mit Dateistrukturen zu arbeiten.

Entliehen aus dem obigen Beispiel könnten wir ShrinkWrap dazu benutzen alle .class aus dem aktuellen Package zusammenzufassen und diese als Standard JAR im ZIP Dateiformat auszugeben. Der Sourcecode dafür würde einfach so aussehen:

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

Was genau passiert hier? Zuerst wird ein JavaArchive erstellt und der komplette Inhalt aus dem Package der aktuellen Klasse hinzugefügt. Danach erfolgt die Ausgabe auf die Konsole um zu kontrollieren was alles im Archiv enthalten ist. Die letzte Zeile nutzt wiederum die Assignable Fähigkeiten um die JavaArchive Sicht mit einer neuen Sicht zu bedienen; Diesemal eine, welche das Exportieren in dsa ZIP Format unterstützt. Konkret wird hier der passend benannte ZipExporter verwendet um den Inhalt in ein File, einen OutputStream oder gar einen InputStream umzuwandeln. Im letzteren Fall kann direkt mit den bytes gearbeitet werden.

Es gibt drei verschiedene Typen von Exporter in ShrinkWrap:

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

Im Gegensatz dazu kann ein ShrinkWrap Archiv auch von einer Datei gelesen werden. Dies geschieht dann analog mit den verfügbaren Importer:

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

Um einen kompletten Round-Trip mit dem vorangegangenen Beispiel zu machen sind folgende Zeilen notwendig:

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

Erwähnenswert ist die direkte Übergabe des ZipImporter in die ShrinkWrap.create Methode. Dies ist möglich, da diese ebenfalls ein Assignable ist! Dies ist das zentrale Thema!

Damit ist die kurze Einführung in die Verarbeitung von Archiven mit ShrinkWrap auch schon beendet. Wir hoffen Du findest die API intuitiv und konsistent und begrüßen Dich in unserer Gemeinschaft.

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 »