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!