Java EE 6 is out and it indeed offers an interesting stack of technologies. So in today’s tutorial we are going to build a small sample web application that builds on this stack using Enterprise JavaBeans, Java Persistence API, Bean Validation, CDI and finally Java Server Faces and PrimeFaces.

The application we’re going to develop is a simple blog app that allows us to create new articles, list them and – finally delete them. We’re also covering some additional topics like JSF navigation, i18n, Ajax-enabled components and the deployment on the GlassFish application server.

Prerequisites

We do not need anything exotic for this tutorial .. just a JDK, Maven and a GlassFish. I’ve chosen the last one because of its status as Java EE 6 reference implementation but inbetween JBoss released a new version that supports the Java EE 6 Web Profile so perhaps it is also worth a look …

Our Technology Stack

We’re going to cover a lot of specifications so here is a brief overview of technologies used in the following tutorial:

Java EE 6 Blog Application Layers
Figure 1. Technology stack

Build management: Maven, Maven EJB Plugin and others ..

Application Server: GlassFish 3 (running on my Ubuntu machine)

Persistence: Java Persistence API as abstraction layer, TopLink/EclipseLink as persistence provider, JavaDB/Derby as concrete underlying RDBMS

Inversion of Control/Dependency Injection: CDI specs using Weld

Validation: Bean Validation / JSR 303

Presentation Tier: Java Server Faces 2 / Mojarra and PrimeFaces 2.2

Middle Tier: Enterprise Java Beans 3.1

Project Setup using Maven Archetypes

We’re too lazy to create a project skeleton and add dependencies by hand so we’re using an archetype to create our project..

  • Create a new Maven project using the webapp-javaee6 (org.codehaus.mojo.archetype:webapp-javaee6) archetype with your favourite IDE and Maven plugin or via console

    mvn archetype:generate
    [INFO] Scanning for projects...
    [..]
    254: remote -> webapp-javaee6 (Archetype for a web application using Java EE 6.)
    [..]
    Choose version:
    1: 1.0
    2: 1.0.1
    3: 1.0.2
    4: 1.1
    5: 1.2
    6: 1.3
    Choose a number: 6: 6
    Define value for property 'groupId': : com.hascode.tutorial.jee6
    Define value for property 'artifactId': : jee6-blog-tutorial
    Define value for property 'version': 1.0-SNAPSHOT: 0.0.1
    Define value for property 'package': com.hascode.tutorial.jee6: com.hascode.tutorial.jee6.blog
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESSFUL
    [INFO] ------------------------------------------------------------------------
  • Add the following dependencies and repositories – your pom.xml should look like this one

    <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>com.hascode.tutorial.jee6</groupId>
    	<artifactId>jee6-blog-tutorial</artifactId>
    	<version>0.0.1</version>
    	<packaging>war</packaging>
    
    	<name>hasCode.com Java EE 6 Blog Tutorial</name>
    
    	<properties>
    		<endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    	</properties>
    
    	<dependencies>
    		<dependency>
    			<groupId>javax</groupId>
    			<artifactId>javaee-web-api</artifactId>
    			<version>6.0</version>
    			<scope>provided</scope>
    		</dependency>
    		<dependency>
    			<groupId>javax.validation</groupId>
    			<artifactId>validation-api</artifactId>
    			<version>1.0.0.GA</version>
    			<scope>provided</scope>
    		</dependency>
    		<dependency>
    			<groupId>org.hibernate</groupId>
    			<artifactId>hibernate-validator</artifactId>
    			<version>4.0.2.GA</version>
    		</dependency>
    		<dependency>
    			<groupId>junit</groupId>
    			<artifactId>junit</artifactId>
    			<version>4.8.2</version>
    			<scope>test</scope>
    		</dependency>
    		<dependency>
    			<groupId>org.apache.derby</groupId>
    			<artifactId>derby</artifactId>
    			<version>10.6.1.0</version>
    		</dependency>
    		<dependency>
    			<groupId>org.primefaces</groupId>
    			<artifactId>primefaces</artifactId>
    			<version>2.2</version>
    		</dependency>
    		<dependency>
    			<groupId>javax.faces</groupId>
    			<artifactId>jsf-api</artifactId>
    			<version>2.0</version>
    			<scope>provided</scope>
    		</dependency>
    	</dependencies>
    
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.apache.maven.plugins</groupId>
    				<artifactId>maven-compiler-plugin</artifactId>
    				<version>2.3.2</version>
    				<configuration>
    					<source>1.6</source>
    					<target>1.6</target>
    					<compilerArguments>
    						<endorseddirs>${endorsed.dir}</endorseddirs>
    					</compilerArguments>
    				</configuration>
    			</plugin>
    			<plugin>
    				<groupId>org.apache.maven.plugins</groupId>
    				<artifactId>maven-war-plugin</artifactId>
    				<version>2.1</version>
    				<configuration>
    					<failOnMissingWebXml>false</failOnMissingWebXml>
    				</configuration>
    			</plugin>
    			<plugin>
    				<groupId>org.apache.maven.plugins</groupId>
    				<artifactId>maven-dependency-plugin</artifactId>
    				<version>2.1</version>
    				<executions>
    					<execution>
    						<phase>validate</phase>
    						<goals>
    							<goal>copy</goal>
    						</goals>
    						<configuration>
    							<outputDirectory>${endorsed.dir}</outputDirectory>
    							<silent>true</silent>
    							<artifactItems>
    								<artifactItem>
    									<groupId>javax</groupId>
    									<artifactId>javaee-endorsed-api</artifactId>
    									<version>6.0</version>
    									<type>jar</type>
    								</artifactItem>
    							</artifactItems>
    						</configuration>
    					</execution>
    				</executions>
    			</plugin>
    			<plugin>
    				<groupId>org.apache.maven.plugins</groupId>
    				<artifactId>maven-ejb-plugin</artifactId>
    				<configuration>
    					<ejbVersion>3.1</ejbVersion>
    					<archive>
    						<manifest>
    							<addClasspath>true</addClasspath>
    						</manifest>
    					</archive>
    				</configuration>
    			</plugin>
    		</plugins>
    		<finalName>jee6-blog-tutorial</finalName>
    	</build>
    
    	<repositories>
    		<repository>
    			<id>maven2-repository.dev.java.net</id>
    			<name>Java.net Repository for Maven</name>
    			<url>http://download.java.net/maven/2</url>
    		</repository>
    		<repository>
    			<id>JBoss repository</id>
    			<url>http://repository.jboss.com/maven2/</url>
    		</repository>
    		<repository>
    			<id>EclipseLink Repo</id>
    			<url>http://www.eclipse.org/downloads/download.php?r=1&amp;nf=1&amp;file=/rt/eclipselink/maven.repo</url>
    			<snapshots>
    				<enabled>true</enabled>
    			</snapshots>
    		</repository>
    		<repository>
    			<id>primefaces-repo</id>
    			<name>Prime Technology Maven Repository</name>
    			<url>http://repository.primefaces.org</url>
    			<layout>default</layout>
    		</repository>
    	</repositories>
    </project>
  • Remove the created index.jsp from src/main/webapp and add the directories for resources, WEB-INF and META-INF

    mkdir -p src/main/resources
    mkdir -p src/main/webapp/WEB-INF
    mkdir -p src/main/resources/META-INF
  • Create an empty file named beans.xml in src/main/webapp/WEB-INF – we need it for CDI .. you’re asking why? Gavin King kindly gives an explanation.

    touch src/main/webapp/WEB-INF/beans.xml

Entity creation using the Java Persistence API 2 / JSR-317

We want to save our blog entries in the persistence layer .. what we’re going to store is a title, the author, the date the entry was created and of course some text content.

  • First create a new class named BlogEntry in the package com.hascode.tutorial.jee6.blog.entity

    package com.hascode.tutorial.jee6.blog.entity;
    
    import java.io.Serializable;
    import java.util.Date;
    
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.Lob;
    import javax.persistence.PrePersist;
    import javax.persistence.Temporal;
    import javax.persistence.TemporalType;
    
    @Entity
    public class BlogEntry implements Serializable {
    	private static final long	serialVersionUID	= 1L;
    
    	@Id
    	@GeneratedValue
    	private Long				id;
    
    	private String				title;
    
    	@Lob
    	private String				content;
    
    	private String				author;
    
    	@Temporal(TemporalType.TIMESTAMP)
    	private Date				created = new Date();
    
    	@PrePersist
    	private void onCreate() {
    		created = new Date();
    	}
    
    	/**
    	 * @param id
    	 *            the id to set
    	 */
    	public void setId(Long id) {
    		this.id = id;
    	}
    
    	/**
    	 * @return the title
    	 */
    	public String getTitle() {
    		return title;
    	}
    
    	/**
    	 * @param title
    	 *            the title to set
    	 */
    	public void setTitle(String title) {
    		this.title = title;
    	}
    
    	/**
    	 * @return the content
    	 */
    	public String getContent() {
    		return content;
    	}
    
    	/**
    	 * @param content
    	 *            the content to set
    	 */
    	public void setContent(String content) {
    		this.content = content;
    	}
    
    	/**
    	 * @return the author
    	 */
    	public String getAuthor() {
    		return author;
    	}
    
    	/**
    	 * @param author
    	 *            the author to set
    	 */
    	public void setAuthor(String author) {
    		this.author = author;
    	}
    
    	/**
    	 * @return the created
    	 */
    	public Date getCreated() {
    		return created;
    	}
    
    	/**
    	 * @param created
    	 *            the created to set
    	 */
    	public void setCreated(Date created) {
    		this.created = created;
    	}
    
    	/**
    	 * @return the id
    	 */
    	public Long getId() {
    		return id;
    	}
    }

