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.

2/10/2011

How to create and look up thread pool resource in GlassFish

In order to create a custom thread pool task executor resource, one needs to implement 2 classes: the thread pool class, and its object factory class.

In my implementation, the thread pool class, test.ThreadPoolExecutor is simply a subclass of java.util.concurrent.ThreadPoolExecutor. It is implemented as a singleton so every lookup and injection always return the same instance.

package test;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutor extends java.util.concurrent.ThreadPoolExecutor {
static final int defaultCorePoolSize = 5;
static final int defaultMaximumPoolSize = 10;
static final long defaultKeepAliveTime = 10;
static final TimeUnit defaultTimeUnit = TimeUnit.MINUTES;
static final BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>();
private static ThreadPoolExecutor instance;

private ThreadPoolExecutor() {
super(defaultCorePoolSize, defaultMaximumPoolSize, defaultKeepAliveTime, defaultTimeUnit, workQueue);
}

synchronized static ThreadPoolExecutor getInstance() {
if (instance == null) {
instance = new ThreadPoolExecutor();
}
return instance;
}
}
The factory class, test.ThreadPoolExecutorFactory, is required to implement javax.naming.spi.ObjectFactory. It also implements a GlassFish-specific interface com.sun.appserv.server.LifecycleListener so that it can also be registered as a GlassFish lifecycle module. Upon receiving a server termination event, this class will shutdown the thread pool resource.
package test;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.concurrent.TimeUnit;

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.Reference;

import com.sun.appserv.server.LifecycleEvent;
import com.sun.appserv.server.ServerLifecycleException;

public class ThreadPoolExecutorFactory implements javax.naming.spi.ObjectFactory,
com.sun.appserv.server.LifecycleListener, java.io.Serializable {
public Object getObjectInstance(Object obj, Name name, Context nameCtx,
Hashtable<?, ?> environment) throws Exception {
ThreadPoolExecutor tp = ThreadPoolExecutor.getInstance();
try {
Reference reference = (Reference) obj;
Enumeration<?> enumeration = reference.getAll();
TimeUnit timeUnit = ThreadPoolExecutor.defaultTimeUnit;
long keepAliveTime = ThreadPoolExecutor.defaultKeepAliveTime;
while (enumeration.hasMoreElements()) {
RefAddr refAddr = (RefAddr) enumeration.nextElement();
String pname = refAddr.getType();
String pvalue = (String) refAddr.getContent();
if ("corePoolSize".equalsIgnoreCase(pname)) {
tp.setCorePoolSize(Integer.parseInt(pvalue));
} else if ("maximumPoolSize".equalsIgnoreCase(pname)) {
tp.setMaximumPoolSize(Integer.parseInt(pvalue));
} else if ("timeUnit".equalsIgnoreCase(pname)) {
timeUnit = TimeUnit.valueOf(pvalue);
} else if ("keepAliveTime".equalsIgnoreCase(pname)) {
keepAliveTime = Long.parseLong(pvalue);
} else if ("allowCoreThreadTimeOut".equalsIgnoreCase(pname)) {
tp.allowCoreThreadTimeOut(Boolean.parseBoolean(pvalue));
} else if ("prestartAllCoreThreads".equalsIgnoreCase(pname)) {
if (Boolean.parseBoolean(pvalue)) {
tp.prestartAllCoreThreads();
}
} else {
throw new IllegalArgumentException("Unrecognized property name: " + pname);
}
}
tp.setKeepAliveTime(keepAliveTime, timeUnit);
} catch (Exception e) {
throw (NamingException) (new NamingException()).initCause(e);
}
return tp;
}

public void handleEvent(LifecycleEvent event) throws ServerLifecycleException {
if (event.getEventType() == LifecycleEvent.TERMINATION_EVENT) {
ThreadPoolExecutor tp = ThreadPoolExecutor.getInstance();
System.out.println("About to purge and shutdown " + tp + ", active thread count: "
+ tp.getActiveCount());
tp.purge();
tp.shutdown();
}
}
}
Copy 2 class files to $GLASSFISH_HOME/domains/domain1/lib/classes directory, with package name, and restart domain:
ls $GLASSFISH_HOME/domains/domain1/lib/classes/test/
ThreadPoolExecutorFactory.class ThreadPoolExecutor.class

