Spring in Workbrain - Part 4 - Forms and Views

Finally we move on to the Spring Web MVC portion of our Workbrain module. This is a long post, so grab some popcorn. Let's create a form in the application that will make use of our previously created services. We'll create a JSP as our view and I'll show you how to create the Java classes that interact with our view and the services.

Let's use a Workbrain traditional training scenario in which a manager needs to order new gloves for a specific employee. The form will take in the employee that needs the gloves and the number of pair of gloves that will be ordered.

First, let's create a Java class that represents our form.

public class GloveOrderForm { 

   private Employee employee;
   private int numGloves; 
   private int mfrmId; 

   public Employee getEmployee() {
      return employee;
   }

   public void setEmployee(Employee emp) {
      this.employee = emp;
   }

   public int getNumGloves() {
      return numGloves;
   }

   public void setNumGloves(int numGloves) {
      this.numGloves = numGloves;
   }

   public int getMfrmId() {
      return mfrmId;
   }

   public void setMfrmId(int mfrmId) {
      this.mfrmId= mfrmId;
   }

}

We include the mfrmId so that our form can be localized. Spring will take care if binding these values to the JSP. I'll show you this shortly. This allows us to reference these values directly in our JSP. So our JSP would look something like this, where I've removed any style aspects:

<spring:bind path="command.mfrmId">
 <c:set scope="request" value="${status.value}" var="mfrmId">
</c:set>
<wb:page login="true" showUIPath='true'
 submitAction="/modules/gloveorderform/action/gloveOrderForm.action"
 popupPage="false"
 maintenanceFormId='<%= request.getParameter("mfrm_id") %>'> 

<!-- We must also print out mfrm_id because the legacy application 
     uses the mfrm_id whereas Spring removes the underscore in binding -->
<spring:bind path="command.mfrmId">
     <input type="hidden" name="<c:out value="${status.expression}" escapeXml="true" />"
          value="<c:out value="${status.value}" escapeXml="true" />" />
     <input type="hidden" name="<c:out value="mfrm_id" escapeXml="true" />"
          value="<c:out value="${status.value}" escapeXml="true" />" />
</spring:bind> 


<table>
     <tr>
          <th><label><wb:localize type="field" id="EMP_ID"
               overrideId="${mfrmId}">Employee</wb:localize>:</label></th>
          <td><spring:bind path="command.employee.empId">
               <wb:controlField overrideId="${mfrmId}" id="EMP_ID"
                cssClass="inputField" submitName="${status.expression}">
                   <c:out value="${status.value}" />
               </wb:controlField>
          </spring:bind></td>
     </tr>
     <tr>
          <th><label><wb:localize type="field" id="NUM_GLOVES"
               overrideId="${mfrmId}">Number of Gloves</wb:localize>:</label></th>
          <td><spring:bind path="command.numGloves">
               <wb:controlField cssClass='dateField' overrideId="${mfrmId}"  id="NUM_GLOVES" submitName="${status.expression}">
                   <c:out value="${status.value}" />
               </wb:controlField>
          </spring:bind></td>
     </tr> 
     <tr><td>
          <wba:button type="submit" label="Submit" labelLocalizeIndex="SUBMIT_BTN" 
          onClick="document.forms.page_form.action.value='submitFormAction';document.forms['page_form'].submit();" /> 
     </td></tr>
</table>
 
</wb:page> 
 
Once you load the form up in the application, you'll have to localize, adding the DBLookup for the employee and a NumberUI for the glove field. Here we can create the controller class, which will handle the requests from the form. I leave out much error handling and validation:

public class GloveOrderFormController extends WbFormController {

    private static String INITIAL_VIEW = "initialFormView";
    private static String SUCCESS_VIEW = "successView"; 
    private EmployeeService employeeService;
    /* The GloveOrderService is a custom service we could have created in
     * Part 3. It handles the processing of the glove order.
     */
    private GloveOrderService gloveOrderService;

    public void setEmployeeService(EmployeeService employeeService) {
        this.employeeService = employeeService;
    }

    public EmployeeService getEmployeeService() {
        return employeeService;
    }

    public void setGloveOrderService(GloveOrderService gloveOrderService) {
        this.gloveOrderService= gloveOrderService;
    }

    public GloveOrderService getEmployeeService() {
        return gloveOrderService;
    }

    /* The calling of this method is automatically derived via reflection 
     * in the WbController class based on the action parameter submitted 
     * from the page.
     */    
    public ModelAndView submitFormAction(HttpServletRequest request, 
            HttpServletResponse response, Object command, BindException errors)
     throws Exception { 
          //Spring automatically binds our form variables in the command object
     GloveOrderForm gloveOrderForm = (GloveOrderForm)command;
     Employee employee = gloveOrderForm.getEmployee();
     int numGloves = gloveOrderForm.getNumGloves();
     int orderNumber = gloveOrderService.submitNewOrder(employee.getEmpId(), numGloves);
     return new ModelAndView(SUCCESS_VIEW); 
   }

