My Wiki!

Java EE project with Maven

Technologies

  • java EE 7
  • EAR package
  • wars, jars, ejb jars
  • java 7
  • maven 3

Composing Basic structure

My Maven Project Structure – Abstract

We are building with Maven, so we need to think in terms of maven pom (s) and modules. In order to create out required ear packaging (see above) we need 5 poms

A pom – acting as a parent
A pom that will contain/define the final ear – responsible for configuring the final package.
A pom that will contain/define the code of the web application, meaning our .war
A pom that will contain/define the code of the ejb-module, the module that we are going to package our EJB(s)
A pom that will contain the classes that are going to be our JPA (Database Entities)

Generating parent project

Create the top-level root:

    mvn archetype:generate \ 
    -DarchetypeGroupId=org.codehaus.mojo.archetypes \
    -DarchetypeArtifactId=pom-root \
    -DgroupId=com.visrc. \
    -DarchetypeVersion=RELEASE

    cd into your newly created root dir.

    For each module: 
    mvn archetype:generate \
    -DarchetypeGroupId=org.apache.maven.archetypes \
    -DarchetypeArtifactId=maven-archetype-quickstart \
    -DgroupId=com.visrc. \
    -DarchetypeVersion=RELEASE

Note that -DarchetypeVersion=RELEASE above will automatically use the latest version of the archetype (Default).

Alternative:

    -Dfilter=pom-root #auto filter archetype list
    # drop -DarchetypeVersion=
    
    Choose archetype:
    1: remote -> org.codehaus.mojo.archetypes:pom-root (Root project archetype for creating multi module projects)
    Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 

Create following structure:

javazine-parent
├── integration-test
│   └── pom.xml        <-- pom-root
├── javazine
│   └── pom.xml        <-- pom-root
├── pom.xml        <-- pom-root
│   
└── README, LICENCE, site, etc.

Create EAR module

  mvn archetype:generate -DgroupId=com.visrc -Dfilter=ear
  filter: ear
  

Interesting archetypes are:

  • org.codehaus.mojo.archetypes:ear-javaee6
  • org.jboss.archetype.eap:jboss-javaee6-webapp-ear-blank-archetype

Let's see. jboss archetype is the complete standalone jboss-ear project so select the mojo one.

Create API module

This contains common code, util.

mvn archetype:generate -DgroupId=com.visrc -Dfilter=org.apache.maven.archetypes:maven-archetype-quickstart

Create webapp (jsf2?) module

This contains common code, util.

mvn archetype:generate -DgroupId=com.visrc -Dfilter=webapp

Again, which archetype

  1. remote → org.codehaus.mojo.archetypes:webapp-javaee6 (-)
  2. org.jboss.archetype.eap:jboss-javaee6-webapp-archetype
  3. org.jboss.spec.archetypes:jboss-javaee6-webapp-blank-archetype

We select the 3rd which supports jboss and a blank project with pom.xml and minimal src.

Create domain (jpa, jta) module

This contains common code, util.

mvn archetype:generate -DgroupId=com.visrc -Dfilter=jpa

Maven dependency for Java EE

From Java EE development point of view, the Maven dependencies can be broadly divided into:

1) Java EE spec API jars (which allows access to spec mandated APIs like javax.ejb.* or javax.http.servlet.* etc...)
2) Application server specific API jars (which allows access to application server specific APIs, like in JBoss you might be interested in @org.jboss.ejb3.annotation.SecurityDomain)
3) Other application specific dependencies (like Hibernate if you are directly dealing with Hibernate instead of JPA)