$ asadmin restart-domain
Next, create the thread pool resource, and register lifecycle module. It can also be done in admin console, in a more user-friendly manner.
$ asadmin create-custom-resource --restype test.ThreadPoolExecutor --factoryclass test.ThreadPoolExecutorFactory --description "A ThreadPoolExecutor backed by LinkedBlockingQueue" --property corePoolSize=6:maximumPoolSize=50:keepAliveTime=4:timeUnit=MINUTES:allowCoreThreadTimeOut=true:prestartAllCoreThreads=true concurrency/TP
Command create-custom-resource executed successfully.

$ asadmin create-lifecycle-module --classname "test.ThreadPoolExecutorFactory" --failurefatal=true concurrency/TP-shutdown
To list and delete custom resources and lifecycle modules:
$ asadmin list-custom-resources

$ asadmin list-lifecycle-modules

$ asadmin delete-lifecycle-module concurrency/TP-shutdown

$ asadmin delete-custom-resource concurrency/TP
This is how it looks like in admin console, where you can easily manage it:

To use this resource, just inject or look up in application components. The following is a test servlet that verifies that the resource can be obtained with either @Resource or regular lookup, and that multiple lookups return the same instance.
package test;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.annotation.*;
import javax.naming.*;

@javax.servlet.annotation.WebServlet(urlPatterns = "/*")
public class TestServlet extends HttpServlet {
@Resource(name="java:app/env/concurrency/TP", mappedName="concurrency/TP")
private test.ThreadPoolExecutor tp;

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
testLookup();
System.out.println("About to submit tasks to " + tp);
for(int i = 0; i < 30; i++) {
tp.execute(new MyRunnable());
}
}

private void testLookup() throws ServletException {
try {
for(int i = 0; i < 10; i++) {
ThreadPoolExecutor t = InitialContext.<ThreadPoolExecutor>doLookup("java:app/env/concurrency/TP");
System.out.println("ThreadPoolExecutor from lookup: " + t);
}
} catch (NamingException e) {
throw new ServletException(e);
}
}

private static class MyRunnable implements Runnable {
public void run() {
System.out.println("Executing task in " + Thread.currentThread());
}
}
}
When running the test webapp at http://localhost:8080/test/, many log messages like these will appear in server.log:
ThreadPoolExecutor from lookup: test.ThreadPoolExecutor@12e0a75a
Executing task in Thread[pool-29-thread-6,5,grizzly-kernel
In the above output, pool-29-thread-6 is the thread name, 5 (normal) is its priority, and grizzly-kernel is thread group name. When shutting down the server, the thread pool is purged and shutdown, thanks to the lifecycle module concurrency/TP-shutdown:
Server shutdown initiated
About to purge and shutdown test.ThreadPoolExecutor@12e0a75a, active thread count: 0
JMXStartupService: Stopped JMXConnectorServer: null
JMXStartupService and JMXConnectors have been shut down.
Shutdown procedure finished
This is just a simplistic implementation of thead pool resource in GlassFish. There is a potential class loader leak and may cause ClassCastException and/or OutOfMemoryError in large applications.

2/06/2011

EJB lite, JPA, DataSource embedded in java application

This post demonstrates how to put EJB lite, JPA, DataSource together in a standalone java application, using standard JavaEE 6 API in GlassFish. The application source consists of 4 files: persistence.xml, JPA entity, no-interface EJB, and main class.

Transaction is managed by embeddable EJB container. The persistence unit is backed by an application-scoped JTA DataSource declared with annotation on EJB class. No need to package or deploy anything. Client and server pieces are all in a single JVM.

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>
JPA entity 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;
}
}
No-interface stateless EJB:
package test;
import javax.ejb.Stateless;
import javax.persistence.*;
import javax.annotation.sql.DataSourceDefinition;

@Stateless
@DataSourceDefinition(
name="java:app/jdbc/test",
className="com.mysql.jdbc.jdbc2.optional.MysqlDataSource",
user="root",
password="root",
databaseName="test",
serverName="localhost",
portNumber=3306 )
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);
}
}
}
main class:
package test;
import javax.ejb.embeddable.EJBContainer;
import javax.naming.*;