So what have we done here?

  • The minimal set of annotations we need is @Entity and @Id - we have added those to the entity class and to the field id

  • We don’t want to set our primary key by hand so we’re adding the @GeneratedValue annotation to our id

  • The text from a blog entry might take some space so we’re predicting that with the @LoB annotation

  • We want to save the creation date of a blog entry as a timestamp and not a date – that’s why we add @Temporal(TemporalType.TIMESTAMP)

  • To set the creation date on our first persist we’re using @PrePersist

Now we need to define a persistence unit ..

  • First we create a new xml file named persistence.xml in src/main/resources/META-INF

  • We’re using EclipseLink/TopLink as PersistenceProvider

  • Because we’re lazy we’re going to use the GlassFish embedded database default” via JNDI “jdbc/default”.SCREENSHOT

  • Out persistence unit is named “defaultPersistenceUnit

  • We definitely want container managed transactions/CTM here that’s why we choose JTA as transaction-type

  • At last we want EclipseLink to create the tables for our entity so the value for the property eclipselink.ddl-generation is “create-tables“. You might want to change this for another persistence unit for integration tests later.

  • Finally the persistence.xml should look like this one

    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="1.0"
    	xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
    	<persistence-unit name="defaultPersistenceUnit"
    		transaction-type="JTA">
    		<provider>oracle.toplink.essentials.PersistenceProvider</provider>
    		<jta-data-source>jdbc/__default</jta-data-source>
    		<class>com.hascode.tutorial.jee6.blog.entity.BlogEntry</class>
    		<properties>
    			<property name="eclipselink.ddl-generation" value="create-tables" />
    		</properties>
    	</persistence-unit>
    </persistence>

Validation using Bean Validation / JSR-303

Now that we have defined our entities and persistence unit we should add some validation rules not to allow to save invalid blog entries.

For a closer look and more detailed information about bean validation and jsr-303 take a look at my article: “https://www.hascode.com/2010/12/bean-validation-with-jsr-303-and-hibernate-validator/[Bean Validation with JSR-303 and Hibernate Validator]”

The rules that we’re going to define are ..

  • The title must not be null and its length must be between 10 and 100 characters

  • The content must not be null and its length must be between 300 and 4000 characters

  • The author must not be null and its length must be between 10 and 40 characters

  • The creation date, created should lie in the past

  • Applying some annotations from the bean validation API like @NotNull, @Size, @Past our BlogEntry’s fields look like this

    package com.hascode.tutorial.jee6.blog.entity;
    
    import java.io.Serializable;
    import java.util.Date;
    
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.Lob;
    import javax.persistence.PrePersist;
    import javax.persistence.Temporal;
    import javax.persistence.TemporalType;
    import javax.validation.constraints.NotNull;
    import javax.validation.constraints.Past;
    import javax.validation.constraints.Size;
    
    @Entity
    public class BlogEntry implements Serializable {
    	private static final long	serialVersionUID	= 1L;
    
    	@Id
    	@GeneratedValue
    	private Long				id;
    
    	@NotNull
    	@Size(min = 10, max = 100)
    	private String				title;
    
    	@Lob
    	@NotNull
    	@Size(min = 300, max = 4000)
    	private String				content;
    
    	@NotNull
    	@Size(min = 10, max = 40)
    	private String				author;
    
    	@Past
    	@Temporal(TemporalType.TIMESTAMP)
    	private Date				created = new Date();
    
    	@PrePersist
    	private void onCreate() {
    		created = new Date();
    	}
    
    	/**
    	 * @param id
    	 *            the id to set
    	 */
    	public void setId(Long id) {
    		this.id = id;
    	}
    
    	/**
    	 * @return the title
    	 */
    	public String getTitle() {
    		return title;
    	}
    
    	/**
    	 * @param title
    	 *            the title to set
    	 */
    	public void setTitle(String title) {
    		this.title = title;
    	}
    
    	/**
    	 * @return the content
    	 */
    	public String getContent() {
    		return content;
    	}
    
    	/**
    	 * @param content
    	 *            the content to set
    	 */
    	public void setContent(String content) {
    		this.content = content;
    	}
    
    	/**
    	 * @return the author
    	 */
    	public String getAuthor() {
    		return author;
    	}
    
    	/**
    	 * @param author
    	 *            the author to set
    	 */
    	public void setAuthor(String author) {
    		this.author = author;
    	}
    
    	/**
    	 * @return the created
    	 */
    	public Date getCreated() {
    		return created;
    	}
    
    	/**
    	 * @param created
    	 *            the created to set
    	 */
    	public void setCreated(Date created) {
    		this.created = created;
    	}
    
    	/**
    	 * @return the id
    	 */
    	public Long getId() {
    		return id;
    	}
    }

