In brief this article discusses

. What are filters
. What are they used for
. Specifying filters in properties file
. A complete example where a filter is used
. What sort of filters available
. An example of a filter class implementation

What are filters

Filters is an architectural concept that works hand in hand with factories. When factories create object or objects a filter provide an option to post-process these object or objects. This ability may not be as important in procedural and OO coding but place an important role in declarative programming. Because "declarative programming" when used as a supporting architecture for OO programming, postpones the need to program. In such a scenario filters provide another place to configure and reuse a given programming asset. Although this article goes into how filters are used and programmed in aspire the content should be applicable to most declarative programming architectures.

What are they used for

If you have a software part that is returning a collection of rows from a database, a filter called "EmptySetValidator" could work as a post filter to throw an exception if the output is not an empty collection. For example if you want to insert a row into a database and if the user is trying to enter a duplicate row, this "EmptySetValidator" could prevent such an insert by throwing a duplicate exception.

There could be a filter for instance that convert a single row of output from a database into a hashtable. There could be many more uses. that are not documented here

Specifying filters in properties file

Here is a quick example of how filters can be specified in config files.



#Specify main object
request.createAnObject.classname=yourpkg.yourclass

#Specify a filtername for that object
request.createAnObject.filterName=filterRequestName

#Finally specify the filter class
request.filterRequestName.classname=yourpkg.yourclass

Should use a filter enabled factory

Aspire comes with an abstraction for a factory. At the most basic level a factory will instantiate an object based on a symbolic name and set of optional arguments. Factory is an interface in Aspire. Factories with different value added services can be installed into Aspire.

One of the factories "com.ai.application.defaultpkg.FilterEnabledFactor2" implements the ability to call post filters on request objects created by a factory. To be able to use filters in Aspire your factory setting should reflect this factory name.

Specify filter at a request level


request.createAnObject.classname=yourpkg.yourclass
request.createAnObject.filterName=filterRequestName

where "filterRequestName" is defined as another request.

Specify the filter class definition


request.filterRequestName.classname=yourpkg.yourclass

Summary

The effect of the two steps above is that the output of step1 is passed as input to the step 2. The output of step2 is returned as if it is the output of step1.

A more complex example

The following example is a real life example taken from AKC where a user is allowed to specify a background master page. This example also demonstrates filters in association with exception based redirecting. A filter called "NonEmptySetValidator" is used on a select to throw an exception when the user is trying to set a master page when that page id does not exist in the database.


#********************************************************************
# Update master page
#********************************************************************
request.UpdateMasterPage.classname=com.ai.db.DBPreTranslateArgsMultiRequestExecutor
request.UpdateMasterPage.db=reportsdb
request.UpdateMasterPage.query_type=update
request.UpdateMasterPage.request.1=UMP.checkItemID
request.UpdateMasterPage.request.2=UpdateMasterPage1

#Redirection in case of success and errors
request.UpdateMasterPage.redirectURL=/akc/servlet/DisplayServlet?url=UpdateMasterPageURL&rtn_code=success
request.UpdateMasterPage.failureRedirectURL.NO_ITEM_FOUND=/akc/servlet/DisplayServlet?url=UpdateMasterPageURL&rtn_code=no_item_found

#
# UMP.checkItemID: Check to make sure the item exists in the database
#
request.UMP.checkItemID.classname=com.ai.db.DBRequestExecutor2
request.UMP.checkItemID.db=reportsDB
request.UMP.checkItemID.stmt=\
\
select * from reports \
where report_id = {master_page_template_item_id}
request.UMP.checkItemID.filterName=UMP.checkItemIDFilter

#
# Applying a filter to throw an exception if the item doesnt exist
# Also notice how the error message begining is used in page redirection
#
request.UMP.checkItemIDFilter.classname=com.ai.filters.NonEmptySetValidator
request.UMP.checkItemIDFilter.exception_message=NO_ITEM_FOUND:No such item found in the database


#
# UMP.UpdateMasterPage1: Actual update
#
request.UpdateMasterPage1.classname=com.ai.db.DBRequestExecutor2
request.UpdateMasterPage1.query_type=update
request.UpdateMasterPage1.stmt=\
\
update users \
set master_page_template_item_id = {master_page_template_item_id} \
where user_id = {profile_user.quote}

What sort of filters available in Aspire

At the time of writing Aspire has the following filters

1. EmptySetValidator: Throw an exception if the set is not empty
2. NonEmptySetValidator: Throw an exception if the set is empty
3. SingleRowToHashtableConverter: Converts a single row of data into a hashtable

An example of a filter class implementation


public class EmptySetValidator implements ICreator
{
    public Object executeRequest(String requestName, Object args)
      throws RequestExecutionException
   {
      // The argument is an IDataCollection
      if (!(args instanceof IDataCollection ))
      {
         AppObjects.log("Wrong type of object passed in : " + args.getClass().getName() );
         AppObjects.log("Expecting a class of type : com.ai.data.IDataCollection");
         throw new RequestExecutionException("Unexpected data type");
      }
      IDataCollection col = null;
      try
      {
         col = (IDataCollection)args;
         IIterator itr = col.getIIterator();
         itr.moveToFirst();
         if (itr.isAtTheEnd())
         {
            // empty data set
            return new Boolean(true);
         }
         //  not an empty data set
         String message = AppObjects.getIConfig().getValue(requestName + ".exception_message","Error:No message specified");
         throw new RequestExecutionException(message);         
      }
      catch(com.ai.data.DataException x)
      {
         AppObjects.log(x);
         throw new RequestExecutionException("Data Exception",x);
      }         
      finally
      {
         try {col.closeCollection();}
         catch(DataException x){ AppObjects.log(x); }
      }
   }      
}