Maven archetypes

We live in a micro-services world, lately, does not matter where you go, big, medium or small companies or start-ups, everyone is trying to implement microservices or migrating to them.

Maybe not initially, but when companies achieve a certain level of maturity, they start having a set of common practices, libraries or dependencies they apply or use in all the micro-services they build. Let’s say, for example, authentication or authorization libraries, metrics libraries, … or any other component they use.

When this level of maturity is achieved, usually, to start a project basically we take the “How-To” article in our wiki and start copying and pasting common code, configurations and creating a concrete structure in the new project. After that, it is all set to start implementing business logic.

This copy and paste process is not something that it usually takes a long time but, it is a bit tedious and prone to human errors. To make our lives easier and to try to avoid unnecessary mistakes we can use maven archetypes.

Taken from the maven website, an archetype is:

In short, Archetype is a Maven project templating toolkit. An archetype is defined as an original pattern or model from which all other things of the same kind are made. The name fits as we are trying to provide a system that provides a consistent means of generating Maven projects. Archetype will help authors create Maven project templates for users, and provides users with the means to generate parameterized versions of those project templates.

In the next two sections, we are going to learn how to build some basic archetypes and how to build a more complex one.

Creating a basic archetype

Following the maven documentation page we can see there are a few ways to create our archetype:

From scratch

I am not going to go into details here because the maven documentation is good enough and because it is the method we are going to use in the “Creating a complex archetype” section below. You just need to follow the four steps the documentation is showing:

  1. Create a new project and pom.xml for the archetype artefact.
  2. Create the archetype descriptor.
  3. Create the prototype files and the prototype pom.xml.
  4. Install the archetype and run the archetype plugin

Generating our archetype

This is a very simple one also described in the maven documentation. Basically, you use maven to generate the archetype structure for you

mvn archetype:generate \
    -DgroupId=[your project's group id] \
    -DartifactId=[your project's artifact id] \
    -DarchetypeGroupId=org.apache.maven.archetypes \
    -DarchetypeArtifactId=maven-archetype-archetype

As simple as that. After executing the command, we can add our personalisations to the project and proceed to install it as seen before.

From an existing project

This option allows us to create a project and when we are happy with how it is, to transform the project into an archetype. Basically we need to follow the next steps:

  1. Build the project layout by scratch and add files as need.
  2. Run the Maven archetype plugin on an existing project and configure from there.
mvn archetype:create-from-project

This will generate an “archetype” folder into the “target” folder:

target/generated-sources/archetype

We just need to copy this folder structure to the desired location and we will have our archetype ready to go. It needs to be installed as usual to be able to use it.

Using our archetype

Once we have install our archetype, we can start using it:

mvn archetype:generate \
    -DarchetypeGroupId=dev.binarycoders \
    -DarchetypeArtifactId=simple-archetype \
    -DarchetypeVersion=1.0-SNAPSHOT \
    -DgroupId=org.example \
    -DartifactId=project1

This will create a new project using the archetype. The information we need to modify in the previous command is:

  • archetypeGroupId: It is the archetype group id we have defined when we created the archetype.
  • archetypeArtifactId: It is basically the name of our archetype.
  • archetypeVersion: It is the version of the archetype we want to use in case the archetype has been evolving over time and we have different versions.
  • groupId: It is the group id our new project is going to have.
  • artifactId: It is the name of our new project.

Deleting our archetype

Right now, after installing our archetype, it is only available in our local repository. This fact allows us to delete the archetype in a very simple way. We just need to take a look at the archetype catalogue in our repository and manually remove the archetype. We can find this file at:

~/.m2/repository/archetype-catalog.xml

Creating a complex archetype

For most cases, the already reviewed ways to create archetypes should be enough but, not for all of them. What happens if we need to define some modules we want to define the name when creating the project? Or classes? Or some other customisations?

Luckily, Maven gives us some level of flexibility allowing us to define some variables and use some concrete patterns to define folders and files in our archetypes in a way they will be replaced when the projects using the archetype are created.

As a general rule we will be using two kinds of notation for our dynamically elements:

  • Defined in files: ${varName}
  • Defined in file system: __varName__ (two underscores)

This will help us to achieve our goals.

As an example, I am going to create a small complex archetype to be able to see this in action. The projects created with the archetype are going to have:

  • A parent project with <artifactId> name.
  • Two modules called <artifactId>-one and artifactId-two.
  • A main class called <classPrefix>OneApp and <classPrefix>TwoApp respectibely.
  • The classes will be located in the package <package>.one and <package>.two respectibely.
  • The module One will have a properties class stored in the resources folder.

The code of the archetype can be found at the GitHub repository.

The first file we can check is archetype-metadata-xml located in META-INF/maven.

We can see here the definition of the variable classPrefix and groupId with a default value assigned.

<requiredProperties>
    <requiredProperty key="classPrefix" />
    <requiredProperty key="groupId">
        <defaultValue>dev.binarycoders</defaultValue>
    </requiredProperty>
</requiredProperties>

After that, we can see the definition of the project structure we want to achieve. In this case, we have the fileSets node with the files on the parent project and, after that, the definition of the modules we want to include. Here we should pay special attention to the way the module attributes are defined:

<module id="${rootArtifactId}-one"
         dir="__rootArtifactId__-one"
         name="${rootArtifactId}-one">

As we can see they use the notation described before, using the “${}” notation for variables in files and the notation “__” (two underscores) for file system elements. The rest of the file is pretty simple.

If we explore the folder structure, we can see a few elements defined with these two underscores notation like the module names and the class names. This will be dynamic elements that will take the name from the variable defined when the project is created.

We can define different filesets for the files we want to be copied to our generated project. For example, we can copy all the .java files we can find inside the path src/main/java:

<fileSet filtered="true" packaged="true" encoding="UTF-8">
    <directory>src/main/java</directory>
        <includes>
            <include>**/*.java</include>
        </includes>
</fileSet>

Finally, if we explore one of the classes, we can see the next content:

#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $smbol_escape = '\' )
package ${package}.one;

public class ${classPrefix}OneApp {
}

The first three lines are just alias to be able to use the symbols that have a specific meaning not just as literals.

After that, we can see the package definition that it is going to be built with one part dynamically added and one part statically defined. We can see the class name follows the same pattern.

Deserves special attention to the fact that, despite we are defining packages into the classes, we are not replicating this structure in the project structure, Maven will take care of that for us. This is because when we defined the fileset we defined the attribute package equals true. If this attribute is set to false, we will be in charge of defining the desired structure.

It is worth it to mention that because of the files in the maven archetype act as velocity templates, we can introduce some logic and some dynamic content in our files. For example, print something or not in a determinate file:

<requiredProperty key="greeting">
    <defaultValue>y</defaultValue>
</requiredProperty>
#if (${greeting == 'y'})
    // Hello, welcome here!
#end

This variable can be set using the command line when we generate our new project:

-Dgreeting=n

Finally, there is one more interesting thing we can do. We can use a post-generation script write in groovy to execute some actions after the project has been generated. One interesting use, it is to remove not desired files based on some variables defined when generating the project. This script will be located in the folder src/main/resources/META-INF with the name archetype-post-generate.groovy.

import groovy.io.FileType

def rootDir = new File(request.getOutputDirectory() + "/"
    + request.getArtifactId())
def oneBundle = new File(rootDir, request.getArtifactId()
    + "-one")

def projectPackage = request.getProperties().get("package")

assert new File(oneBundle, "src/main/java/" 
    + projectPackage.split("\\.").join('/')
    + "/toDelete.txt").delete()

With this, every time that we use the archetype to create a new project we will obtain the desired results.

We can use our recently created archetype with:

mvn archetype:generate \
    -DarchetypeGroupId=dev.binarycoders \
    -DarchetypeArtifactId=simple-archetype \
    -DarchetypeVersion=1.0-SNAPSHOT \
    -DgroupId=org.example \
    -DartifactId=project

And the result:

And, one of the classes:

This is all. I hope is useful.

See you.

Maven archetypes

Java REST API + cURL + python3

During the next lines, I am going to implement a very simple REST API using JavaEE 7 and Java SE 8 technologies, maven as build tool and GlassFish as application server. I am going to test this API with cURL and, I am going to consume this API using python3 using the Requests module.

Java EE 7 REST API

First, we need to create a maven project in our favorite IDE and add our dependencies to the pom.xml file

...
<properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
 
<dependencies>
    <dependency>
        <groupId>org.eclipse.persistence</groupId>
        <artifactId>eclipselink</artifactId>
        <version>2.5.2</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.eclipse.persistence</groupId>
        <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
        <version>2.5.2</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax</groupId>
        <artifactId>javaee-api</artifactId>
        <version>7.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
 
<build>
    <finalName>apicrud</finalName>
</build>
...

Second, we are going to create a persistence.xml file. In this case, we are not interested in how to configure a DB, then, we are going to use the default datasource that GlassFish provides us as default “jdbc/__default“. With all of this in mind, the file should look like this:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
        http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="ApiCrudPU" transaction-type="JTA">
        <jta-data-source>jdbc/__default</jta-data-source>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="javax.persistence.schema-generation.database.action" value="create"/>
        </properties>
    </persistence-unit>
</persistence>

Now, we need to create our Entity. In this case, I have chosen a simple one, User with three attributes:

  • id: Autogenerated id to identify the user.
  • name: User’s name.
  • email: User’s email.

The result should be something like:

package com.wordpress.binarycoders.apicrud;
 
import java.io.Serializable;
import java.util.Objects;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
 
@Entity
@Table (name = "users")
@NamedQueries ({
    @NamedQuery (name = User.FIND_ALL, query = "SELECT u FROM User u")
})
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class User implements Serializable {
     
    private static final long serialVersionUID = 1L;
     
    public static final String FIND_ALL = "User.findAll";
     
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
     
    @Column(name = "name")
    private String name;
     
    @Column(name = "email")
    private String email;
 
    /* Getters, Setters, equals and hashCode methods */   
}

