Maven Archetypes
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against Apache Maven 3.10.0 with the Maven Archetype Plugin 3.4.1 (the version Maven 3.10.0 resolves for the archetype prefix when
nothing pins it). Every example was run on JDK 26 on 2026-10-16 with a scratch local repository, except where the text says JDK 21; the commands that could
differ between releases were repeated on Maven 3.9.16 and on Maven 4.0.0-rc-7 (a release candidate, not a final release), and the differences are in
What differs between Maven versions. Paths in the console output were shortened to /work/... and /work/m2 is the scratch local repository;
long Maven output was filtered to the lines of the plugin involved, and a line made of ... marks where lines were removed. Anything that was not run is
labelled "not run here".
An archetype is a project template. It is packaged as a JAR that holds a description of the project (the archetype descriptor) and a set of files with placeholders (the prototype); the Archetype Plugin combines them with a few values that you give (group id, artifact id, version, package, and anything the template author asked for) and writes a ready-to-build project. The first project of Getting Started was made that way. This page explains how generating works, where the list of archetypes comes from, and how to write, test and publish one of your own. The Ant and Gradle counterparts are in The same idea in Ant and Gradle.
What an archetype is made of
An archetype is an ordinary Maven artifact whose packaging is maven-archetype. Inside its JAR there are exactly these parts:
| Path in the JAR | Role |
|---|---|
|
The archetype descriptor: lists which files make up the project and how each group of files is processed (copied as is, filtered, moved under the package directory), and which properties the user must supply. |
|
The prototype: the files and directories of the project to generate. The |
|
Optional script that runs after the project was written (see The post-generation script). |
|
Added by the build, like in every JAR: the archetype’s own POM. |
The archetype’s own pom.xml (at the root of the archetype project) is small, because nothing is compiled. It declares the packaging maven-archetype
and the archetype-packaging extension, which supplies that packaging:
<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>io.tidepool</groupId>
<artifactId>tidepool-lib-archetype</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>maven-archetype</packaging>
<name>tidepool-lib-archetype</name>
<build>
<extensions>
<extension>
<groupId>org.apache.maven.archetype</groupId>
<artifactId>archetype-packaging</artifactId>
<version>3.4.1</version>
</extension>
</extensions>
</build>
</project>
This is the POM that create-from-project wrote for the tidepool archetype built below. The extension is what gives the packaging its lifecycle; see
Extensions for how build extensions work and
Build Lifecycle for the phases it binds.
Source: Maven Archetype Packaging: Usage
Generating a project
In batch mode
The goal that uses an archetype is archetype:generate, with the prefix archetype mapped to org.apache.maven.plugins:maven-archetype-plugin. Batch
mode (-B, or -DinteractiveMode=false) takes everything from the command line:
mvn -B -ntp archetype:generate \
-DarchetypeGroupId=org.apache.maven.archetypes \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DarchetypeVersion=1.5 \
-DgroupId=io.tidepool -DartifactId=tidepool-lib \
-Dversion=1.0.0-SNAPSHOT -Dpackage=io.tidepool.lib
[INFO] --- archetype:3.4.1:generate (default-cli) @ standalone-pom ---
[INFO] Loaded 74 auto-discovered prefixes for remote repository apache.snapshots (prefixes-apache.snapshots.txt)
[INFO] Generating project in Batch mode
[WARNING] Remote repository central serves path archetype-catalog.xml that its auto-discovered prefixes file denies; the prefixes file appears stale. ...
[INFO] Archetype repository not defined. Using the one from [org.apache.maven.archetypes:maven-archetype-quickstart:1.5] found in catalog remote
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: maven-archetype-quickstart:1.5
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: io.tidepool
[INFO] Parameter: artifactId, Value: tidepool-lib
[INFO] Parameter: version, Value: 1.0.0-SNAPSHOT
[INFO] Parameter: package, Value: io.tidepool.lib
[INFO] Parameter: packageInPathFormat, Value: io/tidepool/lib
[INFO] Parameter: junitVersion, Value: 5.11.0
...
[INFO] Project created from Archetype in dir: /work/gen/tidepool-lib
[INFO] BUILD SUCCESS
The generated tree (the same quickstart project as in Getting Started, with .mvn/jvm.config and .mvn/maven.config files next to the POM):
tidepool-lib
|-- .mvn/jvm.config
|-- .mvn/maven.config
|-- pom.xml
`-- src
|-- main/java/io/tidepool/lib/App.java
`-- test/java/io/tidepool/lib/AppTest.java
The parameters of the goal (from mvn archetype:help -Ddetail -Dgoal=generate on 3.4.1):
| Property | Default | Meaning |
|---|---|---|
|
none |
The coordinates of the archetype. When all are omitted the goal asks (interactive) or uses |
|
|
Comma-separated catalogs to search: |
|
none |
Narrows the list shown in interactive mode (see Filtering the list). |
|
|
The coordinates of the new project. |
|
current directory |
Where the project directory is created. |
|
none |
Maven goals to run on the new project right after it was generated (see Running goals after generating). |
|
|
Whether to ask. Any other property that the archetype declares (such as |
|
|
Documented as asking also for properties that have defaults, in interactive mode. In a run with |
The archetype itself accepts properties too: maven-archetype-quickstart:1.5 declares javaCompilerVersion (default 17) and junitVersion (default
5.11.0), and -DjavaCompilerVersion=21 -DjunitVersion=5.12.2 produced a POM with maven.compiler.release set to 21 and the JUnit BOM at 5.12.2.
Two behaviours of batch mode surprise people:
-
The directory must not exist. A second run for the same
artifactIdfails withA Maven project already exists in the directory /work/use/tidepool-boston, and nothing is overwritten. -
Leaving out the archetype version is risky. With only
-DarchetypeArtifactId=maven-archetype-quickstart, batch mode silently used the old 1.0 archetype, whose POM has nomaven.compiler.releaseand depends on JUnit 3.8.1. The goal’s output said so only in one word:Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:1.0. Always passarchetypeVersion.
In interactive mode
Without -B and without archetype coordinates the goal lists the catalog and asks. The real list has thousands of entries (the remote catalog held 3767 on
2026-10-16), so you type a filter instead of a number:
mvn archetype:generate -Dfilter=org.apache.maven.archetypes: -DgroupId=io.tidepool -DartifactId=tp-inter -Dversion=1.0.0-SNAPSHOT
(Run here with Enter pressed at every prompt, to accept the defaults; the prompts and the answers therefore share a line below.)
[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: remote -> org.apache.maven.archetypes:maven-archetype-archetype (An archetype which contains a sample archetype.)
2: remote -> org.apache.maven.archetypes:maven-archetype-j2ee-simple (An archetype which contains a simplified sample J2EE application.)
3: remote -> org.apache.maven.archetypes:maven-archetype-marmalade-mojo (-)
4: remote -> org.apache.maven.archetypes:maven-archetype-mojo (An archetype which contains a sample a sample Maven plugin.)
5: remote -> org.apache.maven.archetypes:maven-archetype-plugin (An archetype which contains a sample Maven plugin.)
...
9: remote -> org.apache.maven.archetypes:maven-archetype-quickstart (An archetype which contains a sample Maven project.)
10: remote -> org.apache.maven.archetypes:maven-archetype-simple (An archetype which contains a simple Maven project.)
...
14: remote -> org.apache.maven.archetypes:maven-archetype-webapp (An archetype which contains a sample Maven Webapp project.)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 9: Choose org.apache.maven.archetypes:maven-archetype-quickstart version:
1: 1.0-alpha-1
...
5: 1.0
6: 1.1
7: 1.3
8: 1.4
9: 1.5
Choose a number: 9: [INFO] Using property: javaCompilerVersion = 17
[INFO] Using property: junitVersion = 5.11.0
[INFO] Using property: groupId = io.tidepool
[INFO] Using property: artifactId = tp-inter
[INFO] Using property: version = 1.0.0-SNAPSHOT
[INFO] Using property: package = io.tidepool
Confirm properties configuration:
javaCompilerVersion: 17
junitVersion: 5.11.0
groupId: io.tidepool
artifactId: tp-inter
version: 1.0.0-SNAPSHOT
package: io.tidepool
Y: [INFO] ----------------------------------------------------------------------------
...
[INFO] Project created from Archetype in dir: /work/inter/tp-inter
[INFO] BUILD SUCCESS
What the transcript shows:
-
The text after each colon (
9:,Y:) is the default, taken when you press Enter. The default archetype ismaven-archetype-quickstart, and the default version is the newest one (1.5). The first message, "No archetype defined. Using maven-archetype-quickstart … :1.0", names version 1.0, but the version prompt that follows defaults to 1.5. -
Only properties without a default are asked.
groupIdandartifactIdcame from the command line,versiontoo, and the package defaulted to the group id. In a real terminal, a property with no default (or whose value you did not pass) prompts asDefine value for property 'version' 1.0-SNAPSHOT:. -
The Confirm properties configuration step is the last chance to answer
Nand re-enter everything.
Filtering the list
The filter is a case-sensitive contains match, either -Dfilter= on the command line or typed at the Choose a number or apply filter prompt:
| Filter | Shows |
|---|---|
|
every archetype whose group id contains |
|
every archetype whose artifact id contains |
|
group id contains |
A trap, found while testing: the prompt’s default is the quickstart archetype. If your filter hides it (for example -Dfilter=org.apache.maven.archetypes:site-simple
leaves one entry), pressing Enter does not choose the single remaining entry; the goal prints the list again and asks again, and with input from a closed
pipe it loops without end, printing the same list. Type the number (1), or use a filter that keeps maven-archetype-quickstart in the list.
The archetypes Maven provides
The guide lists ten archetypes under the group id org.apache.maven.archetypes. The catalog of 2026-10-16 listed fourteen with that group id. The ones
that matter:
| Artifact id | Generates |
|---|---|
|
A jar project with |
|
A simple Maven project (the catalog’s description); same group id. |
|
A sample archetype project: the quickest way to start writing your own (see Starting an archetype from a template). |
|
A |
|
A sample Maven plugin project (see Writing Plugins). |
|
Site sources, meant to be layered on an existing project (see Site & Reporting). |
|
Old Java EE and portlet samples; not maintained for current platforms. |
|
In the catalog (without a description) but missing from the guide’s table. |
The archetypes of other projects (frameworks, vendors) live in the same catalog under their own group ids, which is why a filter is needed.
Catalogs
An archetype is found through an archetype catalog: an archetype-catalog.xml that lists group id, artifact id, version and description of archetypes.
-DarchetypeCatalog takes a comma-separated list of these schemes (default remote,local):
| Scheme | Meaning |
|---|---|
|
The |
|
The |
|
A small catalog built into the plugin: ten entries, all of the |
The default merges remote and local, which is why an archetype you installed yourself is found without extra options: running the batch command with the
coordinates of the tidepool archetype (built below) but without -DarchetypeCatalog generated a project, with the Maven Central catalog consulted as
well. Internally the catalog is the fallback: if the remote catalog is empty or cannot be read, the goal says so and uses internal.
A request for an archetype that no consulted catalog lists does not stop at once. -DarchetypeCatalog=local and a wrong artifact id gives
The desired archetype does not exist (io.tidepool:nope:1.0); with the default catalogs the goal first prints
Archetype not found in any catalog. Falling back to central repository. and then tries to resolve the coordinates from Maven Central. Note that the
missing version was filled in as 1.0.
Serving a catalog from your own repository
A company that distributes archetypes needs a repository where archetype-catalog.xml lives next to the artifacts. The goal looks for a repository with the
id archetype. The recommended way (from the plugin documentation) is to put that repository in a profile that is not active by default, so that normal
builds do not download from it, and to activate the profile only when generating:
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0">
<profiles>
<profile>
<id>tidepool-archetypes</id>
<repositories>
<repository>
<id>archetype</id>
<url>http://localhost:19473/</url>
</repository>
</repositories>
</profile>
</profiles>
</settings>
If the repository needs credentials, add a <server> with the same id archetype (see
Settings.xml & Password Encryption). To test it without a repository
manager, a read-only static file server on localhost is enough: here the local repository of the build that installed the archetype was served with
python3 -m http.server 19473 --bind 127.0.0.1 (a repository manager such as Nexus, Artifactory or Reposilite would be the production choice; none was
installed, so that case is not run here):
mvn -B -ntp -s settings-archetype.xml -Ptidepool-archetypes archetype:generate \
-DarchetypeCatalog=remote -Dfilter=io.tidepool: \
-DarchetypeGroupId=io.tidepool -DarchetypeArtifactId=tidepool-lib-archetype -DarchetypeVersion=1.0.0-SNAPSHOT \
-DgroupId=io.tidepool -DartifactId=tidepool-remote -Dpackage=io.tidepool.remote
[INFO] Generating project in Batch mode
[WARNING] Could not validate integrity of download from http://localhost:19473/archetype-catalog.xml: no checksums available
[INFO] Archetype repository not defined. Using the one from [io.tidepool:tidepool-lib-archetype:1.0.0-SNAPSHOT] found in catalog remote
[WARNING] Could not validate integrity of download from http://localhost:19473/io/tidepool/tidepool-lib-archetype/1.0.0-SNAPSHOT/tidepool-lib-archetype-1.0.0-SNAPSHOT.jar: no checksums available
...
[INFO] Project created from Archetype in dir: /work/remote/tidepool-remote
[INFO] BUILD SUCCESS
The server log showed the requests: archetype-catalog.xml (and its checksums), then the metadata and the JAR of the archetype. The checksum warnings are
because the static server has no .sha1 files.
Creating and refreshing a catalog file
Two more goals work on catalog files:
-
archetype:update-local-catalogadds the archetype of the current project to the local catalog. It is bound to themaven-archetypelifecycle (afterinstall), so you rarely call it. After the install below the local repository’sarchetype-catalog.xmlheld:<archetype-catalog ...> <archetypes> <archetype> <groupId>io.tidepool</groupId> <artifactId>tidepool-lib-archetype</artifactId> <version>1.0.0-SNAPSHOT</version> <description>tidepool-lib-archetype</description> </archetype> </archetypes> </archetype-catalog> -
archetype:crawlwalks a repository on the file system (not HTTP) and writes a catalog of every archetype JAR it finds, for you to publish at the root of a repository:mvn archetype:crawl -Dcatalog=/work/crawl/archetype-catalog.xml -Drepository=/work/m2. It printed aScanningline for each of the more than a hundred JARs of that scratch repository, and wrote a catalog withmaven-archetype-quickstart,maven-archetype-site-simpleand the tidepool archetype (the three JARs that contain an archetype descriptor).
Running goals after generating
-Dgoals makes the plugin invoke Maven on the new project once it exists, which is useful in scripts:
mvn -B -ntp archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart \
-DarchetypeVersion=1.5 -DgroupId=io.tidepool -DartifactId=goals2 -Dgoals=verify
[INFO] Project created from Archetype in dir: /work/use/goals2
[INFO] Invoking post-archetype-generation goals: verify
[WARN] Maven will be executed in interactive mode, but no input stream has been configured for this MavenInvoker instance.
[INFO] Scanning for projects...
[INFO] Building goals2 1.0-SNAPSHOT
...
[INFO] --- surefire:3.3.0:test (default-test) @ goals2 ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
...
The nested build is a separate Maven process whose output is copied into the same console (on Maven 4 each line gets an extra [INFO] [stdout]
prefix, and in the archetype:integration-test output of 3.10.0 a [INFO] [INFO] prefix). The plugin versions used by the new project are the
ones pinned by the archetype’s prototype POM, so they differ from the Maven 3.10.0 defaults (compare
Getting Started).
Creating an archetype
From an existing project
The archetype plugin can turn a working project into an archetype. That is how the tidepool archetype was made: a small project from the quickstart archetype, with two files added to it:
tidepool-lib
|-- README.md # Built as io.tidepool:tidepool-lib.
|-- pom.xml
`-- src
|-- main
| |-- java/io/tidepool/lib/{App.java, Station.java}
| `-- resources/stations.csv
`-- test/java/io/tidepool/lib/AppTest.java
Run the goal in that project (it does not need a lifecycle; it reads the project and writes files):
mvn -B -ntp -s /work/settings.xml archetype:create-from-project
[INFO] --- archetype:3.4.1:create-from-project (default-cli) @ tidepool-lib ---
[INFO] Setting default groupId: io.tidepool
[INFO] Setting default artifactId: tidepool-lib
[INFO] Setting default version: 1.0.0-SNAPSHOT
[INFO] Setting default package: io.tidepool.lib
[WARN] Maven will be executed in interactive mode, but no input stream has been configured for this MavenInvoker instance.
[INFO] Scanning for projects...
[INFO] -----------------< io.tidepool:tidepool-lib-archetype >-----------------
[INFO] Building tidepool-lib-archetype 1.0.0-SNAPSHOT
[INFO] --------------------------[ maven-archetype ]---------------------------
[INFO] --- resources:3.3.1:resources (default-resources) @ tidepool-lib-archetype ---
[INFO] Copying 9 resources from src/main/resources to target/classes
...
[INFO] --- archetype:3.4.1:jar (default-jar) @ tidepool-lib-archetype ---
[INFO] Building archetype jar: /work/tidepool-lib/target/generated-sources/archetype/target/tidepool-lib-archetype-1.0.0-SNAPSHOT.jar
[INFO] Archetype project created in /work/tidepool-lib/target/generated-sources/archetype
[INFO] BUILD SUCCESS
The -s /work/settings.xml is not decoration. After writing the archetype project, the goal starts a nested Maven build of it (default phase package,
the parameter archetypePostPhase) and passes it the path of the user settings file, ~/.m2/settings.xml, even if that file does not exist. Without
the option the goal wrote the archetype project and then failed with:
[ERROR] Error executing Maven.
[ERROR] The specified user settings file does not exist: /home/dev/.m2/settings.xml
...
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-archetype-plugin:3.4.1:create-from-project (default-cli) on project tidepool-lib: Invoker process ended with result different than 0!
This happened on 3.9.16, 3.10.0 and 4.0.0-rc-7 alike. Either create a settings.xml (an empty <settings/> is enough, see
Settings.xml & Password Encryption) or pass -s with a file, as here.
(This page’s file was <settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"/>.) With -s, the same goal gave the same result on all three versions: an archetype
project with the same files.
What the goal wrote under target/generated-sources/archetype:
pom.xml
src/main/resources/META-INF/maven/archetype-metadata.xml
src/main/resources/archetype-resources/.mvn/jvm.config
src/main/resources/archetype-resources/.mvn/maven.config
src/main/resources/archetype-resources/README.md
src/main/resources/archetype-resources/pom.xml
src/main/resources/archetype-resources/src/main/java/App.java
src/main/resources/archetype-resources/src/main/java/Station.java
src/main/resources/archetype-resources/src/main/resources/stations.csv
src/main/resources/archetype-resources/src/test/java/AppTest.java
src/test/resources/projects/basic/archetype.properties
src/test/resources/projects/basic/goal.txt
and what it did to the files:
-
It read the coordinates of the project and replaced them with properties: in the prototype POM
<groupId>io.tidepool</groupId>became<groupId>${groupId}</groupId>, likewiseartifactIdandversion, and<name>tidepool-lib</name>became<name>${artifactId}</name>. -
It detected the base package of the sources (
io.tidepool.lib), removed it from the directory names (src/main/java/App.java) and replacedpackage io.tidepool.lib;withpackage ${package};in every source. In the project generated later, the files go under whatever-Dpackagesays. -
It put a Velocity header on each filtered file that defines
$symbol_pound,$symbol_dollarand$symbol_escape, so that a literal#or$in your file can be written as${symbol_pound},${symbol_dollar}: Velocity treats those characters as syntax. -
It wrote a first integration test (
src/test/resources/projects/basic), see Testing the archetype.
The goal does not know which of your files are templates. It filtered .java and the POM, but it copied stations.csv and README.md as they were,
so the README of every generated project would still say tidepool-lib. Turning a copy into a template is what the next part is for.
Other parameters of create-from-project (from archetype:help -Ddetail -Dgoal=create-from-project):
| Property | Default | Meaning |
|---|---|---|
|
|
A properties file in the project (the parameter |
|
guessed |
The package to relocate, if the guess is wrong. |
|
a list of known text extensions |
Extensions treated as text; a file with another extension is copied as binary. |
|
off |
Keep the original parent in POMs, keep CDATA sections, produce a partial archetype (one that adds files to an existing project). |
|
|
Phase of the nested build: |
|
|
Ask for the values instead of guessing. |
|
|
Where the archetype project is written. |
Note the spelling of two of them: archetype.filteredExtentions as a user property of the goal, but archetype.filteredExtensions inside the properties file.
For a multi-module project the plugin documentation has a dedicated page (the goal is run in the root; each module becomes a <module> in the descriptor).
That case is not run here.
Starting an archetype from a template
The alternative is to start with a skeleton and fill it in, which the guide recommends as a quicker path. maven-archetype-archetype generates an archetype
project:
mvn -B -ntp archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-archetype \
-DarchetypeVersion=1.5 -DgroupId=io.tidepool -DartifactId=my-archetype
my-archetype/.mvn/jvm.config
my-archetype/.mvn/maven.config
my-archetype/pom.xml
my-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
my-archetype/src/main/resources/archetype-resources/pom.xml
my-archetype/src/main/resources/archetype-resources/src/main/java/App.java
my-archetype/src/main/resources/archetype-resources/src/test/java/AppTest.java
my-archetype/src/test/resources/projects/it-basic/archetype.properties
my-archetype/src/test/resources/projects/it-basic/goal.txt
Its descriptor uses the old 1.0.0 descriptor schema and name="${artifactId}"; the guide’s own example uses 1.2.0 and a literal name that equals the
archetype’s artifactId. Either schema works; validationRegex and includeCondition below need the newer ones.
The archetype descriptor
The descriptor is src/main/resources/META-INF/maven/archetype-metadata.xml. Its elements, from the reference page:
| Element or attribute | Meaning |
|---|---|
|
The name shown to the user while choosing. |
|
|
|
A property the user must supply. Children: |
|
A group of files: |
|
A sub-module of a multi-module archetype: attributes |
Source: Archetype Descriptor reference
The generated descriptor listed every file group but had no required properties and did not filter the README or the CSV. After editing, the descriptor of the tidepool archetype is:
<archetype-descriptor name="tidepool-lib"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.2.0 https://maven.apache.org/xsd/archetype-descriptor-1.2.0.xsd"
xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<requiredProperties>
<requiredProperty key="defaultStation">
<defaultValue>SF</defaultValue>
<validationRegex>[A-Z]{2,4}</validationRegex>
</requiredProperty>
</requiredProperties>
<fileSets>
<fileSet filtered="true" packaged="true" encoding="UTF-8">
<directory>src/main/java</directory>
<includes>
<include>**/*.java</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
<directory>src/main/resources</directory>
<includes>
<include>**/*.csv</include>
</includes>
</fileSet>
<fileSet filtered="true" packaged="true" encoding="UTF-8">
<directory>src/test/java</directory>
<includes>
<include>**/*.java</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
<directory>docs</directory>
<includes>
<include>**/*.txt</include>
</includes>
</fileSet>
<fileSet encoding="UTF-8">
<directory>.mvn</directory>
<includes>
<include>**/*.config</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
<directory></directory>
<includes>
<include>README.md</include>
</includes>
</fileSet>
</fileSets>
</archetype-descriptor>
The changes against the generated file: the requiredProperties block, filtered="true" on the CSV and README sets, and a new docs set. The prototype
files that go with it:
# ${artifactId}
Built as ${groupId}:${artifactId}:${version}, default station ${defaultStation}.
id,name,latitude,longitude
${defaultStation},Default station,0.0,0.0
Default station: ${defaultStation}
How filtering and naming work:
-
In a
filteredset every file is a Velocity template:${name}is replaced by the property, and#set,#if,#foreachdirectives work. A$or#that must stay needs the${symbol_dollar}/${symbol_pound}variables from the header thatcreate-from-projectadded. This is Velocity, not the Resources Plugin: the@name@delimiter, filter files and the other rules of Resources & Filtering do not apply. -
A file or directory name that contains
__name__has that part replaced by the property’s value, filtered or not:docs/__defaultStation__.txtbecamedocs/BOS.txt. -
packaged="true"prepends the package directory (io/tidepool/boston) to the paths of the set;packaged="false"keeps the paths as they are. -
The descriptor cannot create an empty directory ("one can only specify individual files"); put a placeholder file in it.
-
Only the files that the
includes/excludespatterns (**/*.java) select are part of the archetype. A file that no set matches is silently left out.
What validationRegex does and does not do
Declared as [A-Z]{2,4} on defaultStation, the check was exercised on 3.10.0 in four ways, against the archetype with a default (SF) and against a copy of
it without a default (tidepool-strict-archetype):
| Situation | Result | Detail |
|---|---|---|
Batch, |
generated |
|
Batch, |
generated |
The value was not checked: |
Batch, property without default, not given |
fails |
|
Interactive, property without default |
asked and checked |
|
So the regex protects only what is typed at a prompt. A property that is passed with -D, or that has a default, is not validated on 3.10.0; do the check
again in the post-generation script if it matters. A property that has a default and is not given in batch mode silently takes the default: the first batch
run of the default-carrying archetype without -DdefaultStation produced README.md saying "default station SF" and no error.
Testing the archetype
create-from-project wrote src/test/resources/projects/basic with archetype.properties (the values for a throw-away project: groupId=archetype.it,
artifactId=basic, package=it.pkg, version=0.1-SNAPSHOT) and an empty goal.txt. They are the input of the archetype:integration-test goal, which the
maven-archetype lifecycle runs in the integration-test phase: for each directory under src/test/resources/projects it generates a project from the
archetype just built, runs the goals listed in goal.txt on it, and then runs an optional verify.groovy. A reference/ directory with an expected tree
can also be compared; that option was not run here.
With goal.txt containing verify and defaultStation=NY added to archetype.properties, the whole build of the archetype project is one command. It
resolves the archetype plugin and the quickstart-like plugin versions pinned in the prototype POM, builds a throw-away project, runs its tests, and installs the
archetype into the local repository (the nested build’s own lines, prefixed [INFO] [INFO], were removed here):
mvn -B -ntp -s /work/settings.xml install
[INFO] --- resources:3.3.1:resources (default-resources) @ tidepool-lib-archetype ---
[INFO] Copying 10 resources from src/main/resources to target/classes
[INFO] --- resources:3.3.1:testResources (default-testResources) @ tidepool-lib-archetype ---
[INFO] Copying 2 resources from src/test/resources to target/test-classes
[INFO] --- archetype:3.4.1:jar (default-jar) @ tidepool-lib-archetype ---
[INFO] Building archetype jar: /work/tidepool-archetype/target/tidepool-lib-archetype-1.0.0-SNAPSHOT.jar
[INFO] --- archetype:3.4.1:integration-test (default-integration-test) @ tidepool-lib-archetype ---
[INFO] Processing Archetype IT project: basic
[INFO] Using following parameters for creating project from Archetype: tidepool-lib-archetype:1.0.0-SNAPSHOT
[INFO] Project created from Archetype in dir: /work/tidepool-archetype/target/test-classes/projects/basic/project/basic
[INFO] Invoking post-archetype-generation goals: verify
[INFO] Post-archetype-generation invoker exit code: 0
[INFO] --- install:3.1.4:install (default-install) @ tidepool-lib-archetype ---
[INFO] Installing /work/tidepool-archetype/pom.xml to /work/m2/io/tidepool/tidepool-lib-archetype/1.0.0-SNAPSHOT/tidepool-lib-archetype-1.0.0-SNAPSHOT.pom
[INFO] Installing /work/tidepool-archetype/target/tidepool-lib-archetype-1.0.0-SNAPSHOT.jar to /work/m2/io/tidepool/tidepool-lib-archetype/1.0.0-SNAPSHOT/tidepool-lib-archetype-1.0.0-SNAPSHOT.jar
[INFO] --- archetype:3.4.1:update-local-catalog (default-update-local-catalog) @ tidepool-lib-archetype ---
[INFO] Updated local archetypes catalog /work/m2/archetype-catalog.xml
[INFO] BUILD SUCCESS
The phases it ran are those of the maven-archetype packaging: resources, test resources, archetype:jar, archetype:integration-test, install:install,
archetype:update-local-catalog. The IT directory projects/basic is therefore generated under target/test-classes, and the throw-away project’s
mvn verify (compile, its own AppTest, jar) is part of the check. A bad template (a typo in a ${...} that Velocity cannot resolve, a descriptor that
leaves out a file the POM needs) shows up here and not in front of the first user.
The same property the other builds depend on applies here: because the POM comes from the prototype, the generated project resolves the plugin versions that the prototype POM pins. Updating those versions regularly is part of maintaining an archetype (see Plugins & Their Configuration).
The Groovy catch on a new JDK
verify.groovy and the post-generation script are Groovy, run by the Groovy that the plugin carries (4.0.28 in the local repository of these runs). That Groovy
cannot read class files of JDK 26. With verify.groovy added to the IT:
def project = new File(basedir, "project/basic")
assert new File(project, "src/main/java/it/pkg/Station.java").isFile()
assert new File(project, "src/main/resources/stations.csv").text.contains("NY,Default station")
assert new File(project, "README.md").text.contains("archetype.it:basic:0.1-SNAPSHOT")
the build on JDK 26 failed in the integration-test phase:
[INFO] run post-build script verify.groovy
[INFO] Running post-build script: /work/tidepool-archetype/target/test-classes/projects/basic/verify.groovy
[ERROR] BUILD FAILURE
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-archetype-plugin:3.4.1:integration-test (default-integration-test) on project tidepool-lib-archetype:
[ERROR] Archetype IT 'basic' failed: post build script failure failure: BUG! exception in phase 'semantic analysis' in source unit 'Script1.groovy' Unsupported class file major version 70
The same build with JAVA_HOME set to JDK 21 ran the script, passed, and installed the archetype. Unsupported class file major version 70 is
the signature of that mismatch (major version 70 is Java 26). If your build JDK is newer than the Groovy of the plugin supports, run the archetype project’s
build on an older JDK (or keep the IT to goal.txt and do the checks in a normal JUnit test of the generated project).
Using the archetype
With the archetype installed, the local catalog contains it. This is the whole command to use it, now with the extra property defaultStation:
mvn -B -ntp archetype:generate -DarchetypeCatalog=local \
-DarchetypeGroupId=io.tidepool -DarchetypeArtifactId=tidepool-lib-archetype -DarchetypeVersion=1.0.0-SNAPSHOT \
-DgroupId=io.tidepool -DartifactId=tidepool-boston -Dpackage=io.tidepool.boston -Dversion=1.0.0-SNAPSHOT -DdefaultStation=BOS
[INFO] Generating project in Batch mode
[INFO] Archetype repository not defined. Using the one from [io.tidepool:tidepool-lib-archetype:1.0.0-SNAPSHOT] found in catalog local
[INFO] Using following parameters for creating project from Archetype: tidepool-lib-archetype:1.0.0-SNAPSHOT
[INFO] Parameter: groupId, Value: io.tidepool
[INFO] Parameter: artifactId, Value: tidepool-boston
[INFO] Parameter: version, Value: 1.0.0-SNAPSHOT
[INFO] Parameter: package, Value: io.tidepool.boston
[INFO] Parameter: packageInPathFormat, Value: io/tidepool/boston
[INFO] Parameter: defaultStation, Value: BOS
...
[INFO] Project created from Archetype in dir: /work/use/tidepool-boston
[INFO] BUILD SUCCESS
tidepool-boston/.mvn/jvm.config
tidepool-boston/.mvn/maven.config
tidepool-boston/README.md
tidepool-boston/docs/BOS.txt
tidepool-boston/pom.xml
tidepool-boston/src/main/java/io/tidepool/boston/App.java
tidepool-boston/src/main/java/io/tidepool/boston/Station.java
tidepool-boston/src/main/resources/stations.csv
tidepool-boston/src/test/java/io/tidepool/boston/AppTest.java
The placeholders were replaced: README.md reads Built as io.tidepool:tidepool-boston:1.0.0-SNAPSHOT, default station BOS., stations.csv has BOS,Default
station,0.0,0.0, and Station.java starts with package io.tidepool.boston;. mvn -B verify in tidepool-boston ran one test and built
tidepool-boston-1.0.0-SNAPSHOT.jar (with an earlier build of the same archetype, on JDK 26).
The version is optional only when a catalog lists the archetype. With -DarchetypeCatalog=local and no -DarchetypeVersion, the version was taken from the local
catalog (found in catalog local). With the catalog internal (which does not list it) and no version the goal looked for io.tidepool:tidepool-lib-archetype:1.0
and failed with the "desired archetype does not exist" error shown earlier. The guide predicts a failure that mentions version:RELEASE; on 3.4.1 the
message names version 1.0 instead.
To share the archetype, publish it like any other artifact. The archetype project is an ordinary Maven project, so mvn deploy with a
distributionManagement repository (see
Install, Deploy & Distribution Management) puts the JAR and
POM on your repository, and a catalog file at its root, written by archetype:crawl, makes it listable. Publishing an archetype to Maven Central follows the same
rules as any library (see
Publishing to Maven Central); that step is not run here.
The post-generation script
META-INF/archetype-post-generate.groovy in the archetype JAR (the file src/main/resources/META-INF/archetype-post-generate.groovy in the archetype project)
runs after the project was written. It sees a variable request (the generation request) in addition to the properties. This script writes a file with the
chosen station:
def project = new File(request.getOutputDirectory(), request.getArtifactId())
new File(project, "STATION.txt").text = "Default station: " + request.getProperties().getProperty("defaultStation") + "\n"
println "post-generate: wrote STATION.txt in " + project.name
[INFO] Executing META-INF/archetype-post-generate.groovy post-generation script
post-generate: wrote STATION.txt in pg-21
[INFO] BUILD SUCCESS
(Run with -DartifactId=pg-21 on JDK 21; the project directory then held README.md STATION.txt pom.xml src.) On JDK 26 the same archetype failed after the files had
been written:
[INFO] Executing META-INF/archetype-post-generate.groovy post-generation script
[ERROR] BUILD FAILURE
Exception in thread "main" BUG! exception in phase 'semantic analysis' in source unit 'Script1.groovy' Unsupported class file major version 70
...
Caused by: java.lang.IllegalArgumentException: Unsupported class file major version 70
and the half-generated project (README.md pom.xml src, without the file the script would have added) was left on disk, so a retry fails with
"A Maven project already exists" until you delete it. That is the practical argument for keeping post-generation scripts rare: they run on the JDK of
whoever generates a project, not on the JDK you tested with.
What differs between Maven versions
The commands of this page were run on three Maven versions with the Archetype Plugin 3.4.1:
| Behavior | 3.9.16 | 3.10.0 | 4.0.0-rc-7 |
|---|---|---|---|
|
same project |
same project |
same project; the model of the generated POM stays |
warning that Maven Central serves |
not printed |
printed |
printed |
nested build output ( |
plain |
plain |
each line prefixed with |
|
fails |
fails |
fails |
|
archetype project written |
archetype project written |
archetype project written |
Nothing in the generation or creation of archetypes changed behavior between the three. The stale-prefix warning comes from the repository filtering by
"auto-discovered prefixes" (see
Maven Resolver); as the warning itself says, the build allows only the verified catalog path
and keeps the filter for everything else. An archetype whose prototype POM uses the Maven 4 model (4.1.0, <subprojects>) was not tried here; see
Maven 4 POM Model & Consumer POM.
Removed: archetype:create, and where the documentation is out of date
-
archetype:createis gone. On Maven 3.10.0 it fails withCould not find goal 'create' in plugin org.apache.maven.plugins:maven-archetype-plugin:3.4.1 among available goals crawl, create-from-project, generate, help, integration-test, jar, update-local-catalog. The goals the plugin has are exactly these seven. Books and older blog posts that usecreateneedgenerate. -
The Advanced Usage page of the plugin still shows a 2008-era console (
task-segment,BUILD SUCCESSFUL, anarchetype:add-archetype-metadatagoal that no longer runs). The goal names and the three-step flow (create-from-project, install, generate) are still right; the output is not what you see today. -
The Guide to Creating Archetypes shows a prototype POM with JUnit 4.12 (the current quickstart imports the JUnit 5 BOM) and says that a missing
-DarchetypeVersionmay fail with a message thatversion:RELEASEwas not found (3.4.1 names version1.0). -
The guide’s table of provided archetypes lists ten; the catalog has fourteen with that group id (see The archetypes Maven provides).
-
The
create-from-projectdocumentation says to put integration tests insrc/it/projects; the goal, as run here, wrote the generated one tosrc/test/resources/projects/basic, which is wherearchetype:integration-testlooks (testProjectsDirectorydefaults totarget/test-classes/projects).
The same idea in Ant and Gradle
Ant has no equivalent: a project template is a directory that you copy, or a shell script. Gradle’s counterpart is the init task, which generates a project from
built-in types, and there is no per-organization template format in core Gradle (organizations write a plugin or use a template repository). The three tools are
compared in Comparing Ant, Maven & Gradle. That Gradle sentence is background, not
something run on this page.
Where to go next
-
Getting Started for the first project from
maven-archetype-quickstart. -
POM Fundamentals for the prototype POM’s elements.
-
Install, Deploy & Distribution Management to publish an archetype.
-
Repositories, Mirrors & Repository Managers for the repository with the id
archetype. -
Maven Wrapper for the
.mvndirectory that the archetypes add.