Convert a JPA-based Java EE Web application to OSGi with Blueprint object injection in WebSphere Application Server V7

The OSGi framework is a dynamic component model that is growing in popularity within enterprise architectures. The IBM® WebSphere® Application Server V7 Feature Pack for OSGi Applications and Java™ Persistence API (JPA) 2.0 was delivered for use with WebSphere Application Server V7.0 with Fix Pack 9 and above. This article describes the steps involved in converting a Java EE Web application that uses JPA 1.0 to an OSGi application that uses features in JPA 2.0 plus Blueprint injection of objects. Sample code for a simple WebSphere Application Server application is included for illustrative purposes. This content is part of the IBM WebSphere Developer Technical Journal.

Karl Gaffney (kgaffney@uk.ibm.com), Software Test Engineer, IBM

author photoKarl Gaffney works in the WebSphere OSGi team at the Hursley Lab in England. He holds a PhD in Artificial Intelligence. Karl has worked at IBM for 10 years, and has significant experience testing the OSGi framework in WebSphere Application Server and also the interoperability of the WebSphere Application Server and WebSphere MQ from a messaging perspective.



December 2010 (First published 25 August 2010)

Also available in Chinese Japanese

Introduction

The OSGi framework is a modular, dynamic system that builds on top of the Java Virtual Machine (JVM) and offers several key features to the enterprise application developer, such as:

  • Lifecycle management that enables applications to be installed, updated, started, stopped, and uninstalled without a system restart.
  • Modularization to reduce the dependency on large, monolithic classpaths.
  • The versioning of bundles so that different versions of the same code can co-exist and be made available to different applications.
  • Removal of the tendency of Java EE applications to have the same JAR files included within many enterprise applications.

The OSGi framework has been around for some time but it is only recently that the tools and implementations have become available for Java EE enterprise developers to create practical business level applications. These tools and implementations are provided with the IBM WebSphere Application Server V7 Feature Pack for OSGi Applications and Java Persistence API (JPA) 2.0 (hereafter referred to as the OSGi feature pack). The Blueprint container offers a dependency injection model designed to deal with the dynamic characteristics of OSGi as services become available and unavailable, and also facilitates the injection of POJOs (Plain Old Java Objects) into OSGi applications.

This purpose of this article is to help you understand how to convert your existing Java EE applications into OSGi-based applications. Along the way, a simple Java EE Web application is presented to illustrate the steps required. This application utilizes JPA 1.0 to persist an entity to a database. The steps outlined here involve:

  • Refactoring the manifest file.
  • Changing the file extension.
  • Blueprint injection of the EntityManager object.
  • Transactions.
  • Blueprint resource references.

Some JPA 2.0 sample code is also presented should you wish to experiment with a new OSGi application.

All of the practical steps described in this article require that WebSphere Application Server V7 together with both the OSGi and JPA 2.0 parts of the IBM WebSphere Application Server V7 Feature Pack for OSGi Applications and Java Persistence API (JPA) 2.0 are installed and operational in a development environment with default options accepted. See Resources for installation and configuration details.


A simple Java EE Web application-based EAR

The sample enterprise application that will be used to illustrate these steps consists of:

  • A Web application (the WAR component) containing an HTML"front page" and a servlet that enables the user to enter a name string in a browser. The servlet invokes classes in the utility JAR.
  • A utility JAR, located within the EAR but outside of the WAR (it is a peer of the WAR). This utility JAR contains a class that performs JPA 1.0 based interactions with a database that writes the data entered in a browser by the user to a database table. The servlet in the WAR invokes this class and passes in the data string entered.

The Web application project is called JPA1Web. The HTML front page code is shown in Listing 1. The servlet is called SubmitServlet_JPA1 and has a doGet method, shown in Listing 2.

