16-Jul-04 (Created: 16-Jul-04) | More in 'OSCON-2004'

Declarative Part Pipelines: Code Examples

Let us Consider a web transation called AppendText


#appendText(reportId, appendText)
#Append the passed in text to the identified report
request.appendText.classname=com.ai.db.DBPreTranslateArgsMultiRequestExecutor
request.appendText.db=reportsDB
request.appendText.query_type=update
request.appendText.request.1=AT.GetText
request.appendText.request.2=AT.ConcatenateText
request.appendText.request.3=AT.UpdateText
request.appendText.request=AT.UpdateDate

If the above transaction succeeds


request.appendText.redirectURL=\
/akc/servlet/DisplayServlet?\
    url=DisplayNoteMPURL&\
    reportId={reportId}\
    &ownerUserId={ownerUserId}

A generic part to get original text


request.AT.GetText.classname=com.ai.db.DBRequestExecutor2
request.AT.GetText.stmt=\
select st.statement as item_text \
   ,st.statement_id as statementId \
from reports r \
   ,sql_statements st \
where 1=1 \
   and r.report_content_id = st.statement_id \
   and r.report_id = {reportId}

Use a part to append the new text to the old text


#concatenate   
request.AT.concatenateText.classname=com.ai.parts.SubstitutionPart
request.AT.concatenateText.substitution={item_text} {appendtext}   
request.AT.concatenateText.resultName=newText

A generic part to update the database


#final update
request.AT.UpdateText.classname=com.ai.db.DBRequestExecutor2
request.AT.UpdateText.query_type=update
request.AT.UpdateText.stmt=\
   update sql_statements \
   set statement={newText.quote} \
   where statement_id = {statementId}

Another generic part to update the last updated date


request.AT.UpdateDate.classname=com.ai.db.DBRequestExecutor2
request.AT.UpdateDate.query_type=update
request.AT.UpdateDate.stmt=\
   update reports \
   set last_updated_on=Now() \
   where report_id={reportId}

Let us investigate code for one of the parts


/**
 * Transforms a specified string with substitution arguments in it and returns it
 *
 * Additional property file arguments
 * 1. substitution=
 *
 * Output
 * 1.resultName: The above value translated after substitution
 *
 */

public class SubstitutionPart extends AFactoryPart
{
    protected Object executeRequestForPart(String requestName, Map inArgs)
            throws RequestExecutionException
    {
       try
       {
          String substString = AppObjects.getValue(requestName + ".substitution");
          String newString = 
        SubstitutorUtils.generalSubstitute(substString,new MapDictionary(inArgs));
          return newString;
       }
       catch(ConfigException x)
       {
          throw new RequestExecutionException("Error:config errror",x);
       }
    }//eof-function
}//eof-class

Lee me include the configuration one more time for this part


#concatenate   
request.AT.concatenateText.classname=com.ai.parts.SubstitutionPart
request.AT.concatenateText.substitution={item_text} {appendtext}   
request.AT.concatenateText.resultName=newText

How does the substitution part look using simple IOC


public class SubstitutionPart extends AFactoryPart
{
    protected Object executeRequestForPart(String substitution, Map inArgs)
            throws RequestExecutionException
    {
       try
       {
          String newString = 
        SubstitutorUtils.generalSubstitute(substitution,new MapDictionary(inArgs));
          return newString;
       }
       catch(ConfigException x)
       {
          throw new RequestExecutionException("Error:config errror",x);
       }
    }//eof-function
}//eof-class

See how we no longer need the configuration context and how substitution is passed in by the container. The container will use the configuration context and configuration api to do this instead of the part.

How does the substitution part look using radical IOC


public class SubstitutionPart
{
    protected Object generalSubstitute(String substitution, Map inArgs)
      throws ConfigException
    {
          String newString = 
        SubstitutorUtils.generalSubstitute(substitution,new MapDictionary(inArgs));
          return newString;
    }//eof-function
}//eof-class

