You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@struts.apache.org by "Givler, Eric" <eg...@state.pa.us> on 2006/10/11 20:32:56 UTC

Code reduction for Action classes

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!



Re: Code reduction for Action classes

Posted by Martin Gainty <mg...@hotmail.com>.
1)rewrite your save() logic to invoke form.getAction() and test on the returned parameter
if(form.getAction() == "save")
{
//do all your save logic here
  return (mapping.findForward("save"));
}
//the last thing you need to do in your save method is to return the mapping for success as in
return (mapping.findForward("success"));

I cant speculate on any of the other classes as you havent displayed the code for those.
M-

This e-mail communication and any attachments may contain confidential and privileged information for the use of the 
designated recipients named above. If you are not the intended recipient, you are hereby notified that you have received
this communication in error and that any review, disclosure, dissemination, distribution or copying of it or its 
contents
----- Original Message ----- 
From: "Givler, Eric" <eg...@state.pa.us>
To: "Struts Users Mailing List" <us...@struts.apache.org>
Sent: Wednesday, October 11, 2006 2:32 PM
Subject: Code reduction for Action classes


> 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!
> 
> 
>

Re: Code reduction for Action classes

Posted by Martin Gainty <mg...@hotmail.com>.
Eric
Take a look at the bottom save method 
HTH
Martin --


> 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"))  */
/*           {*/
            if(form.getAction() == 'save')
           {
             saveMultiRecordForm ( mapping, form, request );
             return(mapping.findForward("save"));
            }
            //return back ActionForward defined for keyword success
           return  (mapping.findForward("success"));
}
> 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!
> 
> 
>