Listing 1. HTML front page
<HTML> 
<HEAD> 
<TITLE>Example</TITLE> 
</HEAD> 
<BODY BGCOLOR="WHITE"> 
<TABLE BORDER="2" CELLPADDING="2"> 
<TR><TD WIDTH="275"> 
<H2>Simple JPA Form</H2> 
Enter a name to be logged in the database.<BR> 
Clicking Submit invokes 
<A HREF="./SubmitServlet_JPA1">SubmitServlet_JPA1.java</A>,<BR>  
<FORM METHOD="POST" ACTION="/JPA1Web/SubmitServlet_JPA1"> 
<INPUT TYPE="TEXT" NAME="DATA" SIZE=30> 
<P> 
<INPUT TYPE="SUBMIT" VALUE="Click Me"> 
<INPUT TYPE="RESET"> 
</FORM> 
</TD></TR> 
</TABLE> 
</BODY> 
</HTML>
Listing 2. Web application servlet
protected void doGet(HttpServletRequest request, HttpServletResponse response) 
	throws ServletException, IOException {
		
	response.setContentType("text/html");

	PrintWriter out = response.getWriter();
		
	String DATA = request.getParameter("DATA");
		
	if (DATA!=null) {

// DBWriterImpl is the implementation class in the utility 
// JAR used for obtaining the EntityManager and invoking 
// the JPA 1.0 APIs
		
		try {
			DBWriterImpl dbWriter = new DBWriterImpl();
	
			dbWriter.setUp();
				
			dbWriter.persistName(DATA);

			out.println("\nDatabase persist complete for JPA1.");
		}
		catch (Exception e) {
			out.println("*** Exception caught in servlet: "+e.toString());
		}
	}
}

The utility JAR, called jpautility for this example, contains all the packages and classes required to lookup the EntityManagerFactory, obtain the EntityManager, and interact with the database via JPA 1.0.

JPA is an industry standard for object-to-relationship mapping and forms part of the Enterprise JavaBeans (EJB) 3.0 specification. Among other things, JPA enables the relatively easy persistence of POJOs and is considered to be an improvement on the relative complexity, performance overhead, and portability issues of EJB 2.0 Entity Beans.

Notice that this example uses resource local transactions rather than container managed transactions. In the subsequent conversion to OSGi and the use of Blueprint injection, the sample application will have container managed JTA based transactionality.

The sample code in the DBWriter class (Listing 3) looks up the EntityManagerFactory, uses it to create an EntityManager, and then instantiates a Customer object with the customer name passed in by the user. This Customer object is a JPA managed class (@Entity) and is then persisted to the database.

The Customer class has been pre-built using the Apache Maven build tool (see Resources) and imported into IBM Rational® Application Developer (or other build environment) for incorporation within the utility JAR. This and other classes reside as .class code in the utility JAR’s /ImportedClasses directory.

Listing 3. DBWriterImpl class within the utility JAR
package com.ibm.ws.eba.example.jpawebapp.impl;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import org.apache.openjpa.persistence.criteria.Customer;
import com.ibm.ws.eba.example.jpawebapp.DBWriter;


public class DBWriterImpl implements DBWriter {
	
	private EntityManagerFactory emf;
	private EntityManager em;

	// obtains the EMF
	public void setUp() throws Exception {
	
		try {
			emf = (EntityManagerFactory) new InitialContext().lookup
				("java:comp/env/myEntityManagerFactory");
		} catch (NamingException e) {
			System.out.println("Failed to obtain the EntityManagerFactory");
			throw new Exception(e); 
		}
	}
	
	
	// persist the Customer entity to the database
	public void persistName(String name) throws Exception {

		try {
			em = emf.createEntityManager();
		
			// resource local transactions
			em.getTransaction().begin();

    			Customer c1 = new Customer();
    			c1.setName(name);
    	 
    			em.persist(c1);
   
    			em.getTransaction().commit();
		}
		catch (Exception e) {
			throw new Exception(e);
		}
		finally {
			em.close();
    			System.out.println("Name has been written to database.");
		}
	}
}

The lookup of the EntityManagerFactory is facilitated by the persistence-unit-ref entry in the web.xml file of the WAR (Listing 4). The corresponding /META-INF/persistence.xml in the utility JAR is shown in Listing 5.

Listing 4. Persistence unit XML entry within web.xml
<persistence-unit-ref>
 <persistence-unit-ref-name>myEntityManagerFactory</persistence-unit-ref-name> 
 <persistence-unit-name>dbpersistence</persistence-unit-name> 
</persistence-unit-ref>
Listing 5. Persistence XML
<persistence 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_1_0.xsd" version="1.0">

 	<persistence-unit name="dbpersistence" transaction-type="RESOURCE_LOCAL">
 <description>Persistence unit for the example JPA1 application</description> 
 <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
 
 <jta-data-source>jdbc/dbpersistence</jta-data-source> 
 <non-jta-data-source>jdbc/dbpersistencenojta</non-jta-data-source> 

 	<properties>
 <property name="openjpa.jdbc.SynchronizeMappings" 
			value="buildSchema(ForeignKeys=true)" /> 
	</properties>
	</persistence-unit>
