With maven's widespread adoption as a build tool, maven archetypes have become the de-facto standard way to share and/or kickstart new projects in a jiffy.
The maven archetype technology is now embedded inside modern Java IDEs, allowing users to create new projects very easily from a nice UI interface.
This document is meant to provide a few guidelines when it comes to building archetypes themselves, so that you can ensure created projects :
a) Build everywhere (CLI and IDEs) out-of-the-box
b) Follow best practices in modular application architecture
c) Are truly portable among JavaEE 6 application servers
Create from an existing project
The best way to create a new maven archetype is to start from an existing maven project. If this is a multimodule project, then it's strongly suggested your sub-modules artifactIds are composed of <parentArtifactId>-<somesuffix> (ex : ee6app has ee6app-web, ee6app-ejb, ee6app-fmk ... submodules). That way, the maven archetype plugin will be able to isolate the common rootArtifactId and users will create truly personalized projects.
At the root of your project <myproject>, just run the "archetype:create-from-project" goal.
mvn clean archetype:create-from-project -Dinteractive=true
Using the attribute -Dinteractive=true will activate well, the interactive mode. It'll allow you to set some default property values.
To ease the integration of the archetype with JBoss Tools, let's add a enterprise property, that will be reused later on.
... [INFO] Setting default groupId: foo.bar [INFO] Setting default artifactId: multi [INFO] Setting default version: 0.0.1-SNAPSHOT [INFO] Setting default package: foo.bar.multi Add a new custom property Y: : Y Define property key: enterprise Define value for enterprise : : false Add a new custom property Y: : N Confirm archetype configuration: archetype.groupId=foo.bar archetype.artifactId=multi-archetype archetype.version=0.0.1-SNAPSHOT version=0.0.1-SNAPSHOT package=foo.bar.multi archetype.artifactId=multi-archetype archetype.groupId=foo.bar groupId=foo.bar targetRuntime=as7 archetype.version=0.0.1-SNAPSHOT artifactId=multi Y: : N Define value for archetype.groupId: foo.bar: : org.jboss.archetypes.sample Define value for archetype.artifactId: multi-archetype: : Define value for archetype.version: 0.0.1-SNAPSHOT: : Define value for groupId: foo.bar: : org.jboss.multi Define value for artifactId: multi: : ee6 Define value for version: 0.0.1-SNAPSHOT: : Define value for package: foo.bar.multi: : org.jboss.multi Add a new custom property Y: : N Confirm archetype configuration: archetype.groupId=org.jboss.archetypes.sample archetype.artifactId=multi-archetype archetype.version=0.0.1-SNAPSHOT package=org.jboss.multi version=0.0.1-SNAPSHOT groupId=org.jboss.multi artifactId=ee6 Y: : Y
It'll create a new maven-archetype project under <myproject>/target/generated-sources/. For multi-module archetypes, you'll see all the module folders are prefixed with __rootArtifactId__. This rootArtifactId is available for templating purposes under the ${rootArtifactId} placeholder. You can find an example of a multi-module archetype metadata here : http://code.google.com/p/open-archetypes/source/browse/multi-javaee5-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml.
Remove hardcoded packages
Now that the archetype skeleton is created, it needs to be curated from some hard-coded values. For instance, in all java classes and configuration files, packages must be changed to ${package}.<module-suffix> (see http://code.google.com/p/open-archetypes/source/browse/multi-javaee5-archetype/src/main/resources/archetype-resources/__rootArtifactId__-ejb/src/main/java/ejb/HelloService.java for instance)
Lock down plugin versions
It's considered a best practice to lock down the plugin versions in parent pom. To do this, use the jboss-parent POM as your parent.
<parent> <groupId>org.jboss</groupId> <artifactId>jboss-parent</artifactId> <version>8</version> <relativePath /> </parent>
Logic in archetype templates
Archetype templates are based on the Velocity template engine. As such, it's possible to embed a certain amount of logic when generating the project files. You can, for instance, define dependencies, based on the value of some archetype property. ex :
<dependencies> #if( $enterprise == "true" || $enterprise == "y" || $enterprise == "yes") <dependency> <groupId>foo.bar</groupId> <artifactId>some.artifact</artifactId> <version>1.0.0</version> </dependency> #end <dependency> <groupId>bar.foo</groupId> <artifactId>another.artifact</artifactId> <version>1.1.0</version> </dependency> #end ... </profile>
This blog's WYSIWYG editor doesn't want to display the velocity syntax properly, be aware strings in the if statements must be surrounded by double quotes. See a proper example here.
This renders maven archetypes much more powerful than what you might expect. Read http://velocity.apache.org/engine/devel/user-guide.html#Conditionals for more informations.
Don't embed JBoss repositories in pom.xml
Putting repositories in your POMs is a Bad Idea. You need to make sure all your dependencies are in Maven Central. If you need help with doing this, email the JDF team - jdf-dev@lists.jboss.org.
Escape special characters
Whenever some special characters need to be used, and to prevent the archetype plugin, or rather Velocity, from filtering, you need to define escaping variables in each processed file, such as :
#set( $symbol_pound = '#' ) #set( $symbol_dollar = '$' ) #set( $symbol_escape = '\' ) jndiPattern=${rootArtifactId}-ear/${symbol_escape}${symbol_pound}{ejbName}/local
Web application context root
Be aware that m2e-wtp, for instance, determines the context root of a standalone web application from the <finalName> value defined in (or inferred from) the <build> section of a war project. So if you are creating a WAR, you should define:
<build> <finalName>${symbol_dollar}{project.artifactId}</finalName> ... </build>
in the pom.xml defined in your archetype.
The <finalName> value of an artifact is used, in maven's reactor, to define the deployed name of war or ear artifacts. However, due to an Eclipse WTP limitation, for EAR packaging, the eclipse project name needs to be the same as the finalName, to prevent any discrepancy between Eclipse andMaven CLI. See this JBoss Tools issue.
Annotation processors
Simply put, annotation processors are not supported yet by m2e. So, if projects from your archetypes are supposed to use this feature, you have no other option (for now) but to add the relevant eclipse configuration files to your archetype resources.
JBoss target runtimes
For archetypes designed to run on either JBoss AS 7 or EAP 6, Bill of Materials (BOM) poms should be used in the <dependencyManagement> section of the pom.xml, in order to control the versions of the dependencies provided by the application server.
BOM | |
---|---|
BOM Pom dependency in dependencyManagement | <dependency> <groupId>org.jboss.bom</groupId> <artifactId>jboss-javaee-6-with-tools</artifactId> <version>{jboss.bom.version}</version> <type>pom</type> <scope>import</scope> </dependency> |
You could also choose to use any BOM from the JBoss Stacks. Switching the BOM value should allow the project to build and deploy directly against the targeted application server.
You shoukld define a version property for the EE6 API and use the previously mentioned enterprise archetype property to insert one value or another :
<properties> ... #if ($enterprise == "true" || $enterprise == "y" || $enterprise == "yes" ) <!-- Certified version of the JBoss EAP components we want to use --> <jboss.bom.version>${jboss-bom-enterprise-version}</jboss.bom.version> <!-- Alternatively, comment out the above line, and un-comment the line below to use version 1.0.2.Final which is based on community built dependencies. --> <!-- <jboss.bom.version>1.0.2.Final</jboss.bom.version> --> #else <jboss.bom.version>1.0.2.Final</jboss.bom.version> <!-- Alternatively, comment out the above line, and un-comment the line below to use version ${jboss-bom-enterprise-version} which is a release certified to work with JBoss EAP 6. It requires you have access to the JBoss EAP 6 maven repository. --> <!-- <jboss.bom.version>${jboss-bom-enterprise-version}</jboss.bom.version>> --> #end </properties>
And in the <dependencyManagement> section :
<dependencyManagement> <dependencies> <dependencies> <!-- JBoss distributes a complete set of Java EE 6 APIs including a Bill of Materials (BOM). A BOM specifies the versions of a "stack" (or a collection) of artifacts. We use this here so that we always get the correct versions of artifacts. Here we use the jboss-javaee-6.0-with-tools stack (you can read this as the JBoss stack of the Java EE 6 APIs, with some extras tools for your project, such as Arquillian for testing) and the jboss-javaee-6.0-with-hibernate stack you can read this as the JBoss stack of the Java EE 6 APIs, with extras from the Hibernate family of projects) --> <dependency> <groupId>org.jboss.bom</groupId> <artifactId>jboss-javaee-6.0-with-tools</artifactId> <version>${jboss.bom.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
Comments