JBoss Maven example: building a Java EE 6 application

In this tutorial we will show how to create a basic Java EE 6 application which will run on JBoss AS 7 using Maven. Apache Maven can provide benefits for your jave ee projects by employing standard conventions and practices to accelerate your development cycle while at the same time helping you achieve a higher rate of success.

Once you have installed Maven, we will generate an archetype of a project. An archetype is a very simple artifact, that contains the project prototype you wish to create. There are lots of prototypes available so you will need to provide the archetypeArtifactId and the archetypeGroupId. In our example we will choose the jboss-javaee6-webapp archetypeArtifactId and the org.jboss.spec.archetypes archetypeGroupId:

$ mvn archetype:generate -DarchetypeArtifactId=jboss-javaee6-webapp \ -DarchetypeGroupId=org.jboss.spec.archetypes \ -DgroupId=com.mastertheboss \ -DartifactId=as7example -DinteractiveMode=false

[INFO] Parameter: groupId, Value: com.mastertheboss
[INFO] Parameter: artifactId, Value: as7example
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: com.mastertheboss
[INFO] Parameter: packageInPathFormat, Value: com/mastertheboss
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: com.mastertheboss
[INFO] Parameter: name, Value: Java EE 6 webapp project
[INFO] Parameter: groupId, Value: com.mastertheboss
[INFO] Parameter: artifactId, Value: as7example
[INFO] project created from Archetype in dir: C:\maven\as7example
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

The following project structure has been created for you:

+---src
    +---main
    ¦   +---java
    ¦   ¦   +---com
    ¦   ¦       +---mastertheboss
    ¦   ¦           +---controller
    ¦   ¦           ¦       MemberRegistration.java
    ¦   ¦           ¦
    ¦   ¦           +---data
    ¦   ¦           ¦       MemberListProducer.java
    ¦   ¦           ¦
    ¦   ¦           +---model
    ¦   ¦           ¦       Member.java
    ¦   ¦           ¦
    ¦   ¦           +---rest
    ¦   ¦           ¦       JaxRsActivator.java
    ¦   ¦           ¦       MemberResourceRESTService.java
    ¦   ¦           ¦
    ¦   ¦           +---util
    ¦   ¦                   Resources.java
    ¦   ¦
    ¦   +---resources
    ¦   ¦   ¦   import.sql
    ¦   ¦   ¦
    ¦   ¦   +---META-INF
    ¦   ¦           persistence.xml
    ¦   ¦
    ¦   +---webapp
    ¦       ¦   index.html
    ¦       ¦   index.xhtml
    ¦       ¦
    ¦       +---resources
    ¦       ¦
    ¦       +---WEB-INF
    ¦           ¦   beans.xml
    ¦           ¦   faces-config.xml
    ¦           ¦
    ¦           +---templates
    ¦                   default.xhtml
    ¦
    +---test
        +---java
        ¦   +---com
        ¦       +---mastertheboss
        ¦           +---test
        ¦                   MemberRegistrationTest.java
        ¦
        +---resources
                arquillian.xml

Next step will be customizing the Maven pom.xml file


Customize JBoss Maven pom.xml

A Project Object Model or POM is the fundamental unit of work in Maven. It is an XML file that contains information about the project and configuration details used by Maven to build the project. It contains default values for most projects.
So open the pom.xml file which is located at the root of your project and modify the following lines:

If you haven’t set your JBOSS_HOME env variable just set it at line 25

<jboss.home>C:\jboss-as-7.1.1.Final</jboss.home>

Then, set the correct JBoss AS version in all dependencies found in the pom.xml file. For example:

<dependency>
            <groupId>org.jboss.as</groupId>
            <artifactId>jboss-as-arquillian-container-managed</artifactId>
            <version>7.1.1.Final</version>
            <scope>test</scope>
</dependency>

That’s all! now you can package your project by issuing:

$ mvn install
[INFO] Scanning for projects...
. . . . . . .
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 34.657s
[INFO] Finished at: Wed Apr 11 12:58:19 CEST 2012
[INFO] Final Memory: 7M/21M
[INFO] ---------------------

Finally deploy the application to JBoss AS 7 using:

$ mvn jboss-as:deploy
. . . . . .
INFO: XNIO Version 3.0.7.GA
nov 05, 2013 5:10:07 PM org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.0.7.GA
nov 05, 2013 5:10:07 PM org.jboss.remoting3.EndpointImpl <clinit>
INFO: JBoss Remoting version 3.2.12.GA
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 11.082s
[INFO] Finished at: Tue Nov 05 17:10:12 CET 2013
[INFO] Final Memory: 13M/175M
[INFO] ------------------------------------------------------------------------

