Moje zdjęcie
Software Craftsman's Blog by Marcin Pieciukiewicz
Java and Scala development

Wednesday, August 21, 2013

Scalatra - How to setup with Maven and Jetty

Recently I was inspired by a colleague to dig into AngularJS and Scalatra web application stack, so today I'll continue with first steps in digging into Scalatra.

Description from www.scalatra.org:
Scalatra is a simple, accessible and free web micro-framework.
It combines the power of the JVM with the beauty and brevity of Scala, helping you quickly build high-performance web sites and APIs.

And when you look at examples you can get feeling that this might be a great framework to help you develop your applications quickly:
package com.example.app
import org.scalatra._

class HelloWorldApp extends ScalatraFilter {
  get("/") {
    <h1>Hello, {params("name")}</h1>
  }
}

So let's start playing with it by creating a sample project that will use Maven as the build tool and a Jetty server as our application's container.

1. Define build process and application dependencies
To start we need to create main pom.xml file required by maven. It will be placed in the main directory we've created for our project.
First we need to configure it to properly compile Scala language sources. I have described this process in article Maven configuration example for Scala. So you should follow instructions from that article.

When this is done we'll add support for Scalatra. To do that we'll need to include org.scalatra:scalatra dependency in pom.xml. Also let's include library org.scalatra:scalatra-specs2 that will add support for writing tests in the future. So add this part of code inside <dependencies> tag (To be clear, I've used a Scalatra dependencies prepared for Scala 2.10, this is the reason for weird suffixes in artrifactId):
<!-- Scalatra -->
<dependency>
    <groupId>org.scalatra</groupId>
    <artifactId>scalatra_2.10</artifactId>
    <version>2.2.0</version>
</dependency>
<dependency>
    <groupId>org.scalatra</groupId>
    <artifactId>scalatra-specs2_2.10</artifactId>
    <version>2.2.0</version>
    <scope>test</scope>
</dependency>

Also Scalatra requires that Java Servlet API 2.5 be available during application compilation, so we'll add it in provided scope:

<!-- Servlet API -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    <scope>provided</scope>
</dependency>

That will be all to support Scalatra in our application, now let's add Jetty so we'll be able to run our application without any external application server. This only requires to add a plugin inside a <build> -> <plugins> tag:
<plugin>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <version>9.0.2.v20130417</version>
</plugin>
Now the configuration of our build process is complete. To summarize this is my complete pom.xml for this example:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>pl.marpiec</groupId>
    <artifactId>scalatratest</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>war</packaging>

    <properties>
        <scala.lang.version>2.10.1</scala.lang.version>
        <scala.maven.version>2.10.1</scala.maven.version>
        <scalatra.version>2.2.0</scalatra.version>
        <jetty.version>9.0.2.v20130417</jetty.version>
    </properties>

    <repositories>
        <repository>
            <id>scala-tools.org</id>
            <name>Scala-tools Maven2 Repository</name>
            <url>https://oss.sonatype.org/content/groups/scala-tools/</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>scala-tools.org</id>
            <name>Scala-tools Maven2 Repository</name>
            <url>https://oss.sonatype.org/content/groups/scala-tools/</url>
        </pluginRepository>
    </pluginRepositories>


    <dependencies>
        <!-- Scala -->
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>${scala.lang.version}</version>
        </dependency>

        <!-- Scalatra -->
        <dependency>
            <groupId>org.scalatra</groupId>
            <artifactId>scalatra_2.10</artifactId>
            <version>${scalatra.version}</version>
        </dependency>
        <dependency>
            <groupId>org.scalatra</groupId>
            <artifactId>scalatra-specs2_2.10</artifactId>
            <version>${scalatra.version}</version>
            <scope>test</scope>
        </dependency>

        <!-- Servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.scala-tools</groupId>
                <artifactId>maven-scala-plugin</artifactId>
                <version>${scala.maven.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>${jetty.version}</version>
            </plugin>
        </plugins>
    </build>
</project>

2. Configure web application by web.xml file
To be able to run our web application we need to create a standard Java web application description file, web.xml. Let's create this file inside [Project Dir]\src\main\webapp\WEB-INF\ directory. It's content is very simple:
<?xml version="1.0" encoding="UTF-8"?>
<web-app 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"
         version="3.0">

    <context-param>
        <param-name>org.scalatra.LifeCycle</param-name>
        <param-value>pl.marpiec.scalatratest.servlets.ScalatraBootstrap</param-value>
    </context-param>
    <listener>
        <listener-class>org.scalatra.servlet.ScalatraListener</listener-class>
    </listener>
</web-app>
This will tell an application's server (Jetty in our case) that it have to call a ScalatraListener when application starts. Also context parameter org.scalatra.LifeCycle points to the ScalatraBootstrap class that will be used by our application. This parameter is not required, but without it you will have to put ScalaBootstrap class in root package, and that would be ugly. Also, you probably want to change the package name that is used to be align with your project ;).

3. Create your first Scalatra servlet
So now let's create our first servlet that will send simple responses for GET requests. This servlet might look like this:
package pl.marpiec.scalatratest.servlets

import org.scalatra.ScalatraServlet

/**
 * @author Marcin Pieciukiewicz
 */
class MainServlet extends ScalatraServlet {
  get("/") {
    <html>
      Hello to scalatra
    </html>
  }

  get("/json-page") {
    "<json-response>Hello to scalatra</json-resonse>"
  }
}
As you can see this servlet will send an html response for the request of root application address, and it will send string containing json if /json-page is requested.

4. Create ScalatraBootstrap class
The last thing to do is to create a class mentioned earlier: ScalatraBootstrap, and then bind our servlet with proper URL path:

package pl.marpiec.scalatratest.servlets

import org.scalatra._
import javax.servlet.ServletContext

class ScalatraBootstrap extends LifeCycle {
  override def init(context: ServletContext) {
    context.mount(new MainServlet, "/*")
  }
}

This class is basicly starting point for scalatra configuration. In our case we've just added our server to be mapped for all url suffixes.

And now our sample application is complete.

5. Test application
To test this application we have to run it on servlet container, and as it was mentioned earlier we'll use Jetty runned as Maven plugin. So to start our server with our application just run:
mvn jetty:run
After a while a Jetty server will be running and listening on port 8080 on localhost. So just check the responses under http://localhost:8080:


And http://localhost:8080/json-page:







No comments:

Post a Comment