Table of Contents
Java EE project with Maven
- Domain driven development: http://www.infoq.com/articles/ddd-in-practice
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
- remote → org.codehaus.mojo.archetypes:webapp-javaee6 (-)
- org.jboss.archetype.eap:jboss-javaee6-webapp-archetype
- 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:
- The Entity class with JPA annotation e.g. @Entity @Column
- The JPA descriptor: src/main/resources/META-INF/persistence.xml
- The class, which provide insert, delete, udpate methods
- 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()); } }