Now launch Eclipse and choose from the Menu: File | Import | Existing Maven Projects

JBoss Maven tutorial

As a result, your Maven project will be imported and you will have automatically imported as well all the required dependencies.

jboss maven example tutorial


The JBoss Maven project in detail

Let’s see how the base project is composed. Basically it’s a simple application which is used to register user names with a simple JSF form and can query the list of users which are also exposed through a REST interface.

The EJB 3 MemberRegistration Stateful bean is marked with the @Model annotation. This annotation does two things:

  • It makes the bean request-scoped (instead of dependent, the default)
  • It gives the bean an EL name

(Please note that creating a request-scoped and named bean is typically accomplished by apply the annotations @RequestScoped and @Named to the class, respectively. Since the combination of these annotations is so common in web applications, the built-in stereotype annotation @Model is provided by CDI as a shorthand).

package com.mastertheboss.controller;

import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.ejb.Stateful;
import javax.enterprise.event.Event;
import javax.enterprise.inject.Model;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import com.mastertheboss.model.Member;

@Stateful
@Model
public class MemberRegistration {

   @Inject
   private Logger log;

   @Inject
   private EntityManager em;

   @Inject
   private Event<Member> memberEventSrc;

   private Member newMember;

   @Produces
   @Named
   public Member getNewMember() {
      return newMember;
   }

   public void register() throws Exception {
      log.info("Registering " + newMember.getName());
      em.persist(newMember);
      memberEventSrc.fire(newMember);
      initNewMember();
   }

   @PostConstruct
   public void initNewMember() {
      newMember = new Member();
   }
}

The MemberListProducer is responsible for instantiating Members objects. As per specs, the getMembers producer method acts as a source of objects to be injected.

Notice we have added an event observer (@Observes) to the MemberListProducer just to see the events are still being raised and can be observed. This will retrieve all members stored as soon as one member has changed.
Events are a great way to decouple parts of the system in a modular fashion as you can add pieces that will subscribe to events with the event producer unaware of the observer as opposed to the even producer having to call the observer manually when events are not used.

Finally, the @Named annotation provides access the return value via the EL variable name “members” in the UI.

package com.mastertheboss.data;

import com.mastertheboss.model.Member;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.event.Reception;

import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

@RequestScoped
public class MemberListProducer {
   @Inject
   private EntityManager em;

   private List<Member> members;

   @Produces
   @Named
   public List<Member> getMembers() {
      return members;
   }

   public void onMemberListChanged(@Observes(notifyObserver = Reception.IF_EXISTS) final Member member) {
      retrieveAllMembersOrderedByName();
   }

   @PostConstruct
   public void retrieveAllMembersOrderedByName() {
      CriteriaBuilder cb = em.getCriteriaBuilder();
      CriteriaQuery<Member> criteria = cb.createQuery(Member.class);
      Root<Member> member = criteria.from(Member.class);

      criteria.select(member).orderBy(cb.asc(member.get("name")));
      members = em.createQuery(criteria).getResultList();
   }
}

This is the Member model class which incarnates the database Member table. Some quick recap about the annotations used here: The @UniqueConstraint specifies that a unique constraint is to be included in the generated DDL for a primary or secondary table. In this case, the email field must be unique. The @Pattern annotation, whose property must match the regular expression defined in the regexp element. The @Size of the field or property is evaluated and must match the specified boundaries. The @Digits means that the value of the field or property must be a number within a specified range.

package com.mastertheboss.model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.Digits;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;

@Entity
@XmlRootElement
@Table(uniqueConstraints = @UniqueConstraint(columnNames = "email"))
public class Member implements Serializable {
   /** Default value included to remove warning. Remove or modify at will. **/
   private static final long serialVersionUID = 1L;

   @Id
   @GeneratedValue
   private Long id;

   @NotNull
   @Size(min = 1, max = 25)
   @Pattern(regexp = "[A-Za-z ]*", message = "must contain only letters and spaces")
   private String name;

   @NotNull
   @NotEmpty
   @Email
   private String email;

   @NotNull
   @Size(min = 10, max = 12)
   @Digits(fraction = 0, integer = 12)
   @Column(name = "phone_number")
   private String phoneNumber;

   public Long getId() {
      return id;
   }

   public void setId(Long id) {
      this.id = id;
   }

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public String getEmail() {
      return email;
   }

   public void setEmail(String email) {
      this.email = email;
   }

