MVC and Spring surf


One of things I missed in Spring Surf are Models and Controllers. I tried many ways to get them working, but still no useful solution. So I chose really simple way to do that – to define another dispatcher and to play with urlrewrite. In this post I’ll describe what I did and as a little bonus you’ll find a breathtaking example 🙂 – YUI based Ajax whisperer.

Let’s start with empty Spring Surf skeleton:

project --topLevelPackage cz.shmoula.controllers
surf install
perform eclipse

Now add dispatcher – in web.xml we have to define new servlet with name controller, loaded as second and with url-pattern /app (default dispatcher is mapped to /service), which is filtered by urlrewrite.

<servlet>
  <servlet-name>controller</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>controller</servlet-name>
  <url-pattern>/app/*</url-pattern>
</servlet-mapping>

In next step we’ll need web application config – Spring names them as -servlet.xml prefixed with servlet name. So let’s create controller-servlet.xml in WEB-INF (in STS New/Spring bean configuration file) and add some useful stuff there: for first we want to use annotations for our controllers, so let’s add there two beans – DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter.

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />

Then we need to define where are our controllers lying, so let’s define base package.

<context:component-scan base-package="cz.shmoula.controllers.controller" use-default-filters="false">
  <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation" />
</context:component-scan>

Last thing is to tell, where will be our views and which suffix they use.

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="prefix" value="/WEB-INF/jsp/" />
  <property name="suffix" value=".jsp" />
</bean>

Okay, folks, that’s almost all! Now the interesting part: Writing the controller. Make a class in package we defined earlier. It should extend some AbstractController or another stuff, but it’s not necessary, we’ll simple it! 😉 Put a test method inside:

package cz.shmoula.controllers.controller;

@Controller
public class AutocompleteController{

  @RequestMapping(value = "/test")
  public String test(Model model){
    model.addAttribute("attribute", "Some sample string");
    return "test";
  }
}

That’s our controller. Now we need a view. Previously we defined parameters for view resolver and in controller we’re returning name of our model. Putted together result is: /WEB-INF/jsp/test.jsp

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Test page backed by controller</title>
  </head>
  <body>
    <h1>Hi, what we have there?</h1>
    <p>${attribute}</p>
  </body>
</html>

Last thing to do is to define some rules for urlrewrite. We’ll try to forward all /app/ requests to our dispatcher and anything else to Default Spring Dispatcher. That’s done by these rules (urlrewrite.xml):

<rule>
  <from>/app/(.*)</from>
  <to>/app/$1</to>
</rule>
<rule>
  <from>/</from>
  <to>/service/</to>
</rule>

Done? So let’s test it! Run it up and open browser at http://localhost:8080/controllers/app/test. Does it work? I hope so!

Test page
Test page

That was the first part – worst one, now came pure fun – some functionality. We want to implement some buzzlike thingy as Autocompletion is – whisperer. To keep it simple our model layer will be just a Collection with Strings, so let’s modify our controller with constructor:

private Collection<String> termList = new HashSet<String>();
public AutocompleteController() {
    termList.add("Prague");
    termList.add("Paris");
    termList.add("Poznan");
}

Now we need controller code – something returning some results. So at /autocomplete will be routine, which waits for „query“parameter with part of String, which tries to find in termList and return:

@RequestMapping(value = "/autocomplete")
public void autocomplete(HttpServletRequest httpRequest, Model model) {
    String query = httpRequest.getParameter("query");
    String response = "";

    if (query != null) {
        for (Iterator<String> i = termList.iterator(); i.hasNext();) {
            String term = i.next();
            if (term.startsWith(query))
                response += "n" + term;
        }
    }
    model.addAttribute("response", response);

}

Now we have to render our model through something, so add view /WEB-INF/jsp/autocomplete.jsp:

${response }

And now to add all that into widget. Let’s extend calendar widget in /WEB-INF/webscripts/calendar – first add styles and YUI scripts into calendar.get.head.ftl:

<link type="text/css" rel="stylesheet" href="http://yui.yahooapis.com/2.8.2r1/build/autocomplete/assets/skins/sam/autocomplete.css">
<script src="http://yui.yahooapis.com/2.8.2r1/build/yahoo-dom-event/yahoo-dom-event.js"></script>
<script src="http://yui.yahooapis.com/2.8.2r1/build/datasource/datasource-min.js"></script>
<script src="http://yui.yahooapis.com/2.8.2r1/build/connection/connection-min.js"></script>
<script src="http://yui.yahooapis.com/2.8.2r1/build/autocomplete/autocomplete-min.js"></script>

<style type="text/css">
    #myAutoComplete {
        width:25em;
        padding-bottom:2em;
    }
</style>

The last step is to create div and put a script into calendar.get.html.ftl:

<div>
    <label for="myInput">Autocomplete:</label>
    <div id="myAutoComplete">
        <input id="myInput" type="text">
        <div id="myContainer"></div>
    </div>
</div>
<script type="text/javascript">
  YAHOO.example.BasicRemote = function() {
    var oDS = new YAHOO.util.XHRDataSource("/controllers/app/autocomplete");
    oDS.responseType = YAHOO.util.XHRDataSource.TYPE_TEXT;
    oDS.responseSchema = {
      recordDelim: "n",
      fieldDelim: "t"
    };
    oDS.maxCacheEntries = 5;
    var oAC = new YAHOO.widget.AutoComplete("myInput", "myContainer", oDS);
    return {
      oDS: oDS,
      oAC: oAC
    };
  }();
</script>

And that should be all, now let’s test it, if it works! So open http://localhost:8080/controllers/calendar and voila…

Working autocompletion
Working autocompletion

Ok, that’s all for this post, looks like it works! So you can download backup of this code somewhere around here and enjoy it on your own devbox. Keep looking forward for next stuff, maybe using facebook login with Spring social, who knows ;-).


17 komentářů: “MVC and Spring surf”

  1. Hi,
    could you show your urlrewrite.xml ?
    I followed your tutorial but when I try to access:
    http://localhost:8080/controllers/app/test I get this error:

    javax.servlet.ServletException: Could not resolve view with name ‚app/test‘ in servlet with name ‚Spring MVC Dispatcher Servlet‘
    org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1042)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:798)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:716)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:647)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:552)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
    org.tuckey.web.filters.urlrewrite.NormalRewrittenUrl.doRewrite(NormalRewrittenUrl.java:195)
    org.tuckey.web.filters.urlrewrite.RuleChain.handleRewrite(RuleChain.java:159)
    org.tuckey.web.filters.urlrewrite.RuleChain.doRules(RuleChain.java:141)
    org.tuckey.web.filters.urlrewrite.UrlRewriter.processRequest(UrlRewriter.java:90)
    org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:417)

  2. hi,
    could you show all of source code of the first part of tutorial…i get the same error of the post above:

    javax.servlet.ServletException: Could not resolve view with name ‘app/test’ in servlet with name ‘Spring MVC Dispatcher Servlet’

  3. Hi,
    I need some guidance to set up the surf in the STS2.7.2, to along with the spring MVC,

    Thanks,
    Mani

  4. Hi
    Am using the latest versions and unable to install surf add on. I would like to integrate spring surf with spring MVC as a view component. But i was unable to set the development environment. Please help me. I was unable to add spring surf roo addon. I tried multiple ways but there is no progress. The results are as follows….
    Welcome to Spring Roo. For assistance press CTRL+SPACE or type „hint“ then hit ENTER.
    roo>
    required –url: The URL to obtain the bundle from; no default value
    roo> osgi install –url file:/C:spring-surf-roo-addon-1.0.0-RC2.jar
    Bundle ID: 66

    roo> addon install id 66
    To install an addon a valid bundle ID is required

    roo> project –topLevelPackage com.tecra
    Created ROOTpom.xml
    Created SRC_MAIN_JAVA
    Created SRC_MAIN_RESOURCES
    Created SRC_TEST_JAVA
    Created SRC_TEST_RESOURCES
    Created SRC_MAIN_WEBAPP
    Created SRC_MAIN_RESOURCESMETA-INFspring
    Created SRC_MAIN_RESOURCESlog4j.properties
    Created SRC_MAIN_RESOURCESMETA-INFspringapplicationContext.xml
    com.tecra roo> addon install id 66
    To install an addon a valid bundle ID is required
    com.tecra roo>
    required –searchResultId: The bundle ID as presented via the addon list or addon search command; no default value
    com.tecra roo> addon install id 66
    To install an addon a valid bundle ID is required
    com.tecra roo>

    Thanks,
    Mani

  5. Hi Bruno, It’s a long time since I last played with this stuff, also I have no source code for that – we switched to Liferay completely. But I think – you have to edit your urlrewrite.xml. You should have configured dispatcher for app/* prefix in web.xml and the suffix you can set in that urlrewrite file. So try to look inside file I added to last answer.

  6. Hi Mani,
    You can’t use newest version of roo with surf addon (it doesnt exist and I also think – package structure for newest version is different). You have to use some old version. I had surf 1.0.2.RELEASE, which worked good.

  7. thanks for the answer shMoula, I resolve my problem, first i try with /app/test in the web.xml, after resolve the problems with this controller, i put a wildcard and solve the problem. I saw that in the forum stackoverflow, but without you aren’t possible.

    thanks

  8. Thank You shMoula. Could you please clarify me which versions are compatible to use surf.
    Present am using STS 2.7.2+spring-roo-1.1.5.RELEASE + maven-2.2.1.RELEASE.

  9. As I wrote – I used roo version 1.0.2RELEASE. STS and maven version doesn’t matter i think ;-).

  10. Thank you shmoula. Everything fine for me from the console. And i am able modify everything for a surf sample site. But the problem is i was unable to get the empty Spring Surf skeleton. How can i get this. I am sorry for the questions. I’ve tried a lot of ways but no luck and I’ve searched for the solutions online still i am missing something. hope you can provide a solution.
    Thanks ,

  11. Thank you, I have started roo and everything but if i would like to start an empty skeleton like
    File –> New –> Spring Template Project here i am not getting Spring Surf Project to get a new spring surf project.

  12. Hi shmoula, i got everything fine. but if i want to start a new project with the help of spring source tool suite, then i was unable to see the Spring Surf Project skeleton. is there any missing thing with me.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *