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

META-INF/maven/archetype-metadata.xml

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.

archetype-resources/

The prototype: the files and directories of the project to generate. The pom.xml in it is the prototype POM of the new project, not the POM of the archetype.

META-INF/archetype-post-generate.groovy

Optional script that runs after the project was written (see The post-generation script).

META-INF/maven/<groupId>/<artifactId>/pom.xml

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.

graph LR P["existing project"] -->|"archetype:create-from-project"| S["archetype source project"] S -->|"mvn install"| J["archetype JAR"] J --> C["local catalog and local repository"] C -->|"archetype:generate"| N["new project"] U["-D properties, or answers to prompts"] --> N R["remote catalog, for example Maven Central"] -->|"archetype:generate"| N

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

archetypeGroupId, archetypeArtifactId, archetypeVersion

none

The coordinates of the archetype. When all are omitted the goal asks (interactive) or uses maven-archetype-quickstart (batch).

archetypeCatalog

remote,local

Comma-separated catalogs to search: remote, local or internal (see Catalogs).

filter

none

Narrows the list shown in interactive mode (see Filtering the list).

groupId, artifactId, version, package

version defaults to 1.0-SNAPSHOT; package to the group id

The coordinates of the new project. artifactId is also the name of the directory that is created, under outputDirectory.

outputDirectory

current directory

Where the project directory is created.

goals

none

Maven goals to run on the new project right after it was generated (see Running goals after generating).

interactiveMode

${settings.interactiveMode}, so false with -B

Whether to ask. Any other property that the archetype declares (such as junitVersion above) is also given as -Dname=value.

askForDefaultPropertyValues

false

Documented as asking also for properties that have defaults, in interactive mode. In a run with -DaskForDefaultPropertyValues=true on 3.10.0 it did not change the prompts: properties with defaults were still taken silently.

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 artifactId fails with A 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 no maven.compiler.release and 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 pass archetypeVersion.

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 is maven-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. groupId and artifactId came from the command line, version too, 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 as Define value for property 'version' 1.0-SNAPSHOT:.

  • The Confirm properties configuration step is the last chance to answer N and 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

org.apache.maven.archetypes:

every archetype whose group id contains org.apache.maven.archetypes (14 entries on 2026-10-16)

:jee or jee

every archetype whose artifact id contains jee

org.apache:jee

group id contains org.apache and artifact id contains jee

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

maven-archetype-quickstart

A jar project with App and AppTest (JUnit 5 in 1.5, JUnit 3.8.1 in 1.0); the one used by almost every tutorial.

maven-archetype-simple

A simple Maven project (the catalog’s description); same group id.

maven-archetype-archetype

A sample archetype project: the quickest way to start writing your own (see Starting an archetype from a template).

maven-archetype-webapp

A war project (see Packaging JARs, WARs & Manifests).

maven-archetype-plugin, maven-archetype-mojo

A sample Maven plugin project (see Writing Plugins).

maven-archetype-site, maven-archetype-site-simple, maven-archetype-plugin-site

Site sources, meant to be layered on an existing project (see Site & Reporting).

maven-archetype-j2ee-simple, maven-archetype-portlet

Old Java EE and portlet samples; not maintained for current platforms.

maven-archetype-profiles, maven-archetype-marmalade-mojo

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

remote

The archetype-catalog.xml at the root of a repository: the repository with the id archetype if you defined one, otherwise Maven Central (id central) or the first mirror of either (see Repositories, Mirrors & Repository Managers).

local

The archetype-catalog.xml at the root of your local repository, kept up to date by mvn install of an archetype project.

internal

A small catalog built into the plugin: ten entries, all of the org.apache.maven.archetypes group (no simple, mojo, marmalade-mojo or profiles; still spelled simplifed in the J2EE description).

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-catalog adds the archetype of the current project to the local catalog. It is bound to the maven-archetype lifecycle (after install), so you rarely call it. After the install below the local repository’s archetype-catalog.xml held:

    <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:crawl walks 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 a Scanning line for each of the more than a hundred JARs of that scratch repository, and wrote a catalog with maven-archetype-quickstart, maven-archetype-site-simple and 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>, likewise artifactId and version, 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 replaced package io.tidepool.lib; with package ${package}; in every source. In the project generated later, the files go under whatever -Dpackage says.

  • It put a Velocity header on each filtered file that defines $symbol_pound, $symbol_dollar and $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

