< Java
Tip >

DEVELOPING A MAVEN ARCHETYPE FOR MICROSERVICES, PART 3

Harry Klerks
Hello, welcome and thank you for reading this third instalment of our series on developing a Maven archetype for microservices. Regrettably almost another three months since the previous one, so you might want to refresh your memory first:

Developing a Maven archetype for microservices, part 1

Developing a Maven archetype for microservices, part 2

At the end of part 2, our archetype is able to generate a single, simple module that pertains to the domain, the core of the microservice in our envisaged architecture. 

In this part we are going to add several interfaces to this module, interfaces that will be implemented by other modules. We will also add the implementation for two of these interfaces (a factory and a repository for the Person class) by means of a JPA-aware Enterprise Java Bean (EJB) module, an Enterprise Archive (EAR) module to package the end result. That sounds like a lot of work to do, so let's get on with it!

Adding a factory interface for Person

The PersonFactory interface exposes methods that pertain to the reconstitution of Person objects from a persistent store. These methods should be implemented by at least one class in the application.

The interface looks like this:

The PersonFactory interface

package ${package}.domain.factory; 

import ${package}.domain.core.Person; 

import java.util.List; 

public interface PersonFactory {     

    Person restoreById(String personId);     

    List<person> restoreAll();}</person>

}

Adding a repository interface for Person

The PersonRepository interface exposes methods that pertain to the persistent state of Person objects. These methods should also be implemented by at least one class in the application.

The interface looks like this:

The PersonFactory interface

package ${package}.domain.repository;

import ${package}.domain.core.Person; 

public interface PersonRepository {     

    void persist(Person newPerson);     

    void remove(Person person);

}

Adding an implementation for both the factory and repository interfaces

As mentioned above, both the PersonFactory and PersonRepository interfaces must be implemented by at least one class in the application. In the case of this microservice implementation this will be done through a JPA-aware EJB module that contains two EJBs; one for the factory and one for the repository.

First, let's add the EJB module.

As you can see, we have added quite a number of files to the archetype. Let's have a look at them.

PersistenEntity.java

package ${package}.domain.core; 

import java.io.Serializable; 

public class PersistentEntity implements Serializable {     

    private String id;    

    private int version;     

    public PersistentEntity(String uuid) {         

        this.id = uuid;    

    }     

    public String getId() {        

        return id;    

    }    

    public int getVersion() {        

        return version;    

    }

}

PersistentEntity is a so-called Mapped Superclass (a JPA concept) and is the superclass for all persistent domain classes.

PersonFactoryBean.java

package ${package}.domain.repository; 

import java.util.List; 

import ${package}.domain.core.Person;
import ${package}.domain.factory.PersonFactory; 

import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery; 

@Stateless
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public class PersonFactoryBean implements PersonFactory {     

    @PersistenceContext(unitName = "pu_${rootArtifactId}")    
    private EntityManager em;     

    @Override    
    public Person restoreById(String id) {         

        return em.find(Person.class, id);    

}     

    @Override    

    public List<person> restoreAll() {         </person>

        TypedQuery<person> query = em.createNamedQuery("Person.searchAll", Person.class);       </person>

        return query.getResultList();     

    }

}

PersonFactoryBean is a JPA-aware EJB implementation of the interface PersonFactory.

PersonRepositoryBean.java

package ${package}.domain.repository; 

import ${package}.domain.core.Person;
import ${package}.domain.repository.PersonRepository; 

import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext; 

@Stateless
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public class PersonRepositoryBean implements PersonRepository {     

    @PersistenceContext(unitName = "pu_${rootArtifactId}")    
    private EntityManager em;     

    @Override    
    public void persist(Person person) {         

        em.persist(person);    

    }     

    @Override    
    public void remove(Person person) {         

        em.remove(person);    

    } 
}

PersonRepositoryBean is a JPA-aware EJB implementation of the interface PersonRepository.