For #1, it would have been great if those spec API jars were published by Sun/Oracle directly into Maven central. But last time I checked it wasn't there. So JBoss publishes them in their own Maven repo under the group id org.jboss.spec.javax.*. For example, you'll find EJB3.1 spec API jars under org.jboss.spec.javax.ejb:jboss-ejb-api_3.1_spec. If you want to access all Java EE APIs and not just EJB3.1 then you can just depend on the aggregated Java EE6 "pom" dependency like:
view plainprint?
Note: Text content in the code blocks is automatically word-wrapped

    <repositories>
	<repository>
		<id>repository.jboss.org</id>
		<name>JBoss Repository</name>
		<url>http://repository.jboss.org/nexus/content/groups/public-jboss/</url>
	</repository>
    </repositories>


    <dependency>  
      <groupId>org.jboss.spec</groupId>  
      <artifactId>jboss-javaee-6.0</artifactId>  
      <version>2.0.0.Final</version>  
      <type>pom</type>  
    </dependency>  



For #2, it's a bit more difficult. Each project within the AS publishes it at a different location. The JBoss EJB3 specific jar which contains the JBoss specific EJB3 annotations (for example) can be found at org.jboss.ejb3:jboss-ejb3-ext-api.

For #3, I usually start looking for the artifacts under the name of their package. For example, If I need Hiberante jars, I look for them under org.hibernate groupd id.

I do agree that it isn't straightforward to narrow down on the group id and artifact id.

Quickstart

  mvn archetype:generate 
  jboss-javaee6-archetype-ear
  cp -rp web webX
  cp -rp ejb ejbX
  # jar?
  mvn standard archetype
  

Root pom.xml

TBD

Module 1: EJB - the domain entities

Backgroud: Java Persistence

  • Object-relational mapping
  • Managing persistence object:
    • Query entities
    • Callbacks & listeners

For each Entity:

  1. The Entity class with JPA annotation e.g. @Entity @Column
  2. The JPA descriptor: src/main/resources/META-INF/persistence.xml
  3. The class, which provide insert, delete, udpate methods
  4. Test class & test setup

Object Relational Mapping

Creating Entity:

forge> [model]$ jpa-new-entity --named Blog --targetPackage com.visrc.javazine.domain.model --idStrategy IDENTITY 


Create Test:

[javazine-domain] javazine-domain $ arquillian setup --containerName WILDFLY_REMOTE 

[javazine-domain] javazine-domain $ arquillian create-test --class com.visrc.javazine.domain.model.User.java --enableJPA
Picked up type <JavaResource>: com.visrc.javazine.domain.model.UserTest
Wrote /home/dang/data/workspace/70_project_vfoss/vfoss_javaee_dev_ws/javazine-parent/javazine/javazine-domain/src/test/java/com/visrc/javazine/domain/model/UserTest.java

Optional: Fix pom.xml, *.xml

Start pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    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>org.arquillian.example</groupId>
    <artifactId>arquillian-tutorial</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>arquillian-tutorial</name>
    <url>http://arquillian.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

New pom.xml. Edit or generate with forge:

 $ project add-dependency org.jboss.spec:jboss-javaee-6.0:1.0.0.Final:provided:pom

You’ll also need to add JUnit 4.8, the minimum required version of JUnit to use Arquillian, as a test-scoped dependency:

 $ project add-dependency junit:junit:4.8.1:test

Forge adds the JBoss Community repository to the pom.xml file. This repository is not required to use Arquillian. (However, you can keep it if you are using other libraries only available in the JBoss Community repository). If you do decide to remove the repository, you can easily do so using the following Forge command:

 $ project remove-repository http://repository.jboss.org/nexus/content/groups/public 

The result of the pom.xml that Forge generates is shown below:

<?xml version="1.0" encoding="UTF-8"?>
<project 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"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.arquillian.example</groupId>
    <artifactId>arquillian-tutorial</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.jboss.spec</groupId>
            <artifactId>jboss-javaee-6.0</artifactId>
            <version>1.0.0.Final</version>
            <type>pom</type>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.arquillian.junit</groupId>
            <artifactId>arquillian-junit-container</artifactId>
            <scope>test</scope>
        </dependency>
 
    </dependencies>
    <build>
        <finalName>arquillian-tutorial</finalName>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
 
    <profiles>
    <profile>
      <id>arquillian-wildfly-remote</id>
      <build>
        <plugins>
          <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.14.1</version>
            <configuration>
              <systemPropertyVariables>
                <arquillian.launch>wildfly-remote</arquillian.launch>
              </systemPropertyVariables>
            </configuration>
          </plugin>
        </plugins>
      </build>
      <dependencies>
        <dependency>
          <groupId>org.wildfly</groupId>
          <artifactId>wildfly-arquillian-container-remote</artifactId>
          <version>8.1.0.Final</version>
        </dependency>
      </dependencies>
    </profile>
 
    </profiles>
