April 18, 2012

Scala + Spring MVC = True?

This is the first part in a series of articles about mixing Scala and Spring MVC. In this post we will start by setting up a Maven project that can handle a Scala-Java mixed project and then we will take a look at a simple example of a web service using both Scala and Spring MVC.


Scala, is a language that has a lot of benefits and some nice characteristics and there are more and more companies that are starting to use it in their production code. Unless you have the privilege to build a brand new system all in Scala, and that new system have no need to integrate with any other code base, you will sooner or later find yourself facing issues around mixing Java and Scala code together if your current systems are written in Java. Spring MVC is a well known framework and is extensively used in a wide variety of applications and chances are you already have a well functioning Spring MVC application. Maybe you also have a lot of infrastructure build around it and integrated to it, like access control and logging for example. And since that infrastructure is written in Java you need to be able to take advantage of those available systems to make the most out of your Scala API.

Ok, so all you need to do is hook up your new shiny Scala API that you have developed. But how do you do it? And does it work? Those are some of the questions that I asked myself and I will try to share some of my findings on the subject here.

This post comes with some example code that will get you up and running with a mixed Scala and Spring MVC project and let you try out the things discussed here. You can find the example code on GitHub here.

Setting up a mixed Scala-Java Maven project

Since we will be working with a project containing both Java and Scala sources I think it might be good to quickly go through how to setup a Maven project that can handle the two languages at the same time. The project will be set up just as any standard Maven project but we need to add some extra configuration for the Scala compiler. And of course we also need to add a dependency to the Scala libraries. The configuration for the maven-scala-plugin that takes care of compiling the Scala source looks like this:
<plugin>
    <groupId>org.scala-tools</groupId>
    <artifactId>maven-scala-plugin</artifactId>
    <version>2.15.2</version>
    <executions>
        <execution>
            <id>scala-compile-first</id>
            <phase>process-resources</phase>
            <goals>
                <goal>add-source</goal>
                <goal>compile</goal>
            </goals>
        </execution>
        <execution>
            <id>scala-test-compile</id>
            <phase>process-test-resources</phase>
            <goals>
                <goal>testCompile</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <scalaVersion>${scala.version}</scalaVersion>
    </configuration>
</plugin>
The newer version of the plugin is now called scala-maven-plugin but at the time of writing, IntelliJ 11.1 had problems detecting the Scala source folders with it so I decided to use the older version for this example.

The default folder structure for the Scala source is src/main/scala and src/test/scala. If you download the example code you will see some additional configuration that enables us to run tests and integrations tests easily.

If you are new to Maven, some basic commands that will get you started with playing around with the example code for this post are:

:> mvn clean test

to run the unit tests, and:

:> mvn clean verify

to run the integration tests. All Maven commands should be executed in the project's root folder where the pom.xml file is located. To run the integration tests we are using the Maven Failsafe Plugin. The way this have been set this up is that when executing verify we start up an in-memory Jetty instance, deploys the web application, runs the integration tests, and then shuts down the Jetty instance. Take a look at the example code if you want to see exactly how this is done.
The Failsafe plugin's default behavior is that any test classes whose names starts or ends with "IT", or ends with "ITCase" is considered an integration test. This means that those tests are excluded when calling mvn test and included when calling mvn verify so you can use that naming convention to differentiate between unit tests and integration tests.

The Java tests are written in JUnit and the Scala tests are using Specs2.

The basics

Lets start out with the most basic example. We want to be able to make a request to our web service and get a simple response back. In other words, if we do a GET http://localhost:9090/scala-spring-mvc/java/ping, we want the server to respond with the text "pong". A Spring MVC controller in Java for achieving this would look something like this:

@RequestMapping("/java")
@Controller
public class JavaController {

    @RequestMapping("/ping")
    @ResponseBody
    public String ping() {
        return "pong";
    }
}

This will also work perfectly good for a controller written in Scala. The Scala version of the same controller would look like this:

@RequestMapping(Array("/scala"))
@Controller
class ScalaController {

    @RequestMapping(Array("/ping"))
    @ResponseBody
    def ping(): String = {
      "pong";
    }
}
The only difference between the Scala and Java implementations is that the Scala controller will respond to the URL http://localhost:9090/scala-spring-mvc/scala/ping instead.
So far so good. In the simplest of examples there is no difference in how we create our Spring MVC controllers whether we write them in Scala or in Java.

Next

Now that we have gotten the Hello World example out of our way we are ready to take a look at something more realistic and usable. In the next part of this series we will turn our application into a web service that returns JSON data using the Jackson JSON Processor.

Feel free to download the example code to play around with mixing Scala and Java code together while waiting for the next article.

Continue with Scala + Spring MVC = True? (Part 2)

2 comments: