If you are creating a cloud of microservices or are in an environment where you have to generate multiple maven projects or modules, it is very useful to create archetypes to handle these new modules/projects creation.
What is an archetype?
A Maven archetype is a templated project. Maven offers several archetypes that allow you to create new maven modules ready to run in few seconds.
How to use a generic archetype?
Archetype usage is quite simple. If you want to generate an archetype based on the default list, just run:
mvn archetype:generate
And then choose one of the options, provide the details and confirm. The new project will be already created.
How to use a custom archetype?
In this case, we need to specify the groupId and artifactId of the archetype to be able to use a custom one. As an example, let’s see the archetype provided by Adobe for AEM:
mvn archetype:generate \
-DarchetypeGroupId=com.adobe.aem \
-D archetypeArtifactId=aem-project-archetype \
-D archetypeVersion=27
If you run the command, you will be asked about some properties of your project. Some properties are standard (groupId
, version
, artifactId
and package
), but others were specific of this artifact, like appTitle
or sdkVersion
. These properties will help the archetype to generate a new project for you and in few seconds and knowing nothing about Adobe AEM you have your new project ready to run.
How does the archetype work?
Internally, an archetype has all the files (pom file, classes, readme, etc…) with variables that will be replaced during project generation. As an example, you can see these variables in several parts of the pom file below.
<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>
<parent>
<groupId>${groupId}</groupId>
<artifactId>${rootArtifactId}</artifactId>
<version>${version}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>${artifactId}</artifactId>
<name>${appTitle} - Core</name>
<description>Core bundle for ${appTitle}</description>
...
Example extracted from Github adobe/aem-project-archetype project
Maven has a good documentation how to organize and create your archetype from scratch.
Creating an archetype from a project
In most cases, it is hard to maintain an archetype because:
- It is not a project itself, so you need to use your archetype to see if the generated project is working.
- Templating is good, but it is hard to read and you need to “imagine” how the code will look like after.
- We are lazy, and if it is hard to understand, it will be hard to maintain. People will go back to copy-paste projects and replace the text.
A simple way to bypass all these constraints is to create an archetype from an example project. Our example project is a perfect compilable project with all desired configurations we want to keep together in the clones.
Create your archetype example project
Some characteristics are desired in the archetype example project:
- It should be simple, but complete. Remember to include all dependencies and one example of each element of your project. For example, if the project is a microservice, it makes sense to include a controller, a service, and a repository.
- Name it in a coherent manner (you will see it in the next steps). I would recommend, for example, having a controller called
ArchetypeExampleController
which talks toArchetypeExampleService
. - In each file, place the same prefix in the variables, so it will be easy to replace them. For example, if you need a variable for your
ArchetypeExampleService
, a good name isarchetypeVariableService
. - Include the dotfiles as part of this project and a good readme.
archetype:create-from-project
The maven goal archetype:create-from-project is the key to generate an archetype from a project.
As part of the configuration, you can pass an archetype.properties file as a parameter. This property file should like that:
# these are standard properties
package=com.almeida.tomas
groupId=archetype.it
artifactId=basic
version=1.0.0-SNAPSHOT
# here we add our personalized properties
defaultClassPrefix=ArchetypeExample
defaultVariablePrefix=archetypeVariable
As you see above, I only included the changing part of the files or variables and maven will take care of replacing them for me.
Step-by-step
Let’s go through all the steps from our archetype example project until our archetype.
1. Rename all dotfiles
It was a known issue that dotfiles are not included in the archetype. So we need to rename them, so they will be also included in the generated archetype (and posterior project).
mv .gitignore dot.gitignore
mv .file dot.file
2. Call the maven goal with the desired parameters.
mvn -U clean archetype:create-from-project \
-Dinteractive=false \
-DkeepParent=true \
-DpropertyFile=archetype.properties \
-DpackageName=com.almeida.tomas \
-Darchetype.filteredExtensions=java,xml,md
In the documentation, you can see more parameters and why and when they are used, but in summary:
-Dinteractive=false
: interactive mode is disabled.-DkeepParent=true
: keep the parent.-DpropertyFile=archetype.properties
: use our archetype.properties file to check for the variables.-DpackageName=com.almeida.tomas
: the package name for java sources that should be included in the archetype.-Darchetype.filteredExtensions=java,xml,md
: files with the selected extensions will be checked and the content and the name of the files will be changed by the variables.
3. Clean up the generated archetype metadata
In some cases, the generation includes a default value of a property and we want to be sure that all values will be filled by the user.
cd target/generated-sources/archetype/
sed -i 's/.*defaultValue.*//g' src/main/resources/META-INF/maven/archetype-metadata.xml
4. Install or deploy the archetype
If you are running these commands locally install, the archetype in your local repo:
# be sure to be in the target/generated-sources/archetype folder
mvn -B -U clean install
My recommendation is to configure a Jenkins job, so every time a change is pushed to the project, Jenkins runs the commands and pushes a new version to your repository.
# deploy the archetype to repository
mvn -B -U clean deploy
Use your created archetype
The created archetype will have the same artifactId with a -archetype
suffix. So based on our example:
mvn archetype:generate \
-DarchetypeGroupId=tomas.examples \
-DarchetypeArtifactId=archetypeProject-archetype \
-DarchetypeVersion=1.0.0-SNAPSHOT
Now, we will be asked to provide a value to the default and personalized variables:
Define value for property 'groupId': com.tomas.almeida
Define value for property 'artifactId': demo-project
Define value for property 'version' 1.0-SNAPSHOT: 1.0.0-SNAPSHOT
Define value for property 'package' com.tomas.almeida: com.tomas.almeida.domain
Define value for property 'defaultClassPrefix': Demo
Define value for property 'defaultVariablePrefix': demo
Confirm the values you have entered are correct:
Confirm properties configuration:
groupId: com.tomas.almeida
artifactId: demo-project
version: 1.0.0-SNAPSHOT
package: com.tomas.almeida.domain
defaultClassPrefix: Demo
defaultVariablePrefix: demo
Y: : Y
A new folder will be created and the name is the artifacId name. In our example, demo-project
.
cd demo-project
The dotfiles need to be renamed:
mv dot.gitignore .gitignore
mv dot.file .file
So, you can now create several clones of your archetype example project in minutes!
If you found any error or want to help me to improve this article, please comment the pull request.