public class Client {
public static void main(String[] args) throws Exception {
EJBContainer container = EJBContainer.createEJBContainer();
Context namingContext = container.getContext();
TestBean testBean = (TestBean) namingContext.lookup("java:global/classes/TestBean");
testBean.addEmployee(args); //need to pass in employee names from command line
container.close();
}
}
To compile from project directory:
$ javac -cp "classes:$GLASSFISH_HOME/lib/embedded/glassfish-embedded-static-shell.jar" classes/test/*java
Start mysqld, and run the app:
cd /usr/local/mysql-5.1.32; sudo bin/mysqld_safe --user root
java -cp "classes:$GLASSFISH_HOME/lib/embedded/glassfish-embedded-static-shell.jar:$HOME/mysql-connector-java-5.1.5-bin.jar" test.Client Jon Jane
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)
If you are wondering why the jar is named xxx-static-shell.jar, I guess "static" means it's not OSGi bundle, and "shell" means it's a wrapper for all the other GlassFish jars.

2/05/2011

A sample glassfish-resources.xml

A sample glassfish-resources.xml file that defines a global datasource and its underlying jdbc connection pool:

<?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="jdbc/test-pool" res-type="javax.sql.DataSource"
datasource-classname="org.apache.derby.jdbc.ClientDataSource"
pool-resize-quantity="1" max-pool-size="5" steady-pool-size="0"
statement-timeout-in-seconds="60" >
<property name="serverName" value="localhost" />
<property name="portNumber" value="1527" />
<property name="dataBaseName" value="sun-appserv-samples" />
<property name="User" value="APP" />
<property name="Password" value="APP" />
<property name="connectionAttributes" value=";create=true" />
<property name="driverType" value="4" />
</jdbc-connection-pool>

<jdbc-resource jndi-name="jdbc/test-ds" pool-name="jdbc/test-pool" />
</resources>
To create the declared jdbc resources with GlassFish asadmin command:
$ asadmin add-resources $HOME/glassfish-resources.xml
Command : JDBC connection pool jdbc/test-pool created successfully.
Command : JDBC resource jdbc/test-ds created successfully.
Command add-resources executed successfully.
To delete both resources:
$ asadmin delete-jdbc-connection-pool
--cascade true jdbc/test-pool
JDBC Connection pool jdbc/test-pool deleted successfully
Command delete-jdbc-connection-pool executed successfully.
To create, delete or list these jdbc resources in a cluster, simply add target option to the asadmin command:
$ asadmin add-resources --target cluster1 $HOME/glassfish-resources.xml
instance1:
Command : JDBC connection pool jdbc/test-pool created successfully.
Command : JDBC resource jdbc/test-ds created successfully.

instance2:
Command : JDBC connection pool jdbc/test-pool created successfully.
Command : JDBC resource jdbc/test-ds created successfully.

Command : JDBC connection pool jdbc/test-pool created successfully.
Command : JDBC resource jdbc/test-ds created successfully.
Command add-resources executed successfully.


$ asadmin list-jdbc-resources cluster1
jdbc/test-ds
Command list-jdbc-resources executed successfully.


$ asadmin list-jdbc-connection-pools cluster1
__TimerPool
DerbyPool
jdbc/test-pool
Command list-jdbc-connection-pools executed successfully.


$ asadmin delete-jdbc-connection-pool --cascade true --target cluster1 jdbc/test-pool
CLI031 Warning: Option "target" is obsolete and will be ignored.
JDBC Connection pool jdbc/test-pool deleted successfully
instance1:
JDBC Connection pool jdbc/test-pool deleted successfully

instance2:
JDBC Connection pool jdbc/test-pool deleted successfully

Command delete-jdbc-connection-pool executed successfully.
You can also create jdbc resources in GlassFish admin console, or via asadmin specifying command-line options. More details are in How to create jdbc connection pool and DataSource in GlassFish

Another usage of glassfish-resources.xml is to include it inside application, e.g., WEB-INF/glassfish-resources.xml for web app, META-INF/glassfish-resources.xml for ejb jar, etc. The declared resources are created upon deployment, and disposed of upon undeployment. More details in GlassFish embedded, JPA, EJB, DataSource and glassfish-resources.xml