Red Hat

The goal of this blog post is to walk you through an Java EE 6 application from a simple, static web page until we have a full blown stack that consist of the stuff in the list below. I'm calling this stack Summer because after a long, hard winter Spring may be nice but boy, wait until Summer kicks in ;-)

  • CDI (Weld)
  • JSF 2 (facelets, ICEfaces 2)
  • JPA 2 (Hibernate, Envers)
  • EJB 3.1 (no-local-view, asynchronous, singletons, scheduling)
  • Bean Validation (Hibernate Validator)
  • JMS (MDB)
  • JAX-RS (RESTEasy)
  • JAX-WS
  • Arqullian (incontainer-AS6)

We will pack all this in a single WAR. Just because we can (spoiler: in part IV). Noticed that apart from the component and testing frameworks, they are all standards? That's a lot of stuff. Fortunately, the appserver already provides most of the stuff so you're app will still be reasonably small.

As for the environment I'm using

  • Eclipse (Galileo SR2)
  • JBoss 6.0 M3
  • Maven 3 (beta1)
  • Sun JDK 6
  • m2eclipse 0.10

This will not be your typical blog post where everything goes well - we will hit bugs. There will be curses, blood and guts and drama and we will do workarounds and rewrites as we move along. Pretty much the same as your average day as a software developer probably looks like. I'm also no expert in the technologies I use here so there are probably things that could be done better. Consider this more of a write-down of my experiences in EE6-land that will probably mirror what others are going through. I will also not point you to links or additional information, I assume that if I say RESTEasy, you can google up more information if you are interested.

And I almost forgot: don't panic.

In the beginning: Project setup

So, lets start things off - go and download the stuff mentioned in the environment if you don't already have it. I'm not going to insult your intelligense by walking you through that (remind me to insult it later). Besides, it's pretty straightforward.

Let's make a new Maven project (File -> New -> Project... -> Maven -> Maven Project. We skip the archetype selection and just make a simple project with group id com.acme, artifact id Greetings of version 1.0.0-SNAPSHOT packed as a WAR. Now finish the wizard and now you should have a nice, perfect project. It will never be this perfect again as our next step is adding code to it.

Maven tip-of-the-day for Windows users. Google up on how you change the path to your local repo as it might be somewhere under Documents And Settings which has two effects: classpath gets huge and there could be problems due to the spaces. Change it to something like c:\java\m2repo

The first thing we notice that m2eclipse has J2SE-1.4 as default. How 2002. Besides, that will make using annotations impossible so lets change that. Edit the pom.xml and throw in

<build>
	<plugins>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-compiler-plugin</artifactId>
			<version>2.3.1</version>
			<configuration>
				<source>1.6</source>
				<target>1.6</target>
			</configuration>
		</plugin>
	</plugins>
</build>

Save and right-click the project root and go Maven -> Update Project Configuration. Aah, that's better

JSF

Let's wake up JSF. We create a folder WEB-INF in src/main/webapp and throw in a web.xml because no web app is complete without it (enforced by the maven war plugin). OK, actually this can be configured in the plugin but let's keep the web.xml since we'll need it later.

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"/>

and an empty faces-config.xml next to it

<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
              version="2.0"/>

and in webapp we add a greeting.xhtml like

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:ui="http://java.sun.com/jsf/facelets">
	<h:head>
		<title>
			Greetings
		</title>
	</h:head>

	<h:body>
		<h:outputText value="Hello world"/>
	</h:body>
</html>

Will it blend? I mean, will it deploy? Do a mvn clean package and you should have a Greetings-1.0.0-SNAPSHOT in your projects target directory. Throw it into the AS server/default/deploy directory and start up the server and go to

http://localhost:8080/Greetings-1.0.0-SNAPSHOT/faces/greetings.xhtml

The url is not pretty, but the server port, web context root, welcome files and JSF mappings can all be tuned later, let's focus on technologies and dependencies for now. But wait - at which point did we define the JSF servlet and mappings in web.xml? We didn't. It's automagic for JSF-enabled applications.

EJB and CDI

Next step is bringing in some backing beans, let's outsource our greeting. We make a stateless EJB and use it in CDI

package com.acme.greetings;

@Stateful
@Model
public class GreetingBean 
{
	public String getGreeting()
	{
		return "Hello world";
	}
}