The next step, it is to implement the boundary or service layer. Being a simple example, we can skip this layer, but I like to implement everything for learning purposes. This layer is going to be very simple, only the necessary object to perform the operation in the DB and the basic operations to implement a CRUD. The result looks like:

package com.wordpress.binarycoders.apicrud;
 
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.validation.constraints.NotNull;
 
@Stateless
public class UserService {
     
    @PersistenceContext
    private EntityManager em;
     
    public void create(@NotNull User user) {
        this.em.persist(user);
    }
     
    public User findById(@NotNull Long id) {
        return this.em.find(User.class, id);
    }
     
    public List<User> findAll() {
        TypedQuery<User> typedQuery = this.em.createNamedQuery(User.FIND_ALL, User.class);
 
        return typedQuery.getResultList();
    }
     
    public void update(@NotNull User user) {
        this.em.merge(user);
    }
     
    public void delete(@NotNull Long id) {
        this.em.remove(this.findById(id));
    }
}

To do all of this available to the world, we need to implement our REST API, and for this, we need to configure the REST capabilities in our system, piece of cake with the last version of Java EE. We just need to create a class to enable these capabilities:

package com.wordpress.binarycoders.apicrud;
 
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
 
@ApplicationPath("/rs")
public class ApplicationConfig extends Application {
     
}

And, finally, we need to implement our REST layer with the four actions that are going to allow us to perform the CRUD operations:

package com.wordpress.binarycoders.apicrud;
 
import java.net.URI;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
 
@Path("/users")
@Produces ({ MediaType.APPLICATION_JSON })
@Consumes ({ MediaType.APPLICATION_JSON })
@Stateless
public class UserEndPoint {
     
    @EJB
    private UserService service;
     
    @Context
    private UriInfo uriInfo;
     
    @GET
    public Response findAll() {
        List<User> users = this.service.findAll();
         
        GenericEntity<List<User>> list = new GenericEntity<List<User>>(users) {};
         
        return Response.ok(list).build();
    }
     
    @GET
    @Path("/{id}")
    public Response findById(@PathParam("id") Long id) {
        User user = this.service.findById(id);
         
        if (user == null) {
            throw new NotFoundException();
        }
         
        return Response.ok(user).build();
    }
     
    @POST
    public Response create(User user) {
        this.service.create(user);
         
        URI uri = uriInfo.getAbsolutePathBuilder().path(String.valueOf(user.getId())).build();
         
        return Response.created(uri).build();
    }
     
    @PUT
    public Response update(User user) {
        this.service.update(user);
         
        return Response.ok().build();
    }
     
    @DELETE
    @Path("/{id}")
    public Response delete(@PathParam("id") Long id) {
        this.service.delete(id);
         
        return Response.noContent().build();
    }
}

I think that in this point, assuming that everyone reading this, it is a developer and this is not an entry level post, all of you should be capable to understand everything has been written in the above lines. If there are things that you do not know or you do not understand due to a lack of information or your knowledge is not enough (all of us have been there), look at the note at the end of this post.

