OSGi and JPA: Websphere/RAD Workbook

Architectural Discussion and Design Overview 



Development Platform and Tools: 
- Windows 7-64
- Rational Application Developer v9 (beta) 
- Linux/CentOS v6.3 OS
- Websphere Application Server v8.5
- Database: DB2 v9.7 sp7


Reference:

Apache ARIES Blueprint tutorial
http://aries.apache.org/documentation/tutorials/blueprinthelloworldtutorial.html 

Blueprint Services by Guillaume Nodet (slideshare)
http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=blueprintservices-090622184559-phpapp01&stripped_title=osgi-blueprint-services-1622424 



Resources:

Architectural Discussion and Design Overview 



OSGi and JPA on Websphere Application Server v8.5


1) Create a Database and Table

Using the DB2 tools (Control Center)


2a) Create a Websphere Application Server 
I used the Profile Management Tool (PMT). I'm using a "stand alone" (not ND) application server because Rational doesn't require a jython/wasadmin script to deploy into this configuration. 


2b) In Websphere Console, Create a JAAS - J2C authentication 
Global security > JAAS - J2C authentication data > new


3) In Websphere Console, Create a DataSource
Resources -> Data sources -> new
NOTE: You may need to add the DB2 drivers to Websphere

After creating the datasource... "Test Connetion"



4) Create Database Connection

From Rational Application Developer (RAD) 


Create a Database Connection
Right click Database Connections -> new


Connection Parameters


After connection is created, you should be able to browse to the test table



5) Create JPA OSGi Bundle Project

From Rational Application Developer (RAD) 

NOTE: Runtime and Server configured for Websphere v8.5. For development I use the "stand-alone" (not ND) application server because RAD doesn't require a wasadmin script to deploy into this environment. 

Top-menu: File -> New -> Other -> filter Wizard on "osgi" ->OSGi Bundle Project
- Next


Configure OSGi Bundle Project for JPA
- check "add persistence support" - JPA 2.0 
- uncheck "add bundle to application (we'll create an application later for this project)
-> Next


Change the Active Target Platform (if required) 
I was previously working on Websphere Liberty... 


Java
- leave defaults


Configure JPA Facet
NOTE: I'm going to try some Junit testing with this project... so I added the "driver library to build path". Normally, this isn't necessary because we'll be using a Websphere provided datasource. 
->  Next 



OSGi Bundle settings
-> Finish


OSGi JPA Project Created




6) Create JPA Entity


Right-click the jpa project -> JPA Tools -> Generate Entities from Tables




Select Tables
-> Next


Table Associations (none for this example)
-> Next


Defaults (no changes required)
- no key generator
- creating a package: model
- no superclass 
-> Next


More options... Customize Individual Entities (none required)
-> Finish



Color Entity Created



Configure Entity: Color

Right-click project -> JPA Tools -> Configure JPA Entities...




Select the Entity: Color (for our example) 
-> Next



Primary Key
- colorCode



Relationships
- Not using "relationships" in this example

Named Queries
-> Add


Add Named Query: Result Attributes


Add Named Query: Order Results
Move colorcode into the "Order Attributes" 
-> OK


Tasks: Other
Make sure "convert Java set objects to Java list objects" is checked
-> Finish



Color.java (should look something like what's listed below)

===========
package model;

import java.io.Serializable;
import javax.persistence.*;


/**
* The persistent class for the COLORS database table.
*
*/
@Entity
@Table(name="COLORS")
@NamedQuery(name = "getColor", query = "SELECT c FROM Color c ORDER BY c.colorCode")
public class Color implements Serializable {
     private static final long serialVersionUID = 1L;

     @Id
     @Column(name="COLOR_CODE")
     private String colorCode;

     @Column(name="COLOR_NAME")
     private String colorName;

     public Color() {
     }

     public String getColorCode() {
          return this.colorCode;
     }

     public void setColorCode(String colorCode) {
          this.colorCode = colorCode;
     }

     public String getColorName() {
          return this.colorName;
     }

     public void setColorName(String colorName) {
          this.colorName = colorName;
     }

}
===========


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 ="jpa_osgi_example_01" transaction-type="RESOURCE_LOCAL" >
            <jta-data-source >jdbc/JPA_TEST </ jta-data-source>
           <non-jta-data-source >jdbc/JPA_TEST</ non-jta-data-source>
            <class >model.Color </class >
     </persistence-unit >
</persistence>






7) Create JPA Entity Manager (Manager Bean)
NOTE: See appendix (bottom of page) for full source list