The middle tier using Enterprise Java Beans / EJB 3.1 – JSR-318

We’re going to use a stateless session bean to provide methods to save, delete and list our blog entry entities. We don’t define any local or remote interfaces to keep it simple here.

  • Create a new package named com.hascode.tutorial.jee6.blog.ejb and a class named BlogEntryEJB

    package com.hascode.tutorial.jee6.blog.ejb;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.ejb.Stateless;
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    import javax.persistence.Query;
    
    import com.hascode.tutorial.jee6.blog.entity.BlogEntry;
    
    @Stateless
    public class BlogEntryEJB {
    	@PersistenceContext(unitName = "defaultPersistenceUnit")
    	private EntityManager	em;
    
    	public BlogEntry saveBlogEntry(BlogEntry blogEntry) {
    		em.persist(blogEntry);
    		return blogEntry;
    	}
    
    	public List<BlogEntry> findBlogEntries() {
    		final Query query = em.createQuery("SELECT b FROM BlogEntry b ORDER BY b.created DESC");
    		List<BlogEntry> entries = query.getResultList();
    		if (entries == null) {
    			entries = new ArrayList<BlogEntry>();
    		}
    		return entries;
    	}
    
    	public void deleteBlogEntry(BlogEntry blogEntry) {
    		blogEntry = em.merge(blogEntry);
    		em.remove(blogEntry);
    	}
    }
  • The unit name that we’re using in @PersistenceContext should correspond to the defined named in our persistence.xml

Creating the presentation layer using Java Server Faces 2 and PrimeFaces

First we’re creating some facelets and decorators .. to keep this example simple we’re only creating a view to create new blog entries and a view that lists existing entries.

  • First we’re creating a decorator template named _decorator.xhtml in src/main/webapp

  • There are three fields in the decorator that may be overridden by our concrete views: the title, the heading and the body:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml"
    	xmlns:h="http://java.sun.com/jsf/html"
    	xmlns:f="http://java.sun.com/jsf/core"
    	xmlns:ui="http://java.sun.com/jsf/facelets"
    	xmlns:p="http://primefaces.org/ui">
    <h:head>
    	<title><ui:insert name="title">hasCode.com - Java EE 6 Blog Tutorial</ui:insert></title>
    	<link rel="stylesheet" type="text/css" href="css/style.css" />
    </h:head>
    <h:body>
    	<p:panel>
    		<h:panelGrid columns="2" cellpadding="10">
    			<img src="image/logo.png" width="100" height="100" alt="hasCode.com logo"/>
    			<h:panelGroup>
    				<h1><ui:insert name="heading">Java EE 6 Tutorial - Blog Application</ui:insert></h1>
    			</h:panelGroup>
    		</h:panelGrid>
    		<ui:insert name="body">Welcome to the tutorial .. you should never see this content ;)</ui:insert>
    	</p:panel>
    </h:body>
    </html>
  • The ugly logo image is saved in src/main/webapp/image, the cascading stylesheets in src/main/webapp/css - this is my style.css

    h1 {
     font-size: 34px;
    }
    .errorMsg {
     background-color:#D97C7C;
     color:#A30000;
     display:block;
     padding:5px;
    }
  • Now we want to display available blog entries this is my view list.xhtml

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml"
    	xmlns:h="http://java.sun.com/jsf/html"
    	xmlns:f="http://java.sun.com/jsf/core"
    	xmlns:ui="http://java.sun.com/jsf/facelets"
    	xmlns:p="http://primefaces.org/ui">
    <ui:composition template="/_decorator.xhtml">
    	<ui:define name="title">
    		<h:outputText value="hasCode.com - Java EE 6 Blog Tutorial - Blog entries overview" />
    	</ui:define>
    	<ui:define name="heading">
    		<h:outputText value="Blog entries overview" />
    	</ui:define>
    	<ui:define name="body">
    		<h:form>
    			<h:commandButton title="Create new article"
    				value="Create new article" action="create" />
    			<br />
    			<hr />
    			<br />
    			<p:panel
    				header="#{blogEntryBean.getBlogEntries().size()} Blog entries available"
    				toggleable="true" closable="true" toggleSpeed="500">
    				<ui:repeat value="#{blogEntryBean.getBlogEntries()}" var="entry">
    					<h:panelGrid columns="2" cellpadding="10">
    						<f:facet name="header">
    							<h:outputText value="#{entry.title}" />
    						</f:facet>
    						<h:outputText value="Author: #{entry.author}" />
    						<h:panelGroup id="pGroup">
    							<h:outputText value="#{entry.content}" />
    							<p:contextMenu for="pGroup">
    								<p:menuitem value="Delete"
    									actionListener="#{blogEntryBean.delete(entry)}" update="@form" />
    							</p:contextMenu>
    						</h:panelGroup>
    						<f:facet name="footer">
    							<h:outputText value="Created: #{entry.created}" />
    						</f:facet>
    					</h:panelGrid>
    					<hr />
    				</ui:repeat>
    			</p:panel>
    		</h:form>
    	</ui:define>
    </ui:composition>
    </html>
  • We’re filling the decorator using ui:composition and ui:repeat to iterate over a list of available blog entries

  • PrimeFaces gives us nice panels and ajax-delete via context menu, nice is the @form expression that forces the form to update its content

  • We should adjust our web.xml in src/main/webapp/WEB-INF/ to load JSF and display list.xhtml per default

    <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     version="3.0"
     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
     <display-name>jee6-blog-tutorial</display-name>
     <servlet>
     <servlet-name>Faces Servlet</servlet-name>
     <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
     </servlet>
     <servlet-mapping>
     <servlet-name>Faces Servlet</servlet-name>
     <url-pattern>*.xhtml</url-pattern>
     </servlet-mapping>
     <welcome-file-list>
     <welcome-file>/list.xhtml</welcome-file>
     </welcome-file-list>
    </web-app>
  • Now we need a ManagedBean to link our views to the middle tier so we’re creating a class named BlogEntryBean in com.hascode.tutorial.jee6.blog.controller

    package com.hascode.tutorial.jee6.blog.controller;
    
    import java.util.List;
    
    import javax.enterprise.context.RequestScoped;
    import javax.inject.Inject;
    import javax.inject.Named;
    
    import com.hascode.tutorial.jee6.blog.ejb.BlogEntryEJB;
    import com.hascode.tutorial.jee6.blog.entity.BlogEntry;
    
    @Named(value = "blogEntryBean")
    @RequestScoped
    public class BlogEntryBean {
    	@Inject
    	private BlogEntryEJB	blogEntryEJB;
    
    	private BlogEntry		blogEntry	= new BlogEntry();
    
    	/**
    	 * @return the blogEntries
    	 */
    	public List<BlogEntry> getBlogEntries() {
    		return blogEntryEJB.findBlogEntries();
    	}
    
    	/**
    	 * @return the blogEntry
    	 */
    	public BlogEntry getBlogEntry() {
    		return blogEntry;
    	}
    
    	/**
    	 * @param blogEntry
    	 *            the blogEntry to set
    	 */
    	public void setBlogEntry(BlogEntry blogEntry) {
    		this.blogEntry = blogEntry;
    	}
    
    	public String saveBlogEntry() {
    		blogEntryEJB.saveBlogEntry(blogEntry);
    		return "success";
    	}
    
    	public void delete(BlogEntry blogEntry) {
    		blogEntryEJB.deleteBlogEntry(blogEntry);
    	}
    }
  • Please note that we’re using CDI for dependency injection here – that means @Named instead of @ManagedBean, @Inject instead of @EJB and javax.enterprise.context.RequestScoped instead of javax.faces.bean.RequestScoped!!

  • In the next step we should add some i18n and move our messages and text content to a resource bundle so create a____directory src/main/resources/com/hascode/tutorial/jee6/blog and a new file named messages.properties in this directory.

    mkdir -p src/main/resources/com/hascode/tutorial/jee6/blog
  • Now we’ve got to register this resource bundle – we’re doing this via declaration in the JSF configuration file. Create a new file named faces-config.xml in src/main/webapp/WEB-INF and declare the resource bundle – in this case we’ve registered the bundle for variable named i18n

    <?xml version="1.0" encoding="UTF-8"?>
    <faces-config version="2.0" xmlns="http://java.sun.com/xml/ns/javaee"
    	xmlns:xi="http://www.w3.org/2001/XInclude" 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">
    	<application>
    		<resource-bundle>
    			<base-name>com.hascode.tutorial.jee6.blog.messages</base-name>
    			<var>i18n</var>
    		</resource-bundle>
    	</application>
    </faces-config>
  • Now we’re able to replace our messages via

    #{i18n.thekeyfromthebundle}
  • Finally out messages.properties looks like this

    listTitle=hasCode.com - Java EE 6 Blog Tutorial - Blog entries overview
    listHeading=Blog entries overview
  • Our update facelet list.xhtml now looks like this

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml"
    	xmlns:h="http://java.sun.com/jsf/html"
    	xmlns:f="http://java.sun.com/jsf/core"
    	xmlns:ui="http://java.sun.com/jsf/facelets"
    	xmlns:p="http://primefaces.org/ui">
    <ui:composition template="/_decorator.xhtml">
    	<ui:define name="title">
    		<h:outputText value="#{i18n.listTitle}" />
    	</ui:define>
    	<ui:define name="heading">
    		<h:outputText value="#{i18n.listHeading}" />
    	</ui:define>
    	<ui:define name="body">
    		<h:form>
    			<h:commandButton title="#{i18n.newArticle}"
    				value="#{i18n.newArticle}" action="create" />
    			<br />
    			<hr />
    			<br />
    			<p:panel
    				header="#{blogEntryBean.getBlogEntries().size()} #{i18n.amountEntries}"
    				toggleable="true" closable="true" toggleSpeed="500">
    				<ui:repeat value="#{blogEntryBean.getBlogEntries()}" var="entry">
    					<h:panelGrid columns="2" cellpadding="10">
    						<f:facet name="header">
    							<h:outputText value="#{entry.title}" />
    						</f:facet>
    						<h:outputText value="#{i18n.author}: #{entry.author}" />
    						<h:panelGroup id="pGroup">
    							<h:outputText value="#{entry.content}" />
    							<p:contextMenu for="pGroup">
    								<p:menuitem value="#{i18n.delete}"
    									actionListener="#{blogEntryBean.delete(entry)}" update="@form" />
    							</p:contextMenu>
    						</h:panelGroup>
    						<f:facet name="footer">
    							<h:outputText value="#{i18n.created}: #{entry.created}" />
    						</f:facet>
    					</h:panelGrid>
    					<hr />
    				</ui:repeat>
    			</p:panel>
    		</h:form>
    	</ui:define>
    </ui:composition>
    </html>

Adding JSF Navigation Rules

We could have managed our user actions completely using nice PrimeFaces AJAX-enriched components or JSF’s native AJAX API but to demonstrate JSF’s navigation rules we’re going to define a new view with a form to create a new blog entry. If the blog entry has been successfully saved using this form we want to redirect our user to the entries-list-overview and the URL should change to avoid posting duplicate content by refreshing in the browser.

  • First we’re creating the new view reusing our decorator .. add a new template named create.xhtml in src/main/webapp

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml"
    	xmlns:h="http://java.sun.com/jsf/html"
    	xmlns:f="http://java.sun.com/jsf/core"
    	xmlns:ui="http://java.sun.com/jsf/facelets"
    	xmlns:p="http://primefaces.org/ui">
    <ui:composition template="/_decorator.xhtml">
    	<ui:define name="title">#{i18n.createTitle}</ui:define>
    	<ui:define name="heading">#{i18n.createHeading}</ui:define>
    	<ui:define name="body">
    		<h:form>
    			<p:panel id="createPnl" header="#{i18n.newArticle}"
    				toggleable="true" closable="false" toggleSpeed="500"
    				onCloseUpdate="growl" closeSpeed="2000" onToggleUpdate="growl"
    				widgetVar="panel">
    				<h:panelGrid columns="3" cellpadding="10">
    					<label>#{i18n.title}</label>
    					<h:inputText label="Title" id="lblTitle"
    						value="#{blogEntryBean.blogEntry.title}" required="true" />
    					<h:message for="lblTitle" class="errorMsg"/>
    					<label>#{i18n.author}</label>
    					<h:inputText label="Author" id="lblAuthor"
    						value="#{blogEntryBean.blogEntry.author}" required="true" />
    					<h:message for="lblAuthor" class="errorMsg"/>
    					<label>#{i18n.content}</label>
    					<h:inputTextarea label="Content" id="lblContent"
    						value="#{blogEntryBean.blogEntry.content}" required="true" />
    					<h:message for="lblContent" class="errorMsg"/>
    					<h:commandButton title="#{i18n.save}" value="#{i18n.save}"
    						action="#{blogEntryBean.saveBlogEntry()}" />
    				</h:panelGrid>
    			</p:panel>
    		</h:form>
    	</ui:define>
    </ui:composition>
    </html>
  • We need to update our internationalized messages in our messages.properties

    listTitle=hasCode.com - Java EE 6 Blog Tutorial - Blog entries overview
    listHeading=Blog entries overview
    newArticle=Create new blog entry
    amountEntries=Blog entries available
    author=Author
    options=Options
    delete=Delete
    created=Created
    createTitle=hasCode.com - Java EE 6 Blog Tutorial - Create a new blog entry
    createHeading=Create a new blog entry
    title=Title
    content=Content
    save=Save article
  • Add navigation rules to your faces-config.xml in src/main/webapp/WEB-INF .. mine now looks like this one

    <?xml version="1.0" encoding="UTF-8"?>
    <faces-config version="2.0" xmlns="http://java.sun.com/xml/ns/javaee"
    	xmlns:xi="http://www.w3.org/2001/XInclude" 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">
    	<application>
    		<resource-bundle>
    			<base-name>com.hascode.tutorial.jee6.blog.messages</base-name>
    			<var>i18n</var>
    		</resource-bundle>
    	</application>
    
    	<navigation-rule>
    		<from-view-id>/list.xhtml</from-view-id>
    		<navigation-case>
    			<from-outcome>create</from-outcome>
    			<to-view-id>/create.xhtml</to-view-id>
    		</navigation-case>
    	</navigation-rule>
    	<navigation-rule>
    		<from-view-id>/create.xhtml</from-view-id>
    		<navigation-case>
    			<from-outcome>success</from-outcome>
    			<to-view-id>/list.xhtml</to-view-id>
    			<redirect/>
    		</navigation-case>
    	</navigation-rule>
    </faces-config>
  • The redirect-Tag in the navigation-case forces the a redirect to list.xhtml

