TOTD #132 explained how to use embedded GlassFish for deploying/testing a simple Servlet. As mentioned in that blog, this Tip Of The Day (TOTD) will extend that entry and show how to create a simple test that:
- Creates a JDBC Connection Pool using GlassFish APIs
- Creates a JDBC Resource using that Connection Pool, again programmatically
- Persists and Queries an entity in that database using JPQL and the newly introduced Criteria APIs in JPA 2
This application uses a "Games" entity with two simple fields – "id" for the primary key and "name" for the name of a game. The test persists 3 entries in the database and then retrieves them by issuing a JPQL statement and Criteria API. There are four tests:
- The first test consists of creating a JDBC Connection Pool & a JDBC Resource using that pool in the embedded GlassFish instance
- The second test stores 3 entities in the database using the JDBC resource
- The third test queries the database using JPQL
- The fourth test queries the database using Criteria API
The entire source code is available here and "mvn test" is all you need to see the action. The updated directory structure from TOTD #132 is:
src
src/main
src/main/java
src/main/java/org
src/main/java/org/glassfish
src/main/java/org/glassfish/embedded
src/main/java/org/glassfish/embedded/samples
src/main/java/org/glassfish/embedded/samples/App.java
src/main/java/org/glassfish/embedded/samples/Games.java
src/main/resources
src/main/resources/META-INF
src/main/resources/META-INF/persistence.xml
src/test
src/test/java
src/test/java/org
src/test/java/org/glassfish
src/test/java/org/glassfish/embedded
src/test/java/org/glassfish/embedded/samples
src/test/java/org/glassfish/embedded/samples/AppTest.java
The newly added files are highlighted in the bold. "Games.java" is the new entity class and "persistence.xml" provides the definition of Persistence Unit.
Here is our simple entity class:
package org.glassfish.embedded.samples;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* @author arungupta
*/
@Entity
@Table
public class Games implements Serializable {
@Id
int id;
@Column
String name;
public Games() { }
public Games(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Adding "@Entity" and "@Table" annotation on this POJO makes it an JPA Entity Bean. In addition, the primary key is defined using the "@Id" annotation on the field and an additional column is marked using "@Column"annotation.
In "AppTest.java", add a new method to create and test for JDBC connection pool and a JDBC resource using that pool as shown below:
public void testJDBCPoolAndResource() throws Throwable {
Server.Builder builder = new Server.Builder("server");
server = builder.build();
server.createPort(8080);
// add the required containers
server.addContainer(ContainerBuilder.Type.web);
server.addContainer(ContainerBuilder.Type.jpa);
// Create a JDBC connection pool
ParameterMap params = new ParameterMap();
params.add("datasourceclassname", "org.apache.derby.jdbc.EmbeddedDataSource");
params.add("property",
"databaseName=games:connectionAttributes=\\;create\\=true");
params.add("jdbc_connection_pool_id", JDBC_POOL);
executeCommand("create-jdbc-connection-pool", server, params);
System.out.println("JDBC Connection Pool successfully created.");
// Assert the JDBC connection pool
List<MessagePart> messageParts =
executeCommand("list-jdbc-connection-pools", server);
assertInArray(messageParts, JDBC_POOL);
// Create a JDBC resource
System.out.println("Creating JDBC resource ...");
ParameterMap params2 = new ParameterMap();
params2.add("connectionpoolid", "MyPool");
params2.add("jndi_name", JDBC_RESOURCE);
executeCommand("create-jdbc-resource", server, params2);
// Assert the JDBC resource
messageParts = executeCommand("list-jdbc-resources", server);
assertInArray(messageParts, JDBC_RESOURCE);
}
A disk representation of the database is created in the current directory by using "databaseName=games". Using in-memory JavaDB would’ve avoided the directory creation but facing issues with that (more on this at the end).
Here are convenience methods to execute an "asadmin"-equivalent command and used in the method above …
private List executeCommand(String command, Server server) throws Throwable {
return executeCommand(command, server, null);
}
private List executeCommand(String command, Server server, ParameterMap params)
throws Throwable {
CommandRunner runner = server.getHabitat().getComponent(CommandRunner.class);
ActionReport report = server.getHabitat().getComponent(ActionReport.class);
if (params == null)
runner.getCommandInvocation(command, report).execute();
else
runner.getCommandInvocation(command, report).parameters(params).execute();
if (report.hasFailures()) {
report.writeReport(System.out);
throw report.getFailureCause();
}
return report.getTopMessagePart().getChildren();
}
The error messages are shown on the console. Add another method for asserting on the created JDBC connection pool and resource as:
private void assertInArray(List messageParts, String resource) throws Throwable {
boolean found = false;
for (MessagePart part : messageParts) {
if (part.getMessage().equals(resource)) {
found = true;
break;
}
System.out.println(part.getMessage());
}
if (!found)
throw new Throwable("\"" + resource + "\" resource not found");
}
Here is a method to insert new values in the database using standard JPA calls:
public void testInsert() throws Throwable {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("webtier2-PU");
EntityManager em = emf.createEntityManager();
// list of games to be added
String[] games = {
"Super Mario Galaxy",
"Super Mario Brothers",
"Mario Kart" };
// create a transaction
EntityTransaction utx = em.getTransaction();
System.out.println("Persisting " + games.length + " games ...");
utx.begin();
for (int i=0; i<games.length; i++) {
em.persist(new Games(i, games[i]));
System.out.println("\t " + games[i]);
}
utx.commit(); // and commit
System.out.println("Committed.");
}
The method to query the database using JPQL …
public void testJPQL() throws Throwable {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("webtier2-PU");
EntityManager em = emf.createEntityManager();
// now query them
List list = em.createQuery("select g from Games g").getResultList();
assertEquals("Games retrieved", 3, list.size());
System.out.println("Found " + list.size() + " games (using JPQL) ...");
for (Object o : list) { // and dump
System.out.println("\t" + ((Games)o).getName());
}
}
And finally a method to query the database using JPA 2 Critieria API …
public void testCriteria() throws Throwable {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("webtier2-PU");
EntityManager em = emf.createEntityManager();
CriteriaBuilder cb = emf.getCriteriaBuilder();
CriteriaQuery<Games> criteria = cb.createQuery(Games.class);
// FROM clause
Root<Games> games = criteria.from(Games.class);
// SELECT clause
criteria.select(games);
// No WHERE clause - pick all
// FIRE
List<Games> list = em.createQuery(criteria).getResultList();
assertEquals("Games retrieved", 3, list.size());
System.out.println("Found " + list.size() + " games (using Criteria) ...");
for (Object o : list) { // and dump
System.out.println("\t" + ((Games)o).getName());
}
}
And here is the "persistence.xml"
<?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="webtier2-PU" transaction-type="JTA">
<jta-data-source>jdbc/MyResource</jta-data-source>
<properties>
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
</properties>
<class>org.glassfish.embedded.samples.Games</class>
</persistence-unit>
</persistence>
The Persistence Unit is referring to "jdbc/MyResource" JDBC Resource created earlier. The table creation strategy is "drop-and-create-tables" and this can be "create-tables" if in-memory JavaDB is used instead.
The version of "glassfish-embedded-all" dependency in "pom.xml" is changed from "3.0" to "3.1-SNAPSHOT" so that latest updates are picked. The updated dependency looks like:
<dependency>
<groupId>org.glassfish.extras</groupId>
<artifactId>glassfish-embedded-all</artifactId>
<version>3.1-SNAPSHOT</version>
</dependency>
Change the source version of "maven-compiler-plugin" from "1.5" to "1.6", the updated entry looks like:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
And now when you fire "mvn clean test", a test output is shown as …
INFO: Instantiated an instance of org.hibernate.validator.engine.resolver.JPATraversableResolver.
[EL Info]: 2010-05-05 13:49:28.339--ServerSession(149074043)--file:/Users/arungupta/samples/v3/
embedded/webtier2/target/classes/_webtier2-PU login successful
Persisting 3 games ...
Super Mario Galaxy
Super Mario Brothers
Mario Kart
Committed.
Found 3 games (using JPQL) ...
Mario Kart
Super Mario Galaxy
Super Mario Brothers
Found 3 games (using Criteria) ...
Mario Kart
Super Mario Galaxy
Super Mario Brothers
You may see the following exception during the test run:
May 5, 2010 1:49:24 PM com.sun.logging.LogDomains$1 log
SEVERE: loader.asurlclassloader_malformed_url
java.util.zip.ZipException: error in opening zip file
at java.util.zip.ZipFile.open(Native Method)
at java.util.zip.ZipFile.(ZipFile.java:114)
at java.util.jar.JarFile.(JarFile.java:133)
at java.util.jar.JarFile.(JarFile.java:97)
This occurs because no URL is specified in the connection properties. This error message can be ignored as sufficient information is available to resolve the database otherwise.
Using the in-memory JavaDB is causing some sort of race condition in the test path and throwing an exception at random runs. After trying the configuration on couple of Macs, Windows 7 and Ubuntu and still not able to detect the cause of randomness, no formal issue is logged yet but its still being followed with the required folks.
In the meanwhile, enjoy the power of embedded GlassFish with JPA and Java DB.
Technorati: totd glassfish v3 javaee embedded servlet jpa persistence javadb
Related posts:- TOTD #148: JPA2 Metamodel Classes in NetBeans 7.0 – Writing type-safe Criteria API
- TOTD #121: JDBC resource for MySQL and Oracle sample database in GlassFish v3
- TOTD #132: Servlets 3.0 in Embedded GlassFish Reloaded – lightweight Java EE 6
- TOTD #128: EJBContainer.createEJBContainer: Embedded EJB using GlassFish v3
- TOTD #108: Java EE 6 web application (JSF 2.0 + JPA 2.0 + EJB 3.1) using Oracle, NetBeans, and GlassFish
I like the Mario Kart game because it is more challenging as you get to the next level.-”:
Comment by Natalie White — May 12, 2010 @ 9:35 am
i used to play Mario Kart a lot last year but suddenly lost interest coz i got busy’;:
Comment by Molly Moore — October 4, 2010 @ 6:55 am
me and my girlfriend loves to play mario kart all day;`*
Comment by Curtain Lining — October 19, 2010 @ 2:14 am
actually Mario Kart is a very addicting game just like Plants Vs. Zombies ”
Comment by Nursery Decoration ยท — November 8, 2010 @ 5:25 pm
I tried to run with glassfish-embedded-all 3.1-SNAPSHOT and that failed to compile. Apparently, the Server, LifecycleException, etc. are not in the jar. That was easy to fix. I just dropped back to 3.0. When I try to run the ‘mvn test’, I get:
Internal Exception: javax.naming.NamingException: Lookup failed for ‘jdbc/MyResource’ in SerialContext [Root exception is javax.naming.NamingException: Unable to lookup resource : jdbc/MyResource [Root exception is com.sun.appserv.connectors.internal.api.ConnectorRuntimeException: rardeployment.jndi_lookup_failed]]
on the testInsert, testJPQL, testCriteria tests. The testApp and testJDBCPoolAndResource appear to work correctly.
Any thoughts?
Comment by Gary Murphy — December 27, 2010 @ 11:23 am