Saturday, January 8, 2011

Setup Eclipse JEE6 Primefaces and Glassfish - Part II

In the first installment we setup a basic web application with JEE6, JSF2, Primefaces running on Glassfish using Eclipse for the IDE.  We'll continue here to add testing with Arquillian and Persistence with EclipseLink/JPA2.  Once again we're simply tweaking Nicklas Karlsson's examples except we're making it a little less JBoss specific.


We'll build upon the primetest project, which you can download from here.  Simply extract the zip into your workspace and use import existing maven projects to get you caught up.


The first thing we'll do is add some dependencies for Arquillian and a Profile for Glassfish Embedded.  Arquillian allows you to run tests with JUnit or TestNG.  It facilitates in-container testing, which means all the CDI injections take place, you don't have to hand-wire together test objects.  Let's add a few properties to the pom for our dependencies:
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <arquillian.version>1.0.0.Alpha4</arquillian.version>
    <glassfish.embedded.version>3.1-b04</glassfish.embedded.version>
    <junit.version>4.8.1</junit.version>
    <slf4j.version>1.5.9.RC1</slf4j.version>
</properties>

and the dependencies themselves:
<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>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
We'll also add a dependency management section like so:

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

and lastly, the profile for Glassfish embedded:

<profile>
<id>glassfish-embedded-3</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>org.jboss.arquillian.container</groupId>
<artifactId>arquillian-glassfish-embedded-3</artifactId>
<version>${arquillian.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.extras</groupId>
<artifactId>glassfish-embedded-all</artifactId>
<version>${glassfish.embedded.version}</version>
</dependency>
</dependencies>
</profile>
Notice the activeByDefault flag.  We'll toggle the flag between true for testing and false for runtime.  A bit problematic, but the best way I've found to have a profile for testing only.

ok, now we're all setup for testing, we just need to write a test......
First we'll create a simply class named TemperatureConverter in the com.examples.test package like so:

package com.examples.temp;
public class TemperatureConverter {
public double convertToCelsius(double f) {
return ((f - 32) * 5 / 9);
}
public double convertToFarenheit(double c) {
return ((c * 9 / 5) + 32);
}
}


and then a test case in src/test/com.examples.temp like this:


package com.examples.temp;
import javax.inject.Inject;
import junit.framework.Assert;
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.junit.Test;
import org.junit.runner.RunWith;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import static org.junit.Assert.assertEquals;
@RunWith(Arquillian.class)
public class TemperatureConverterTest {
@Inject
private TemperatureConverter converter;
@Deployment
public static JavaArchive createTestArchive() {
return ShrinkWrap.create(JavaArchive.class, "test.jar")
.addClasses(TemperatureConverter.class)
.addManifestResource(
EmptyAsset.INSTANCE,
ArchivePaths.create("beans.xml"));
}
@Test
public void testConvertToCelsius() {
Assert.assertEquals(converter.convertToCelsius(32d), 0d);
Assert.assertEquals(converter.convertToCelsius(212d), 100d);
}
@Test
public void testConvertToFarenheit() {
Assert.assertEquals(converter.convertToFarenheit(0d), 32d);
Assert.assertEquals(converter.convertToFarenheit(100d), 212d);
}
}

A breif description of what's happening here.  The @Deployment annotation designates the container setup for the test.  We will create a temporary jar (test.jar) to put in the glassfish embedded container and add an empty beans.xml for CDI.  With that done, the container will properly inject the TemeratureConverter class that we'll use in the 2 test cases.


I tried several ways of getting the maven integration to work for this, including setting the default profile in the project's maven properties, setting the default profile in the pom and creating a special run config for the tests.  All had issues, so what I've settled on is changing the activeByDefault flag in the pom, and then perform mvn test (or run as/maven test in eclipse).  Try that and see the test results... 
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0


Not much in the way of logging is happening, so add a log4j.xml file in your src/test/resources:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="true">
   <!-- ============================== -->
   <!-- Append messages to the console -->
   <!-- ============================== -->
   <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
      <param name="Target" value="System.out"/>
      <param name="Threshold" value="ALL"/>
      <layout class="org.apache.log4j.PatternLayout">
         <!-- The default pattern: Date Priority [Category] Message\n -->
         <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n"/>
      </layout>
   </appender>
   <!-- ================ -->
   <!-- Limit categories -->
   <!-- ================ -->
   <category name="com.examples">
     <priority value="ALL" />
   </category>
   <root>
     <appender-ref ref="CONSOLE"/>
   </root>
</log4j:configuration>


Ok, now we have some running tests for our simple web app.  If you downloaded the zip file, you'll notice a few extra beans and additions to the greetings.xhtml to add a few more primefaces widgets (autocomplete and date picker).  

Now let's add some persistence for our greetings.  We'll use an Apache Derby database since it's simple to setup and comes with Glassfish.  So we first need to define a dataSource in Glassfish.  For that we use the sun-resources.xml file.  Create/register the jdbc/primetest jndi resource by adding sun-resources.xml in the src/main/resources folder like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 Resource Definitions //EN"
   "http://www.sun.com/software/appserver/dtds/sun-resources_1_4.dtd">
<resources>
   <jdbc-resource pool-name="EmbeddedDerbyPool"
      jndi-name="jdbc/primetest"/>
   <jdbc-connection-pool name="EmbeddedDerbyPool"
      res-type="javax.sql.DataSource"
      datasource-classname="org.apache.derby.jdbc.EmbeddedDataSource"
      is-isolation-level-guaranteed="false">
      <property name="databaseName" value="/tmp/databases/derby"/>
      <property name="createDatabase" value="create"/>
   </jdbc-connection-pool>
</resources>

Then we'll add the JPA persistence file in the src/main/resources/META-INF/persistence.xml like this:


<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.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_2_0.xsd">
   <persistence-unit name="test">
      <!--
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      -->
      <jta-data-source>jdbc/primetest</jta-data-source>
      <properties>
         <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
         <property name="hibernate.show_sql" value="true"/>
         <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
         <property name="eclipselink.logging.level" value="FINE"/>
      </properties>
   </persistence-unit>
</persistence>

Now we have our JPA setup and ready to go, let's create a Greeting entity that we can persist.  Create Greeting.java in the com.examples.greeting package:

package com.examples.greeting;
import static javax.persistence.TemporalType.TIMESTAMP;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Temporal;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Entity
public class Greeting
{
   @Id
   @GeneratedValue
   int id;
   String text;
   
   @Temporal(TIMESTAMP)
   Date created = new Date();
   public Greeting(String greet) {
   text = greet;
   }
   public Greeting() {
   }
   public int getId()
   {
      return id;
   }
   public void setId(int id)
   {
      this.id = id;
   }
   public String getText()
   {
      return text;
   }
   public void setText(String text)
   {
      this.text = text;
   }
}

We are going to raise an event when a Greeting is added, so we'll create an Interface named Added and put it in the com.examples.greeting package.

package com.examples.greeting;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
@Qualifier
@Retention(RUNTIME)
@Target( { METHOD, FIELD, PARAMETER, TYPE })
public @interface Added
{
}

Now we need to create something to get and save Greetings.  We'll call it GreetingArchiver and put it in the com.examples.greeting package too.


package com.examples.greeting;
import java.util.List;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.UserTransaction;
@ApplicationScoped
public class GreetingArchiver
{
   @PersistenceContext
   EntityManager db;
   @Inject
   UserTransaction userTransaction;
   public void saveGreeting(@Observes @Added Greeting greeting)
   {
      try
      {
         userTransaction.begin();
         db.joinTransaction();
         db.persist(greeting);
         db.flush();
         userTransaction.commit();
      }
      catch (Exception e)
      {
         e.printStackTrace();
         // The recommended way of dealing with exceptions, right?
      }
   }
   public List<Greeting> loadGreetings()
   {
      return db.createQuery("select g from Greeting g").getResultList();
   }
}
Lastly, we need to update GreetingBean to use all this new persistence setup.


package com.examples.greeting;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.inject.Named;
@ApplicationScoped
@Named
public class GreetingBean implements Serializable
{
   Greeting greeting = new Greeting();
   List<Greeting> greetings = new ArrayList<Greeting>();
   @Inject
   @Added
   Event<Greeting> greetingAddedEvent;
   @Inject
   GreetingArchiver greetingArchiver;
   
   @PostConstruct
   public void init()
   {
      greetings = greetingArchiver.loadGreetings();
   }
   public void addGreeting()
   {
      greetings.add(greeting);
      greetingAddedEvent.fire(greeting);
      greeting = new Greeting();
   }
   public Greeting getGreeting()
   {
      return greeting;
   }
   public void setGreeting(Greeting greeting)
   {
      this.greeting = greeting;
   }
   public List<Greeting> getGreetings()
   {
      return greetings;
   }
   public void setGreetings(List<Greeting> greetings)
   {
      this.greetings = greetings;
   }
}
Notice that the greeting attribute is used as a template to hold information from the page.  Also note the postConstruct is used to load greetings upon initialization.  Lastly notice the greetingAddedEvent and that it is fired in the addGreeting method.  This is what triggers the saveGreeting method in GreetingArchiver because it is listening for Added Greetings.

A few tweaks are required to our page to enter and list the greetings and we'll be ready to run the application.  First, set the activeByDefault flag to false in the pom.  Then update the greetings.xhtml to look like this:


<?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"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.prime.com.tr/ui">
<h:head>
<title>Greetings</title>
</h:head>
<h:body>
<h:form>
<p:ajaxStatus>
<f:facet name="start">
<h:outputText value="Loading..." />
</f:facet>
<f:facet name="complete">
<h:outputText value="Done!" />
</f:facet>
</p:ajaxStatus>
<p:accordionPanel id="mainPanel">
<p:tab title="Greetings" id="firstTab">
<p:dataTable id="myTable" value="#{greetingBean.greetings}" var="greeting">
<f:facet name="header"><h:outputText value="Greeting"/></f:facet>
<p:column>
<h:outputText value="#{greeting.text}"/>
</p:column>
</p:dataTable>
<h:inputText value="#{greetingBean.greeting.text}"/>
<p:commandButton value="Add" action="#{greetingBean.addGreeting}" update="mainPanel"/>
</p:tab>
<p:tab title="Date Picker">
<h:outputText value="Ipsum" />
<p:calendar value="#{dateBean.date}" />
</p:tab>
<p:tab title="Auto Complete">
<p:autoComplete value="#{autoCompleteBean.text}"
completeMethod="#{autoCompleteBean.complete}" />
</p:tab>
</p:accordionPanel>
<h:outputText value="#{greetingBean.greeting.text}" />
</h:form>
</h:body>
</html>

Notice that we named the main accordian panel "mainPanel" and that the Add button refreshes the "MainPanel".  This is all you have to do to have a partial page refresh using primefaces!  Pretty sweet.


Start your server and refresh the goto http://localhost:8080/primetest/greetings.jsf and you should get a page like this.



In order to setup our test environment for JPA we need to do a few things.  First add a src/test/resources-glassfish-embeded to hold our configuration files for the embedded test environment.  We'll need a persistence.xml and sun-resources.xml.  They'll look similar to the ones we created for the webapp so just copy them.  Rename the persistence.xml to test-persistence.xml.  Then add an arquillian.xml to the src/test/resources folder.


<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns="http://jboss.com/arquillian"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:glassfish-embedded="urn:arq:org.jboss.arquillian.container.glassfish.embedded_3">
   <!-- Uncomment to have test archives exported to the file system for inspection -->
   <!--
   <engine>
      <deploymentExportPath>target/</deploymentExportPath>
   </engine>
   -->
   <glassfish-embedded:container>
      <glassfish-embedded:sunResourcesXml>src/test/resources-glassfish-embedded/sun-resources.xml</glassfish-embedded:sunResourcesXml>
   </glassfish-embedded:container>  
</arquillian>

Add the src/test/resources-glassfish-embedded to the build path.  With that we're all ready to test.  We just need to write the test in src/test/com.examples.greeting:

package com.examples.temp;
import static org.junit.Assert.assertEquals;
import java.io.PrintStream;
import java.util.List;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.transaction.UserTransaction;
import org.jboss.arquillian.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.examples.greeting.Greeting;
@RunWith(Arquillian.class)
public class GreetingPersistenceTest {
private static Logger log = LoggerFactory.getLogger(GreetingPersistenceTest.class);
   @PersistenceContext
   EntityManager em;
   
   @Inject
   UserTransaction utx;
   @Deployment
   public static Archive<?> createDeployment()
   {
      return ShrinkWrap.create(WebArchive.class, "test.war")
            .addPackage(Greeting.class.getPackage())
            .addManifestResource("test-persistence.xml", "persistence.xml")
            .addWebResource(EmptyAsset.INSTANCE, "beans.xml");
   }
   private static final String[] GREETING_TEXT =
   {
      "Super Mario Brothers",
      "Mario Kart",
      "F-Zero"
   };
   public void insertSampleRecords() throws Exception
   {
      utx.begin();
      em.joinTransaction();
    
      log.info("Clearing the database...");
      em.createQuery("delete from Greeting").executeUpdate();
    
      log.info("Inserting records...");
      for (String title : GREETING_TEXT)
      {
         Greeting greet = new Greeting(title);
         em.persist(greet);
      }
    
      utx.commit();
   }
   @Test
   public void should_be_able_to_select_games_using_jpql() throws Exception
   {
      insertSampleRecords();
      
      utx.begin();
      em.joinTransaction();
    
      log.info("Selecting (using JPQL)...");
      List<Greeting> greetings =
         em.createQuery("select g from Greeting g order by g.id", Greeting.class)
         .getResultList();
      log.info("Found {} greetings (using JPQL)",greetings.size());
      assertEquals(GREETING_TEXT.length, greetings.size());
    
      for (int i = 0; i < GREETING_TEXT.length; i++) {
         assertEquals(GREETING_TEXT[i], greetings.get(i).getText());
         log.info("{}",greetings.get(i).getText());
      }
      
      utx.commit();
   }
   
   @Test
   public void should_be_able_to_select_games_using_criteria_api() throws Exception
   {
      insertSampleRecords();
      
      utx.begin();
      em.joinTransaction();
    
      CriteriaBuilder builder = em.getCriteriaBuilder();
      CriteriaQuery<Greeting> criteria = builder.createQuery(Greeting.class);
      // FROM clause
      Root<Greeting> greeting = criteria.from(Greeting.class);
      // SELECT clause
      criteria.select(greeting);
      // ORDER BY clause
      criteria.orderBy(builder.asc(greeting.get("id")));
      // No WHERE clause, select all
    
      log.info("Selecting (using Criteria)...");
      List<Greeting> greetings = em.createQuery(criteria).getResultList();
      log.info("Found " + greetings.size() + " greetings (using Criteria)");
      assertEquals(GREETING_TEXT.length, greetings.size());
    
      for (int i = 0; i < GREETING_TEXT.length; i++) {
         assertEquals(GREETING_TEXT[i], greetings.get(i).getText());
         log.info("{}",greetings.get(i).getText());
      }
    
      utx.commit();
   }
}


Set the activeByDefault flag to true in the pom and run/as maven test.

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


Awesome!  Till next time, keep learning!


Here's a link to the finished project.

No comments:

Post a Comment