</persistence>

Two data sources need to be created in WebSphere Application Server with JNDI names jdbc/dbpersistence and jdbc/dbpersistencenojta. The latter is non-transactional (select Non-transactional data source under the WebSphere Application Server data source properties link in the admin console).

The openjpa.jdbc.SynchronizeMappings property ensures that the correct database tables and schemas are created automatically for the data entities (in this case, Customer) to be persisted to the database, if those tables and schemas have not already been created.

Because the utility JAR lies outside of the WAR file, the WAR’s manifest.mf file contains:

Manifest-Version: 1.0
Class-Path: jpautility.jar

The only external library on the application’s build path for both the Java EE and OSGi applications is javax.j2ee.persistence.jar, obtainable from the OSGi feature pack installation in this directory:

<WAS_INSTALL_ROOT>/feature_packs/jpa/plugins

Older versions of this JAR could be acceptable for the JPA 1.0 calls, but the version specified above ensures the presence of JPA 2.0 classes.

You can deploy this sample application as an enterprise application on WebSphere Application Server, where it will run in the Web container and persist Customer objects to the database with a name entered by the user.


Converting to an OSGi application

Using Java EE JAR-based applications essentially means that there is no mechanism for the JARs to declare their dependencies, there are no means to encompass component versioning, and there is no real method of modularization (there is usually a large collection of classes on a classpath). Java EE applications often use vendor libraries and it is common to include these libraries within all the EAR files that require them. This results in overly large files and multiple copies of libraries in memory. It is also often the case that vendor libraries utilize other vendor libraries, which can lead to conflicts with versions. Within a Java EE application, it is only possible to have one version of each class. If, for example, you use multiple vendor libraries that have dependencies on incompatible versions of a common class, then you might have a problem.

The OSGi framework is designed to overcome these drawbacks.

Converting a Java EE Web application to an OSGi application basically means that:

  • The WAR component becomes a Web application bundle (WAB).
  • The utility JAR becomes a bundle.
  • The EAR file becomes an enterprise bundle archive (EBA).

The EBA can then be imported as an asset into WebSphere Application Server and the application can be deployed as a business level application.

The OSGi framework is designed to enable the building, deployment, and management of modular, reusable, and versioned applications by providing these key elements:

  • Bundles are JAR components with extra manifest headers, and can exist as multiple versions that can be installed and run without clashing. Manifest attributes can dictate which versions the application can use. How bundles import and export code is handled through a modules layer.
  • Service registry provides a model for bundle co-operation, such as class sharing and communication between bundles. The services layer connects bundles in a dynamic way.
  • OSGi lifecycle model enables installing, starting, stopping, updating, and uninstalling of bundles at run time without the need for a restart or reboot.
  • Execution environment defines what methods and classes are available in a specific platform.

To convert the utility JAR into an OSGi bundle, the /META-INF/MANIFEST.MF file has to contain certain headers. For the sample application described above, the new bundle manifest would look like Listing 6.

Listing 6. Bundle MANIFEST.MF file
Manifest-Version: 1.0
Bundle-SymbolicName: jpautility
Bundle-Version: 1.0.0
Bundle-ManifestVersion: 2
Meta-Persistence: 
Export-Package: com.ibm.ws.eba.example.jpawebapp;version=”1.0.0”
Import-Package: javax.persistence

Possible headers in this file include:

  • Bundle-SymbolicName: This is the name that will appear, for example, in the admin console, and is generally the name of the JAR. It is unique and required.
  • Bundle-Version: The version number designated to the bundle.
  • Bundle-ManifestVersion: The value 2 indicates that the latest version of the Equinox Eclipse project (which provides a certified implementation of the OSGi core framework specification) is being used. This is provided with the WebSphere Application server environment.
  • Meta-Persistence: The presence of this header indicates that this is a persistence bundle and can be used to specify the location of the persistence.xml file (if it does not reside in the default /META-INF directory).
  • Export-Package: List of packages contained within the utility JAR. The version shown at the end of the package is optional and designates a version number to the package exported as part of this bundle.
  • Import-Package: List of packages required (as imports) by classes within the bundle. This will vary from application to application. A version number at the end of the package is optional and will designate the minimum version or a range of versions that can accepted as package imports into the bundle.

Any empty header, such as Meta-Persistence above, must include a single space after the colon. Apart from this, no extra "white space" should exist at the end of a line or on the line beneath the last entry (after a carriage return at the end of this last entry).

This is not an exhaustive list of the possible headers available in the MANIFEST.MF file, but the ones shown here are sufficient for this example.

Bundle-Version becomes particularly important if an OSGi application becomes complex and many versions exist of a specific bundle. In their versioned forms, for example, these bundles could reside in the WebSphere Application Server internal bundle repository (although this will not be shown here). An OSGi application could utilize these bundles with a /META-INF/APPLICATION.MF file that would contain the names and versions of the bundles to use under the Application-Content header. The version of the bundle specified will define a minimum version (if a single version number is given), or a range (specified using square brackets) that can be accepted. Different OSGi applications can therefore utilize different versions of the same bundle. (See Apache Aries for more details.)

The Class-Path: jpautility.jar entry of the Web component’s MANIFEST.MF can now be removed.


Blueprint injection of the EntityManager

The OSGi Service Platform Release 4 V4.2 introduced the Blueprint Container specification. Blueprint is a standardization of the Spring injection dependency model and specifies how individual components of an OSGi application are wired together, and how services should be dealt with as they dynamically become available and unavailable. The model is designed to enable application building using POJOs so that they can be used inside and outside of an OSGi application.

The integration of Blueprint and OSGi means that bundles can publish services that can be injected into other bundles or components. An OSGi bundle is considered a Blueprint bundle if it contains either:

  • A blueprint.xml file in the default /OSGI-INF/blueprint directory of the bundle.
  • A blueprint.xml file in a non-default directory in the bundle, accompanied by a Bundle-Blueprint header in the MANIFEST.MF specifying this location.

Any Blueprint bundle will have a Blueprint container created for it (in this case, within the WebSphere Application Server runtime). A Blueprint extender bundle monitors the state of bundles in the framework, and, if they are Blueprint bundles, can perform actions on their behalf when they become active. One such example is the injection of objects useful to the application, such as EntityManagers.

The servlet code shown in Listing 7 now does a lookup of the DBWriter bean instead of the regular class instantiation that took place in the Java EE application. Here, DBWriter is the interface class. There is no requirement to call the DBWriter setUp method because the EntityManager will be directly injected into the bundle. The interface class is shown in Listing 8.

Listing 7. Servlet’s new doGet method code within OSGi application
if (DATA!=null) {

try {

	DBWriter dbWriter = (DBWriter) new InitialContext().lookup
		("osgi:service/com.ibm.ws.eba.example.jpawebapp.DBWriter");

		dbWriter.persistName(DATA);

		out.println("\nDatabase persist complete for JPA1.");
	}
	catch (Exception e) {
		out.println("*** Exception caught in servlet: "+e.toString());
	}
}
Listing 8. DBWriter interface class
package com.ibm.ws.eba.example.jpawebapp;

public interface DBWriter {

	public void persistName(String name);

}

When an OSGi bundle is deployed in the OSGi framework, the beans provided as part of that bundle are registered as services within the framework (in this case, the DBWriter bean). The lookup(“osgi:service/…”) call obtains the object dynamically from the OSGi services registry.

To utilize Blueprint injection of the EntityManager, the file /OSGI-INF/blueprint/blueprint.xml is created in the jpautility bundle (formerly the utility JAR in the Java EE application). In Rational Application Developer, this can be achieved by simply creating a new source folder then a new file. The XML for this is shown in Listing 9.

Listing 9. Blueprint XML file
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
 xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.0.0" 
	default-activation="lazy">

	<bean id="DBWriterBean" 
		class="com.ibm.ws.eba.example.jpawebapp.impl.DBWriterImpl">
	  <tx:transaction method="persistName" value="Required"/>
  	</bean>

  	<service interface="com.ibm.ws.eba.example.jpawebapp.DBWriter" 
		ref="DBWriterBean"/>

</blueprint>

The blueprint.xml file is identified as a Blueprint module by the top-level blueprint element. This contains the namespace declaration:

xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"

This specifies that the document conforms to Blueprint 1.0.0. The top level element also contains:

xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.0.0" default-activation="lazy"

This namespace declares a dependency on the Blueprint extension for declarative transactions. This extension is provided by the Blueprint implementation in WebSphere Application Server. The default-activation=”lazy” parameter in the XML indicates that the component’s manager is activated on demand and not on initialization of the Blueprint container.

The bean id is an arbitrary identifier so that the class ("bean" in Blueprint terminology) can be referenced within this XML code. The method persistName specified in the <tx:transaction> header indicates that this method on the DBWriterImpl bean and the associated persist calls within it will be handled as a container managed transaction. The transaction method has a transactional attribute (value) associated with it. In this case, the value is Required which indicates that the container ensures that the bean’s method will always be invoked within a JTA transaction. See Resources for other possible values, and for more information on the blueprint.xml file and the various component managers.

The persistence.xml file in the jpautility bundle is modified to include a transaction-type of JTA instead of RESOURCE_LOCAL:

<persistence-unit name="dbpersistence" transaction-type="JTA">

To complete the Blueprint injection of the EntityManager, the @PersistenceContext annotation needs to be embedded within the bean before the declaration, as shown in Listing 10.

Listing 10. DBWriter bean using Blueprint injection of the EntityManager
package com.ibm.ws.eba.example.jpawebapp.impl;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.apache.openjpa.persistence.criteria.Customer;
import com.ibm.ws.eba.example.jpawebapp.DBWriter;

public class DBWriterImpl implements DBWriter {
	
	@PersistenceContext(unitName="dbpersistence")
	private EntityManager em;
	
	
	public void persistName(String name) {
				
    		Customer c1 = new Customer();
    		c1.setName(name);
    	 
    		em.persist(c1);
	}
}

The @PersistenceContext annotation of the EntityManager object ensures that the Blueprint container injects this object, ready for use, into the bundle at run time. The unitName is the persistence-unit name in the persistence.xml file, so that critical information is set when the object is being injected into the bundle, such as the data source names to be used in conjunction with the EntityManager. Notice the absence of the resource local transaction calls because the transactionality is now container-managed.

JTA is a standard transaction API used within Java EE and OSGi that enables distributed transactions to be carried out across multiple XA resources in a Java environment. JTA-based transactions can either be managed by the application (uses javax.transaction.UserTransaction API) or managed by the application server or transaction manager (uses javax.transaction.TransactionManager API). In this example, transactions are now being handled by the TransactionManager.

The persistence-unit-ref entry for the EntityManagerFactory within the WAR’s web.xml file can now be removed.


Changing the file extension

At this point, the application can be compiled in the same manner as the Java EE EAR and (if using Rational Application Developer) exported as an EAR. The filename.ear is modified to be filename.eba and can now be imported as an asset in WebSphere Application Server and subsequently used within a business level application; that is, it is now a functional OSGi-based application.


Installing the application

Import the asset in the admin console by navigating to Applications > Application Types > Assets, and create the subsequent business level application by navigating to Applications > Application Types > Business-level applications.

In general, accepting the defaults on import and installation will usually suffice, but a particular business need might necessitate changes to the defaults. (See Resources for information on importing assets and installing business level applications.)

Accepting the defaults when installing business level applications will yield slightly different context roots from installing an enterprise application with defaults. For example, if you accept the defaults, the URLs of the Java EE and the OSGi applications would be similar to the following (assuming a WC_defaulthost of 9080 and an application name of JPAWebApp.ear and JPAWebApp.eba):

  • Java EE (EAR): http://hostname:9080/JPA1Web/frontPage.html
  • OSGi (EBA): http://hostname:9080/JPAWebApp.eba.JPA1Web/frontPage.html

This might require a slight modification to the value of the ACTION= call in frontPage.html of the OSGi application, or a change to the context root default of JPAWebApp.eba.JPA1Web when installing the business level application to make them consistent with each other.

If the bundle is likely to be shared between many applications with many versions of the bundle potentially to be developed, then it is worth it to consider the Internal Bundle Repository in WebSphere Application Server as a location for deployment for simple maintenance, as mentioned earlier. (Details on the Internal Bundle Repository is beyond the scope of this article.)

When the installation is complete, the utility component is deployed as an OSGi bundle and the Web component is deployed as a WAB.


Utilizing Blueprint resource references

Using resource references is a standard way to obtain centrally managed and administered Java objects in both Java EE and OSGi environments. One main advantage of this is that the DataSource object, for example, can have authentication aliases associated with it to provide a degree of security and to help ensure that only applications with the right credentials can access system resources.

