Manually creating a new Maven project is cumbersome. Most companies have a set of shared libraries (e.g. security) and pre-defined configurations they need for each project. Copy and pasting these files over and over is a tedious task. With Maven we have the option to build project templating toolkits with so-called Maven Archetypes. Learn how to create your own Maven Archetype in 5 simple steps with this blog post.
As an example for this blog post, I'll demonstrate how to create a minimal Jakarta EE 8 with Java 11 Maven Archetype.
Archetype project setup
Creating a custom Maven Archetype is simple. You just a Maven project with the following setup:
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 27 28 29 | <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"> <modelVersion>4.0.0</modelVersion> <groupId>de.rieckpil.archetypes</groupId> <artifactId>custom-maven-archetype</artifactId> <version>1.0.0</version> <name>Custom Maven Archetype</name> <packaging>maven-archetype</packaging> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <archetype-packaging.version>3.1.2</archetype-packaging.version> </properties> <build> <extensions> <extension> <groupId>org.apache.maven.archetype</groupId> <artifactId>archetype-packaging</artifactId> <version>${archetype-packaging.version}</version> </extension> </extensions> </build> </project> |
Compared to normal Maven projects this is a little bit different. First, the packaging
type is maven-archetype
. While your usual service is packaged as a war
or jar
, Maven provides a dedicated type to create Archetypes.
Next, you don't need any dependency or plugin but a Maven extension: archetype-packaging
. This extension is capable of building the final Archetype later on.
Define your project template
The actual project is located at src/main/resources/archetype-resources
. Within this folder, you specify the target project template like a usual Maven project. Here you have to set up the folder structure of your project and include all files you want to use.
For our Jakarta EE 8 project, this might look like the following:
You can add any file you like and are not limited to only Java classes. To instruct Maven which files to include, you need to create a archetype-metadata.xml
file within src/main/resources/META-INF/maven
.
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 27 28 29 30 31 32 | <?xml version="1.0" encoding="UTF-8"?> <archetype-descriptor xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd" xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="custom-maven-archetype"> <fileSets> <fileSet filtered="true" packaged="true" encoding="UTF-8"> <directory>src/main/java</directory> </fileSet> <fileSet filtered="true" packaged="true" encoding="UTF-8"> <directory>src/test/java</directory> </fileSet> <fileSet filtered="true" encoding="UTF-8"> <directory>src/main/resources</directory> </fileSet> <fileSet filtered="true" encoding="UTF-8"> <directory>src/main/webapp</directory> </fileSet> <fileSet filtered="true" encoding="UTF-8"> <directory></directory> <includes> <include>.gitignore</include> <include>README.md</include> <include>Dockerfile</include> <include>server.xml</include> <include>myScript.sh</include> </includes> </fileSet> </fileSets> </archetype-descriptor> |
Each fileSet
describes a set of files or directories you want to include within your Maven Archetype. The attributes filtered
and packaged
like you see them in the code snippet above are important to make use of placeholders. For a detailed explanation of the available elements and attributes in this file, have a look at the official documentation.
Add placeholder and dynamic content to your Maven Archetype
To make the resulting project and its files more dynamic, you can add placeholders to your files. Once we use the Archetype we will specify the groupId
, artifactId
and version
of our project. These values can be injected to any file you like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <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"> <modelVersion>4.0.0</modelVersion> <groupId>${groupId}</groupId> <artifactId>${artifactId}</artifactId> <version>${version}</version> <packaging>war</packaging> <build> <finalName>${artifactId}</finalName> </build> </project> |
In this example, I'll use these variables to also prepare the Dockerfile and a bash script:
1 2 3 | FROM open-liberty:kernel-java11 COPY --chown=1001:0 target/${artifactId}.war /config/dropins/ COPY --chown=1001:0 server.xml /config |
1 2 3 | #!/bin/sh mvn clean package && docker build -t ${groupId}/${artifactId} . docker rm -f ${artifactId} || true && docker run -d -p 9080:9080 -p 9443:9443 --name ${artifactId} ${groupId}/${artifactId} |
Please note that this variable replacement only takes place for files or directory you marked as filtered
in the archetype-metadata.xml
file.
Similarly, if your Archetype contains pre-constructed Java classes, you might want to place them in the target package structure. You can achieve this by setting the packaged
attribute in archetype-metadata.xml
to true and use ${package}
placeholder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | package ${package}; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.core.Response; @Path("sample") public class SampleResource { @GET public Response message() { String message = "Hello World from ${groupId}/${artifactId}"; return Response.ok(message).build(); } } |
If placeholders and the packaging option are not enough, you can add an archetype-post-generate.groovy
script within src/main/resources/META-INF
. This file gets executed after Maven successfully creates a new project out of your Archetype. With this file, you can do any customization to the resulting project you want.
As an example, I'll use this file to mark the bash script as an executable file:
1 2 | def file = new File(request.getOutputDirectory(), request.getArtifactId() + "/myScript.sh") file.setExecutable(true, false) |
Publish your Maven Archetype
Now we are ready to publish the Maven Archetype. For a company internal Archetype, you might choose your internal artifact manager. You can also publish it to the Sonatype Nexus Repository Manager to make it available for everyone.
For a first demo, you can publish the Archetype to your local Maven repository first:
1 | mvn clean install |
This will install the Archetype in your local repository (~/.m2/repository
) and just can start using it.
Make use of your Archetype to bootstrap projects faster
Finally, we can now make use of the Archetype. Maven offers a plugin for this, which we can use with the following command:
1 2 3 4 5 6 7 | mvn archetype:generate \ -DarchetypeGroupId=de.rieckpil.archetypes \ -DarchetypeArtifactId=custom-maven-archetype \ -DarchetypeVersion=1.0.0 \ -DgroupId=de.rieckpil.blog \ -DartifactId=new-project \ -DinteractiveMode=false |
The users of your Maven Archetype just need to specify their own groupId
and artifactId
, that's all.
You can find the code for this Maven Archetype on GitHub.
The Archetypes I use on a regular basis are available here (they are all published to Sonatype).
Have fun creating your own Maven Archetype,
Phil