GlassFish Configuration

The GlassFish setup is quite easy we simply need a new domain and the embedded database because we’re too lazy to connect or create an external database ..

  • Start the GlassFish console via

    asadmin
  • Create a new domain named blog-domain

    asadmin> create-domain blog-domain
    Enter admin user name [Enter to accept default "admin" / no password]>
    Using port 4848 for Admin.
    Using default port 8080 for HTTP Instance.
    Using default port 7676 for JMS.
    Using default port 3700 for IIOP.
    Using default port 8181 for HTTP_SSL.
    Using default port 3820 for IIOP_SSL.
    Using default port 3920 for IIOP_MUTUALAUTH.
    Using default port 8686 for JMX_ADMIN.
    Using default port 6666 for OSGI_SHELL.
    Distinguished Name of the self-signed X.509 Server Certificate is:
    [CN=yourworkstationname,OU=GlassFish,O=Oracle Corporation,L=Santa Clara,ST=California,C=US]
    No domain initializers found, bypassing customization step
    Domain blog-domain created.
    Domain blog-domain admin port is 4848.
    Domain blog-domain allows admin login as user "admin" with no password.
    Command create-domain executed successfully.
  • Start the created domain

    asadmin> start-domain blog-domain
    Waiting for DAS to start .................................
    Started domain: blog-domain
    Domain location: /somepath/app/glassfishv3/glassfish/domains/blog-domain
    Log file: /somepath/app/glassfishv3/glassfish/domains/blog-domain/logs/server.log
    Admin port for the domain: 4848
    Command start-domain executed successfully.
  • Start the embedded Derby/JavaDB database

    asadmin> start-database
    Starting database in Network Server mode on host 0.0.0.0 and port 1527.
    [..]
    ------------------------------------------------------
    Starting database in the background.
    Log redirected to /somepath/derby.log.
    Command start-database executed successfully.
  • Visit the administration console in your browser at http://localhost:4848/ and enjoy ;)

Deploying and Running the Application

  • First build the war file for deployment using

    mvn package
  • Ensure that your GlassFish server is running, the database is started and the domain “blog-domain” is created and started

  • Log into the GlassFish administration console at http://localhost:4848/ with login “admin” and empty password if you haven’t defined one in the domain creation process

  • Click on “Applications” and deploy the application via file upload as shown as in the following screenshots

    glassfish deployment step11 300x142
    Figure 2. GlassFish Application Dep loyment Step 1
    glassfish deployment step2 300x55
    Figure 3. GlassFish Application Deployment Step 2
    glassfish deployment step3 300x147
    Figure 4. GlassFish Application Deployment Step 3
    GlassFish Application Deployment Step 4

    glassfish jdbc resources 300x108

    glassfish jdbc resources 300x108
  • Now click on "Launch" and visit your application at this location: http://localhost:8080/blog-tutorial

Download Sources

The sources for this tutorial are available on my GitHub repository .. download it from here or check it out using

git clone https://hascode@github.com/hascode/javaee6-blog-tutorial.git

Troubleshooting

  • Exception while preparing the app : org.hibernate.AnnotationException: No identifier missing @Id attribute“: You’ve forgotten to specify an identifying property for your entity class .. e.g.: @Id private Long id;

  • Exception [EclipseLink-4002] (Eclipse Persistence Services – 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: java.sql.SQLNonTransientConnectionException: No current connection. java.sql.SQLNonTransientConnectionException: No current connection. org.apache.derby.client.am.SqlException: No current connection.“: In this tutorial we’re lazy and rely on GlassFish’s embedded database. To use this database it must be started first as this

    asadmin
    asadmin> start-database
    Starting database in Network Server mode on host 0.0.0.0 and port 1527.
  • [#|2011-01-16T17:47:47.364+0100|WARNING|glassfish3.0.1|javax.enterprise.system.container.web.com.sun.enterprise.web|_ThreadID=30;_ThreadName=Thread-1;|StandardWrapperValve[Faces Servlet]: PWC1406: Servlet.service() for servlet Faces Servlet threw exception javax.el.PropertyNotFoundExceptionor: CDI does not work somehow“: You need to put an empty text file named beans.xml in src/main/webapp/WEB-INF – strange but necessary ;) For more information take a look at Gavin King’s blog article: “http://relation.to/Bloggers/WhyIsBeansxmlRequiredInCDI[Why is beans.xml required in CDI?]“

  • Caused by: java.lang.IllegalArgumentException: Entity must be managed to call remove: com.hascode.tutorial.jee6.blog.entity.BlogEntry@184340e, try merging the detached and try the remove again. at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.performRemove(UnitOfWorkImpl.java:3539)“: This is the standard detached object problem .. in JPA there are four possible object states .. new, removed, managed and of course detached .. the last one has a persistent identity that no longer is associated with a persistence context. EntityManager’s merge method is what helps you to “reattach” the bean .. look at this sample code

    public void theMethod(XXX entityBean) {
     entityBean = em.merge(entityBean);
     em.remove(entityBean);
    }
  • PrimeFaces is not defined“: There was an issue with some menu components in PrimeFaces 2.2.RC1, Cagatay Civici has already fixed this one in version 2.2.RC2 and above.

  • Duplicate menuitem in contextmenu“: Again Cagatay Civici saved the day and fixed this issue, update to version 2.2-SNAPSHOT or above.

  • Plugin execution not covered by lifecycle configuration: org.apache.maven.plugins:maven-dependency-plugin:2.1:copy (execution: default, phase: validate)” – It’s the m2eclipse plugin .. why does this error occur? They’re explaining it in this article .. for now add the following code to your pom.xml and update your project configuration

    <pluginManagement>
    	<plugins>
    		<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
    		<plugin>
    			<groupId>org.eclipse.m2e</groupId>
    			<artifactId>lifecycle-mapping</artifactId>
    			<version>1.0.0</version>
    			<configuration>
    				<lifecycleMappingMetadata>
    				<pluginExecutions>
    					<pluginExecution>
    						<pluginExecutionFilter>
    							<groupId>org.apache.maven.plugins</groupId>
    							<artifactId>maven-dependency-plugin</artifactId>
    							<versionRange>[2.1,)</versionRange>
    							<goals>
    								<goal>copy</goal>
    							</goals>
    						</pluginExecutionFilter>
    						<action>
    							<ignore></ignore>
    						</action>
    					</pluginExecution>
    				</pluginExecutions>
    				</lifecycleMappingMetadata>
    			</configuration>
    		</plugin>
    	</plugins>
    </pluginManagement>

Additional Articles: Testing with Arquillian

Article Updates

  • 2018-06-01: Embedded YouTube video removed (GDPR/DSGVO).

  • 2015-03-21: Links to my Arquillian articles added, formatting fixed.