Version 14

    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>