Java Transaction API in a desktop application

JTA in a desktop application

Download the source code here.

Not so recently I was involved in a project divided in

  • A desktop application executing in a offline machine (not connected to internet)
  • A web portal

So far, so good - but these parts should share exactly the same logic, almost the same database structure and we don't like to write anything twice! Of course the web portal would run in a fully J2EE application server and obviously the desktop application won't.

So our strategy was:

  • Simulate the environment in the J2EE container in the desktop application with a simple framework
  • Do not even think about EJB

The desktop application framework should handle connection polling, logs and - most important - transactions. Hence this sample.

The sample application

This sample is provided AS IT IS under the Apache Software Foundation licence. It uses portions of Jonas application server code as a simple implementation of JTA. These portions of code are well named (see the tm package)

When I developed this framework (almost a year from now) I din't know Apache DBCP, so here it is yet another Pooling framework :-\ But we can say that this pooling capability is transaction-aware as it enlists its resource if it detect a transaction in the current thread.

We had a different classloader strategy, which I had stripped from this sample, so ignore classloader handling code.

How to use

Well, don't try to run the unit tests. Unfortunatelly they depends an environment we had at the project (three different databases: MSSQL, Oracle 8 and HSQLDB).

The process can be defined this way:

  1. A business class that needs transaction in one or more methods must implement the Inspectable interface
  2. Thus business classes shall be created using a well-know form - through ClassFactory.createConcreteInstance (Yes, I didn't know Avalon)
  3. If the business class implements the Inspectable interface, a CommonProxy will be returned
  4. The business classes should provide metadata describing the methods that needs transaction and what transaction strategy they want
  5. The proxy uses these metadata to starts the transactions

The business classes code are completely unaware of transactions. That's very nice. All they have to do is use JNDI to obtain their resources and voila!


public void createNew() throws Exception
{
    Apolice apol = (Apolice) 
        ClassFactory.createConcreteInstance(this.getClass().getClassLoader(),
	    "br.com.keldor.core.sample.business.Apolice");

    apol.setupDatabaseTables();
}
			

And the implementation:


public class ApoliceImpl extends AbstractInspectable implements Apolice 
{
    static
    {
	// Thats pretty ugly !

        Preferences pref = Preferences.userNodeForPackage(ApoliceImpl.class);
        pref.put(
        "Metadata", 
        "<?xml version=\"1.0\" standalone=\"yes\"?>" + 
        "<Metadata class=\"br.com.keldor.core.sample.business.ApoliceImpl\">" + 
        "  <MethodInfo>" + 
        "    <name>store</name>" + 
        "    <Transaction>Required</Transaction>" + 
        "  </MethodInfo>" + 
        "  <MethodInfo>" + 
        "    <name>load</name>" + 
        "    <Transaction>Supports</Transaction>" + 
        "  </MethodInfo>" + 
        "  <MethodInfo>" + 
        "    <name>remove</name>" + 
        "    <Transaction>Never</Transaction>" + 
        "  </MethodInfo>" + 
        "  <MethodInfo>" + 
        "    <name>setupDatabaseTables</name>" + 
        "    <Transaction>Required</Transaction>" + 
        "  </MethodInfo>" +         
        "</Metadata>");

    }

    ....

    /** Transaction Required */
    public void setupDatabaseTables()  throws Exception
    {
        ApoliceServices serv = new ApoliceServices();
        serv.setupDatabase();
    }
    
    /** Transaction Required */
    public void store() throws Exception
    {
	...
    }
    
    /** Transaction Supports */
    public void load(int id) throws Exception
    {
	...
    }
    
    /** Transaction Never */
    public void remove(int id) throws Exception
    {
	...
    }
}
			

What to do from here

Well, a refactor will be nice :-)

Today XML is used as metadata, but attributes will be better. Use Avalon Merlin or Fortress would be nice!!! If you use this and improve this framework, let me know, ok?

by Hamilton Verissimo