Running a simple entity bean test

In this section, we will run a simple entity bean test with the Arquillian framework.

The Gradle build file for the entity bean test

We are using the setup of Gradle, Arquillian test framework, Apache Derby,and embedded Glassfish server. The project's Gradle build filebuild.gradle is as follows:

apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'eclipse'
apply plugin: 'idea'

// Define equivalent Maven GAV coordinates.
group = 'com.javaeehandbook.book1'
archivesBaseName = 'ch04-jpa-simple'
version = '1.0'

repositories {
  mavenLocal()
  mavenCentral()
  maven {
    url 'https://maven.java.net/content/groups/
    promoted'
    }
  maven {
    url 'http://repository.jboss.org/nexus/content/groups/public'
    }
  }

dependencies {
  compile 'javax:javaee-api:7.0'
  runtime 'javax:javaee-api:7.0'
  
  testCompile 'junit:junit:4.11'
  testCompile 'org.jboss.arquillian.junit:
  arquillian-junit-container:1.0.3.Final'
  testCompile 'org.jboss.arquillian.container:
  arquillian-glassfish-embedded-3.1:1.0.0.CR4'
  runtime 'org.glassfish.main.extras: glassfish-embedded-all:4.0.1-b01'
  }

task wrapper(type: Wrapper) {
  gradleVersion = '1.6'
  }

// Override Gradle defaults - a force an exploded JAR view
sourceSets {
  main {
    output.resourcesDir = 'build/classes/main'
    output.classesDir = 'build/classes/main'
    }
  test {
    resources {
      srcDir 'src/test/resources'
      }
    resources {
      srcDir 'src/test/resources-glassfish-embedded'
      }
    
    output.resourcesDir = 'build/classes/test'
    output.classesDir = 'build/classes/test'
    }
  }

We included a couple of extra dependencies in order to run the unit tests, which are the GlassFish embedded container and the Java EE 7 API. We also have two separate resource folders under the test tree src/test/resources and src/test/resources-glassfish-embedded. The reason why we have done this will become apparent.

A stateful session bean

First, we will define a session bean to act as a mediator to the database as follows:

packageje7hb.basic.jpa;

import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.persistence.Query;
import java.util.List;
@Stateful
public class SpyThrillerBookBean {
  @PersistenceContext(unitName = "testDatabase", type = PersistenceContextType.EXTENDED)
  private EntityManager entityManager;
  
  public void addBook(SpyThriller movie) throws Exception {
    entityManager.persist(movie);
    }
  
  public void deleteBook(SpyThriller movie) throws Exception {
    entityManager.remove(movie);
    }
  
  public List<SpyThriller> getBooks() throws Exception {
    Query query = entityManager.createQuery("SELECT m from SpyThriller as m");
    return query.getResultList();
    }
  }

The code shows a session EJB named SpyThrillerBookBean of the stateful type, which we have already seen in Chapter 3, Enterprise Java Beans. The stateful session bean requires a javax.persistence.PersistenceContext object that is injected into the service by the EJB container. The SpyThrillerBookBean acts as the central processor for the SpyThriller entity beans.

PersistenceContext is annotated with an extended conversation scope, which allows the transaction context to live through multiple method requests. This is useful to support work flow in a web application. We also have to explicitly name the persistence unit, with which the persistence context used in this stateful EJB is associated. In this case, the persistent context refers to a unit named testDatabase.

It is entirely possible to have multiple persistence contexts injected into a session bean.

There are methods on the stateful bean to add a book, addBook(), remove a book, deleteBook(), and retrieve a list of the books from the database, getBook().

In the listing method getBook(), we make use of the JPA query facility from the entity manager. We create a query object that uses the Java Persistence API Query Language (JPQL), which looks like native SQL, but it is different.

JPQL is the query language for EJB and is designed to support portable entity bean with retrieving data, updating data, and insertion of new data, and JPQL also supports bulk update and delete operations. In the example code, we are using JPQL with a static String statement. There are other ways to define the JPQL statements with the annotations, and we will come to them in a later section of this chapter.

An entity bean integration test

Let us now examine the unit test for this stateful EJB with the entity bean with Arquillian.

packageje7hb.basic.jpa;
// other imports omitted
import javax.ejb.EJB;
import java.util.List;
import static org.junit.Assert.*;

@RunWith(Arquillian.class)
public class SpyThrillerBookBeanTest {
  @Deployment
  public static JavaArchive createDeployment() {
    JavaArchive jar = ShrinkWrap.create(JavaArchive.class)
    .addPackage(SpyThrillerBookBean.class.getPackage())
    .addAsResource("test-persistence.xml", "META-INF/persistence.xml")
    .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
    return jar;
    }
  
  @EJBSpyThrillerBookBeanbookService;
  
