Andreas Bruns

Softwareentwicklung für Oldenburg und Bremen

Eigenen Maven-Archetype erstellen

Bei der Erstellung neuer Java-Projekte will man meistens die Projekt-Einstellungen bestehender Projekte übernehmen. Ein einfacher Weg ist es daher, ein bestehendes Projekt zu kopieren, überflüssige Dateien zu löschen und die restlichen Dateien für das neue Projekt anzupassen.

Eleganter geht es mit Maven und dessen Konzept von Archetypen. Der Maven-Befehl „mvn archetype:generate“ stellt eine Liste mit einer Vielzahl von Projekt-Templates bereit. Nach Auswahl eines Templates und der Angabe einiger Projekt-Informationen wird ein Java-Projekt mit allen benötigten Dateien generiert, beispielsweise Android-, GWT- oder JEE-Projekt.

In der 1. Version des Maven-Archetype-Plugins war das Erstellen eines eigenen Maven-Archetypes noch relativ aufwendig, weil alle Projekt-Dateien händisch erstellt und angepasst werden mussten. Mit der 2. Version ist alles viel einfacher geworden, da das Archetype-Plugin alle Projekt-Dateien auf Basis eines bestehenden Maven-Projekts erzeugt. Das funktioniert auch für Multi-Module-Projekte.

In einem vorherigen Artikel habe ich ein kleines Maven-Projekt vorgestellt, in dem SMS-Nachrichten über den Anbieter BulkSMS versendet und empfangen werden. Das Projekt dient in diesem Artikel als Vorlage für einen Maven-Archetypen. Wenn das Maven-Projekt vorliegt, kann man mit dem Maven-Befehl „mvn archetype:create-from-project“ aus dem Projekt die Archetype-Dateien in das Verzeichnis „target/generated-sources/archetype/“ generieren lassen. Das Installieren des Archetypen in das lokale Repository erfolgt über den gewohnten Befehl „mvn clean install" aus dem erzeugten Verzeichnis heraus. Wenn man dann auf Basis des Archetypen ein Projekt generieren möchte, erfolgt das über den Maven-Befehl „mvn archetype:generate -DarchetypeCatalog=local„. Zusammenfassend ergibt sich mit den folgenden Shell-Befehlen die anschließend angegebene Shell-Ausgabe:

