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?
- annotate all business classes with @@pat.bo annotation.
- annotate one root class with @@pat.root annotation.
- 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
- 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 :)
- 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..
- Any argument of a transaction must implement
Serializable
interface.
- @@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.
- 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.
- 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.
-
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..)
-
If you are Prevayler user your snapshot files are not compatible with PAT
snapshots. You have to write an import script by yourself.
- 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); }
}
- 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. :)
- static members -
One can't annotate with @@pat.bo
any class that exists somewhere as a static member of some class.
- repository.dir - this variable specifies the location of the
database files
- jboss.aop.path - this variable specifies the location of the
AOP framework (PAT or your own) files
- 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.
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