archetype.properties

archetype.properties

A properties file in the project (the parameter propertyFile) with groupId, artifactId, version, package, excludePatterns and custom properties: a line cxf-version=2.5.1-SNAPSHOT makes the goal replace that string in the files by ${cxf-version} and add it to the descriptor as a required property with that default. Property names may not contain a dot.

packageName

guessed

The package to relocate, if the guess is wrong.

archetype.filteredExtentions (sic)

a list of known text extensions

Extensions treated as text; a file with another extension is copied as binary.

archetype.keepParent, archetype.preserveCData, archetype.partialArchetype

off

Keep the original parent in POMs, keep CDATA sections, produce a partial archetype (one that adds files to an existing project).

archetype.postPhase

package

Phase of the nested build: package, integration-test, install or deploy.

interactive

false

Ask for the values instead of guessing.

outputDirectory

target/generated-sources/archetype

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

archetype-descriptor@name

The name shown to the user while choosing.

archetype-descriptor@partial

true for an archetype that adds files to an existing project instead of making a whole one. Default false.

requiredProperties/requiredProperty@key

A property the user must supply. Children: defaultValue, and (since descriptor 1.1.0) validationRegex.

fileSets/fileSet

A group of files: directory, includes, excludes, and the attributes filtered (treat the files as Velocity templates; default false), packaged (put the files under the package directory; default false), encoding, and (since 1.2.0) includeCondition: a Velocity expression that includes the file set only when it is true.

modules/module

A sub-module of a multi-module archetype: attributes id, dir, name; its own fileSets and nested modules. (includeCondition and modules were not run here.)

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:

README.md
# ${artifactId}

Built as ${groupId}:${artifactId}:${version}, default station ${defaultStation}.
src/main/resources/stations.csv
id,name,latitude,longitude
${defaultStation},Default station,0.0,0.0
The docs file (its name carries the property between double underscores)
Default station: ${defaultStation}

How filtering and naming work:

  • In a filtered set every file is a Velocity template: ${name} is replaced by the property, and #set, #if, #foreach directives work. A $ or # that must stay needs the ${symbol_dollar}/${symbol_pound} variables from the header that create-from-project added. 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__.txt became docs/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/excludes patterns (**/*.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, -DdefaultStation=BOS

generated

docs/BOS.txt created.

Batch, -DdefaultStation=boston1

generated

The value was not checked: docs/boston1.txt, Default station: boston1.

Batch, property without default, not given

fails

[WARNING] Property defaultStation is missing. Add -DdefaultStation=someValue, then Failed to execute goal ... :generate (default-cli) on project standalone-pom: Archetype io.tidepool:tidepool-strict-archetype:1.0.0-SNAPSHOT is not configured.

Interactive, property without default

asked and checked

Define value for property 'defaultStation' (should match expression '[A-Z]{2,4}'): Value does not match the expression, please try again:; after typing boston1 and then BOS, the project was generated with BOS.

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

archetype:generate with maven-archetype-quickstart:1.5 in batch mode

same project

same project

same project; the model of the generated POM stays 4.0.0

warning that Maven Central serves archetype-catalog.xml which its prefix file denies

not printed

printed

printed

nested build output (-Dgoals, create-from-project)

plain

plain

each line prefixed with [INFO] [stdout]

archetype:create-from-project without a settings.xml

fails

fails

fails

archetype:create-from-project -s file

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:create is gone. On Maven 3.10.0 it fails with Could 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 use create need generate.

  • The Advanced Usage page of the plugin still shows a 2008-era console (task-segment, BUILD SUCCESSFUL, an archetype:add-archetype-metadata goal 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 -DarchetypeVersion may fail with a message that version:RELEASE was not found (3.4.1 names version 1.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-project documentation says to put integration tests in src/it/projects; the goal, as run here, wrote the generated one to src/test/resources/projects/basic, which is where archetype:integration-test looks (testProjectsDirectory defaults to target/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