See how we no longer even derive from the class. The invoker will figure out the input parameters from the context and also the configuration file. Most likely this is where things are heading. In this case the properties will look like


#concatenate   
request.AT.concatenateText.classname=com.ai.parts.SubstitutionPart
request.AT.concatenateText.substitution={item_text} {appendtext}   
request.AT.concatenateText.resultName=newText

#invocation
request.AT.request.1=AT.concatenateText(#inArgs)

The invocation will rename the parameters to suit the method specification. The unmatched parameters will be resolved from configuration. This is paving the way for any class to play the role of a part.

Examples of some Parts that I have used


DBRequestExecutor
DBStoredProcedureRequestExecutor
RowFileReader
InvalidateCachePart
DBLoopPart
IfPart
SessionLoaderPart
StringToInputStreamPart
SubstitutionPart
URLExporterPart
ValueDecoderPart
DOMFileWriterPart
DOMUpdaterPart
GetDOMPart

DBLoopRequestExecutor: Loop Part example


public class DBLoopRequestExecutor extends DBBaseJavaProcedure
{

   public Object executeProcedure(Connection con, 
                                    boolean bConnectionCreator,
                                    String requestName, 
                                    Hashtable arguments )
         throws DBException
   {
      String fieldSpec = 
      AppObjects.getIConfig().getValue(requestName + ".fieldSpec",null);
      String individualRequest = 
      AppObjects.getIConfig().getValue(requestName + ".individualRequest",null);
      if (fieldSpec == null)     
      {
         throw new DBException("Error: Field spec required");
      }
      if (individualRequest == null)     
      {
         throw new DBException("Error: Individual request not specified");
      }
      // field spec if mentioned 
      Vector fieldNameVector = Tokenizer.tokenize(fieldSpec,"|");
      String pluralFieldName = (String)fieldNameVector.get(0);
      String singularFieldName = (String)fieldNameVector.get(1);
      String pluralFieldValue = (String)arguments.get(pluralFieldName);
      if (pluralFieldValue == null)
      {
         throw new DBException(
           "Error: No value for the plural field name: " + pluralFieldName);
      }
      // plural field value found
      Vector singularFieldVector = Tokenizer.tokenize(pluralFieldValue,"|");
      try
      {
         for(Enumeration e=singularFieldVector.elements();e.hasMoreElements();)
         {
            String singularFieldValue = (String)e.nextElement();
            arguments.put(singularFieldName,singularFieldValue);
            arguments.put("aspire.reserved.jdbc_connection",con);
            Object obj = AppObjects.getIFactory().getObject(individualRequest,arguments);
         }
      }
      catch(RequestExecutionException x)
      {
         throw new DBException("Error: Could not execute a request", x);
      }         
      return new RequestExecutorResponse(true);
   }
}   

IfPart Example


public class IfPart extends AFactoryPart
{
    protected Object executeRequestForPart(String requestName, Map inArgs)
        throws RequestExecutionException
    {
        PrintWriter w = null;
        String aspireFilename = null;
        try
        {
        //mandatory args
            String expression = AppObjects.getValue(requestName + ".expression");
            String ifRequestName = AppObjects.getValue(requestName + ".if");
            String elseRequestName = AppObjects.getValue(requestName + ".else",null);

            boolean r
            = ExpressionEvaluationUtils
         .evaluateBooleanExpressionUsingDictionary(
             expression,new MapDictionary(inArgs));
            
            if (r == true)
            {
               return AppObjects.getObject(ifRequestName,inArgs);
            }
            else
            {
               if (elseRequestName == null)
                  return new Boolean(true);
               return AppObjects.getObject(elseRequestName,inArgs);
            }
        }
        catch(ConfigException x)
        {
            throw new RequestExecutionException
             "Error: ConfigException. See the embedded exception for details", x);
        }
        catch(CommonException x)
        {
            throw new RequestExecutionException(
           "Error: Expression evaluation error. See the embedded exception for details", x);
        }
    }
}

References

1. General Introduction to other Server side Patterns in this series

2. OSCON 2004 Summary page for Server side patterns session