Saturday, August 29, 2009

Binding XML to Java Code with JiBX

I was looking for a simple, lightweight, fast xml-java binding solution. Tried few libraries like JAXB, XMLBean, JiBX, Castor and finally settled down with JiBX. JiBX is simple, it generates very few classes from a given schema and its fast! We can also write a binding.xml to map java to xml document. JiBX project site has great documentation if you want to get started with JiBX.
Lets go through the basic steps of generating java objects, doing jibx binding and marshal & unmarshal
  • download JiBX
  • set JIBX_HOME environment variable
  • generate java source from my_schema.xsd
java -cp $JIBX_HOME/lib/jibx-tools.jar org.jibx.schema.codegen.CodeGen -t source_dir my_schema.xsd
  • compile generated java source:- javac source_dir/**/*.java
  • jibx binding:- java -jar $JIBX_HOME/lib/jibx-bind.jar binding.xml
  • write sample code to marshal & unmarshal
java -cp $JIBX_HOME/lib/jibx-run.jar:$$JIBX_HOME/lib/xpp3.jar MyTestClass
Code in MyTestClass would look as below
IBindingFactory bfact = BindingDirectory.getFactory(MyAnyGenerated.class); IUnmarshallingContext uctx = bfact.createUnmarshallingContext(); IMarshallingContext mctx = bfact.createMarshallingContext();
Object obj = uctx.unmarshalDocument(new FileInputStream("input.xml"), null); mctx.marshalDocument(obj, "UTF-8", null, new FileInputStream("output.xml"));
Compare to JAXB, JiBX generates very few classes from a given schema. We can also customize source code generation using custom.xml. JiBX generates a binding.xml which is used for JIBX binding. We can also write the binding.xml manually and bind existing value object to xml definition.  I had run a small test to compare marshal & unmarshal with JIBX, JAXB & XMLBean and found that JIBX is almost 2x faster than other two.

Sunday, August 23, 2009

Unit Test Java EE Code Outside Container

Java EE application depends a lots on the application server container for various resources like JNDI datasource, transaction manager etc. The application I am working on uses Hibernate as persistence layer. It should support transactions spanning multiple hibernate sessions.
Distributed transaction is piece of cake when the application runs in the container. Datasources can be configured in the application server and be bound to JNDI tree. UserTransaction is also available in the JNDI tree. Application code just uses the JTA interface, UserTransaction, for transaction demarcation.
Lets have a quick look at how this is done in the application code. Then we will see how to unit test this code layer, that participate in distributed transaction, outside the container.
  • Configure XA datasources in application server with JNDI name say ds1 & ds2. You can either use admin console provided by the application server or update server specific datasource configuration file.
  • Configure two hibernate session factory, one for each datasource.
one_hibernate.cfg.xml for datasource: ds1
two_hibernate.cfg.xml for datasource: ds2
Code in my Hibernate Util class:
SessionFactory sessionFactory = new Configuration().configure("/one_hibernate.cfg.xml").buildSessionFactory();
static_sessionFactory_Map.put("DS_ONE", sessionFactory);
sessionFactory = new Configuration().configure("/two_hibernate.cfg.xml").buildSessionFactory();
static_sessionFactory_Map.put("DS_TWO", sessionFactory);
  • Add transactional properties to hibernate.cfg.xml. I am using JBoss Application Server.
<property name="hibernate.current_session_context_class">jta</property>
<property name="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</property>
<property name="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup</property>
  • Add transaction demarcation at business layer. Code in the business component typically would look like below
InitialContext ic = new InitialContext();
UserTransaction ut = (javax.transaction.UserTransaction) ic.lookup("UserTransaction");
try
{
ut.begin();
//invoke dao that uses ds1
//send xa jms messages
//run some other business logic
//invoke dao that uses ds2
//etc etc
ut.commit();
}
catch(SomeException ex)
{
ut.rollback();
}
So we are all set. Code will nicely handle distributed transactions! Now challenge here is: how to write junit tests to test this application outside the server container. How do we make JNDI datasources, UserTransaction, Transaction context available to the code? We need a standalone Transaction Manager. Junit tests need setup code to bind datasources and UserTransaction object. Lets go step by step.
  • Configure hibernate to use Bitronix Transaction Manager
<property name="hibernate.current_session_context_class">jta</property>
<property name="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</property>
<property name="hibernate.transaction.manager_lookup_class">custom.lookup.manager.see.bitronix.doc</property>
  • Use Apache Naming in-memory JNDI service provider to bind the resources
Setup code in junit tests:
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.naming.java.javaURLContextFactory");
System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming");
InitialContext ic = new InitialContext();
//create and bind datasources. We are using Oracle database server
OracleConnectionPoolDataSource ds = new OracleConnectionPoolDataSource();
ds.setURL("jdbc:oracle:thin:@host:port:sid1");
ds.setUser("username");
ds.setPassword("password");
ic.rebind("ds1", ds);
ds = new OracleConnectionPoolDataSource();
ds.setURL("jdbc:oracle:thin:@host:port:sid2");
ds.setUser("username");
ds.setPassword("password");
ic.rebind("ds2", ds);
//we are using bitronix.tm.TransactionManagerServices
UserTransaction userTransaction = TransactionManagerServices.getTransactionManager();
ic.rebind("UserTransaction", userTransaction);
  • All set! Just write junit tests to test business methods
public void testBuzApi()
{
MyBizClass bizObj = new MyBizClass();
SomeObject retObj = bizObj.myMethodOne();
//some assertXXX(.....);
}
Looks like its not a challenging task to unit test java EE code, right. We just need to provide all the resources application code expect while running in a server container.