  @Test
  public void shouldPersistEntities() throws Exception {
    assertEquals(0, bookService.getBooks().size());
  bookService.addBook(new SpyThriller("The Spy Who Came in from the Cold", 1963, "John Le Carre" ));
    bookService.addBook(new SpyThriller("Casino Royale", 1953, "Ian Fleming"));
    bookService.addBook(new SpyThriller("The Hunt for Red October ", 1984, "Tom Clancy"));
    bookService.addBook(new SpyThriller("Bravo Two Zero", 1993, "Andy McNab"));
    bookService.addBook(new SpyThriller("On Her Majesty's Secret Service", 1963, "Ian Fleming"));
    
    List<SpyThriller> list = bookService.getBooks();
    assertEquals( 5, list.size());
    
    for (SpyThriller movie : list) {
      System.out.printf("movie = %s
", movie);
      bookService.deleteBook(movie);
      }
    
    assertEquals(0, bookService.getBooks().size());
    }
  }

The first difference between this test class, SpyThrillerBookBeanTest, and the previous integration tests is the method named addPackage(), which is part of the ShrinkWrap builder. AddPackage() registers all of the classes in the supplied Java package for deployment.

The second difference is that we supplied an additional XML configuration for deployment, which is named test-persistence.xml in the source code. In the build tree, the file becomes the persistence context configuration META-INF/persistence.xml. We will see this XML configuration in a bit.

The EJB container injects a reference to the stateful bean SpyThrillerBookBean for the purpose of testing. We could also use @javax.inject.Inject for future proofing.

In the actual test method, we invoke all the service methods of the stateful bean. First, we assert that the database table should be empty; there should be no SpyThriller entities in it. Next, we insert a number of entities. We assert again the number of entities that should be stored in the database. After that, we retrieve a list collection of the entities, we iterate over this list in order to delete individual entities. Finally, we assert the database is empty, and if all things are right and good, then the test should pass. However, we need to set up and configure the persistence context.

A persistence context XML configuration

Let us look at the persistence context file for the unit test as follows:

<persistence version = "2.1"xmlns = "http://xmlns.jcp.org/xml/ns/persistence"xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation = "http://xmlns.jcp.org/xml/ns/persistencehttp://java.sun.com/xml/ns/persistence/persistence_2_1.xsd">

<persistence-unit name = "testDatabase" transaction-type = "JTA">
<provider>
org.eclipse.persistence.jpa.PersistenceProvider
</provider>
<jta-data-source>jdbc/arquillian</jta-data-source>
<properties>
<property name = "eclipselink.ddl-generation" value = "drop-and-create-tables"/>
<property name = "eclipselink.ddl-generation.output-mode" value = "both"/>
<property name = "eclipselink.logging.level.sql" value = "FINE"/>
<property name = "eclipselink.logging.parameters" value = "true"/>
<property name = "eclipselink.create-ddl-jdbc-file-name" value = "createDDL.jdbc"/>
</properties>
</persistence-unit>
</persistence>

The persistence configuration defines one persistence-unit XML element and that is the session context, if you will, mapping EntityManager to the database. It is possible to have more than one persistence-unit inside the persistence configuration file.

There are two transaction types of persistence units: JTA and RESOURCE_LOCAL.

The JTA variety is the type that can only run inside a Java EE application server; JTA stands for Java Transaction API. These persistence unit types have transactional support by default. The application server, via the EJB and JPA container, will provide a JTA data source to the entity manager.

The RESOURCE_LOCAL variety is for standalone Java SE applications, and therefore allows JPA to be used outside of a Java EE application server. It is also possible to specify a non-JTA data source inside a Java EE application. In this case, using RESOURCE_LOCAL in such an application means the data source is not going to be a JTA data source, and therefore will not have transactions.

When we declare a persistence unit with a JTA data source, then we also need to specify the name of the data source, which in this configuration is named jdbc/arquillian.

A table of the EclipseLink configuration properties is as follows:

Property

Description

eclipselink.ddl-generation

Specifies how EclipseLink can automatically generate the table and database for a persistence unit. The values can be either create-table or drop-and-create-tables.

eclipselink.ddl-generation.output-mode

Specifies how Eclipse can execute the DDL schema. The valid values can be set to either sql-script, database, or both.

eclipselink.logging.level.sql

Specifies the JDK logging level for EclipseLink. The valid values are OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL.

eclipselink.logging.parameters

Specifies if the SQL parameters should be logged or not. This is useful for debugging the native SQL the JPA provider generates.

eclipselink.create-ddl-jdbc-file-name

Specifies the name of the DDL created tables and schema script that is generated when the output-mode is set to both or sql-script.

eclipselink.drop-ddl-jdbc-file-name

Specifies the name of the DDL drop tables and schema script that is generated when the output-mode is set to both or sql-scrip t.

eclipselink.jdbc.driver

Specifies the JDBC database driver class name for the Java SE deployment.

eclipselink.jdbc.url

Specifies the JDBC database URL class name for the Java SE deployment.

eclipselink.jdbc.user

Specifies the JDBC database login username for the Java SE deployment.

eclipselink.jdbc.password

Specifies the JDBC database login password for the Java SE deployment.

Since JPA 2.0, these EclipseLink JDBC configuration properties have been superseded by standard additional properties for the JPA providers.

Appendix B, Java EE 7 Persistence, has a full description of the standard JPA properties.

Arquillian configuration for the embedded GlassFish server

We configure specific properties for the JPA provider in order to allow it to connect to the database. In this case, we are using the embedded GlassFish server. We need to tell Arquillian how to set up the data source. We set this up in another configuration file named src/test/resources/arquillian.xml.

<?xml version = "1.0" encoding = "UTF-8"?>
<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 = "glassfish-embedded" default = "true">
<configuration>
<property name = "resourcesXml">
src/test/resources-glassfish-embedded/
glassfish-resources.xml
</property>
</configuration>
</container>
</arquillian>

Since our example is using Apache Derby, we tell Arquillian where to find the additional setup of the GlassFish resources.

I know it looks a little complicated, this indirection, but it is useful when switching between different application servers in order to make a separate integration test.

Suppose next week, Monday, there was suddenly a requirement to make a test with the JBoss application server, then we would simply make a change, and add in extra configurations in a new folder, presumably named src/test/resources-jboss-as-embedded. (Of course we would modify SourceSet accordingly in the build.gradle file and make sure we create the profile configuration, in order to anticipate every new application server request that arrives in the product backlog, going forward.)

The GlassFish resource configuration file, src/test/resources-glassfish-embedded/glassfish-resources.xml, is as follows:

<?xml version = "1.0" encoding = "UTF-8"?>
<!DOCTYPE resources PUBLIC"-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN""http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
<jdbc-resource pool-name = "ArquillianEmbeddedDerbyPool"jndi-name = "jdbc/arquillian"/>
<jdbc-connection-pool name = "ArquillianEmbeddedDerbyPool"res-type = "javax.sql.DataSource" datasource-classname = "org.apache.derby.jdbc.EmbeddedDataSource" is-isolation-level-guaranteed = "false">
<property name = "databaseName" value = "build/databases/derby"/>
<property name = "createDatabase" value = "create"/>
</jdbc-connection-pool>
</resources>

The XML schema for this file is specific to the GlassFish application server. It configures a JDBC connection pool, and thus the JTA enabled data source. Notice how the Derby database is stored in the build folder, and is compatible with the Gradle build infrastructure build/databases/derby.

Finally, we throw in a log configuration file so that developers can see the console output. The file, which is specific only to the GlassFish application server, and is named src/test/resources-glassfish-embedded/glassfish-resources.xml is as follows:

handlers = java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format = %4$s: %5$s%n
java.util.logging.ConsoleHandler.level = FINEST

As we can see, GlassFish makes use of the standard JDK logging facility.

Running an integration test

The output running the Arquillian integration unit test is as follows:

JUnitStarter -ideVersion5 je7hb.basic.jpa.SpyThrillerBookBeanTest
sql: --SELECT BOOK_ID, TITLE, AUTHORS, BOOK_YEAR FROM SPY_THRILLER
sql: --UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?bind => [50, SEQ_GEN]
sql: --SELECT SEQ_COUNT FROM SEQUENCE WHERE SEQ_NAME = ?bind => [SEQ_GEN]
sql: --INSERT INTO SPY_THRILLER (BOOK_ID, TITLE, AUTHORS, BOOK_YEAR) VALUES (?, ?, ?, ?)
bind => [1, John Le Carre, The Spy Who Came in from the Cold, 1963]

movie = SpyThriller{id = 1, writer = 'The Spy Who Came in from the Cold', title = 'John Le Carre', year = 1963, secretCode = '823056538'}
movie = SpyThriller{id = 2, writer = 'Casino Royale', title = 'Ian Fleming', year = 1953, secretCode = '416058911'}
sql: --DELETE FROM SPY_THRILLER WHERE (BOOK_ID = ?)
bind => [2]
movie = SpyThriller{id = 3, writer = 'The Hunt for Red October ', title = 'Tom Clancy', year = 1984, secretCode = '825011405'}
movie = SpyThriller{id = 4, writer = 'Bravo Two Zero', title = 'Andy McNab', year = 1993, secretCode = '831259338'}
movie = SpyThriller{id = 5, writer = 'On Her Majesty's Secret Service', title = 'Ian Fleming', year = 1963, secretCode = '56561882'}

INFO: HV000001: Hibernate Validator 5.0.0.Final

PlainTextActionReporterSUCCESSDescription: add-resources AdminCommandnull
JDBC connection pool ArquillianEmbeddedDerbyPool created successfully.
JDBC resource jdbc/arquillian created successfully.

--Connected: jdbc:derby:build/databases/derby
  User: APP
  Database: Apache Derby  Version: 10.9.1.0 - (1344872)
  Driver: Apache Derby Embedded JDBC Driver Version: 10.9.1.0 - (1344872)
Aug 03, 2013 11:42:36 AM com.sun.enterprise.v3.server.AppServerStartup stop

INFO: Shutdown procedure finished
Process finished with exit code 0
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.140.188.244