Close
Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

LABS
Spring 4 MVC with Scala

Why?

In my previous job at The Guardian I used Scala on various projects and enjoyed it. We employed various dependency injection (DI) frameworks from Spring, Guice and a lightweight homegrow version. Now I’m at Pivotal Labs Spring is part of the family and some recent JVM projects combined with the recent release of Spring 4 means we’ve been looking at how these tools can help our clients.

I think there’s a natural tendency when you hear ‘Spring’ to think ‘Java’, but I wanted to show that this isn’t the case anymore, and I’m going to walk through a simple web application using Scala and Spring 4. Here’s the toolset I’m using here:

Project setup

So first off, you’ll notice I’m not using Maven or Gradle but SBT. This is the build.sbt file which outlines the name and version of the application along with the Scala version. It pulls in the webSettings from a plugin which I’ll drop into in a moment. Then it outlines the dependencies, spring-mvc for the web component of the application, jetty-container is required for the complication and runtime while jetty-jsp is only required for the runtime used by the plugin.

name := "Hello World"

version := "1.0"

scalaVersion := "2.10.2"

seq(webSettings : _*)

libraryDependencies ++= Seq(
  "org.springframework" % "spring-webmvc" % "4.0.0.RELEASE",
  "org.eclipse.jetty" % "jetty-webapp" % "9.1.0.v20131115" % "container, compile",
  "org.eclipse.jetty" % "jetty-jsp" % "9.1.0.v20131115" % "container"
)

In projects/plugins.sbt there are two plugins, one for running the web application within SBT and the other to create IntelliJ project files.

addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "0.6.0")

addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.5.2")

The project uses the ‘standard’ Java convention of having a src folder at the root, followed by webapp which includes the web application configuration and static resources as well as the source folder called scala. Within the scala directory there is a namespaced directory structure of com/robb1e/helloworld although in Scala unlike in Java there is not a one to one mapping of file to classname. In the codebase I’ve included the Config class in the root of the src/scala directory to demonstrate this. But let’s first head into webapp/WEB-INF/web.xml. This is the deployment descriptor from the Servlet standard.

Configuration

Web.xml

This file is what’s loaded by the servlet container and although it looks big, there’s not all that going on here.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <display-name>Scala Spring MVC 4 Web Site</display-name>
  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    <init-param>
      <param-name>contextClass</param-name>
      <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </init-param>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>com.robb1e.helloworld.Config</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

The second init-param element within the servlet element defines the class with the application configuration which we’ll come to shortly.

<init-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>com.robb1e.helloworld.Config</param-value>
</init-param>

We then tell the defined servlet to serve the root path

<servlet-mapping>
  <servlet-name>dispatcher</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>

Wiring

This configuration uses Spring annotations @ComponentScan to declare which package to look for Spring wiring to occur in. The @Bean annotation makes the viewResolver available which is required by the controller to render HTML.

package com.robb1e.helloworld

import org.springframework.context.annotation.{Bean, ComponentScan}
import org.springframework.web.servlet.view.{InternalResourceViewResolver, JstlView}

@ComponentScan(basePackages = Array("com.robb1e.helloworld"))
class Config {

    @Bean
    def viewResolver = {
        val viewResolver = new InternalResourceViewResolver
        viewResolver.setViewClass(classOf[JstlView])
        viewResolver.setPrefix("/WEB-INF/views/")
        viewResolver.setSuffix(".jsp")
        viewResolver
    }

}

See this great writeup for more on Spring MVC Configuration.

Dependencies

To show a simple dependency this example includes a ‘service’ which provides the name to render in HTML.

package com.robb1e.helloworld

import org.springframework.stereotype.Service

trait Name {
  def name: String
}

@Service
class NameService extends Name {

  def name = "world"

}

The trait isn’t strictly required, but enables me to introduce the comparison between Java Interfaces and how they are used in testing and Java Abstract classes in that they can be partially implemented.

Controller

The dependency of the Name gets injected into the controller using the autowired command in the class definition. The class has a constructor which requires a Name class and that’s created and injected from the snippet above.

package com.robb1e.helloworld

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.{RequestMapping, RequestMethod}

@Controller
@RequestMapping(Array("/"))
class HelloWorldController @Autowired() (nameService: Name) {

  @RequestMapping(method = Array(RequestMethod.GET))
  def index (model: Model) = {
    model.addAttribute("name", nameService.name)
    "index"
  }
}

There’s a lot going on here, let’s break it down, starting with the package definition. The interesting thing with Scala is the package isn’t dependent on the directory structure. In the example it is, but it doesn’t need to be. Say good bye to those empty directories.

package com.robb1e.helloworld

We have to import a few classes, Autowired for the automatic injecting of dependencies; Controller to declare what type of class this is, Model to hold the variables to be passed down to the view. The last line includes two imports in one line, RequestMapping and RequestMethod for the path and HTTP method being used. In Java you’d need to import these classes separately or have a wildcard import.

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.{RequestMapping, RequestMethod}

Here we declare that this class is a controller and it listens to the path ‘/’. We also declare it to be autowired and define the Name dependency.

@Controller
@RequestMapping(Array("/"))
class HelloWorldController @Autowired() (nameService: Name) {

Now we map our method to the HTTP method that comes through on the path this class is listening too. We receive the model and add our name to it so that it’s used in the view.

  @RequestMapping(method = Array(RequestMethod.GET))
  def index (model: Model) = {
    model.addAttribute("name", nameService.name)
    "index"
  }
}

View