cURL test

Now, obviously, we need to test if our REST API is working properly. For this, we can implement some solution based on test frameworks or manual code but, due to the simplicity of the object User, it is enough with the cURL tool. To test our API we are going to perform these operations:

  • Select all the users
  • Create a user
  • Select the created user
  • Update the user
  • Delete the user

These operations can be performed with the next commands:

curl -i -H "Content-Type: application/json" http://localhost:8080/ApiCRUD/rs/users
curl -i -H "Content-Type: application/json" -X POST -d '{"name":"john","email":"john@example.org"}' http://localhost:8080/ApiCRUD/rs/users
curl -i -H "Content-Type: application/json" http://localhost:8080/ApiCRUD/rs/users/1
curl -i -H "Content-Type: application/json" -X PUT -d '{"id”:1,”name":"John Doe","email":"john.doe@example.org"}' http://localhost:8080/ApiCRUD/rs/users
curl -i -H "Content-Type: application/json" -X DELETE http://localhost:8080/ApiCRUD/rs/users/1

Everything should look well, and the appropriate HTTP codes should be received.

Python3 client

The last step today, it is to implement a very very simple python3 client to consume our API. This is going to be a basic script to invoke the CRUD operations available in the API.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
 
import requests
import json
 
URL = 'http://localhost:8080/ApiCRUD/rs/users'
 
def getUsers():
    print('Get users...\n')
 
    response = requests.get(URL)
 
    assert response.status_code == 200
 
    users = response.json()
    print(json.dumps(users, indent = 4, separators = (',', ':')))
 
def getUser(location):
    print('Get single user ...\n')
 
    response = requests.get(location)
 
    assert response.status_code == 200
 
    users = response.json()
    print(json.dumps(users, indent = 4, separators = (',', ':')))
 
def createUser():
    print('Creating user...\n')
 
    headers = {'content-type': 'application/json'}
    user = {"name": "John Doe", "email": "john.doe@example.org"}
    response = requests.post(URL, data = json.dumps(user), headers = headers)
 
    assert response.status_code == 201
 
    print('Location: ' + response.headers['location'])
 
    return response.headers['location']
 
def updateUser(location):
    print('Updating user...')
 
    loc = location.split('/')
 
    headers = {'content-type': 'application/json'}
    user = {"id": loc[-1], "name": "Jane Doe", "email": "jane.doe@example.org"}
    response = requests.put(URL, data = json.dumps(user), headers = headers)
 
    assert response.status_code == 200
 
def deleteUser(location):
    print('Deleting user...')
 
    response = requests.delete(location)
 
    assert response.status_code == 204
 
    print('Checking delete operation')
    response = requests.get(location)
 
    assert response.status_code == 404
 
def main():
    getUsers()
    location = createUser()
    getUser(location)
    updateUser(location)
    getUser(location)
    deleteUser(location)
 
if __name__ == "__main__":
    main()

And that’s all. Now, we have a little REST AP, a python3 client and a basic knowledge about how to use cURL to test it.

See you.

You can find the code for the REST API available here.

Note: All the content in this post will be splitter and explained in future post, one for each one of the three big point explained in here: Java EE 7 REST API creation, cURL test and python3 REST client.

Java REST API + cURL + python3

Adding a library to our maven repository

Sometimes the maven repositories don’t have all the libraries we need and, we need to add a concrete library to our repository. Usually, this is because there is a problem with its license, something similar or only because it is a library that we have built for our own projects. In both cases, the steps to add the library to our own repository are quite simple.

For this example, we are going to use the library example-1.0-jar and the information about this library is going to be:

  • groupId: org.example
  • artifactId: example
  • version: 1.0

To add this library to our repository, we only need to execute a simple command:

mvn install:install-file -Dfile=<libraryName> -DgroupId=<groupId> -DartifactId=<artifactId> -Dversion=<version> -Dpackaging=jar

With our example data, the command should look like this:

mvn install:install-file -Dfile=example-1.0.jar -DgroupId=org.example -DartifactId=example -Dversion=1.0 -Dpackaging=jar

Finally, we only need to add our new dependency in our maven file, and that’s all.

<dependency>
    <groupId>org.example</groupId>
    <artifactId>example</artifactId>
    <version>1.0</version>
</dependency>

See you.

Adding a library to our maven repository