JSPWiki logo
Main page
About

Getting Started

Documentation
Developers
Javadocs
Designers
Sample RSF Apps
Presentations
Acronyms

Downloads
Current Release
Trunk
Distributions
Old Versions

Community
Q&A
Forums
Mailing Lists
Issue Tracker
People

Design
Roadmap
Integrations
Concepts
Philosophy

Pages
Find
Recent Changes
Unused
Undefined
Index

Set your name in
UserPreferences

Edit this page


Referenced by
Integrations
News
SampleRSFApps
SpringMVCStepRSF




JSPWiki v2.2.33


SpringMVCStep


SpringMVC Step by Step Sample

This is a very clear and carefully presented sample contributed by Thomas Risberg to the Spring documentation for SpringMVC. It manages to show off pretty much the complete spectrum of core SpringMVC idioms and APIs in the shortest possible space, which is why I chose it to illustrate RSF's SpringMVC integration capabilities - I recommend it to anyone as the first port of call in trying to understand how SpringMVC works.

Firstly, since the actual code for the example doesn't seem to be available in source form anywhere, I have imported it into the SpringMVCStep-orig project held in the RSF SVN - this is a direct copy of the source as presented at the end of step 3 of the tutorial, with only the following minimal changes

  • The original Ant build has been replaced by a standard Maven build
  • The "no-package" code has been imported into a fully qualified package uk.org.ponder.rsf.springmvcstep - e.g. the original bus package for business model definitions becomes uk.org.ponder.rsf.springmvcstep.bus
  • The original Spring 1.1.x-style context definitions have been updated to the modern 1.2.x "concise" definition form

Other than that, the code is identical to the way Thomas presented it, and is ready to auto-deploy (after editing project.properties) by the default Maven build.

Stage 1 - IKAT as a view layer for SpringMVC

The first reworking of this app illustrates the "minimum-impact" integration with RSF. We simply replace the view definitions from the original sample, which were written using JSPs, with pure HTML templates and ViewProducers rendered using IKAT. This involves changing only the definition of the SpringMVC ViewResolver bean to be an IKATViewResolver rather than an InternalResourceViewResolver - the rest of the SpringMVC code, including the entire Controller layer remains untouched. This IKAT version of SpringMVC Step-by-step is held in SVN at SpringMVCStep-IKAT

web.xml and libraries

In order to set up the IKAT environment, as well as including the standard RSF JARs into lib for the webapp (via Maven project.xml, the web.xml needs to import the definitions for the RSF Spring contexts - the following stanza is added to web.xml :

 <!-- Configure standard Spring application contexts. Be sure to mention
    rsf config files first, so any overrides may be processed. The first two
    config files are loaded from inside the rsfutil.jar  -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
      classpath:conf/rsf-config.xml,
      classpath:conf/blank-applicationContext.xml
      </param-value>
  </context-param>
  <!-- Configure "resource scope" Spring application contexts (RSAC).
     Be sure to mention rsf config files first, so any overrides may be
    processed -->
  <context-param>
    <param-name>requestContextConfigLocation</param-name>
    <param-value>classpath:conf/rsf-requestscope-config.xml,
                 classpath:conf/blank-requestContext.xml,
                 classpath:conf/springmvc-requestContext.xml</param-value>
  </context-param>

JSPs to HTML

Next, we replace the JSP view definitions with the properly factored HTML + ViewProducer combination which constitute an IKAT view.

For example, the following definition of the front page view hello.jsp

<%@ include file="/WEB-INF/jsp/include.jsp" %>

<html>
<head><title><fmt:message key="title"/></title></head>
<body>
<h1><fmt:message key="heading"/></h1>
<p><fmt:message key="greeting"/> <c:out value="${model.now}"/>
</p>
<h3>Products</h3>
<c:forEach items="${model.products}" var="prod">
  <c:out value="${prod.description}"/> <i>$<c:out value="${prod.price}"/></i><br><br>
</c:forEach>
<br>
<a href="<c:url value="priceincrease.htm"/>">Increase Prices</a>
<br>
</body>
</html>

becomes as an RSF template hello.html

<html xmlns:rsf="http://ponder.org.uk/rsf/">
<head><title rsf:id="title">Page Title</title></head>
<body>
<h1 rsf:id="heading">Heading</h1>
<p><span rsf:id="greeting">Hello, it is now</span> 
<span rsf:id="timenow">Octeen O'Clock</span>

</p>
<h3>Products</h3>
<div rsf:id="productrow:">
  <span rsf:id="description">Frotzer</span> <i>$<span rsf:id="price">582.50</span></i><br><br>
<div>
<br>
<a href="priceincrease.html" rsf:id="increase">Increase Prices</a>
<br>
</body>
</html>
Note that this file (including its internal HTML link to priceincrease.html) is fully previewable in a browser, and would be safe to hand to a design team armed with DreamWeaver (or NotePad!!) for UI work in full confidence that i) they could easily work with the template with standard tools, and ii) they could not hand back anything that could corrupt the application data model. In some deployment models, it's even imaginable that an application's template space could be exposed via WebDAV for live updates by reasonably expert users - certainly they can be edited live in the filesystem by developers with the same (only slightly faster and safer) update cycle model as JSPs.

Spring definitions

All that changes in the Spring context definition springapp-servlet.xml is a change of the SpringMVC InternalResourceViewResolver for the IKATViewResolver as follows:

  <bean id="viewResolver" class="uk.org.ponder.rsf.springmvc.IKATViewResolver"/>

The IKATViewResolver is entirely autonomous and needs no configuration as to URL mappings &c, it will infer all it needs from the MVC environment and the templates.

ViewProducers

The completion of the refactoring process we started above when we converted JSPs to HTML, is to split off all the view logic that was in them into standard ViewProducers. These produce RSF component trees in the same way as in all RSF applications, although there is the slight difference that where components make references to URLs, these must be made explicitly rather than through the abstraction of the RSF ViewParameters system. Within SpringMVC we do not have control of the dispatching process and so must fall back to the same level of URL abstraction as the rest of SpringMVC, for reasons of compatibility.

Here is the producer for the main (initial) view of the application, "hello":

public class HelloProducer implements ViewComponentProducer, DefaultView {
  public static final String VIEW_ID = "hello";
  private List products;

  public String getViewID() {
    return VIEW_ID;
  }

  public void setProducts(List products) {
    this.products = products;
  }

  public void fillComponents(UIContainer tofill, ViewParameters viewparams,
      ComponentChecker checker) {
    UIOutput.make(tofill, "title", null, "#{messages.title}");
    UIOutput.make(tofill, "heading", null, "#{messages.heading}");
    UIOutput.make(tofill, "greeting", null, "#{messages.greeting}");
    String now = (new java.util.Date()).toString();
    UIOutput.make(tofill, "timenow", now);

    for (int i = 0; i < products.size(); ++i) {
      Product product = (Product) products.get(i);
      UIBranchContainer productrow = UIBranchContainer.make(tofill,
          "productrow:", Integer.toString(i));
      UIOutput.make(productrow, "description", product.getDescription());
      UIOutput.make(productrow, "price", product.getPrice().toString());
    }
    // SpringMVC doesn't allow us to do much better than this in URL-space
    // independence
    UIInternalLink.makeURL(tofill, "increase", "priceincrease.htm");
  }

}

The first three lines show use of the RSF MessageLocator as a BeanLocator - these messages within the message file messages.properties inherited from the original version of the application can be resolved by key simply through EL paths, without needing to resolve the MessageSource bean itself as a dependency.

The main body of the producer contains a Java for loop replacing the JSP <c:foreach> tag. Of course with Java 5 this loop could be made more concise using Java's own foreach syntax. Finally, we emit a "manual" (i.e. not using the RSF ViewParameters abstraction) UIInternalLink referencing the application's second view.

Request-scope injection for the model

One interesting point is that while transferring the loop logic out of the JSP, at the same time we took the opportunity to transfer the dependence on the list of Product items to be delivered via request-scope injection, rather than having to resolve it via EL in the request context. This was done by making the following definition in our requestContext.xml:

  <bean id="products" class="java.util.List" />

Due to the RSF-SpringMVC library's automatic transfer of the SpringMVC request context into RSF's request container, we don't need to do any work to populate this bean, other than to declare it's name and type like this so that we can set up the following injection to our HelloProducer:

  <bean id="helloProducer"
    class="uk.org.ponder.rsf.springmvcstep.producers.HelloProducer">
    <property name="products" ref="products" />
  </bean>