To use Blueprint resource references for the data sources within an OSGi application, you must first specify them within the blueprint.xml file. For the transactional and non-transactional data sources defined in the WebSphere Application Server JNDI namespace (jdbc/dbpersistence and jdbc/dbpersistencenojta) you can include the code shown in Listing 11 in the XML.

Listing 11. Blueprint XML with data source resource references
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
 xmlns:rr="http://www.ibm.com/appserver/schemas/8.0/blueprint/resourcereference"
 xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.0.0" 
	default-activation="lazy">

	<bean id="DBWriterBean" 
		class="com.ibm.ws.eba.example.jpawebapp.impl.DBWriterImpl">
	  <tx:transaction method="persistName" value="Required"/>
  	</bean>

  	<rr:resource-reference id="res-ref"
          interface="javax.sql.DataSource"
          filter="(osgi.jndi.service.name=jdbc/dbpersistence)">
    		<rr:res-auth>Container</rr:res-auth>
    		<rr:res-sharing-scope>Shareable</rr:res-sharing-scope>
  	</rr:resource-reference>

  	<rr:resource-reference id="res-refnojta"
          interface="javax.sql.DataSource"
          filter="(osgi.jndi.service.name=jdbc/dbpersistencenojta)">
    		<rr:res-auth>Container</rr:res-auth>
    		<rr:res-sharing-scope>Shareable</rr:res-sharing-scope>
  	</rr:resource-reference>

  	<service interface="com.ibm.ws.eba.example.jpawebapp.DBWriter" 
		ref="DBWriterBean"/>

</blueprint>

Notice the addition of the namespace declaration xmlns:rr. The two data sources have been given resource-reference IDs of "res-ref" and "res-refnojta" for the transactional and non-transactional data sources, respectively. The jpautility bundle’s persistence.xml file is now modified to account for these new resource references:

<jta-data-source>blueprint:comp/res-ref</jta-data-source>
<non-jta-data-source>blueprint:comp/res-refnojta</non-jta-data-source>

Be aware that this lookup mechanism is only supported through the use of the persistence.xml file. When the business level application is created and the asset added for this application, there will be an extra panel in the admin console that summarizes the resource reference mappings and enables you to link an authentication alias to the data sources for enhanced security (Figure 1).

Figure 1. Authentication aliases for data source resource references
Figure 1. Authentication aliases for data source resource references

Including JPA 2.0 code within the application

The main focus of JPA 2.0 is to provide function that is currently provided disparately across various object relational mapping vendors but was not provided in JPA 1.0. This includes support for ordered lists, combinations of access types, multiple levels of embedded objects, and collections of embedded objects.

Another key aspect of the JPA 2.0 specification is the Criteria query API. This API is used to define queries for retrieving persistent entities and their state by creating query defining objects. The Criteria queries are portable and typesafe and are designed to work regardless of the data store used. They provide a convenient mechanism for search-like queries where there might not be a fixed number of conditions placed upon the results obtained.

Listing 12 illustrates the use of the JPA 2.0 Criteria API (javax.persistence.criteria). The sample code is again invoked from the servlet (although no user-provided data is required). Three managed (@Entity) classes are used: Customer, Account, and Order, the internal details of which are not covered here, but illustrate the use of the API.

Listing 12. DBWriter bean modified to use the JPA 2.0 Criteria API
package com.ibm.ws.eba.example.jpawebapp.impl;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Fetch;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Root;

import org.apache.openjpa.persistence.criteria.Address;
import org.apache.openjpa.persistence.criteria.Customer;
import org.apache.openjpa.persistence.criteria.Order;
import com.ibm.ws.eba.example.jpawebapp.DBWriter;

public class DBWriterImpl implements DBWriter {
	
	@PersistenceContext(unitName="dbpersistence")
	private EntityManager em;
	private CriteriaBuilder cb;