The @Stateful defines a stateful session EJB (3.1 since it's a POJO) and the @Model is a CDI stereotype that is @RequestScoped and @Named (which means the lifecycle is bound to a single HTTP request and it has a name that can be referenced in EL and defaults to greetingBean in this case). But we have a problem - the annotations don't resolve to anything. So we need to pick them up from somewhere(tm). Fortunately we can have all the APIs picked up for us by adding the following to our pom.xml

<dependencies>
	<dependency>
		<groupId>org.jboss.spec</groupId>
		<artifactId>jboss-javaee-6.0</artifactId>
		<version>1.0.0.Beta4</version>
		<type>pom</type>
		<scope>provided</scope>
	</dependency>
</dependencies>

Sun Java API artifacts are a bit amusing since getting hold of them can be a bit tricky. First they publish them in the JSR:s and then they treat them like they're top secret. Fortunately Glassfish and now JBoss have started making them available in their repositories (although under their own artifact names, but still)...

We also need to make sure we have set up the JBoss repositories for this according to http://community.jboss.org/wiki/MavenGettingStarted-Users. Have a look at what happened in the projects Maven Dependencies. Good. Now close it and back away. It's getting hairy in there so better trust Maven to keep track of the deps from now on.

The imports should now be available in our bean so we import

import javax.ejb.Stateful;
import javax.enterprise.inject.Model;

and EL-hook the bean up with

<h:body>
	<h:outputText value="#{greetingBean.greeting}"/>
</h:body>

in greetings.xhtml.

Just as no web application is complete without web.xml, no CDI application is complete without beans.xml. Let's add it to WEB-INF

<?xml version="1.0" encoding="ISO-8859-1"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
      http://java.sun.com/xml/ns/javaee 
      http://java.sun.com/xml/ns/javaee/beans_1_0.xsd" />

Package and redeploy. We get a warning about encoding when compiling so lets add this to our pom.xml

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

Back to http://localhost:8080/Greetings-1.0.0-SNAPSHOT/faces/greetings.xhtml SUCCESS! No. Wait. Huge stack trace hits you for 300 points of damage. Let's back up on our EJB, there are still some issues with 3.1 style EJBs in WAR-only-packaging on AS 6 M3. Remove the @Stateful annotation and it becomes a normal CDI managed POJO. Repackage. Redploy. Recoyce.

Testing

Testing is hip nowadays so let's bring in Arquillian. Arquillian is the latest and greatest in EE testing (embedded or incontainer). Start using it now. In a year or so when everone else catch up you can go I've been using it since Alpha. Add the following property to pom.xml:

<arquillian.version>1.0.0.Alpha2</arquillian.version>

and these deps

<dependency>
	<groupId>org.jboss.arquillian</groupId>
	<artifactId>arquillian-junit</artifactId>
	<version>${arquillian.version}</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.8.1</version>
	<scope>test</scope>
</dependency>

and this profile

<profiles>	
	<profile>
		<id>jbossas-local-60</id>
		<dependencies>
			<dependency>
				<groupId>org.jboss.arquillian.container</groupId>
				<artifactId>arquillian-jbossas-local-60</artifactId>
				<version>1.0.0.Alpha2</version>
			</dependency>
			<dependency>
				<groupId>org.jboss.jbossas</groupId>
				<artifactId>jboss-server-manager</artifactId>
				<version>1.0.3.GA</version>
			</dependency>
			<dependency>
				<groupId>org.jboss.jbossas</groupId>
				<artifactId>jboss-as-client</artifactId>
				<version>6.0.0.20100429-M3</version>
				<type>pom</type>
			</dependency>
		</dependencies>
	</profile>
</profiles>

Maven will probably now download the entire internet for you.

Let's write our first test and place it in the test source folder:

package com.acme.greetings.test;

import javax.inject.Inject;

import org.jboss.arquillian.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ArchivePaths;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.jboss.shrinkwrap.impl.base.asset.ByteArrayAsset;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

import com.acme.greetings.GreetingBean;

@RunWith(Arquillian.class)
public class GreetingTest 
{
	@Inject
	GreetingBean greetingBean;

	@Deployment
	public static JavaArchive createTestArchive() 
	{
		return ShrinkWrap.create("test.jar", JavaArchive.class).addClass(
				GreetingBean.class).addManifestResource(
				new ByteArrayAsset("<beans/>".getBytes()),
				ArchivePaths.create("beans.xml"));
	}

	@Test
	public void testInjection() 
	{
		Assert.assertEquals("Hello World", greetingBean.getGreeting());
	}

}

and then we try it out with mvn test -Pjbossas-local-60. If we have the AS running we can save some time, otherwise the manager will start it automagically. Setting the JBOSS_HOME env helps. What happens here is we use Shrinkwrap to create a deployment which consist of our GreetingBean and an empty beans.xml file (for CDI) and the bean is then injected for use in our tests.

This concludes Part I. In part II we will set up ICEfaces and expand our application and in part III we'll set up JPA. Part IV is for MDB and EJB and part V for adding JAX-RS and JAX-WS for importing and exporting stuff.

back to top