This is more in keeping with the Spring philosophy of avoidance of explicit bean fetches in favour of dependency injection. Note however that to make this injection work, we need to slightly alter the SpringMVC Controller (SpringappController.java) for this view so that it deposits its model at the root of the request namespace, rather than at the path "model" as we inherited it:

        myModel.put("products", getProductManager().getProducts());
// IKAT version - Change from original: place model at root of namespace 
// to allow request-scope injection.
        return new ModelAndView("hello", myModel);

RSFSpringMVC and errors

RSF-SpringMVC integrates seamlessly with SpringMVCs error reporting system, as operated by Spring Validators and the Errors interface. Any errors arising through the PriceIncreaseController are available in the PriceIncreaseProducer, as injection as a standard RSF TargettedMessageList. These messages are enqueued for display at the component responsible for them (if any) in the normal way, and we can for example conditionally display the "Please fix all errors!" message by testing the size of this error list. That is, where we used the Spring JSP tag

  <spring:hasBindErrors name="priceIncrease">
    <b>Please fix all errors!</b>
  </spring:hasBindErrors>
, the corresponding section of the HTML template becomes simply
  <div rsf:id="hasErrors">
    <b>Please fix all errors!</b>
  </div>
and we conditionally emit this component in PriceIncreaseProducer.java with the following:
    if (tml.size() > 0) {
      UIOutput.make(tofill, "hasErrors");
    }

Testing

The app functions identically to the original, including request-cycle semantics (the following browser warning typically appears if using the "Back" button after a successful price increase follows an invalid one):

springapp-postbox.png

Stage 1a - Economising on Controllers

In transferring our view layer into HTML, we have gained much better insulation from the view technology and cleaner separation of logic and content. Unfortunately in this straight one-for-one translation, we have gained quite a bit of verbosity, and also some functional duplication. The point is that a SpringMVC "view-only" controller like SpringappController is functionally redundant with respect to an RSF ViewProducer, since the ViewProducer, already being a Spring bean, is capable of performing all the functions of the controller. This reveals the fact that the only real function of a "view-only" controller is as a "collector and focusser" of dependencies - beans are collected from the Spring context and general environment of the app, and collected into a Map, the request context map. The ViewProducer is fully capable of these functions itself, so if we weren't concerned with minimising deltas to an existing SpringMVC application, but still wanted to use IKAT + SpringMVC, we could gain a lot of concision by simply "knocking through" controller+producer combinations like this (i.e. SpringappController.java + HelloProducer) and fold all of the functions into the Producer.

The only real function of SpringappController is to collect the products list and place it into the request context. We could achieve the same effect by replacing the dummy request-scope definition of products that we saw above with the following "pure Java":

<bean id="products" factory-bean="prodMan" factory-method="getProducts"/>

With this, the SpringappController could be destroyed - that is, replaced with a no-op controller that simply returns an empty model.

Since we are still using SpringMVC to handle the results of POST sumbissions, we cannot destroy FormControllers like PriceIncreaseFormController correspondingly - although we *can* slim them down by removing those parts of it which specifically refer to view generation (i.e. which populate the model). Since in this case PriceIncreaseFormController simply renders a RedirectView, there are no further savings to be had. We might like to economise on the command object priceIncrease by moving it to the producer, but command objects have a special status within SpringMVC which is not equivalent to normal Model elements.

Taking stock

We have inherited a SpringMVC sample application for which we have ported the view layer from JSPs to RSF's pure HTML templating engine, IKAT, while making no changes either to business logic nor to the definitions of SpringMVC's controller[1] or validator beans. However, the app has become somewhat more verbose as a result, and it is still not portable to other environments, as a result of the concrete dependence in SpringMVC's controller layer on the HttpServletRequest object, as well as the particular URL mapping scheme set up in the context. In the next section we will port over the "other half" of the app, completing its transformation to RSF. While we will inherit our HTML templates and business logic from this application unchanged, we will remove the SpringMVC controller layer, and replace it with RSF's (largely implicit) "controller".

Head - Spring MVC Step by Step - IKAT versions
Page 1 - Spring MVC Step by Step - RSF version


[#1]There was one incidental change to the naming structure of the SpringMVC model.

Attachments:

springapp-postbox.png Info on springapp-postbox.png 26607 bytes


Go to top   Edit this page   More info...   Attach file...
This page last changed on 11-Sep-2006 00:35:05 UTC by AntranigBasman.