2/11/2011

GlassFish embedded, JPA, EJB, DataSource and glassfish-resources.xml

In a previous post I wrote about using @DataSourceDefinition to create a JTA data source for JPA in a standalone java application. Another option is to use glassfish-resources.xml to define the data source. The following is a standalone test app that includes glassfish embedded, JPA, EJB, Servlet, and JTA data source defined with glassfish-resources.xml.

As with the previous test app, GlassFish server is running in the same JVM as the test app. And in this example, the test program starts the GlassFish embedded, constructs a scattered web app, deploys it, sends http request, undeploys it, and finally shuts down the server.

WEB-INF/classes/META-INF/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="test_pu" transaction-type="JTA">
<jta-data-source>java:app/jdbc/test</jta-data-source>
<properties>
<property name="eclipselink.ddl-generation" value="create-tables"/>
<property name="eclipselink.logging.level" value="FINE"/>
</properties>
</persistence-unit>
</persistence>
WEB-INF/glassfish-resources.xml
<?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-connection-pool
name="java:app/jdbc/test_pool"
res-type="javax.sql.DataSource"
datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource"
pool-resize-quantity="1"
max-pool-size="5"
steady-pool-size="0"
statement-timeout-in-seconds="30" >
<property name="User" value="root"></property>
<property name="Password" value="root"></property>
<property name="portNumber" value="3306"></property>
<property name="dataBaseName" value="test"></property>
<property name="serverName" value="localhost"></property>
</jdbc-connection-pool>
<jdbc-resource pool-name="java:app/jdbc/test_pool" jndi-name="java:app/jdbc/test"></jdbc-resource>
</resources>
WEB-INF/classes/test/Employee:
package test;
import javax.persistence.*;

@Entity
public class Employee implements java.io.Serializable {
private static final long serialVersionUID = 1L;

@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Basic private String name;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}

@Override public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Employee)) {
return false;
}
Employee other = (Employee) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}

@Override public String toString() {
return "Employee id=" + id + ", name=" + name;
}
}
WEB-INF/classes/test/TestBean:
package test;
import javax.ejb.Stateless;
import javax.persistence.*;
import javax.annotation.sql.DataSourceDefinition;

@Stateless
public class TestBean {
@PersistenceContext private EntityManager em;

public void addEmployee(String[] names) {
for(String name : names) {
Employee e = new Employee();
e.setName(name);
em.persist(e);
}
}
}
WEB-INF/classes/test/TestServlet:
package test;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.annotation.*;
import javax.ejb.*;

@javax.servlet.annotation.WebServlet(urlPatterns = "/*")
public class TestServlet extends HttpServlet {
@EJB private TestBean testBean;

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
String[] names =request.getParameterValues("name");
testBean.addEmployee(names);
out.println("Added employees " + java.util.Arrays.toString(names));
}

@Override protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

@Override protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
}
WEB-INF/classes/test/Client:
package test;

import org.glassfish.embeddable.Deployer;
import org.glassfish.embeddable.GlassFish;
import org.glassfish.embeddable.GlassFishProperties;
import org.glassfish.embeddable.GlassFishRuntime;
import org.glassfish.embeddable.archive.ScatteredArchive;

import java.io.*;
import java.net.*;

public class Client {
private static final String warName = "test";
private static final int portNumber = 8080;
private GlassFish glassfish;
private Deployer deployer;

public static void main(String[] args) throws Exception {
Client client = new Client();
client.setUp();
client.test(args);
client.tearDown();
}

protected void setUp() throws Exception {
GlassFishProperties props = new GlassFishProperties();
props.setPort("http-listener", portNumber);
glassfish = GlassFishRuntime.bootstrap().newGlassFish(props);
glassfish.start();
deployer = glassfish.getDeployer();
}

protected void tearDown() throws Exception {
deployer.undeploy(warName);
glassfish.dispose();
}

protected void test(String[] names) throws Exception {
ScatteredArchive scattered = new ScatteredArchive(warName, ScatteredArchive.Type.WAR, new File("./"));
deployer.deploy(scattered.toURI());
URL url = new URL("http://localhost:" + portNumber + "/" + warName + "/?name=Jon&name=Jane");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
System.out.println("Request url: " + url);
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close();
conn.disconnect();
}
}
To compile from project directory:
$ javac -cp "$GLASSFISH_HOME/lib/embedded/glassfish-embedded-static-shell.jar" WEB-INF/classes/test/*java
Start mysqld, and run the app:
cd /usr/local/mysql-5.1.32; sudo bin/mysqld_safe --user root
java -cp "WEB-INF/classes:$GLASSFISH_HOME/lib/embedded/glassfish-embedded-static-shell.jar:$HOME/mysql-connector-java-5.1.5-bin.jar" test.Client
Among very verbose command-line output are these lines:
Request url: http://localhost:8080/test/?name=Jon&name=Jane
Added employees [Jon, Jane]
classLoader = WebappClassLoader (delegate=true; repositories=WEB-INF/classes/)
Verify entities are successfully created by running mysql:
mysql> select * from EMPLOYEE;
+----+------+
| ID | NAME |
+----+------+
| 1 | Jon |
| 2 | Jane |
+----+------+
2 rows in set (0.00 sec)
The test client main class mimics the strucutre of a JUnit test case, and can be easily converted to one if needed.

7 comments:

vic said...

I'm interested in creating JDBC conneciton pools on the fly in code, specifically for Glassfish V3.1 -- the problem is that I have code that will do that for me under Glassfish v2 but trying the same thing under GF V3.1 does not work. I have been trying to figure out how to do it through AMX but have had no success.

Matthew Cornell said...

Question: How did you get GF to know where persistence.xml was located? Thank you.

javahowto said...

The spec defines standard locations for persistence.xml, and at deployment time, the server will searches for persistence.xml in these standard paths.

Frode said...

This seems fine for just creating the entites. But what If my servlet is retrieving an entity in order to change it ?
I am struggeling to persist changes in the entities I retrieve from this injected EJB.

It seems that the transaction is flushed when the EJB's retrieve-method returns the entity, and that further changes inside the servlet is abandoned.

Do I have to actally merge the retrieved entity back to the EJB in order to make it persisted ?

Russkill said...

Very interesting post. What if I would like to create several databases (Test,Development and Production)? What would be the best solution? Should I create 3 pools and 3 different datasources? Thanks!

Anna said...

Great and Useful Article.

J2EE Training

Anna said...

Great and Useful Article.

Online Java Course

Java Online Training

Java Course Online

J2EE training
Great and Useful Article.

online J2EE training

Best Recommended books for Spring framework

Java Interview Questions












Java Training Institutes in Chennai

Java Training in Chennai

J2EE Training in Chennai

java j2ee training institutes in chennai