Finally we have a simple JSP which uses the name variable passed into it from the controller which it’s dependency injected into it.

<h1>Hello, ${name}</h1>

Wrap up

If you’ve followed this through, you’ve just got a glimpse of what Scala can provide, and how it can work with your existing tools.

The source code for this example can be found on Github

Comments
  1. Chris Beams says:

    Hi Robbie,

    Glad to see your post. Have you considered doing away with web.xml and replacing it with a WebApplicationInitializer instead?

    http://docs.spring.io/spring/docs/4.0.1.RELEASE/javadoc-api/org/springframework/web/WebApplicationInitializer.html

  2. Robbie Clutton says:

    Hi Chris,

    I’ll take a look, my follow up was going to be using Jetty as an embedded server which looks remarkably similar to the example you’ve posed. Maybe I’ll do away with the web.xml first before moving to an embedded server.

  3. Chris Sekaran says:

    Tried running the example and its very neat and consise but got stuck with an error at the line

    seq(webSettings : _*)

    in the build.sbt file. Any pointers would be much appreciated. Thanks in advance

  4. Robbie Clutton says:

    Hey Chris, what’s the error you’re getting?

  5. Chris Sekaran says:

    Ive changes the plugins.sbt to add 0.7.0 version

    addSbtPlugin(“com.earldouglas” % “xsbt-web-plugin” % “0.7.0”)

    Ive also added the following lib-dependency

    “org.eclipse.jetty” % “jetty-plus” % “9.1.0.v20131115″ % “container, compile”,

    but even in the original form I get a redline underscoring seq(webSettings : _*)

    When I try container:start it fails with
    > container:start
    [error] Not a valid key: start (similar: state, startYear, target)
    [error] container:start
    [error]

    Thanks in advance

  6. Robbie Clutton says:

    That seq(webSettings : _*) is the xsbt-web-plugin, I don’t think it would depend on a different version of Jetty. What version of sbt are you using? I’m on 0.13.1.

    I’ve made some changes to include the same version of SBT that I’m using. If you try that, do you still get the same issue?

  7. Chris Sekaran says:

    Hi Robbie

    Apologies for cluttering up you blog but I wish and hope I didnt.

    I installed the intellij plugin for SBT. Im not sure about the version but the plugin description says 1.5.1

    From the build.sbt file I have the scala version as
    scalaVersion := “2.10.2”

    After doing a ‘reload’ on SBT and doing ‘container:start’ I get the following

    > last container:start
    java.lang.NoClassDefFoundError: Could not initialize class org.eclipse.jetty.http.HttpScheme
    at org.eclipse.jetty.server.HttpConfiguration.(HttpConfiguration.java:52)
    at org.eclipse.jetty.server.HttpConnectionFactory.(HttpConnectionFactory.java:41)
    at org.eclipse.jetty.server.ServerConnector.(ServerConnector.java:99)
    at com.earldouglas.xsbtwebplugin.Jetty9Runner.configureConnector(Jetty9Runner.scala:73)
    at com.earldouglas.xsbtwebplugin.Jetty9Runner.start(Jetty9Runner.scala:100)

    If I change the scala version to
    scalaVersion := “2.10.3”

    and do the same I get

    > container:start
    [info] Compiling 3 Scala sources to /Users/myHome/temp_dwnloads/scala-spring4-master/target/scala-2.10/classes…
    [trace] Stack trace suppressed: run ‘last container:start’ for the full output.
    [error] (container:start) java.lang.NoClassDefFoundError: java/nio/charset/StandardCharsets
    [error] Total time: 27 s, completed Mar 4, 2014 10:18:44 AM

    I am using Intellij IDEA 13.0.2 on a mac osx( mavericks) and openjdk 1.7.0_45

    Again, thanks a lot

  8. Robbie Clutton says:

    Hi Chris, maybe you want to move this Github?

    I added some changes to try and enforce the SBT version which may help. There’s also a plugin in the project to generate Idea files, run ‘gen-idea’ when SBT has loaded.

  9. Chris Sekaran says:

    Hi Robbie

    Thanks for coming back. Would you be kind enough to email me. I have my email entered when I posted these comments.
    Im not sure how I could discuss the issues on Github. Its quite possible its got something to do with the Java/sbt/OS version etc.
    I could raise an issue on the repo if you like. Either way.

    Thanks
    Chris

  10. Robbie Clutton says:

    Hi Chris,

    Yeah, create an issue on Github. I think that’d be best.

    Robbie

  11. Craig says:

    Hi,

    I have added the eclipse sbt configuration to the plugins, and generated and imported the eclipse project.

    How do I debug the string / scala application in eclipse. I am rusty, and can’t seem to get the configuration right.

    Please give me some pointers…
    Thanks.

    • Robbie Clutton says:

      Hi Craig,

      I haven’t used Eclipse in many years so I don’t think I can help you. The code should ‘just work’ if you run from the command line. Try that first and make sure before trying to get the IDE working.

      Cheers

      Robbie

  12. Craig says:

    Hi,

    I have everything working from the command line.
    sbt ~container:start get’s everything up and running, and your sample works just fine. Additionally the eclipse projects are created as expected and are working just great.

    I just don’t know how to setup a run configuration or debug configuration in eclipse, rather than using the SBT command line.

    I will perhaps have to do some more research.
    I was hoping to bypass a lot of reading, but:

    ‘Quick and Easy’, the dark side is!

  13. Robbie Clutton says:

    I’ve used the IntelliJ plugin, which is included in the project, but haven’t tried getting Eclipse to work, sorry.

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *