I've been trying to get the hang of struts now for the past few months, and am 
wondering how people write "re-usable" base classes for Struts.  If you have 
the time, I'd really appreciate your thoughts on this - bear with me, as it's a 
lot of reading:

My actions tend to be "similar" but making them "reusable" seems to be eluding 
me.

What I would like to have is a set of reusable actions that handle the 
following standard application features:

1. A wizard (multiple jsps, one form bean, data formatting, validation specific 
per page, with persistence when any navigation occurs, not just at the end).  
I've seen an example of an "at-the-end" wizard in the sample Chapter from 
Struts Recipes by George Franciscus and Danilo Gurovich.
2. A multi-record screen ( one jsp, array of items, formatting of data, 
validation w/ messages, and persistence)

Are these already written, so I can save some time?

So far, I've tried to write a re-usable piece of code for #2.
Based on ideas in the ObjectSource Struts Survival Guide, Chapter 10:

#1: I wrote my own mapping.  Here are the properties:

public class CRUDActionMapping extends ActionMapping{
   private String formType;
   private String formMultiRecordProperty;
   private String formDefConfig;
   private String databaseBeanClassName;
   private String saveServiceMethod;
   private String retrieveServiceMethod;
   private String serviceName;
    
#2: My struts-config.xml for the action for this class is defined as:

         <action path="/cbdeqr_engcodehours"
                className="dep.ccdreporting.view.mappings.CRUDActionMapping"
                
type="dep.ccdreporting.view.actions.cbp.CbdeqrEngCodeHoursAction"
                name="CbdeqrEngCodeHoursForm" 
                input="/WEB-INF/jsp/cbp/cbdeqr_engcodehours.jsp"
                scope="session"
                validate="false"
                parameter="event_save=save,event_codehours=initializePage,
                event_reportInProgress=previous,event_countyHours=next,
                default=unspecified">
                <set-property property="cancellable" value="true"/>
                <set-property property="formType" value="MultiRecord"/>
                <set-property property="formMultiRecordProperty" 
value="codehours"/>
                <set-property property="formDefConfig" 
value="CbdeqrEngCodeHoursForm"/>
                <set-property property="databaseBeanClassName" 
value="dep.ccdreporting.model.vo.cbp.CbdeqrCodeHours"/>
                <set-property property="saveServiceMethod" 
value="saveCbdeqrCodeHours"/>
                <set-property property="retrieveServiceMethod" 
value="getEngCodeHours"/>
                <set-property property="serviceName" 
value="dep.ccdreporting.model.service.cbp.CBPDaoService"/>
                <forward name="hourspercounty" 
path="/cbdeqr_engcountyhours.do"/>
                <forward name="cbdeqr_main" path="/cbdeqr.do"/>
                <forward name="success" 
path="/WEB-INF/jsp/cbp/cbdeqr_engcodehours.jsp"/>
        </action>

#3: The action class CbdeqrEngCodeHoursAction is reduced to two overridden 
methods (save and initializePage, due to calling a calculation) of a base 
class, and one method that gets the parameters to retrieve the data, which is:

   public HashMap getRetrieveServiceMethodParams( HttpServletRequest request ) {
       HttpSession session = request.getSession();
       UserBean userbean = (UserBean) session.getAttribute("usrbean");
       HashMap codeparams = new HashMap();
       codeparams.put("report_id" , userbean.getReport_id()); 
       return codeparams;
   } // getRetrieveServiceMethodParams

#4: The base class handles the initialization of the page and the save.  Here's 
the save() call so this message doesn't get too wordy:

public ActionForward save(ActionMapping mapping, 
                          ActionForm form, 
                          HttpServletRequest request, 
                          HttpServletResponse response) 
  throws Exception, 
         ServletException 
{
   ActionForward fwd = mapping.getInputForward();
   if (isFormValid(mapping, form, request)) {
      if ( mapping instanceof CRUDActionMapping ) {
         CRUDActionMapping myMapping = (CRUDActionMapping) mapping;
         if (myMapping.getFormType().equalsIgnoreCase("MULTIRECORD")) {
             saveMultiRecordForm ( mapping, form, request );
...

and the save code -- this is where I'm confused - is this a reasonable 
approach, or am I really missing the boat.  Specifically:  Should I be creating 
the service like this?  Also, I really want the ability to log who is 
performing the service methods as well - i.e. request.getRemoteUser(), so do I 
have to pass this to the service method, or can I get it another way??

public void saveMultiRecordForm ( ActionMapping mapping,
                                  ActionForm form,
                                  HttpServletRequest request ) 
   throws Exception
{
   // get all the action-mapping properties
   CRUDActionMapping crudMapping = (CRUDActionMapping) mapping;
   String formPropertyName = crudMapping.getFormMultiRecordProperty();
   String formDefConfigName = crudMapping.getFormDefConfig();
   String databaseBeanClass = crudMapping.getDatabaseBeanClassName();
   String saveServiceMethodName = crudMapping.getSaveServiceMethod();
   
   // form-def returns an array of dynaActionForms, which is stored in the: 
formPropertyName     
   DynaActionForm dynaform = (DynaActionForm ) form;
   ArrayList listOfFormDetails = (ArrayList) dynaform.get( formPropertyName );

   // db bean array needed for all our service method calls
   Class classOfDbBean = Class.forName( databaseBeanClass );
   Object[] details = (Object[]) Array.newInstance( classOfDbBean, 
listOfFormDetails.size() );

   // Create an instance of the service class.
   Class serviceClass = Class.forName( crudMapping.getServiceName() ); 
   Object service = serviceClass.newInstance();

   // find the method that takes an array of data, and invoke it.
   Object arguments[] = new Object[] { details };
   Method svcMethod = serviceClass.getMethod(saveServiceMethodName, new Class[] 
{ details.getClass()} );
   Object result = svcMethod.invoke( service, arguments );
} // saveMultiRecordForm 

Thanks for anyone who has made it this far!


Reply via email to