    /* Here we set default form values. You must get and set the mfrm_id 
     * here. We'll also set the default number of gloves
     * to order to 1.
     */
    protected Object formBackingObject(HttpServletRequest request) throws Exception {
     
     GloveOrderForm gloveOrderForm = (GloveOrderForm)command;      
     gloveOrderForm.setMfrmId(ServletRequestUtils.getStringParameter(request, "mfrm_id"));
     gloveOrderForm.setEmployee(new Employee());
     gloveOrderForm.setNumGloves(1);
     setBindOnNewForm(true); 
     return gloveOrderForm; 
    }
} 
We need to configure these items in Spring. First, in the web.xml, you need to add a servlet and then a servlet mapping:
 
Servlet:
  <servlet id="<client>_Servlet_1">
    <servlet-name>GLOVEORDERFORM</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:/com/<client>/app/modules/gloveorderform/web/web-config.xml</param-value>
    </init-param> 
    <init-param>
      <param-name>contextClass</param-name>
      <param-value>com.workbrain2.platform.core.context.WbXmlWebApplicationContext</param-value>
    </init-param>
  </servlet>

Servlet Mapping
    <servlet-mapping id="<client>ServletMapping_1">
    <servlet-name>GLOVEORDERFORM</servlet-name>
    <url-pattern>/modules/gloveorderform/action/*</url-pattern>
    </servlet-mapping> 

In the servlet definition, you will see a reference to a web-config.xml that we have not yet created. Before creating that, add a maintenance form to the application, as that will handle the security (visibility and accessibility) to the form. Use /modules/gloveorderform/action/gloveOrderForm.action as the JSP parameter. Get the mfrm_id created and define the following beans in the web-config.xml:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>

  <!-- Intercepter that assigns the maintenance form security -->
  <bean id="gloveForm-loadSecurityHandlerMappings"
  class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetObject"
    ref="webcontext-securityHandlerInterceptor" />
    <property name="targetMethod">
      <value>setUrlSecurityHandlerMappings</value>
    </property>
    <property name="arguments">
      <map>
        <entry key="/modules/gloveorderform/action/*.action">
          <map>
            <entry key="securityId">
              <!--Insert mfrm_id value in place of 9999 -->
              <value>9999</value>
            </entry>
            <entry key="securityVal">
              <value>all</value>
            </entry>
          </map>
        </entry>
      </map>
    </property>
  </bean>
  
  <!-- Maps the url file request to the controller we defined above -->
  <bean id="gloveForm-handlerMapping"
  parent="webcontext-baseSecureHandlerMapping"
  class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
      <props>
        <prop key="/gloveOrderForm.action">
        gloveOrderFormController</prop>
      </props>
    </property>
  </bean>
  
  <!-- Defines the views will be defined externally in a view.xml -->
  <bean id="gloveForm-viewResolver"
  class="org.springframework.web.servlet.view.XmlViewResolver">
    <property name="location"
    value="classpath:com/&lt;client&gt;/app/modules/gloveorderform/web/views.xml" />
    <property name="order" value="10" />
  </bean>
  
  <!-- A bean for our controller. You can also define the validators property,
           which defines a class that handles validation of your form,
           which we did not handle in this post -->
  <bean id="gloveOrderFormController"
  class="com.&lt;client&gt;.app.modules.gloveorderform.GloveOrderFormController">
    <property name="sessionForm" value="true" />
    <property name="commandClass"
    value="com.&lt;client&gt;.app.modules.gloveorderfrom.web.GloveOrderForm" />
    <property name="employeeService"
    ref="platform-employeeService" />
    <property name="gloveOrderService" ref="gloveOrderService" />
    <property name="pages">
      <map>
        <entry key="initialFormView" value="initialFormView" />
      </map>
    </property>
    <property name="secured" value="true" />
  </bean>
  
</beans>

Also referenced above views.xml that we have not yet defined. This will contain the reference to the initial view and the success view that we defined in our controller. Views.xml should look like this:
<beans>

    <bean id="initialFormView" class="org.springframework.web.servlet.view.JstlView">
        <property name="url" value="/modules/gloveorderform/gloveOrderFom.jsp"/>
    </bean>

    <!-- let's just redirect to the menu home on success -->

    <bean id="successView" class="org.springframework.web.servlet.view.RedirectView">
        <property name="url" value="/menu.jsp"/>
    </bean>

</beans>

All errors will be automatically redirected to a predefined error view, so we don't need to worry about defining that.

Once you get all these items setup as directed above, you should have a basic working Glove Order Form. Be sure to add a validator class, implementing org.springframework.validation.Validator in the process, to ensure the values sent to the server are what you expect.

I've extended the form functionality further using AJAX and JSON (with JQuery) to communicate back to the controller, so that the page doesn't have to refresh when the form needs additional information/validation before submitting. How have you applied this technology on your Workbrain implementation? What other technologies and methodologies are you integrating into Workbrain and Spring?

0 comments:

Post a Comment