OpenSubsystems

Business Components for Java Applications

Open Core

Tutorial

Documentation
Last modified

$Author: bastafidli $
$Date: 2007/03/11 06:30:45 $
$Revision: 1.25 $
$RCSfile: tutorial_businesslogic.html,v $

Developing the business logic

Business logic is what makes each application unique and represents the heart and brain of the software system. In general, it is responsible for the processing of data utilizing the persistence tier. The same business logic should apply regardless if the application is presented to the user in a browser, as a desktop application or using a PDA. That said, the business logic provides services to the user interface tier and therefore the designer must consider how these services will be used to make the implementation of different kinds of clients easier.

Best practices tell us to separate the presentation layer from the business logic by interfaces making it independent from the specific implementation. Another good practice is to make the business logic stateless and therefore easily deployable in a clustered environment. This makes the application more scalable and allows it to support larger number of users. Open Core adopts these best practices and provides set of generic high level interfaces and stateless base classes to make the design and implementation of your business logic easier. The provided classes define and often provide basic implementation for the methods necessary to access and process the data objects.

Our tasks

We will decide, what type of processing is required by our application and derive from the appropriate interfaces and classes our own ones. Once that's done, we will add to our newly defined interfaces any methods to process data in a way unique to our application. Then we just need to implement any of our custom methods and optionally override the default implementation of those provided by Open Core that doesn't match our needs. Later on, we will use controller manager to create and access the implementation of these interfaces in our user interface tier.

StatelessController and StatelessControllerImpl - interface and base class that should be implemented by all business logic modules acting as business delegates. By utilizing these as a base for your business logic interfaces and classes your application can access them regardless if they are deployed as a simple Java objects (POJOs) or as an enterprise beans in J2EE environment (EJBs).

DataController and DataControllerImpl - interface and base class for all controllers managing data objects. The primary purpose of the data controller is to implement additional functionality on top of the persistence layer such as security checks, logging, data processing etc. Every data object should be available to the presentation tier somehow and controller that makes data objects accessible to the rest of the system should implement this interface and derive from this base class.

BasicDataController and BasicDataControllerImpl - interface and base class that provide the most basic operations to perform with data objects in an application. Each data object needs to be somehow created and removed (if not for anything else at least for automated tests) and you gain such capabilities by deriving your interfaces and classes from these ones.

ModifiableDataController and ModifiableDataControllerImpl - interface and base class that add support and simple implementation of methods allowing data object modification.

ControllerManager - factory class responsible for instantiation of controllers. User interface and other controllers use controller manager to get references to your controller to execute the business logic it provides. This class determines what environment the application is running in (J2EE, POJO, etc.) and what is the desired access model (local interfaces, direct references, etc.) and creates the controller instances. This way it acts as a service locator for services provided by the controllers. It utilizes ClassFactory derived class to implement the strategy pattern to express the exact mechanism how to determine what class to instantiate.

OpenChronicle business logic

The business logic of OpenChronicle is defined in BlogController View source interface and implemented in BlogControllerImpl View source class. These are derived from ModifiableDataController and ModifiableDataControllerImpl because both Blog View source and Entry View source data objects can be modified by user maintaining his or her chronicle.

Our simple application doesn't require very much functionality. In our case the main purpose of the controller is to coordinate both factories and provide data required by the user interface efficiently, usually with a single method call. The single method call idea follows the Session Facade pattern. For example, the page displaying list of all entries in chronicle will need both, the list of Entry objects and the Blog object since it will be nice to show to the user what is the name of the chronicle the entries belong to. The controller interface therefore provides method

   /**
    * Get blog and its entries knowing just the folder where it's entries 
    * are displayed.
    *
    * @param strFolder - folder where entries for given folder are displayed
    * @return Object[] - index 0 is blog and index 1 is list of its entries
    * @throws OSSException - an error has occured
    * @throws RemoteException - required since this method can be called remotely
    */
   Object[] getWithEntries(
      String strFolder
   ) throws OSSException,
            RemoteException;
               

and controller class provides its implementation

   /**
    * {@inheritDoc}
    * 
    * @ejb.interface-method
    * @ejb.transaction type="Supports"
    */
   public Object[] getWithEntries(
      String strFolder
   ) throws OSSException
   {
      Object[] returnValue = {null, null};
      Blog     blog;
      List     lstEntries;
      
      // This demonstrate important technique and that is session facade pattern
      // One call to controller is used to fetch multiple pieces of data.
      // This is important if controller is remote from the presentation tier
      // and then we want to perform only as little remote calls as possible.
      blog = m_blogFactory.get(strFolder);
      if (blog != null)
      {
         returnValue[0] = blog;
         lstEntries = m_entryFactory.getAll(blog.getId());
         if (lstEntries != null)
         {
            returnValue[1] = lstEntries;
         }
      }
      
      return returnValue;
   }
               

Constructor

The controller coordinates the data factories to retrieve the data. Since the data factories are stateless and therefore thread safe, controller caches them in member variables. These are populated in method called constructor. The main reason for such oddly named method is that the controller can be run as a stateless session EJB. EJBs are not allowed to have class constructors. Open Core provides this method to perform the same tasks that would be normally done in class constructors. Open Core invokes the constructor method as soon as the controller manager creates the controller instance and before any other method can be invoked.

   /**
    * Factory to use to execute persistence operations.
    */
   protected BlogFactory m_blogFactory = null;
   
   /**
    * Factory to use to execute persistence operations.
    */
   protected EntryFactory m_entryFactory = null;

   /**
    * {@inheritDoc}
    * 
    * @ejb.interface-method
    * @ejb.transaction type="Supports"
    */
   public void constructor(
   ) throws OSSException
   {
      m_blogFactory = (BlogFactory)DataFactoryManager.getInstance(BlogFactory.class);
      m_entryFactory = (EntryFactory)DataFactoryManager.getInstance(EntryFactory.class);
   }
               

