SyntaxHighlighter

Friday, December 21, 2012

Part 1: Embedded EJB Container Using Weblogic Server

Overview:
As we all know that ,In Java EE world, Enterprise Java Beans (EJB) have gained a lot of popularity in IT industry.Despite of their development made lot easier in 3.0 version, developers still had to struggle to test them out by building and bundling them using Java EE package structure and then deploying them into application servers (EJB Containers) and then writing a test client to run in different JVM to invoke EJB etc...

Not only me, I know we all had wished it many times , how nice it would be if we could test EJB code just like regular java class by writing a main method and invoke it via command line and JVM could provide me all the services like
  1. Inject Resources (like another EJB , Persistence etc...)
  2. manage transactions like CMT or BMT
  3. Invoke Interceptors
  4. provide security etc...
Good News to EJB developers, your voice has been heard by Java Community and this dream has come true in Java EE 6. EJB 3.1 supports this feature which will make EJB testing lot easier using "Embeddable EJB Containter".


Introduction:
Embeddable Usage allows client code to instantiate EJB Container within the same JVM of client code by calling embeddable API. Embeddable EJB Container provides same supports as Java EE environment such as Injecting resources , providing transaction and security etc...EJB code feels as if its running in Java EE environment.

As per EJB 3.1 specs, Implementation providers or Emebeddable container must provide supports for following features. (they may provide extra features but not guaranteed).
  1. Local Or No Interface Session Beans (Stateless, Stateful and Singleton).
  2. Interceptors (Class Level or Method Level)
  3. Security (Declarative Or Programmatic)
  4. Transactions (CMT or BMT)
  5. Deployment Descriptor (optioanl ejb-jar xml).
How does it work ?
By default, Embeddable Container uses the same classpath as client. In other words, Embeddable Container will scan the classpath which has been set at the time of executing client code and check if
  • META-INF/ejb-jar.xml , if found then read the file and deploy EJBs.
  • Find, If any of the class in any module (jar file or exploded directory structure) has been decorated with @Stateless,@Stateful or @Singleton and deploy them.
 Steps to Instantiate Embeddable Container and Call EJB Method
In EJB Client code, inside main() method
  1. import javax.ejb.embeddable.EJBContainer.
  2. Use EJBContainer container=EJBContainer.createEJBContainer() OR EJBContainer.createEJBContainer(Map<String,String>) to specifically mention which module you want to load etc...
  3. Context context=container.getContext()
  4. context.lookup(EJB_JNDI_NAME), which returns reference to EJB object 
  5. invoke EJB method.
Lets Demonstrate:
To demonstrate and test , I have created
  • FirstEJB.java which ask container to inject another EJB named "SecondEJB" using @EJB annotation.
  • SecondEJB.java has @PersistenceContext annotation to do JPA operation.
  • KeyValueProperties.java as JPA Entity class decorated with @Entity annotation.
  • I compile all the classes under "build_me/testRideEAR/jar" folder.
  • Under "build_me/testRideEAR/jar", I have created "META-INF/persistence.xml" file for JPA.
Lets take a peak at each code.
FirstEJB.java
package com.test.ejb.embedded;

import javax.ejb.EJB;
import javax.ejb.Stateless;

@Stateless

public class FirstEJB {
 private @EJB SecondEJB second_ejb;
 
 public String doOperation(){
  return "doOperation() calling Second EJB --Response="+second_ejb.storeKeyValueProperties();
 }
}


SecondEJB.java

package com.test.ejb.embedded;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import com.test.jpa.KeyValueProperties;

@Stateless

public class SecondEJB {
 @PersistenceContext(name="JPA_RIDE")
 private EntityManager em;
 
 public String storeKeyValueProperties(){
  System.out.println("Storing Key Value Properties in table via JPA");
  KeyValueProperties reader=new KeyValueProperties();
  reader.setRegion("MYREGION");
  reader.setField("TESTJPA");
  reader.setFieldValue("WELL TESTED");
  em.persist(reader);
  em.flush();  
  return "Successfully stored data";
 }

}


KeyValueProperties .java
package com.test.jpa;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

@Entity
@Table(name="ctr_accord_properties")
public class KeyValueProperties implements Serializable{

 @Id
 private String field;
 @Column(name="field_value")
 private String fieldValue;
 @Column(name="region",updatable=false)
 private String region;
 
 public String getRegion() {
  return region;
 }
 public void setRegion(String region) {
  this.region = region;
 }
 public String getField() {
  return field;
 }
 public void setField(String field) {
  this.field = field;
 }
 public String getFieldValue() {
  return fieldValue;
 }
 public void setFieldValue(String fieldValue) {
  this.fieldValue = fieldValue;
 }
 
 
 public String toString(){
  return "Field="+field+": Field_value="+fieldValue+" : Region="+region;
 }
}

Now we have got EJB code ready. Time to write a Test code.

EmbeddedEjbContainerTest.java

package com.test.client;

import java.util.Properties;

import javax.ejb.embeddable.EJBContainer;
import javax.naming.Context;

import com.test.ejb.embedded.FirstEJB;



public class EmbeddedEjbContainerTest {

 public static void main(String[] args) throws Exception {
  //setSystemProp();
  EJBContainer container=null;
  try{
   Properties prop=new Properties();
   prop.setProperty(EJBContainer.MODULES,"jar");
   container=EJBContainer.createEJBContainer(prop);
   System.out.println("Got EJB COntainer="+container);
   Context context=container.getContext();
   System.out.println("Got Context="+context);
   FirstEJB ejb_ref=(FirstEJB)context.lookup("java:global/jar/FirstEJB");
   System.out.println("EJB Returned Message = "+ejb_ref.doOperation());
  }catch(Exception ex){
   ex.printStackTrace();
  }finally{
   container.close();
   System.exit(0);
  }
 }
}

Running EJB Test Client:
Its as simple as running regular java class.
For example, Lets say we have EJBs developed and complied them in "build_me/testRideEAR/jar" folder.

NOTE : We have to include "Embeddable Container Provider" jar file in classpath. In my case, "weblogic.jar"

Windows environment:
java -cp build_me/testRideEAR/jar;%BEA_HOME_SERVER_LIB_PATH%\weblogic.jar;%BEA_HOME_MODULES_PATH%\javax.ejb_3.1.0.jar;;%BEA_HOME_MODULES_PATH%\javax.persistence_2.0.0.0_2-0.jar com.test.client.EmbeddedEjbContainerTest

NOTE: In above example we are using @EJB and JPA features, so we are including ejb3.1 and persistence related jars in classpath.

Hint: If you get PermGenSpace error Or OutOfMemory while running Test Client, then run program by adding following JVM Options
java -Xms256m -Xmx256 -XX:PermSize=128m -XX:MaxPermSize=128m -cp ABOVE_MENTIONED_PATH

Where does Server Create Logs ?
Weblogic Embedded Container use "java.io.tmpdir" to start temporary Embedded server and create default domain and log folder etc...

Default server name "myserver".
Default port=65432



Caveat:
In above example we have seen how to store data into database using JPA.
Genuine question, What if my EJB uses DataSource Connection (instead of JPA Entity class) to persist data ? How Can i configure DataSource in Embedded EJB Container ?

Answer is: As of now (if i am not wrong), Weblogic server doesn't provide such feature to configure DataSource for Embedded EJB Container where as Websphere folks thought step ahead and provided hook.
In Weblogic, There is a way to acheive this using some hack , we will discuss that in Part 2. So stay tuned.

Enjoy EJB Testing.

No comments:

Post a Comment