git clone https://github.com/me4bruno/blog-examples.git
cd blog-examples/example-bulksms
mvn clean 
mvn archetype:create-from-project
tree -a
cd target/generated-sources/archetype/
mvn install
cd ../../../../../
mvn archetype:generate -DarchetypeCatalog=local
cd [Projektname]
mvn clean install
mvn exec:java -Dexec.mainClass="[Package].ReceiveSmsService"
weizenbaum:tmp bruno$ git clone https://github.com/me4bruno/blog-examples.git
Cloning into 'blog-examples'...
remote: Reusing existing pack: 615, done.
remote: Total 615 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (615/615), 10.95 MiB | 691 KiB/s, done.
Resolving deltas: 100% (58/58), done.
weizenbaum:tmp bruno$ cd blog-examples/example-bulksms
weizenbaum:example-bulksms bruno$ mvn clean 
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building bulksms 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.4.1:clean (default-clean) @ bulksms ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.480s
[INFO] Finished at: Sun Jun 29 23:48:49 CEST 2014
[INFO] Final Memory: 3M/81M
[INFO] ------------------------------------------------------------------------
weizenbaum:example-bulksms bruno$ mvn archetype:create-from-project
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building bulksms 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] >>> maven-archetype-plugin:2.2:create-from-project (default-cli) @ bulksms >>>
[INFO] 
[INFO] <<< maven-archetype-plugin:2.2:create-from-project (default-cli) @ bulksms <<<
[INFO] 
[INFO] --- maven-archetype-plugin:2.2:create-from-project (default-cli) @ bulksms ---
[INFO] Setting default groupId: de.bruns.example
[INFO] Setting default artifactId: bulksms
[INFO] Setting default version: 1.0.0-SNAPSHOT
[INFO] Setting default package: de.bruns.example.bulksms
Scanned 4 filtered files in 4 files: src/main/java/de/bruns/example/bulksms/Constants.java, src/main/java/de/bruns/example/bulksms/ReceiveSmsService.java, src/main/java/de/bruns/example/bulksms/SendSmsLibService.java, src/main/java/de/bruns/example/bulksms/SendSmsManualService.java
Scanned 1 filtered files in 1 files: src/main/resources/log4j.properties
Scanned 1 filtered files in 9 files: libs/org/smslib/smslib/maven-metadata.xml
Scanned 0 filtered files in 2 files: 
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building bulksms-archetype 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.5:resources (default-resources) @ bulksms-archetype ---
[debug] execute contextualize
[WARNING] Using platform encoding (MacRoman actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 18 resources
[INFO] 
[INFO] --- maven-resources-plugin:2.5:testResources (default-testResources) @ bulksms-archetype ---
[debug] execute contextualize
[WARNING] Using platform encoding (MacRoman actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 2 resources
[INFO] 
[INFO] --- maven-archetype-plugin:2.2:jar (default-jar) @ bulksms-archetype ---
[INFO] Building archetype jar: /private/tmp/blog-examples/example-bulksms/target/generated-sources/archetype/target/bulksms-archetype-1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.418s
[INFO] Finished at: Sun Jun 29 23:49:03 CEST 2014
[INFO] Final Memory: 7M/81M
[INFO] ------------------------------------------------------------------------
[INFO] Archetype created in /private/tmp/blog-examples/example-bulksms/target/generated-sources/archetype
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.339s
[INFO] Finished at: Sun Jun 29 23:49:03 CEST 2014
[INFO] Final Memory: 8M/81M
[INFO] ------------------------------------------------------------------------
weizenbaum:example-bulksms bruno$ tree -a
.
├── README.md
├── bulksms-ReceiveSms.launch
├── libs
│   └── org
│       └── smslib
│           └── smslib
│               ├── 3.5.2
│               │   ├── smslib-3.5.2.jar
│               │   ├── smslib-3.5.2.jar.md5
│               │   ├── smslib-3.5.2.jar.sha1
│               │   ├── smslib-3.5.2.pom
│               │   ├── smslib-3.5.2.pom.md5
│               │   └── smslib-3.5.2.pom.sha1
│               ├── maven-metadata.xml
│               ├── maven-metadata.xml.md5
│               └── maven-metadata.xml.sha1
├── pom.xml
├── src
│   └── main
│       ├── java
│       │   └── de
│       │       └── bruns
│       │           └── example
│       │               └── bulksms
│       │                   ├── Constants.java
│       │                   ├── ReceiveSmsService.java
│       │                   ├── SendSmsLibService.java
│       │                   └── SendSmsManualService.java
│       └── resources
│           └── log4j.properties
└── target
    └── generated-sources
        └── archetype
            ├── pom.xml
            ├── src
            │   ├── main
            │   │   └── resources
            │   │       ├── META-INF
            │   │       │   └── maven
            │   │       │       └── archetype-metadata.xml
            │   │       └── archetype-resources
            │   │           ├── README.md
            │   │           ├── bulksms-ReceiveSms.launch
            │   │           ├── libs
            │   │           │   └── org
            │   │           │       └── smslib
            │   │           │           └── smslib
            │   │           │               ├── 3.5.2
            │   │           │               │   ├── smslib-3.5.2.jar
            │   │           │               │   ├── smslib-3.5.2.jar.md5
            │   │           │               │   ├── smslib-3.5.2.jar.sha1
            │   │           │               │   ├── smslib-3.5.2.pom
            │   │           │               │   ├── smslib-3.5.2.pom.md5
            │   │           │               │   └── smslib-3.5.2.pom.sha1
            │   │           │               ├── maven-metadata.xml
            │   │           │               ├── maven-metadata.xml.md5
            │   │           │               └── maven-metadata.xml.sha1
            │   │           ├── pom.xml
            │   │           └── src
            │   │               └── main
            │   │                   ├── java
            │   │                   │   ├── Constants.java
            │   │                   │   ├── ReceiveSmsService.java
            │   │                   │   ├── SendSmsLibService.java
            │   │                   │   └── SendSmsManualService.java
            │   │                   └── resources
            │   │                       └── log4j.properties
            │   └── test
            │       └── resources
            │           └── projects
            │               └── basic
            │                   ├── archetype.properties
            │                   └── goal.txt
            └── target
                ├── bulksms-archetype-1.0.0-SNAPSHOT.jar
                ├── classes
                │   ├── META-INF
                │   │   └── maven
                │   │       └── archetype-metadata.xml
                │   └── archetype-resources
                │       ├── README.md
                │       ├── bulksms-ReceiveSms.launch
                │       ├── libs
                │       │   └── org
                │       │       └── smslib
                │       │           └── smslib
                │       │               ├── 3.5.2
                │       │               │   ├── smslib-3.5.2.jar
                │       │               │   ├── smslib-3.5.2.jar.md5
                │       │               │   ├── smslib-3.5.2.jar.sha1
                │       │               │   ├── smslib-3.5.2.pom
                │       │               │   ├── smslib-3.5.2.pom.md5
                │       │               │   └── smslib-3.5.2.pom.sha1
                │       │               ├── maven-metadata.xml
                │       │               ├── maven-metadata.xml.md5
                │       │               └── maven-metadata.xml.sha1
                │       ├── pom.xml
                │       └── src
                │           └── main
                │               ├── java
                │               │   ├── Constants.java
                │               │   ├── ReceiveSmsService.java
                │               │   ├── SendSmsLibService.java
                │               │   └── SendSmsManualService.java
                │               └── resources
                │                   └── log4j.properties
                └── test-classes
                    └── projects
                        └── basic
                            ├── archetype.properties
                            └── goal.txt

52 directories, 59 files
weizenbaum:example-bulksms bruno$ cd target/generated-sources/archetype/
weizenbaum:archetype bruno$ mvn install
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building bulksms-archetype 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.5:resources (default-resources) @ bulksms-archetype ---
[debug] execute contextualize
[WARNING] Using platform encoding (MacRoman actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 18 resources
[INFO] 
[INFO] --- maven-resources-plugin:2.5:testResources (default-testResources) @ bulksms-archetype ---
[debug] execute contextualize
[WARNING] Using platform encoding (MacRoman actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 2 resources
[INFO] 
[INFO] --- maven-archetype-plugin:2.2:jar (default-jar) @ bulksms-archetype ---
[INFO] Building archetype jar: /private/tmp/blog-examples/example-bulksms/target/generated-sources/archetype/target/bulksms-archetype-1.0.0-SNAPSHOT
[INFO] 
[INFO] --- maven-archetype-plugin:2.2:integration-test (default-integration-test) @ bulksms-archetype ---
[INFO] Processing Archetype IT project: basic
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: bulksms-archetype:1.0.0-SNAPSHOT
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: archetype.it
[INFO] Parameter: artifactId, Value: basic
[INFO] Parameter: version, Value: 0.1-SNAPSHOT
[INFO] Parameter: package, Value: it.pkg
[INFO] Parameter: packageInPathFormat, Value: it/pkg
[INFO] Parameter: version, Value: 0.1-SNAPSHOT
[INFO] Parameter: package, Value: it.pkg
[INFO] Parameter: groupId, Value: archetype.it
[INFO] Parameter: artifactId, Value: basic
[INFO] project created from Archetype in dir: /private/tmp/blog-examples/example-bulksms/target/generated-sources/archetype/target/test-classes/projects/basic/project/basic
[INFO] No post-archetype-generation goals to invoke.
[INFO] 
[INFO] --- maven-install-plugin:2.3.1:install (default-install) @ bulksms-archetype ---
[INFO] Installing /private/tmp/blog-examples/example-bulksms/target/generated-sources/archetype/target/bulksms-archetype-1.0.0-SNAPSHOT.jar to /Users/bruno/.m2/repository/de/bruns/example/bulksms-archetype/1.0.0-SNAPSHOT/bulksms-archetype-1.0.0-SNAPSHOT.jar
[INFO] Installing /private/tmp/blog-examples/example-bulksms/target/generated-sources/archetype/pom.xml to /Users/bruno/.m2/repository/de/bruns/example/bulksms-archetype/1.0.0-SNAPSHOT/bulksms-archetype-1.0.0-SNAPSHOT.pom
[INFO] 
[INFO] --- maven-archetype-plugin:2.2:update-local-catalog (default-update-local-catalog) @ bulksms-archetype ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.756s
[INFO] Finished at: Sun Jun 29 23:49:31 CEST 2014
[INFO] Final Memory: 7M/81M
[INFO] ------------------------------------------------------------------------
weizenbaum:archetype bruno$ pwd
/tmp/blog-examples/example-bulksms/target/generated-sources/archetype
weizenbaum:archetype bruno$ cd ../../../../../
weizenbaum:tmp bruno$ mvn archetype:generate -DarchetypeCatalog=local
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] >>> maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom >>>
[INFO] 
[INFO] <<< maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom <<<
[INFO] 
[INFO] --- maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
Choose archetype:
1: local -> de.bruns.example:bulksms-archetype (bulksms-archetype)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 1
...
...
...

Sollen Dateinamen oder Dateiinhalte aufgrund der angegebenen Maven-Archetype-Parameter (z.B. ArtifactId) angepasst werden, dann kann man das entsprechend konfigurieren. Beispielsweise soll die Eclipse-Launcher-Datei „bulksms-ReceiveSms.launch“ im Hauptverzeichnis des Projekts auch im generierten Projekt funktionieren. Die Launcher-Datei ist mit dem ursprünglichen Zustand im neuen Projekt nicht funktionsfähig.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.m2e.Maven2LaunchConfigurationType">
<booleanAttribute key="M2_DEBUG_OUTPUT" value="false"/>
<stringAttribute key="M2_GOALS" value="exec:java -Dexec.mainClass=&quot;de.bruns.example.bulksms.ReceiveSmsService&quot;"/>
<booleanAttribute key="M2_NON_RECURSIVE" value="false"/>
<booleanAttribute key="M2_OFFLINE" value="false"/>
<stringAttribute key="M2_PROFILES" value=""/>
<listAttribute key="M2_PROPERTIES"/>
<stringAttribute key="M2_RUNTIME" value="EMBEDDED"/>
<booleanAttribute key="M2_SKIP_TESTS" value="false"/>
<booleanAttribute key="M2_UPDATE_SNAPSHOTS" value="false"/>
<booleanAttribute key="M2_WORKSPACE_RESOLUTION" value="false"/>
<stringAttribute key="bad_container_name" value="/ReseiveSms"/>
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="/tmp/eclipse/blog-examples/example-bulksms"/>
</launchConfiguration>

Folgende Punkte müssen angepasst werden:

  1. ArtefactId im Dateinamen bulksms-ReceiveSms.launch ersetzen:
    Im Dateinamen den Platzhalter „__rootArtifactId__“ für „bulksms“ angeben:

    mv target/generated-sources/archetype/src/main/resources/archetype-resources/bulksms-ReceiveSms.launch target/generated-sources/archetype/src/main/resources/archetype-resources/__rootArtifactId__-ReceiveSms.launch
    
  2. Launcher-Datei für Auswertungen in der Konfigurationsdatei "archetype-metadata.xml" mit Filter aufnehmen:
    Eintrag ergänzen in Datei: „target/generated-sources/archetype/src/main/resources/META-INF/maven/archetype-metadata.xml

       <fileSet filtered="true" encoding="UTF-8">
          <directory></directory>
          <includes>
            <include>*.launch</include>
          </includes>
        </fileSet>
    
  3. Launcher-Datei enthält unbrauchbaren absoluten Verzeichnispfad:
    absoluten Pfad „WORKING_DIRECTORY“ ersetzen: value="${project_loc}"
  4. Launcher-Datei enthält einen anzupassenden Paketnamen
    Maven-Platzhalter für den Paketnamen in „M2_GOALS“ nutzen: value="exec:java -Dexec.mainClass="${groupId}.ReceiveSmsService""

Damit würde die Launcher-Datei „__rootArtifactId__-ReceiveSms.launch“ dann so aussehen und auch funktionieren:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.m2e.Maven2LaunchConfigurationType">
  <booleanAttribute key="M2_DEBUG_OUTPUT" value="false"/>
  <stringAttribute key="M2_GOALS" value="exec:java -Dexec.mainClass=&quot;${groupId}.ReceiveSmsService&quot;"/>
  <booleanAttribute key="M2_NON_RECURSIVE" value="false"/>
  <booleanAttribute key="M2_OFFLINE" value="false"/>
  <stringAttribute key="M2_PROFILES" value=""/>
  <listAttribute key="M2_PROPERTIES"/>
  <stringAttribute key="M2_RUNTIME" value="EMBEDDED"/>
  <booleanAttribute key="M2_SKIP_TESTS" value="false"/>
  <booleanAttribute key="M2_UPDATE_SNAPSHOTS" value="false"/>
  <booleanAttribute key="M2_WORKSPACE_RESOLUTION" value="false"/>
  <stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc}"/>
</launchConfiguration>

Wenn alles funktioniert, dann können wir den Archetypen mit dem gewohnten Maven-Befehl „mvn deploy“ veröffentlichen. Dann bleibt noch zu überlegen, wie wir unser Projekt in eine Quellcode-Verwaltung einchecken. Wir sollten die fertige Archetype-Version sichern, damit wir stets den Quellcode zum aktuell verwendeten Archetype vorliegen haben. Bei größeren Vorlagen (z.B. Multi-Module-Projekten im JEE-Umfeld) ist es auch sinnvoll, das gesamte Vorlagen-Projekt einzuchecken, aus dem anschließend der Maven-Archetype generiert wird. Damit kann gewährleistet werden, dass das Vorlagen-Projekt funktioniert und deploybar ist.

Bei einem generierten Projekt aus dem eigenen Maven-Archetypen kann es vorkommen, dass Maven im Quellcode Maven-Platzhalter entdeckt hat, die eigentlich nicht hätten ersetzt werden sollen. Als Lösung für das Problem kann man Quellcode auslagern (z.B. URLs in Property-Dateien) und in der Datei archetype-metadata.xml das Ersetzen von Texten (Filtern) für die jeweilige Datei „fileSet filtered="false"“ deaktivieren.

Abschließend sollte der erstelle Archetype in die Datei ‚archetype-catalog.xml‚ aufgenommen werden, damit man zur Erzeugung eines Maven-Projekts mit dem Archetypen nicht mehr den Parameter ‚archetypeCatalog=local‚ angeben muss. Viel Spaß mit den eigenen Maven-Archetypen 🙂

Kommentare sind geschlossen.