Modifying the data

Looking at the controller source code you may notice the lack of methods that allow modifying the data. The ability to create, modify and delete the data objects is provided by the parent classes, from which our controller is derived. If you wonder how do the parent classes know, what data objects to manipulate, notice that the controller implements two abstract methods defined in these classes.

   /**
    * {@inheritDoc}
    */
   protected DataFactory getDataFactory(
   )
   {
      // Blog is the default entity for this controller so return the blog factory
      return m_blogFactory;
   }

   /**
    * {@inheritDoc}
    */
   protected BasicDataFactory getDataFactory(
      DataObject data
   )
   {
      BasicDataFactory factory = null;
      
      if (GlobalConstants.ERROR_CHECKING)
      {
         assert data != null : "Cannot return factory for null data object";
      }
      
      if (data instanceof Blog)
      {
         factory = m_blogFactory;
      }
      else if (data instanceof Entry)
      {
         factory = m_entryFactory;
      }
      else
      {
         if (GlobalConstants.ERROR_CHECKING)
         {
            assert false : "Cannot return factory for unrecognized data type "
                           + data.getClass().getName();
         }
      }
      
      return factory;
   }
               

These two methods tell the parent classes what factory to use based on the method arguments. This work just fine for methods that take a data object as their argument but it doesn't work for those that take just a data object id. Example of such method is method delete

   /**
    * Delete data object. 
    *
    * @param iId - id of the data object to delete
    * @throws OSSException - an error has occured 
    * @throws RemoteException - required since this method can be called remotely
    */
   void delete(
      int iId
   ) throws OSSException,
            RemoteException;
               

declared in BasicDataController interface. Because our controller supports two types of data objects, Blogs and Entries, we have to provide counterparts of these methods for data objects that are not handled by default in this controller. The BlogController has Blogs as its default data object as described in the comment of the getDataFactory method and we have to provide custom method

   /**
    * Delete entry. We have to define separate method for this operation since
    * the default data type for this controller is blog and therefore the default
    * implementation will just delete blogs.
    *  
    * @param iId - id of the entry to delete
    * @throws OSSException - an error has occured
    * @throws RemoteException - required since this method can be called remotely
    */
   void deleteEntry(
      int iId
   ) throws OSSException,
            RemoteException;
            
   /**
    * {@inheritDoc}
    * 
    * @ejb.interface-method
    * @ejb.transaction type="Required"
    */
   public void deleteEntry(
      int iBlogEntryId
   ) throws OSSException
   {
      m_entryFactory.delete(iBlogEntryId,
                            CallContext.getInstance().getCurrentDomainId());
   }
               

to allow deletion of Entries.

EJB support

It has been mentioned several times that controllers can be deployed as plain old Java objects (POJO) or as a stateless session enterprise java beans (EJB). To run the business logic as POJO, the only thing we need is Java Runtime Environment (JRE) and the simple java code we have already demonstrated. To run it as EJB the application needs to be deployed using one of the supported J2EE application servers. Open Core controller manager, which is responsible for controller creation (instantiation) supports both options. Open Core is using XDoclet to generate the classes and files required by J2EE application servers. To take advantage of this functionality, we just need to add the XDoclet tags to the JavaDoc comments in the controller implementation class and it's public methods. This is only necessary if we want to run your application's business logic as EJB, the business logic will function just fine when deployed in J2EE application server as POJO even without these tags.

The class level tags tell XDoclet what type of EJB to generate, how to name it and what other EJBs it is using

   /**
    * The main entry point to all business functionality connected with blogs.
    * 
    * View-type has to be set to local due to bug XDT-867 affecting WebSphere
    * Refs has to be set to local JNDI name since we do not want to use remote objects.
    * 
    * @ejb.bean type="Stateless"
    *           name="BlogController"
    *           view-type="local" 
    *           jndi-name="org.opensubsystems.blog.logic.BlogControllerRemote"
    *           local-jndi-name="org.opensubsystems.blog.logic.BlogController"
    * @ejb.interface 
    *     local-extends="javax.ejb.EJBLocalObject, org.opensubsystems.blog.logic.BlogController"
    *     extends="javax.ejb.EJBObject, org.opensubsystems.blog.logic.BlogController"
    * 
    * @jonas.bean ejb-name="BlogController"
    *             jndi-name="org.opensubsystems.blog.logic.BlogControllerRemote"
    *
    */
   public class BlogControllerImpl extends    ModifiableDataControllerImpl 
                                   implements BlogController
   {
   ...
               

The method level tags tell XDoclet if the method requires transaction. All public methods modifying data in the persistence store should require transactions.

   /**
    * {@inheritDoc}
    * 
    * @ejb.interface-method
    * @ejb.transaction type="Required"
    */
   public void deleteEntry(
      int iBlogEntryId
   ) throws OSSException
   {
      m_entryFactory.delete(iBlogEntryId,
                            CallContext.getInstance().getCurrentDomainId());
   }
               

All public methods merely reading the data should support transactions in case they are called within a transaction that modifies data.

   /**
    * {@inheritDoc}
    * 
    * @ejb.interface-method
    * @ejb.transaction type="Supports"
    */
   public List getEntries(
      int iBlogId
   ) throws OSSException
   {
      List lstEntries;
      
      lstEntries = m_entryFactory.getAll(iBlogId);
      
      return lstEntries;
   }
               

The business logic is now implemented and it is time to take a look at how to create user interface for our application.

Next: Constructing the web application
Previous: Implementing the persistence layer