Aspect Oriented Programming (AOP) is an approch to manage functionality (aspects) of crosscutting concerns. The dedicated development environments allow the user of a developed functionality to add join points in method calls, etc. to add new behavior or even overwrite current behavior. Aspects are a great way to isolate customisations and extension functionality.
Compiere is developed in a "classic" non-aspect oriented Java environment, but allows the user to "plug-in" their extension quickly without the need to modify or extend the underlying class. This eases development and certainly migraion of that functionality to new versions or distributing that functionality to multiple sites.
Compiere provided the following join points for quite some time:
- Before Save (new or update) or Delete of any Persistent Object (PO)
- Before the workflow step Prepare or After the workflow step Complete
- After the user and role is identified in the Login process
- Before a Info window is displayed to modify or add information columns
To utilize the aspect features in Compiere, you implement the interface org.compiere.api.ModelValidator - you find an example in the class org.compiere.xuom.XUMModelValidator. You register your class either by adding it's name to the Model Validator field in the Entity Type for your extension (or in the Model Validator field in the Tenant/Client).
This causes, that the method initialize of your implementation is called when the system starts up:
public void initialize (int AD_Client_ID, ModelValidationEngine engine)
in your implementation, you register the tables you want to create join points - example:
engine.addModelChange(
"M_Product", this);engine.addDocValidate("C_Order", this); When a Persistent Object (here Product) is added, changed or deleted, the modelChange method is called: /**
* Model Change of a monitored Table.
* Called after PO.beforeSave/PO.beforeDelete
* when you called addModelChange for the table
* @param po persistent object
* @param changeType CHANGETYPE_
* @return error message or null
* @exception Exception if the recipient wishes the change to be not accept.
*/
public String modelChange (PO po, int changeType) throws Exception
You return either null or an error message or thriw an exception - both will prevent any further processing of other model listeners and that the persientent object is changed. In the call you can change any value of the object - but be aware that there is no doule check of safety net.
Similar is the behavior of the docValidate interface:
/*** Validate Document.
* Called as first step of DocAction.prepareIt
* or at the end of DocAction.completeIt
* when you called addDocValidate for the table.
* Note that totals, etc. may not be correct before the prepare stage.
* @param po persistent object
* @param docTiming see DOCTIMING_ constants|
* @return error message or null -
* if not null, the document will be marked as Invalid.
*/
public String docValidate (PO po, int docTiming)