</project>

[dang@localhost javazine-domain]$ cat src/test/resources/arquillian.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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">
  <container qualifier="wildfly-remote"/>
  <container qualifier="wildfly-managed"/>
</arquillian>

Modify generated test class:

package com.visrc.javazine.domain.model;

import java.util.List;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import com.visrc.javazine.domain.model.User;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.UserTransaction;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.junit.Assert;
import org.junit.Test;
import org.junit.Before;
import org.junit.After;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
import static org.hamcrest.core.Is.*;

@RunWith(Arquillian.class)
public class UserTest
{

   @Deployment
   public static JavaArchive createDeployment()
   {
      return ShrinkWrap.create(JavaArchive.class, "test.jar")
            .addClass(User.class)
						.addAsManifestResource("META-INF/persistence.xml",
								"persistence.xml")
            .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
   }

	 private static final String[] USERS = {
			"Max",
			"Paul",
			"Pat"
	 };
   
	 @PersistenceContext
	 EntityManager em;

	 @Inject
	 UserTransaction utx;

	 @Inject
	 User user;

	 @Test
	 public void should_be_deployed()
	 {
		 Assert.assertNotNull(user);
	 }
}

Run Test:

[javazine-domain] model $ test --profile arquillian-wildfly-remote 

Managing Persistence Objects

Entities manager provides methods to work with persistence objects: CRUD. They are known as DAO, (Persistence) Repository, EJB EntityBean…

DAOs inject EntityManager and delegate management methods. They are EJBs so dependencies need to be updated:

 forge> ejb setup
 

Write DAO Beans

With Forge:

 ejb new-ejb --named UserRepository --package com.visrc.javazine.domain.model.persistence --type STATELESS
 
package com.visrc.javazine.domain.persistence;
 
import java.util.List;
 
import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
 
import javax.ejb.Stateless;
import javax.ejb.LocalBean;
 
import com.visrc.javazine.domain.model.User;
 
@Stateless
@LocalBean
public class UserRepository
{
 
	@PersistenceContext
	private EntityManager em;
 
	public List<User> listUsers() {
		return em.createQuery("select u from User u").getResultList();
	}
 
	@PostConstruct
	public void insertTestData() {
		User user = new User();
		user.setName = "john doe";
		em.persist(user);
 
		User user = new User();
		user.setName = "paul the fish";
		em.persist(user);
 
		User user = new User();
		user.setName = "turtle ninja";
		em.persist(user);
	}
}

Test

Create test:

 forge> arquillian create-test --class org.arquillian.example.dao.LanguageDao.java --enableJPA
 

Edit the generated code:

package com.visrc.javazine.domain.persistence;
 
import com.visrc.javazine.domain.persistence.UserRepository;
import javax.inject.Inject;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
import static org.hamcrest.core.Is.*;
 
@RunWith(Arquillian.class)
public class UserRepositoryTest
{
 
   @Inject
   private UserRepository userrepository;
 
   @Deployment
   public static JavaArchive createDeployment()
   {
      return ShrinkWrap.create(JavaArchive.class, "test.jar")
            .addClass(User.class, UserRepository.class)
            .addAsManifestResource("META-INF/persistence.xml", "persistence.xml")
            .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
   }
 
   @Test
   public void test_list_users()
   {
      //Assert.assertNotNull(userrepository);
		 Assert.assertEquals(3, userrepository.listUsers().size());
   }
}

Module x: EAR


Navigation