	...
public void JPA2Distinct() throws Exception {
    	    
	createDataForJPA2Distinct();
	   	
	cb = em.getCriteriaBuilder();
    	
     	CriteriaQuery<Customer> cq = cb.createQuery(Customer.class);
     	Root<Customer> customer = cq.from(Customer.class);
	Fetch<Customer, Order> o = customer.fetch("orders", JoinType.LEFT);

	// define the query to get all entries where the address state
	// is either “Y”or “I”line
	cq.where(customer.get("address").get("state").in("NY", "RI"));
	// select only the distinct entries
     	cq.select(customer).distinct(true);
     	TypedQuery<Customer> distinctQuery = em.createQuery(cq);
     	distinctQuery.setMaxResults(20);
     	List<Customer> distinctResult = distinctQuery.getResultList();  

     	if (distinctResult.size()==2) System.out.println("JPA2 Criteria API
		(1) invocation complete.");
     	else throw new Exception("JPA2 Criteria API (1) invocation failed.");

	// select all entries, not just distinct ones
     	cq.distinct(false);
     	TypedQuery<Customer> indistinctQuery = em.createQuery(cq);
     	indistinctQuery.setMaxResults(20);
     	List<Customer> indistinctResult = indistinctQuery.getResultList();
        
     	if (indistinctResult.size()==3) System.out.println("JPA2 Criteria API 
		(2) invocation complete ");
     	else throw new Exception("JPA2 Criteria API (2) invocation failed.");

     	deleteDataForJPA2Distinct();
}

...
}

The jpautility bundle’s MANIFEST.MF file now has an additional Import-Package entry to cater for the JPA 2.0 Criteria APIs (Listing 13).

Lisitng 13. The bundle’s MANIFEST.MF file modified to import the Criteria API
Manifest-Version: 1.0
Bundle-SymbolicName: jpautility
Bundle-Version: 1.0.0
Bundle-ManifestVersion: 2
Meta-Persistence: 
Export-Package: com.ibm.ws.eba.example.jpawebapp;version=”1.0.0”
Import-Package: javax.persistence,
javax.persistence.criteria

In this code sample (see the Appendix for the data creation and deletion methods required to use the Criteria API):

  • A Customer entity contains an Address, and an Address contains a state.
  • An Order entity has a Customer associated with it and a Customer can have many Orders associated with it.
  • Customer #1 makes two Orders and Customer #2 makes one Order.

All these entities are persisted to the database. The application is then comprised of two parts:

  • The Criteria API is utilized to define a query to select all entries where the Address’s state attribute is either "NY" or "RI." As one customer (whose state = NY) has made two orders and the other (whose state = RI), one order, there will be three indistinct entries in all (based on Address). The query then utilizes the distinct(true) method to select the two distinct entries (based on Address) from the list.
  • The application code simply calls the distinct(false) method to obtain the three indistinct entries. No user data needs to be passed into the servlet (for example, from the browser) and the sample can be run as before.

Running this sample should yield the following in the WebSphere Application Server SystemOut.log:

JPA2 Criteria API (1) invocation complete.
JPA2 Criteria API (2) invocation complete.


Conclusion

This article described the component parts of a simple Java EE Web application that uses JPA 1.0 calls and explained how to convert it into an OSGi-based application. The application JAR was converted to a bundle by refactoring the manifest, and the resulting OSGi application was modified to use the Blueprint container to inject the EntityManager dynamically and manage the data source resource references. The use of JPA 2.0 was exemplified using the Criteria query API. The sample application was imported as an asset into WebSphere Application Server, and then a business level application was deployed that comprises an OSGi WAB and a bundle.


Appendix

Listing 14. DBWriter methods for creating and deleting entity data in the database
private void createDataForJPA2Distinct() {
    	        
Address a1 = new Address(); a1.setState("NY");
      Address a2 = new Address(); a2.setState("RI");
        
      Customer c1 = new Customer(); c1.setAddress(a1);
      Customer c2 = new Customer(); c2.setAddress(a2);
        
      Order o1 = new Order(); o1.setCustomer(c1); 
      Order o2 = new Order(); o2.setCustomer(c1); 
      Order o3 = new Order(); o3.setCustomer(c2); 
        
      Set<Order> orders = new HashSet<Order>();
      orders.add(o1); orders.add(o2);
      c1.setOrders(orders);
      orders.clear();
      orders.add(o3);
      c2.setOrders(orders);
        
      em.persist(c1);
      em.persist(c2);
      em.persist(a1);
      em.persist(a2);
      em.persist(o1);
      em.persist(o2);
      em.persist(o3);
        
}


private void deleteDataForJPA2Distinct() {
    	    	
      em.createQuery("delete from Customer o").executeUpdate();
      em.createQuery("delete from Address o").executeUpdate();
      em.createQuery("delete from Order o").executeUpdate();
        
}

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=512958
ArticleTitle=Convert a JPA-based Java EE Web application to OSGi with Blueprint object injection in WebSphere Application Server V7
publish-date=12252010