Right-click project -> JPA Tools -> Add JPA Manager Beans




JPA ManagerBean Wizard
- Select Color Entity
-> Next


Tasks: Other
- select "I want the container to inject the persistence unit..."
- We'll be generating an interface for BluePrint. So, add the "Impl" suffix to the generated Bean Name. 
-> Finish


Controller Created
- We'll follow the RAD package naming conventions for now



Note the "persistence injection" 
- NOTE: Don't forget to add in the "unitName" value. This is the "persistence-unit name" from "persitsence.xml".  
- We'll get Blueprint to make this happen

@SuppressWarnings("unchecked" )
@JPAManager(targetEntity = model.Color.class)
public class ColorManagerImpl {

      // @PersistenceUnit
      @PersistenceUnit(unitName="jpa_osgi_example_01") // NOTE: Added this in for now - will set later in blueprint
     private EntityManagerFactory emf;
     @Resource
     private UserTransaction utx;

     public ColorManager() {
     
     }


Generate an Interface from the ColorManagerImpl
right-click ColorManagerImpl -> Refactor -> Extract interface




- check  "Declare interface methods as public"
- uncheck "Declare interface methods as abstract"
-> OK



Generated Interface ColorManager
-------------
package model.controller;

import java.util.List;

import model.Color;

import com.ibm.jpa.web.Action;
import com.ibm.jpa.web.NamedQueryTarget;

public interface ColorManager {

     public void createColor(Color color) throws Exception;

     public void deleteColor(Color color) throws Exception;

     public void updateColor(Color color) throws Exception;

     public Color findColorByColorCode(String colorCode);

     public Color getNewColor();

     public List<Color> getColor();

}
------------




7a) Make sure the JTA datasource matches your Websphere datasource! 

I had to go back and change "jpa_test" to upper-case (new value: jdbc/JPA_TEST)




8) Add Blueprint to JPA OSGI Bundle
Blueprint helps us manage component instances and their interdependencies/wiring to each other. 

right-click project -> new -> under osgi -> blueprint file
-> next




leave default name and location:
[project_name]/BundleContent/OSGI-INF/blueprint

-> next


Only using the standard features for now (no IBM proprietary extensions or security)
-> Finish


Blueprint file created


Add Bean
right-click Blueprint -> select Bean
-> OK


1) Browse for the ColorManagerImple
2) Filter on the package path
3) Select ColorManagerImpl (within model.controller)
4) Make sure you're looking within the correct project!
5) OK

NOTE: Make sure you pick the class from the correct project!


Accept the default assigned Bean ID name
-> OK


Create a Service
Select Blueprint -> Click add -> Select Service
-> OK
NOTE:  We'll reference the service from within other components


1) Click Brows
2) filter on model.controller package
3) Select the interface: ColorManager (from within model.controller package) 
4) Make sure you pointing to the correct project!
5) OK



(continued)

6) Click browse for "Bean Reference"
7) Select the Bean we just created above, "ColorManagerImplBean"
8) OK


Leave the default generated Service ID name
-> OK


ColorManagerImpleBeanService (service) created


Showing relationship between interface, implementationbean and service


Adding some methods for notification purposes only 
- This helps us track state as we create and test our JPA bundle service

Add this to the interface:
     public void startUp();
     
     public void onRegisterService(ColorManager colorManager, Map props);

     public void onUnRegisterService(ColorManager colorManager, Map props);
     
     public void referenceBind(ColorManager colorManager);
     
     public void referenceUnBind(ColorManager colorManager);

