Spark Java

Todd Sharp


Who Is This Guy?


Why Am I Here?

To Learn About The Spark Java Framework

  • What It Is
  • What It Isn't
  • What It Is Good For
  • What It Isn't Good For
  • How To Use Groovy With It



If you haven't figured it out yet, this presentation is NOT about...


What Is Spark Java?

   Spark Framework is a simple and expressive Java/Kotlin web framework DSL built for rapid development. Sparks intention is to provide an alternative for Kotlin/Java developers that want to develop their web applications as expressive as possible and with minimal boilerplate. With a clear philosophy Spark is designed not only to make you more productive, but also to make your code better under the influence of Spark’s sleek, declarative and expressive syntax.


Without The Marketing Speak...

  • Lightweight Framework
  • Microservice Friendly
  • Easy To Use
  • Embedded Jetty
  • Many Templating Options


Before We Begin...


Why Groovy?

It's Less Verbose Than Java

  • public modifier implied
  • Semi-colons optional
  • Parenthesis optional
  • Brackets optional
  • return keyword optional
  • etc...


A layer on top of the JDK to enhance the language and make certain things easier to do.

  • Files/IO
  • Lists
  • Maps
  • Etc..


// reading a file
new File(baseDir, "eps3.3_metadata.par2").eachLine { line ->
    println line
// writing a file
new File(baseDir, "eps3.8_stage3.torrent").withWriter('utf-8') { writer ->
    writer.writeLine "Are you seeing this?"
// executing external processes
def fiveNine = "ecorp".execute()             
// make an http request

Dynamic Typing Is Concise And Powerful

def groovy = 'language'

// vs

String groovy = 'language'

def sayHello(name) {
    name ? "Hello, ${name}" : 'Hello, Friend'

// vs

public String sayHello() {
    return "Hello, Friend";

public String sayHello(String name) {
    return "Hello, " + name;

Implicit Getters/Setters

class User {
    def firstName
    def lastName

def user = new User()
user.firstName = 'Todd'
user.lastName = 'Sharp'

assert user.firstName + ' ' + user.lastName == 'Todd Sharp'
// true

Null Safe Operator


Spread Operator

class Car {
    String make
    String model

def cars = [
       new Car(make: 'Peugeot', model: '508'),
       new Car(make: 'Renault', model: 'Clio')
def makes = cars*.make                                

assert makes == ['Peugeot', 'Renault'] 

Elvis Operator

displayName = user?.name ? : 'White Rose'   
displayName = user?.name ?: 'White Rose'     

Left Shift Operator

def x = [1,2]

x << 3

assert x == [1,2,3]

GDK Collection Methods

def list = [1,2,3]

list.each { it ->
    println it

assert list.collect { it ->
    it * 2
} == [2,4,6]

assert list.sum { it ->
} == 6

assert list.find { it ->
    it == 1
} == 1

assert list.findAll { it ->
    it < 3
} == [1,2]

Pretty Groovy, Eh?


Back To Spark...

Obligatory Hello World

import static spark.Spark.*;

public class HelloWorld {
    public static void main(String[] args) {
        get("/hello", (req, res) -> "Hello World");



import spark.kotlin.*

fun main(args: Array<String>) {
    val http: Http = ignite()

    http.get("/hello") {
        "Hello Spark Kotlin!"


Let's Make It Groovy!

import static spark.Spark.*;

public class HelloWorld {
    public static void main(String[] args) {
        get("/hello", (req, res) -> "Hello World");


import static spark.Spark.*

class HelloWorld {
    static void main(String[] args) {
        get "/hello", { req, res -> "Hi!" }



Getting Started

Java (Maven)


Kotlin (Maven)



Getting Started

Other Dependency Managers

// Java/Groovy - Gradle: 
compile "com.sparkjava:spark-core:2.7.1" 

// Kotlin - Gradle: 
compile "com.sparkjava:spark-kotlin:1.0.0-alpha" 

    rev="2.7.1" conf="build" />

libraryDependencies += "com.sparkjava" % "spark-core" % "2.7.1" 


Getting Started

build.gradle for Groovy

group 'codes.recursive'
version '1.0-SNAPSHOT'

apply plugin: 'idea'
apply plugin: 'groovy'
apply plugin: 'java'

configurations {
repositories {

dependencies {
    localGroovyConf localGroovy()
    compile 'org.codehaus.groovy:groovy-all:2.3.11'
    compile 'com.sparkjava:spark-core:2.6'

task runServer(dependsOn: 'classes', type: JavaExec) {
    // put Groovy classes in src/main/groovy!
    classpath = sourceSets.main.runtimeClasspath
    main = 'Bootstrap'


Spark Application Structure


Skeleton App


The "Controller"

All Routes are declared in the application's main() method, but of course there are techniques to modularize your code and make it more manageable.



  • Verb
  • Path
    • /user/edit, /home/view
  • Callback
    • Lambda/Closure
      • Get data, Render view, etc

Routes consist of three parts:



Routes can be grouped into paths


path "/blog", {
    path "/post", {
        get "/show", { req, res -> return 'blog post'}
        post "/edit", { req, res -> return 'post saved'}

Route Parameters

Routes can contain named parameters


// matches "GET /hello/foo" and "GET /hello/bar"
// request.params(":name") is 'foo' or 'bar'

get "/hello/:name", { req, res ->
    return "Hello: ${req.params(':name')}"

Wildcard Parameters


// matches "GET /say/hello/to/world"
// request.splat()[0] is 'hello' and request.splat()[1] 'world'

get "/say/*/to/*", { req, res ->
    return "Number of splat parameters: ${request.splat().size()}"

Response Transformers



public class JsonTransformer implements ResponseTransformer {

    private Gson gson = new Gson();

    public String render(Object model) {
        return gson.toJson(model);

get("/hello", "application/json", (request, response) -> {
    return new MyMessage("Hello World");
}, new JsonTransformer());

Response Transformers


Gson gson = new Gson();
get("/hello", (request, response) -> 
    new MyMessage("Hello World"), gson::toJson);

Using Java 8 Method References

Response Transformers


get "/json", {req, res ->
    JsonOutput.toJson([json: true, uncool: false])

Alternative:  Don't use a transformer, just return JSON

*If you really want to, create a ResponseTransformer that uses JsonOutput.toJson()



request.attributes();             // the attributes list
request.attribute("foo");         // value of foo attribute
request.attribute("A", "V");      // sets value of attribute A to V
request.body();                   // request body sent by the client
request.bodyAsBytes();            // request body as bytes
request.contentLength();          // length of request body
request.contentType();            // content type of request.body
request.contextPath();            // the context path, e.g. "/hello"
request.cookies();                // request cookies sent by the client
request.headers();                // the HTTP header list
request.headers("BAR");           // value of BAR header;                   // the host, e.g. ""
request.ip();                     // client IP address
request.params("foo");            // value of foo path parameter

//...and many more

The request parameter contains information and functionality related to the HTTP request



post "/qmap", { req, res ->
    def a = req.queryMap().get("user", "name").value()
    def b = req.queryMap().get("user").get("name").value()
    def c = req.queryMap("user").get("age").integerValue()
    def d = req.queryMap("user").toMap()

                    "req.queryMap().get('user', 'name').value()": a,
                    "req.queryMap().get('user').get('name').value()": b,
                    "req.queryMap('user').get('age').integerValue()": c,
                    "req.queryMap('user').toMap()": d,

Group parameters to a map by their prefix. 

<form action="/qmap" method="POST">
    <input type="text" name="user[name]" />
    <input type="number" name="user[age]" />



response.body();               // get response content
response.body("Hello");        // sets content to Hello
response.header("FOO", "bar"); // sets header FOO with value bar
response.raw();                // raw response handed in by Jetty
response.redirect("/example"); // browser redirect to /example
response.status();             // get the response status
response.status(401);          // set status code to 401
response.type();               // get the content type
response.type("text/xml");     // set content type to text/xml

The response parameter contains information and functionality related to the HTTP response



request.cookies();                         // get map of all request cookies
request.cookie("foo");                     // access request cookie by name
response.cookie("foo", "bar");             // set cookie with a value
response.cookie("foo", "bar", 3600);       // set cookie with a max-age
response.cookie("foo", "bar", 3600, true); // secure cookie
response.removeCookie("foo");              // remove cookie



request.session(true);                     // create and return session
request.session().attribute("user");       // Get session attribute 'user'
request.session().attribute("user","foo"); // Set session attribute 'user'
request.session().removeAttribute("user"); // Remove session attribute 'user'
request.session().attributes();            // Get all session attributes
request.session().id();                    // Get session id
request.session().isNew();                 // Check if session is new
request.session().raw();                   // Return servlet object



halt();                // halt 
halt(401);             // halt with status
halt("Body Message");  // halt with message
halt(401, "Go away!"); // halt with status and messageCopy



response.redirect("/bar", 301);

// redirect a GET to "/fromPath" to "/toPath"
redirect.get("/fromPath", "/toPath");

// redirect a POST to "/fromPath" to "/toPath", with status 303"/fromPath", "/toPath", Redirect.Status.SEE_OTHER);

// redirect any request to "/fromPath" to "/toPath" with status 301
redirect.any("/fromPath", "/toPath", Redirect.Status.MOVED_PERMANENTLY);



  • before
  • after
  • afterAfter
  • supports pattern matching on path
before "/*", { req, res ->
    def authenticated = false
    if( req.cookie('isSuperCool') == true ) {
        authenticated = true
    if ( !authenticated ) {
        println('You are not welcome here!')
        // res.redirect('/login')
        // halt(401, "Unauthorized")

Static File Filters:

Error Handling


// Using string/html
notFound("<html><body><h1>Custom 404 handling</h1></body></html>")

// Using Route
notFound { req, res -> 
    return "{\"message\":\"Custom 404\"}";

// 500 error handling
internalServerError("<html><body><h1>Custom 500 handling</h1></body></html>")

// Using Route
internalServerError {req, res) -> 
    return "{\"message\":\"Custom 500 handling\"}";

// Exception Mapping
exception(YourCustomException.class, (exception, request, response) -> {
    // Handle the exception here

Static Files



// You can also assign an external folder 
// (a folder not in the classpath) to serve 
// static files


staticFiles.expireTime(600); // ten minutes

staticFiles.header("Key-1", "Value-1");
staticFiles.header("Key-2", "Value-2");
staticFiles.header("Key-3", "Value-3");

You can assign a folder in the classpath serving static files with the staticFiles.location() method. Note that the public directory name is not included in the URL.

Views And Templates

  • Velocity
  • Freemarker
  • Mustache
  • Handlebars
  • Jade
  • Thymeleaf
  • Pebble
  • Water
  • jTwig
  • Jinjava
  • Jetbrick

Spark has community-provided wrappers for a lot of popular template engines:



Views And Templates

  • Angular
  • Vue
  • React
  • Etc...

But obviously a microservice framework is probably better suited to be an API gateway for:


Views And Templates

Create engine, call render():

static void main(String[] args) {

    ThymeleafTemplateEngine engine = new ThymeleafTemplateEngine()
    engine.templateEngine.addDialect(new LayoutDialect())

    get "/thymeleaf", { req, res ->
        def model = [users: personService.list()]
        return engine.render(new ModelAndView(model, "thymeleaf"))


Embedded Web Server

Standalone Spark runs on an embedded Jetty web server.


secure(keystoreFilePath, keystorePassword, truststoreFilePath, truststorePassword);

// thread pool
int maxThreads = 8;
int minThreads = 2;
int timeOutMillis = 30000;
threadPool(maxThreads, minThreads, timeOutMillis);


Spark With Embedded Jetty Is Lightweight Enough To Run on a Raspberry Pi

Embedded Web Server

Jetty Supports WebSockets!!

// Bootstrap.groovy:

static List<Session> users = [];

webSocket("/socket", WebSocketHandler.class)

static void broadcastMessage(String sender, Object message) {
    users.findAll{ Session it -> it.isOpen() }.each { Session it ->
        try {
            def msg = JsonOutput.toJson([message: message])
        } catch (Exception e) {


Embedded Web Server

Jetty Supports WebSockets!!

import org.eclipse.jetty.websocket.api.*
import org.eclipse.jetty.websocket.api.annotations.*

class WebSocketHandler {
    void onConnect(Session user) throws Exception {
        Bootstrap.users << user
                        message: "connection established from ${user.remoteAddress}"
    void onClose(Session user, int statusCode, String reason) {
                        message: "connection disconnected from ${user.remoteAddress}"
    void onMessage(Session user, String message) {
        Bootstrap.broadcastMessage(user, [message: message])



Handling File Uploads


Data Access With Morphia And MongoDB


Using Thymeleaf Views


Documenting Your API With Swagger


Inspired By:

// new dependencies

dependencies {
    localGroovyConf localGroovy()
    compile 'org.codehaus.groovy:groovy-all:2.5.0-beta-1'
    compile 'com.sparkjava:spark-core:2.7.0'
    compile 'org.slf4j:slf4j-simple:1.7.21'
    compile 'org.reflections:reflections:0.9.10'
    compile 'io.swagger:swagger-core:1.5.8'
    compile 'io.swagger:swagger-jaxrs:1.5.8'
    compile ''

Swagger Parser


  • Uses Reflection
  • Scans Package
  • Reads Annotations
  • Generates JSON

Not 'out-of-the-box'


Swagger annotations are applied per class/method so each route must correspond to a class and method

Not As Bad As It Sounds


static void main(String[] args) {
    path "/user", {
        get "/hello", { req, res -> 
            return User.hello(req, res)
        get "/goodbye", { req, res -> 
            return User.goodbye(req, res)
    get "/swagger", { req, res -> 
        return SwaggerParser.getSwaggerJson('codes.recursive') 

Not As Bad As It Sounds


@Api(value = "/user", tags = ["User"], description = "User API")
class User {

        value = 'Says hello to you', 
            name = 'name', 
            paramType = 'query', 
            required = true, 
            dataType = 'string' )
    static def hello(@ApiParam(hidden=true) Request req, @ApiParam(hidden=true) Response res){
        return JsonOutput.toJson([message: "Hello, ${req.queryParams('name')}"])

    @ApiOperation(value = 'Says goodbye to you', nickname='goodbye', httpMethod='GET', produces='application/json')
        @ApiImplicitParam( name = 'name', paramType = 'query', required = true, dataType = 'string' )
    static def goodbye(@ApiParam(hidden=true) Request req, @ApiParam(hidden=true) Response res){
        return JsonOutput.toJson([message: "Goodbye, ${req.queryParams('name')}"])


Now What???

When Should I Use It?

  • Microservices 
    • The Java Version Of Node/Express
  • Monoliths With Very Light View Requirements
  • Embedded Devices?
  • Need For Simple Websocket Support

When Shouldn't I Use It?

  • Applications With Heavy Data Access Requirements
    • Use Grails with GORM
  • Monoliths With Extensive Views
    • Use Grails with GSPs


Thank You!