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.

Saturday, November 22, 2008

Leverage FlexSession to secure java based amf destination

Flex is gaining popularity in enterprise world. Authentication & authorization are common requirement of an enterprise application. To achieve this we often provide a login screen as the entry point to the application. Login screen validates user's credentials and based on role user will see various menu options and screens. 
Typical flex application consists of one or more swf file hosted on the net and remote service deployed as amf destinations. Using flex remoting application can talk to various backend systems based on ColdFusion, java etc. 
So where is the question of security. Flex app won't let you in unless you login to the application and flex app would let you perform only those operations available to your role.
Lets see how a java based remote object would look like. Before that lets consider this requirement
  • Two type of user: Read Only User & R/W user
  • Read-only user can see data on a screen
  • Read/Write user can read as well as update data on that screen
So, java backend class will have 3 main API exposed via remoting 
  • public User login(String username, String password) throws AuthException
  • public MyData getData() 
  • public void updateData(MyData data)
Login screen makes remote call to login() API to validate credentials and get users detail including roles. Based on role UI shows/hides the Update button to view/update data on post login screen.
Everything looks good but note that all the above 3 APIs are publicly exposed! Its very easy to forget about the fact that any remoting client, knowing the call details, will be able to invoke any of those exposed APIs. Thats where flex session would be useful to secure the APIs. Let see code snippet to get better picture.
public User login(String username, String password) throws AuthException
{
User user = myBusObj.login(username, password);
registerSession(user);
}
public MyData getData() throws AuthException
{
checkSession(reqRole);
return myBusObj.getData();
}
public void updateData(MyData data) throws AuthException
{
checkSession(reqRole);
return myBusObj.updateData(data);
}
private void registerSession(User user)
{
FlexContext.getFlexSession().setAttribute("AUTH_USER", user);
}
private void checkSession(String reqRole)
{
if(FlexContext.getFlexSession() == null || FlexContext.getFlexSession().getAttribute("AUTH_USER") == null || FlexContext.getFlexSession().getAttribute("AUTH_LEVEL") == null) throw new AuthException("some error message");
//check users role and compare with reqRole. if req role not present throw auth exception
}
With the above code in place, remote APIs can only called by authenticated & authorized clients. Find more detail on Flex Session here