Implementations: 
     @Override
     public void startUp() {
           System. out.println( "*** ColorManagerImpl.startUp()");
           
     }

     @Override
     public void onRegisterService(ColorManager colorManager, Map props) {
           System. out.println( "*** ColorManagerImpl.startUp()");
           
     }

     @Override
     public void onUnRegisterService(ColorManager colorManager, Map props) {
           System. out.println( "*** ColorManagerImpl.onUnRegisterService()");
           
     }

     @Override
     public void referenceBind(ColorManager colorManager) {
           System. out.println( "*** ColorManagerImpl.referenceBind()");
           
     }

     @Override
     public void referenceUnBind(ColorManager colorManager) {
           System. out.println( "*** ColorManagerImpl.referenceUnBind()");
           
     }


Set Initialization method to "startUp"
using "blueprint" editor


Add RegisterService Listeners
1) Select ColorManagaerImplBleanService
2) Add
3) Reigstration listener 
-> OK


Fill in property details for Registration Listener
NOTE: Bind listeners will be added next step


blueprint.xml
-------------------------
<?xml version="1.0" encoding= "UTF-8"?>
<blueprint xmlns= "http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:bpjpa= "http://aries.apache.org/xmlns/jpa/v1.0.0" xmlns:bptx= "http://aries.apache.org/xmlns/transactions/v1.0.0" xmlns:bpresref="http://www.ibm.com/appserver/schemas/8.0/blueprint/resourcereference" >
     <bean id ="ColorManagerImplBean"
            class= "model.controller.ColorManagerImpl" init-method= "startUp">
            <!-- <bptx:transaction method="*" value="Required"/>  -->
            <!-- <bpjpa:unit property=" emf" unitname="jpa_osgi_example_01"/> -->
     </bean >
     <service id ="ColorManagerImplBeanService" ref= "ColorManagerImplBean"
            interface= "model.controller.ColorManager">
            <registration-listener ref ="ColorManagerImplBean" registration-method="onRegisterService" unregistration-method="onUnRegisterService" />
     </service >
</blueprint>




Export the model and controller so it's available for other bundles
Edit the MANIFEST.MF and export the two packages
Runtime tab -> Exported Packages
1) Add
2) select model and model.controller
3) OK





9) Create JPA Test Harness
Because OSGi can be a little difficult to unit-test... well, I wasn't able to get jUnit running well enough for a test. So, quick workaround is to simply role-a-custom unit test harness for now (PaxExam is the recommended approach - and we'll get to that later).

This will be yet-another OSGi bundle that tests the JPA bundle... 

Create a Test OSGi bundle project - named: jpa_osgi_example_01_test


Accept defaults except that we set the versio nto "1.0.0"
-> Finish



Create a blueprint that we'll use to wire into the jpa bundle
accept defaults except for setting support for: JPA, Transaction, and Resource Reference
-> Finish


Blueprint ready to be used for connecting into JPA project



Import Packages
Edit MANIFEST.MF
Dependencies tab

1) Imported Packages -> Add
2) Select the "model" and "model.controller" packages
3) OK



Create a simple test class
- add a property and setter for reference to ColorManager (the thing we'll be testing)
- add one method for now: startUp()

package jpa_tests;

import model.controller.ColorManager;

public class JpaExampleTest {
     
     private ColorManager colorManager = null ;
     
     public ColorManager getColorManager() {
            return colorManager;
     }

     public void setColorManager(ColorManager colorManager) {
            this. colorManager = colorManager;
     }

     public void startUp() {
           System. out.println( "*** CJpaExampleTest.startUp CALLED");
     }

}


Configure blueprint to wire JpaExampleTest the ColorManager... 

Create a "Bean" for JpaExampleTest 
NOTE: Moving more quickly through the tutorial since we already covered bean and service creation...
- Set "initialization method" to our "startUp()" method




In blueprint, create a Reference to ColorManager service
1) Select "Blueprint" 
2) click Add
3) Select Reference
4) OK


(continued...)

5) Click Browse
6) Filter on "model.controller." package
7) Select the interface from the jpa_osgi_example_01 project
8) verify we're pointing to the correct project and interface
9) OK



(continued...)

10) manually type in the ID we'll use: ColorManagerReference
11) OK

NOTE: Do Not set "component name" (for some reason this prevented the reference from working correctly)


blueprint reference created
NOTE: Leave component name blank!



Create a Reference Listener for the ColorManagerReference

1) select ColorManagerReference
2) Click Add
3) Select Reference Listener
4) OK




Add details to Reference Listener
1) Browse and select ColorManagerReference (don't leave it blank...)
2) Browse to referenceBind and referenceUnBind methods we created in ColorManager/ColorManagerImpl



Add a blueprint Property to JpaExampleTestBean
1) Select ColorManagerReference 
2) Click Add
3) Select Property
4) OK



(continued...)

5) With colorManager Property selected
6) type in the property name used in JpaExapmleTest class
7) Browse Reference 
8) select ColorManagerReference
9) OK



Slightly confusing map of pointers between blueprint and java classes



blueprint.xml
---------------------------
<?xml version="1.0" encoding= "UTF-8"?>
<blueprint xmlns= "http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:bpjpa= "http://aries.apache.org/xmlns/jpa/v1.0.0" xmlns:bptx= "http://aries.apache.org/xmlns/transactions/v1.0.0" xmlns:bpresref="http://www.ibm.com/appserver/schemas/8.0/blueprint/resourcereference" >
     <bean id ="JpaExampleTestBean" class= "jpa_tests.JpaExampleTest"
            init-method= "startUp">
            <property name ="colorManager" ref= "ColorManagerReference"/>
     </bean >
     <reference id ="ColorManagerReference"
            interface= "model.controller.ColorManager">
            <reference-listener bind-method ="referenceBind" unbind-method="referenceUnBind" ref ="ColorManagerReference"/>
     </reference >
</blueprint>






10) Create an OSGi app and Deploy
- run a quick "sanity" check to make sure everything is in-place prior to coding our actual tests


Create an OSGi Application Project
-> Next


Naming it something identifiable... example: jpa_osgi_example_01_test_app
-> Next 



Add our two OSGi bundles
- jpa_osgi_example
- jpa_osgi_example test

-> Finish


Add the OSGi app to our Websphere Server
-> Finish


Debugging... 
Well, I did have a few errors (and yes... I did update the instructions!) 

My mistakes: 
1) Forgot (somehow) to save the jpa_osgi_example_01_test blueprint. Didn't have a required reference value...
2) Forgot to add "unitName" to ColorManagerImple "@PersistenceUnit" annotation
-> @PersistenceUnit(unitName="jpa_osgi_example_01"We'll later migrate this setting/value into blueprint
3) copy/paste error... pasted the "startup" sys'out message into the "onRegisterService" callback

I noticed the "waiting for" message on Websphere's "SystemOut.log" - this usually means something was missed. The missing "unitName" value caused the blueprint service to wait... (but since I've already made that mistake a few times - was easy to fix). I'll cover adding the property later in the blueprint itself. 

If everything is working properly, the startup and listener callbacks should report: 
- using CentOS v6.3 KDE console ("konsole") with a "find" left looking for "***" prefixes to all my "systemout" messages

1) ColorManagerImple.startUp() - we have ignition... 
2) ColorManagerImple.onRegisterService() - service registers with manager (called twice due to it being referenced by our test)
3) ColorManagerImple.referenceBind() - The jpa_osgi_exapmle_01_test binds to the (above) published (registered) interface
4) JpaExampleTest.startUp() - The test harness starts and is ready... for testing! 



Test Code (roll-your-own... )
Couldn't get something simple like junit running within OSGi
See appendix for "JpaExampleTest.java" test code. 
- Tests execute within the "startUp()" method that's set to run, via blueprint, at jpa_osgi_example_01_test app startup




APPENDIX 



---------------------------------------
ColorManager.java (interface)
---------------------------------------
package model.controller;

import java.util.List;
import java.util.Map;

import model.Color;

import com.ibm.jpa.web.Action;
import com.ibm.jpa.web.NamedQueryTarget;

public interface ColorManager {

     public void createColor(Color color) throws Exception;

     public void deleteColor(Color color) throws Exception;

     public void updateColor(Color color) throws Exception;

     public Color findColorByColorCode(String colorCode);

     public Color getNewColor();

     public List<Color> getColor();
    