The file load-data.sql is empty for now, but it is mentioned in the file persistence.xml and can be used to load initial data in the store.

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>

    xmlns="http://xmlns.jcp.org/xml/ns/persistence"            
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">    
    <persistence-unit name="pu_${rootartifactid}">        </persistence-unit name="pu_${rootartifactid}">
        <jta-data-source>jdbc/__default</jta-data-source>        
        <mapping-file>META-INF/orm.xml</mapping-file>        
        <properties>            </properties>
        <property name="javax.persistence.schema-generation.database.action" value="drop-and-create">            </property name="javax.persistence.schema-generation.database.action" value="drop-and-create">
        <property name="javax.persistence.schema-generation.create-database-schemas" value="false">            </property name="javax.persistence.schema-generation.create-database-schemas" value="false">
        <property name="javax.persistence.sql-load-script-source" value="meta-inf load-data.sql"="">        </property name="javax.persistence.sql-load-script-source" value="meta-inf>
        

This file contains the configuration for persistence unit pu_${rootArtifactId}, a value that you provide when this archetype is executed. You can see the reference to the sql-file described above and a reference to file orm.xml, described below.

orm.xml

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http: xmlns.jcp.org="" xml="" ns="" persistence="" orm"version="2.1">    </entity-mappings xmlns="http:>
    <persistence-unit-metadata>        </persistence-unit-metadata>
        <persistence-unit-defaults>            </persistence-unit-defaults>
            <schema>person</schema>        
            
        
    <mapped-superclass class="com.bongaro.diagora.domain.core.persistententity" access="field">        </mapped-superclass class="com.bongaro.diagora.domain.core.persistententity" access="field">
    <attributes>            </attributes>
        <id name="id">                </id name="id">
            <column length="36">            </column length="36">
                    
        <version name="version">                </version name="version">
            <column nullable="false">            </column nullable="false">
                
        
   
<entity class="com.bongaro.diagora.domain.core.person" access="field">        </entity class="com.bongaro.diagora.domain.core.person" access="field">
    <named-query name="person.searchall">            </named-query name="person.searchall">
        <query>SELECT p FROM Person p</query>      
            
    <attributes>            </attributes>
        <basic name="name" optional="false">                  </basic name="name" optional="false">
            <column nullable="false">            </column nullable="false">
                
        
   

This file contains the Obect Relational Mapping (hence the name orm.xml) information.

POM for the module

?xml version="1.0" encoding="UTF-8"?>

        xmlns="http://maven.apache.org/POM/4.0.0"        
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    
    <parent>        </parent>
        <artifactid>${rootArtifactId}</artifactid>        
        <groupid>${groupId}</groupid>        
        <version>${version}</version>    
        
    <modelversion>4.0.0</modelversion>    
    <artifactid>${artifactId}</artifactid>    
    <packaging>ejb</packaging>    
    <name>${artifactId}:${project.packaging}</name>    
    <dependencies>        </dependencies>
        <dependency>            </dependency>
            <groupid>com.bongaro.diagora</groupid>            
            <artifactid>${rootArtifactId}-domain</artifactid>            
            <version>${version}</version>            
            <scope>provided</scope>        
            
    

The Maven POM for module ${rootArtifactId}-jpa-factory-and-repository.

The amended root POM

<?xml version="1.0" encoding="UTF-8"?>

        xmlns="http://maven.apache.org/POM/4.0.0"        
        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>${groupId}</groupid>    
    <artifactid>${artifactId}</artifactid>   
    <version>${version}</version>    
    <packaging>pom</packaging>    
    <properties>        </properties>
        <ejb.ejbversion>3.2</ejb.ejbversion>
        <maven.compiler.source>1.8</maven.compiler.source>           
        <maven.compiler.target>1.8</maven.compiler.target>        
        <version.java.ee>7.0</version.java.ee>    
        
    <dependencymanagement>        </dependencymanagement>
        <dependencies>            </dependencies>
            <dependency>                </dependency>
                <groupid>javax</groupid>                
                <artifactid>javaee-api</artifactid>                
                <version>${version.java.ee}</version>                
                <scope>provided</scope>            
                    
            
        
    <dependencies>        </dependencies>
        <dependency>            </dependency>
            <groupid>javax</groupid>            
            <artifactid>javaee-api</artifactid>        
            
    

