Architecture‎ > ‎

Extensibility

Automated Business Logic is designed to not only to offer significant declarative power, but also to provide a smooth path to leverage Java / Groovy Object technology for complex logic and re-use.  This removes barriers often associated with automation, and in fact enables Systems Programmers to extend the automation.  This can be characterized as Object Declarative logic.

Most Business Logic specifications are simple spreadsheet-like expressions, expressed in annotations:

price * quantity

Sum(orders.amount where isPaid=true)

balance < creditLimit

Beyond such expressions, significant additional power is provided by Open Business Logic:

  1. You can express your logic in logic methods.  This gives you all the power of Java/Groovy to express complex logic

  2. Your logic methods can invoke Java/Groovy methods. You can use existing Java/Groovy methods, or utilize the extensibility support described here.

This enables your organization to use/build a library of re-usable logic services such as date functions, or completely new Business Logic Rule Types such as allocation or copy. The latter are shipped with the product, both for your use, and as illustrations of how to build Extensible logic.

In addition to logic extensibility, the Business Logic Engine is architected for systems extensibility. You can supply your own factories to select the Business Logic Component for a Domain Object (e.g., PurchaseorderRetail vs. PurchaseorderWholesale). Events are provided so you can determine all the changes in a transaction. And, mechanisms are provided to accomodate your Hibernate extensions, such as configuration.



Using Logic Methods

The screen shot below illustrates how you can employ just annotations (derivePartPrice) or Java Methods (deriveAmount) to specify your logic.  Java methods are still largely declarative:
  • Assured Integrity / Automatic re-use
The Business Logic Engine detects your logic via the annotation, and ensures it is called whenever your data is updated (subject to optimizations noted below).
  • Dependency Management
Your logic remains automatically ordered per its dependencies on other attributes.  These are discovered through techniques such as byte code analysis.
  • Automatic Optimization
Your logic is pruned (not executed) if the referenced attributes are not changed in a particular row instance.



Business Logic Components can invoke Java/Groovy Methods

Your logic methods can invoke Java/Groovy methods.  Such methods can be within the Business Logic Component, or in an external re-usable library.

Extensible Functions (e.g., date functions)

We are using this term simply to denote methods that return a value that can be assigned to an attribute.  A good example is a date function.


Extensible Rule Types (e.g., insert/copy, allocate)

You can build Java/Groovy methods that are generic to any Business Logic Component so they are re-usable.  To particularize their operation, you can use the usual technique of parameters, introspection and so forth, as well as Hibernate meta data.


BusLogicExt - pre-supplied logic extensions

Important

BusLogicExt represents considerable value.  

The culmination substantial experience, its use will dramatically reduce ad hoc logic coding.
The system ships with several categories of such re-usable services listed below.  These are provided:
  • for use (with source) for your use (that is important - they automate key patterns), and 

  • as illustrations of techniques to build such extensions
For more information, see the Logic Pattern documentation for

Change Events

You can subscribe to change events published by the Business Logic Engine, either on a row-by-row basis, or for a transaction summary.

Row Change Events

These events are useful if you wish to capture each row change, such as shown in the Logic Analysis Diagram.  This is implemented in the BusLogicIntro sample:


Transaction Summary

You can also get a change summary at the end of processing.  For example, you might wish to update model elements within an MVC framework to reflect derivations after a transaction is committed, without requerying the database.

 

Factory Substitution

The ABL Engine utilizes a factory you can supply to create customized instances of key system objects such as LogicContext.  Explore the BusLogicIntro example, illustrated below.  

You can explore the BusinessLogicFactoryImpl for other system objects you may wish to override.  You will typically implement these as extensions of the system classes - see, for example, MyLogicContext.



Hibernate extension interoperation

Configuration


Use Case

Business Logic determines a default Use Case name, and enables you to specify your own as described below. You can use this in a number of ways, including:

  1. Debugging
    In longer programs, you can search the console log for the Use Case to facilitate finding the relevant portions of the log
  2. Logging
    You can save the Use Case name into an external log for analysis. The Use Case name enables analysis programs to determine what transactions were running.
  3. Rule Execution
    You can test the Use Case name in your Business Logic. This provides what is essentially a "free parameter" that can be sent from clients, and test it your formulas, constraints, etc.


Use Case Default

If you do not set the Use Case by default, the system determines it by concatenating the Domain Object name with the verb, eg

 Purchaseorder_save
 Purchaseorder_update


Setting the Use Case Name

You can set the Use Case name with the following API:

    LogicContext.setCurrentUseCaseName (tx, "Purchaseorder_save_noEmptyOrder_test")

You can also override the default setting mechanism by overriding the BusinessLogicFactory#computeUseCaseName(LogicContext aLogicContext)


Testing the Use Case Name

You can test the Use Case name, as shown in the following example from CustomerLogic in the sample database:

 /**
   * Deep Copy Customer, Purchaseorders & Lineitems to create a new account.
   */
 @Action<br>
 public void actionCloneCustomer() {
    if ('''logicContext.getUseCaseName()''' != null && 
          logicContext.getUseCaseName().equalsIgnoreCase("Customer_clone") &&
          logicContext.getLogicNestLevel() == 0) {
        String[] deepCopy = {"purchaseorders", "purchaseorders.lineitems"};
        MethodClosure eachInsertedCallback = new MethodClosure(this, "insertingIntoFrom");  
                // passing Java method as Groovy closure<br>
        BusinessLogic.insertIntoFrom(Customer.class, logicContext, deepCopy, eachInsertedCallback);
    }
  }

Extensibility Coding Guidelines

The following guidelines apply only to your Business Logic Components, and extensions they invoke. They do not apply to user / tool logic that submits changed data to Hibernate.


Updates via Logic Context (not directly to Hibernate)

When your Business Logic Component code runs, it is during the Business Logic phase of the transaction. In particular, this is long after pre-events fire. Recall that pre-events are what invokes the Business Logic runtime, which invokes your Business Logic Components.

The implication is that if you issue updates/saves directly to Hibernate session, this will not invoke your Business Logic. You must therefore invoke LogicContext methods, as illustrated in BusinessLogicBase:

  aProviderLogicContext.save newAllocation

LogicContext provides current/old domain instance for re-usable code

 Re-usable code must make generic reference to domain objects, i.e., it cannot expect in advance that it is operating on a Customer, an Order and so forth.  Instead of passing the @CurrentBean / @OldBean, it is simpler to pass the Logic Context.  Your generic code can use this to obtain current / old bean instances, issue logic-safe updates, and gain access to useful set of services provided by LogicContext.

Be aware: Domain Object Proxies

Both Hibernate and Business Logic often build Proxy objects, and pass these rather than direct Domain Object beans. These objects reflect your inheritance path (i.e., inherit bean methods), so preserve your expected interfaces.

However, you must use care in checking the Class Name. You may get a proxy when you expect one of your Domain Objects. If you wish to acquire the class name, do not code this:

       String toParentClassName = aParent.getClass().getName()

Instead, use this:

       String toParentClassName = TransactionContext.getEntityNameForObject(aParent)

Reattachment loses old state

The following coding sequence will fail:

<begin tx>
Child child = ...
session.save(child); 
<commit>
...
<begin tx>
session.update(child); // Reattach the object
child.setParent(parent);
<commit>

The underlying cause is that re-attachment does not generate old values for the runtime. These are required to perform Business Logic processing, so the logic will fail.


Bulk Update Verbs do not invoke Business Logic

Mass update statements do not generate events that invoke Business Logic.


Old Values

Note that the old value instance variables reflect the values last seen by your logic, not the "original" values before the transaction began. The underlying concept is that your Business Logic Component can be invoked multiple times per transaction, on the same row. For example, saving n Lineitems for a Purchaseorder would execute your Business Logic Component n times. On call "i", the old values pertain to call "i-1". 

Comments