     // notifications
     // --------------------
     public void startUp();
    
     public void onRegisterService(ColorManager colorManager, Map props);

     public void onUnRegisterService(ColorManager colorManager, Map props);
    
     public void referenceBind(ColorManager colorManager);
    
     public void referenceUnBind(ColorManager colorManager);
     // --------------------
}


--------------------------------
ColorManagerImpl.java
--------------------------------
package model.controller;

import com.ibm.jpa.web.JPAManager;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;

import com.ibm.jpa.web.NamedQueryTarget;
import com.ibm.jpa.web.Action;
import javax.persistence.PersistenceUnit;
import javax.annotation.Resource;
import javax.transaction.UserTransaction;
import model.Color;
import java.util.List;
import java.util.Map;

import javax.persistence.Query;

@SuppressWarnings("unchecked")
@JPAManager(targetEntity = model.Color.class)
public class ColorManagerImpl implements ColorManager {

     @PersistenceUnit(unitName="jpa_osgi_example_01")
     private EntityManagerFactory emf;
     // Couldn't get UserTransaction working within OSGi
     //      Using EntityTransaction instead
     // @Resource
     // private UserTransaction utx;

     public ColorManagerImpl() {
    
     }

     private EntityManager getEntityManager() {
          return emf.createEntityManager();
     }

     @Override
     @Action(Action.ACTION_TYPE.CREATE)
     public void createColor(Color color) throws Exception {
          EntityManager em = this.getEntityManager();
          EntityTransaction et = em.getTransaction();
          try {
               et = em.getTransaction();
               et.begin();
               em.persist(color);
               et.commit();
          } catch (Exception ex) {
               ex.printStackTrace();
               try {
                    et.rollback();
               } catch (Exception e) {
                    ex.printStackTrace();
                    throw e;
               }
               throw ex;
          } finally {
               em.close();
          }
     }

     @Override
     @Action(Action.ACTION_TYPE.DELETE)
     public void deleteColor(Color color) throws Exception {
          EntityManager em = getEntityManager();
          EntityTransaction et = em.getTransaction();
          try {
               et.begin();
               color = em.merge(color);
               em.remove(color);
               et.commit();
          } catch (Exception ex) {
               try {
                    et.rollback();
               } catch (Exception e) {
                    ex.printStackTrace();
                    throw e;
               }
               throw ex;
          } finally {
               em.close();
          }
     }

     @Override
     @Action(Action.ACTION_TYPE.UPDATE)
     public void updateColor(Color color) throws Exception {
          EntityManager em = getEntityManager();
          EntityTransaction et = em.getTransaction();
          try {
               et.begin();
               color = em.merge(color);
               et.commit();
          } catch (Exception ex) {
               try {
                    et.rollback();
               } catch (Exception e) {
                    ex.printStackTrace();
                    throw e;
               }
               throw ex;
          } finally {
               em.close();
          }
     }

     @Override
     @Action(Action.ACTION_TYPE.FIND)
     public Color findColorByColorCode(String colorCode) {
          Color color = null;
          EntityManager em = getEntityManager();
          try {
               color = (Color) em.find(Color.class, colorCode);
          } finally {
               em.close();
          }
          return color;
     }

     @Override
     @Action(Action.ACTION_TYPE.NEW)
     public Color getNewColor() {
    
          Color color = new Color();
    
          return color;
     }

     @Override
     @NamedQueryTarget("getColor")
     public List<Color> getColor() {
          EntityManager em = getEntityManager();
          List<Color> results = null;
          try {
               Query query = em.createNamedQuery("getColor");
               results = (List<Color>) query.getResultList();
          } finally {
               em.close();
          }
          return results;
     }

     @Override
     public void startUp() {
          System.out.println("*** ColorManagerImpl.startUp()");
         
     }

     @Override
     public void onRegisterService(ColorManager colorManager, Map props) {
          System.out.println("*** ColorManagerImpl.onRegisterService()");
         
     }

     @Override
     public void onUnRegisterService(ColorManager colorManager, Map props) {
          System.out.println("*** ColorManagerImpl.onUnRegisterService()");
         
     }

