Persistent Applications Toolkit

Choose font sizeNormal TextBigger TextHuge Text

Framework

Like everything out there - PAT gives you a lot, but it also takes something.

Below, you will find debate on advantages, disadvantages, requirements.

Dictionary:

Lets introduce few definitions.
  • POJO - Plain Old Java Object
  • BO - business object (any of your custom objects)
  • ROOT - root of graph of BOs. Has indirect references to all of BOs
  • TRANSACTION - method of BO that changes state of the system
  • OID - Object Identifier - system unique number

How to use?

  1. annotate all business classes with @@pat.bo annotation.
  2. annotate one root class with @@pat.root annotation.
  3. annotate all methods which change state of the system with @@pat.transaction annotation (those methods usually occur within Use Cases, User Stories).

Example

    package business.objects;

    /** 
     *  @@pat.bo
     */
    public class MyBusinessObject
    {
        private Field field1;
        private Collection collection;

        public MyBusinessObject()
        {
        }

        /** @@pat.transaction */
        public void changeMyCollection(String param)
        {
            this.collection.remove(param);
        }
    }
    

Limitations, issues, rules - things PAT user should know

  1. Important: PAT's transaction (Java methods) cannot take as an argument any object that:
    a) is not a BO
    AND
    b) has indirect reference to a BO object

    It means you cannot put simple java.util.Collection of your favourite Person objects. What you can do is to create MyPersons which extends java.util.List and PAT is happy then.
    You can put any BO as an argument, any simple type, any complex type, any Struts' class, any ... until it doesn't violate the rule above.

    Hopefully, this is not so restrictive as it looks like :)

  2. Remember to annotate all methods which change any of your BOs. If you have a public setter method for example, and your requirements require you to call it from controller layer (GUI, Struts' actions), then it must be annotated as @@pat.transaction.
    Externall configuration of application may help with annotations on setters if you prefer..
  3. Any argument of a transaction must implement Serializable interface.
  4. @@pat.root_injected - use this annotation to inject root object into a member of some of your classes. If you need to access root object just declare an object's field, annotate and access.
    Example of dependency injection with PAT.
    /** @@pat.bo */
    public class Author
    {
    /** @@pat.root_injected */
    private World world;

    public Author()
    {
      log("World has: " + world.authors.size() ...
    }
    }

    Do not instantiate your ROOT object more than once. There is no need. Instantiate it once, and if you need access to ROOT object use the annotation above.

  5. testing your system - There is a case, where you need to instantiate the same object more than once - Tests cases. Appropriate solution for that is to unload PAT instance before any test case, by using TestCase.setUp() method (I'm still in doubt if that should be done magically by PAT..)

    public class MyTestCase extends PatTestCase
    {
    private Root myRoot;
    public void setUp()
    {

    cleanPrevaylersRepo();
    Pat.unload(); //<-- this one is a must

    }
    private void testMyCase1()
    {

    myRoot = new Root();
    assertEquals(0, myRoot.elements().size());

    }
    private void testAnotherTestCase()
    {

    myRoot = new Root(new Element());
    assertEquals(1, myRoot.elements().size());

    }

    cleanPrevaylersRepo is an useful method for cleaning your database directory from PAT's/Prevayler's data files.

  6. If you wish to evolve your classes (and you wish) you have to manually insert:
    protected final static long serialVersionUID = 1L;
    into all of your (BO) classes.

    There is no possibility to do this with JBossAOP introductions for now.

  7. Due to bytecode manipulation, the actual class' code differs from source code. Be warned when you use tools which depend on your original code (other AOP frameworks, etc..)
  8. If you are Prevayler user your snapshot files are not compatible with PAT snapshots. You have to write an import script by yourself.
  9. If your business class (BO) extends some collections class and you need extended methods to be transactions you must redefine them and put annotation in the comment's body. This is consistent of course with the first rule of transactions, where you need to annotate every method which changes state of the system.
    public class MyContainer extends java.util.ArrayList
    {
    /** @@pat.transaction */
    public void remove(Object o){ super.remove(o); }
    }
  10. We've entered real object oriented world with PAT. You are free to design it as you wish. But: from experience and because of architecture of Prevayler, which uses besides others: Java serialization for persistence, under some _very_ special circumstances when having a lot of objects referencing them you might find it difficult to store your data. And this is because of buggy implementation of serialization mechanisms. You might then need a little redesign of your object world.

    I speak about deep object graph, and problems with too low stack size. It's been found out that different JVMs behave different, so it may be dependant on your VM and -Xss option.

    Basically, my application works with more than 13.000 of business objects with history of operations on each object stored within the object. Objects are referencing each other, many of them consists of Maps which have references to other objects which have Maps - and it hopefully works. :)

  11. static members - One can't annotate with @@pat.bo any class that exists somewhere as a static member of some class.
  12. repository.dir - this variable specifies the location of the database files
  13. jboss.aop.path - this variable specifies the location of the AOP framework (PAT or your own) files
  14. pat.no-commands - when this variable exists, PAT will not write any command to disk. This speeds up situations when you don't need durability - importing data to your application for example.

Recursive (nested) transactions

PAT recognizes recursive transactions and executes the top-level transaction with everything nested.

What are these top-level methods?

Let's use Banking demo as an example. In a banking system we have three operations:
  • transfer(Account from, Account to)
  • withdraw(Account a)
  • deposit(Account a)
All three must be transactions when we have three user stories for: transferring money, withdrawing money and depositing money.
Here's the example:
/** @@pat.bo */
public class Bank
{
/** @@pat.transaction */
public transfer(Account from, Account to)
{
  withdraw(from);
  deposit(to);
}

/** @@pat.transaction */
public withdraw(Account account)
{
  ...
}
...
}

What is this written with?

Language: Java (1.4, 1.5 tested and works)
Prevayler version 1 - www.prevayler.org (modified version)
AOP, JBossAOP - www.jboss.org (1.0 FINAL)
Log4j - apache.org/log4j
Ant - ant.apache.org

SourceForge.net Logo

Revision: $Id: framework.gtml 3731 2005-06-30 23:25:59Z nthx $
Generated with GTML