   public String getPhoneNumber() {
      return phoneNumber;
   }

   public void setPhoneNumber(String phoneNumber) {
      this.phoneNumber = phoneNumber;
   }
}

The JaxRsActivator class extends javax.ws.rs.core.Application so that is automatically enlisted into JAX-RS resources and served relative to the servlet path specified in the @ApplicationPath annotation.

package com.mastertheboss.rest;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/rest")
public class JaxRsActivator extends Application {
   /* class body intentionally left blank */
}

Next, the MemberResourceRESTService class produces a RESTful service to read the contents of the members table. It contains two methods which return via XML the list of all Members and a member lookup by id.

package com.mastertheboss.rest;

import java.util.List;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import com.mastertheboss.model.Member;

@Path("/members")
@RequestScoped
public class MemberResourceRESTService {
   @Inject
   private EntityManager em;

   @GET
   @Produces("text/xml")
   public List<Member> listAllMembers() {

      @SuppressWarnings("unchecked")
      final List<Member> results = em.createQuery("select m from Member m order by m.name").getResultList();
      return results;
   }

   @GET
   @Path("/{id:[0-9][0-9]*}")
   @Produces("text/xml")
   public Member lookupMemberById(@PathParam("id") long id) {
      return em.find(Member.class, id);
   }
}

Finally, in the Resources class, the @Produces annotation identifies the produceLog() method as a producer method. A producer method is called whenever another bean in the system needs an injected object of the specified type. In this example this method is used to alias the Logger resource using CDI.

package com.mastertheboss.util;

import java.util.logging.Logger;

import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;


public class Resources {

   @SuppressWarnings("unused")
   @Produces
   @PersistenceContext
   private EntityManager em;
   
   @Produces
   public Logger produceLog(InjectionPoint injectionPoint) {
      return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
   }
}

Testing the Maven project

Ok, you can test your project using the browser by pointing at the home page (home.xhtml)
http://localhost:8080/as7example/index.jsf
jboss maven tutorial
This will display a Welcome page with the user and password text fields. In the lower part of the GUI, you can access to the RESTful service address where you can query for the list of all members: http://localhost:8080/as7example/rest/members or of a single member http://localhost:8080/as7example/rest/members/0
In our example, it will deliver:

<collection>
    <member>
        <email>[email protected]</email>
        <id>0</id>
        <name>John Smith</name>
        <phoneNumber>2125551212</phoneNumber>
    </member>
</collection>

Testing with Arquillian

The Member project ships also with a set of Arquillian test cases which can be used to test your application either on a local application server, or on a remotely running JBoss AS. Before running your Arquillian Test case we need to fix the Arquillian Container dependencies which need to point to the 1.0.0.CR7 release. So open up your pom.xml and fix it:

<dependency>
  <groupId>org.jboss.arquillian.junit</groupId>
  <artifactId>arquillian-junit-container</artifactId>
  <version>1.0.0.CR7</version>
<scope>test</scope>
</dependency>

Then in in your Arquillan configuration file (arquillian.xml) set up the JBoss Home which is needed if you plan to run the test on a remote JBoss AS 7 installation:

<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns="http://jboss.org/schema/arquillian"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://jboss.org/schema/arquillian
        http://jboss.org/schema/arquillian/arquillian_1_0.xsd">

   <!-- Example configuration for a remote JBoss AS 7 instance -->
   <container qualifier="jboss" default="true">
      <protocol type="jmx-as7">
         <property name="executionType">REMOTE</property>
      </protocol>
      <configuration>
         <property name="jbossHome">C:\\jboss-as-7.1.1.Final</property>
      </configuration>
   </container>

</arquillian>

That’s all. The following Maven goal will test your project on a local AS 7:

$ mvn -e clean test -Parq-jbossas-managed

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 14.58 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 25.463s
[INFO] Finished at: Sun Apr 15 14:27:55 CEST 2012
[INFO] Final Memory: 18M/44M
[INFO] ------------------------------------------------------------------------

On the other hand, if you want to test on a remote AS 7 server, use the -Parq-jbossas-remote flag:

$ mvn -e clean test -Parq-jbossas-remote

So with this tutorial we have showed how to create a vanilla Java EE 6 project with Maven and how to import the Maven project into Eclipse. Maven allows a project to build using its project object model (POM) and a set of plugins that are shared by all projects using Maven, providing a uniform build system. Once you familiarize yourself with how one Maven project builds you automatically know how all Maven projects build saving you immense amounts of time when trying to navigate many projects.

Found the article helpful? if so please follow us on Socials