     @Override
     public void referenceBind(ColorManager colorManager) {
          System.out.println("*** ColorManagerImpl.referenceBind()");
         
     }

     @Override
     public void referenceUnBind(ColorManager colorManager) {
          System.out.println("*** ColorManagerImpl.referenceUnBind()");
         
     }

}


------------------------------------
JpaExampleTest.java
------------------------------------

package jpa_tests;

import java.util.List;

import model.Color;
import model.controller.ColorManager;

public class JpaExampleTest {
    
     private Color testColor = new Color("TestCode1","TestName1");
    
     private ColorManager colorManager = null;
    
     public ColorManager getColorManager() {
          return colorManager;
     }

     public void setColorManager(ColorManager colorManager) {
          this.colorManager = colorManager;
     }

     public void startUp() {
          System.out.println("*** JpaExampleTest.startUp CALLED");
         
          // start tests
          testAllOps();
     }
    
     public String testAllOps() {
         
         
         
         
          System.out.println("*** JpaExampleTest: STARTING TESTS");
          System.out.println("*** JpaExampleTest: ------------------");
          // ---------------------------------
          // colorManager must be set by blueprint (cannot be null)
          // ---------------------------------
          if(this.colorManager==null){
               System.out.println("*** JpaExampleTest: colorManager==null ");
               return "fail";
          }
          // ---------------------------------
         
          // ---------------------------------
          // test select
          // ---------------------------------
          System.out.println("*** JpaExampleTest: ------------------");
          System.out.println("*** JpaExampleTest: Test getColor");
          List<Color> list = null;
          try {
               list = colorManager.getColor();
          } catch (Exception e) {
               e.printStackTrace();
          }
         
          for (Color color : list) {
               System.out.println("*** color code: "+ color.getColorCode());
          }

         
         
          System.out.println("*** JpaExampleTest: createColor(myTestColor)");
          // Color myNewColor = new Color("foo","bar");
          try {
               this.colorManager.createColor(this.testColor);
               // this.colorManager.createColor(myNewColor);
               System.out.println("*** JpaExampleTest: createColor -> no errors so far");
          } catch (Exception e) {
               e.printStackTrace();
          }

         
         
          System.out.println("*** JpaExampleTest: ------------------");
          System.out.println("*** JpaExampleTest: verify Create - look up created color");
         
          Color color = this.colorManager.findColorByColorCode(this.testColor.getColorCode());
         
          if (color.getColorCode().equals(this.testColor.getColorCode())) {
               System.out.println("*** JpaExampleTest created color found: "+ color.getColorCode());
          } else {
               System.out.println("*** JpaExampleTest created color NOT FOUND!");
          }


          System.out.println("*** JpaExampleTest: ------------------");
          System.out.println("*** JpaExampleTest: update color name");
         
          Color changedColor = new Color(this.testColor.getColorCode(),
                                                this.testColor.getColorName()+"_meh");
          try {
               this.colorManager.updateColor(changedColor);
          } catch (Exception e) {
               e.printStackTrace();
          }
         
          Color myChangedSavedColor = this.colorManager.findColorByColorCode(this.testColor.getColorCode());
         
          if (myChangedSavedColor.getColorName().equals(changedColor.getColorName())) {
               System.out.println("*** JpaExampleTest changed color did change: "+ myChangedSavedColor.getColorName());
          } else {
               System.out.println("*** JpaExampleTest hanged color did NOT change!: "+ myChangedSavedColor.getColorName());
          }
    
         
          System.out.println("*** JpaExampleTest: ------------------");
          System.out.println("*** JpaExampleTest: delete test color");
          try {
               this.colorManager.deleteColor(testColor);
          } catch (Exception e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
          }
          Color myDeletedColorGone = this.colorManager.findColorByColorCode(this.testColor.getColorCode());
          if (myDeletedColorGone == null ) {
               System.out.println("*** JpaExampleTest: delete test color PASSED... NOT FOUND");
          }
         
         
         
          System.out.println("*** JpaExampleTest: DONE...");
          System.out.println("*** JpaExampleTest: ------------------");
          System.out.println("*** JpaExampleTest: ------------------");
          return "pass";
     }

}