The new Maven root POM for the entire microservice.

Metadata


                xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0"                    
               xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd"                      
               name="bongaro-microservice-archetype" partial="false">    

    <modules>        </modules>
        <module id="${rootartifactid}-domain" dir="__rootartifactid__-domain" name="${rootartifactid}-domain">            </module id="${rootartifactid}-domain" dir="__rootartifactid__-domain" name="${rootartifactid}-domain">
        <filesets>                </filesets>
            <fileset filtered="true" packaged="true" encoding="utf-8"></fileset filtered="true" packaged="true" encoding="utf-8">
                <directory>src/main/java</directory>                    
                <includes>                        </includes>
            <include>**/*.java</include>                    
                        
                     

    <fileset filtered="true" packaged="false" encoding="utf-8">                    </fileset filtered="true" packaged="false" encoding="utf-8">
        <directory>                    </directory>
        <includes>                        </includes>
            <include>pom.xml</include>                    
                        
                    
            
         

<module id="${rootartifactid}-jpa-factory-and-repository" dir="__rootartifactid__-jpa-factory-and-repository" name="${rootartifactid}-jpa-factory-and-repository">            </module id="${rootartifactid}-jpa-factory-and-repository" dir="__rootartifactid__-jpa-factory-and-repository" name="${rootartifactid}-jpa-factory-and-repository">
    <filesets>                </filesets>
        <fileset filtered="true" packaged="true" encoding="utf-8">       </fileset filtered="true" packaged="true" encoding="utf-8">
             <directory>src/main/java</directory>                    
             <includes>                        </includes>
                <include>**/*.java</include>                    
                            
                         

        <fileset filtered="true" packaged="true" encoding="utf-8">                  </fileset filtered="true" packaged="true" encoding="utf-8">
            <directory>src/main/resources</directory>                    
            <includes>                        </includes>
                <include>**/persistence.xml</include>                        
                <include>**/orm.xml</include>                                   
           
                         

        <fileset filtered="true" packaged="false" encoding="utf-8">                    </fileset filtered="true" packaged="false" encoding="utf-8">
                <directory>                    </directory>
                <includes>                       </includes>
                        <include>pom.xml</include>                    
                                    
                            
                    

            
     

And finally the amended archetype metadata where we added a module containing three filesets for the ${rootArtifact}-jpa-factory-and-repository module.

Adding a module to produce an Enterprise Archive (EAR)

This is the moment to add a module that will produce an Enterprise Archive (EAR) artefact that we can deploy. The EAR will contain one EJB module for now. In support of this module it will also contain the ${rootArtifactId}-domain module as a library.

The following files were added/changed:

src/main/resources/archetype-resources/_rootArtifactId_-application/pom.xml

<?xml version="1.0" encoding="UTF-8"?>

        xmlns="http://maven.apache.org/POM/4.0.0"        
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    
    <parent>        </parent>
        <artifactid>${rootArtifactId}</artifactid>
                
        <groupid>${groupId}</groupid>        
        <version>${version}</version>    
        
    <modelversion>4.0.0</modelversion>    
    <artifactid>${artifactId}</artifactid>    
    <packaging>ear</packaging>    
    <name>${artifactId}:${project.packaging}</name>    
    <dependencies>        </dependencies>
        <dependency>            </dependency>
            <groupid>com.bongaro.diagora</groupid>            
            <artifactid>${rootArtifactId}-domain</artifactid>            
            <version>${version}</version>        
                
        <dependency>            </dependency>
            <groupid>com.bongaro.diagora</groupid>            
            <artifactid>${rootArtifactId}-jpa-factory-and-repository</artifactid>          
            <version>${version}</version>            
            <type>ejb</type>        
            
        
    <build>        </build>
        <plugins>            </plugins>
            <plugin>                </plugin>
                <artifactid>maven-ear-plugin</artifactid>                
                <version>2.10.1</version>                
                <configuration>                    </configuration>
                    <version>7</version>                    
                    <defaultlibbundledir>/lib</defaultlibbundledir>                    
                    <finalname>${rootArtifactId}</finalname>                
                            
                    
            
    

A configuration for the maven ear plugin to indicate the Java EE version number (7), the location of libraries in the EAR (/lib folder) and a final name for the artefact.

Person class

package ${package}.domain.core; 

import java.util.UUID; 

public class Person extends PersistentEntity {     

    private String name;     

    protected Person(){         

        super(UUID.randomUUID().toString());    

        }     

        public String getName() {        

            return name;    

    }

}

The Person now extends the PersistentEntity in order to have a JPA-aware ID and version number (for the optimistic locking scheme). A constructor was added.

src/main/resources/META-INF/maven/archetype-metadata.xml


        xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0"                    
        xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd"                      
        name="bongaro-microservice-archetype" partial="false">    
    <modules>         </modules>

        <module id="${rootartifactid}-domain" dir="__rootartifactid__-domain" name="${rootartifactid}-domain">            </module id="${rootartifactid}-domain" dir="__rootartifactid__-domain" name="${rootartifactid}-domain">
            <filesets>                </filesets>
                <fileset filtered="true" packaged="true" encoding="utf-8">                                       </fileset filtered="true" packaged="true" encoding="utf-8">
                    <directory>src/main/java</directory>                    
                    <includes>                        </includes>
                        <include>**/*.java</include>                    
                                    
                                 

                <fileset filtered="true" packaged="false" encoding="utf-8">                    </fileset filtered="true" packaged="false" encoding="utf-8">
                    <directory>                    </directory>
                    <includes>                        </includes>
                        <include>pom.xml</include>                    
                                
                        
                
             

    <module id="${rootartifactid}-application" dir="__rootartifactid__-application"vname="${rootartifactid}-application">             </module id="${rootartifactid}-application" dir="__rootartifactid__-application"vname="${rootartifactid}-application">

        <filesets>                </filesets>
            <fileset filtered="true" encoding="utf-8">                    </fileset filtered="true" encoding="utf-8">
                <directory>                    </directory>
                <includes>                        </includes>
                    <include>pom.xml</include>                    
                                
                        

                 

         


    name="${rootArtifactId}-jpa-factory-and-repository">            
        <filesets>                </filesets>
            <fileset filtered="true" packaged="true" encoding="utf-8">                    </fileset filtered="true" packaged="true" encoding="utf-8">
                <directory>src/main/java</directory>                    
                <includes>                        </includes>
                    <include>**/*.java</include>                    
                                
                             

            <fileset filtered="true" encoding="utf-8">                    </fileset filtered="true" encoding="utf-8">
                <directory>src/main/resources/META-INF</directory>                    
                <includes>                        </includes>
                    <include>persistence.xml</include>                        
                    <include>orm.xml</include>                        
                    <include>load-data.sql</include>                    
                                
                             

            <fileset filtered="true" encoding="utf-8">                    </fileset filtered="true" encoding="utf-8">
                <directory>                    </directory>
                <includes>                        </includes>
                    <include>pom.xml</include>                    
                                
                        
                
        
 

A module was added for the application.

Summary

In this third part of our series we have made our archetype generate a buildable and deployable Java EE 7 application, containing a JPA-enabled domain. Of course, it is not able to do anything useful yet, so there will have to be a fourth part!

Thank you for reading and see you in Part 4!