Spark (for Java)

I almost missed this goodie on Technology Radar -- not only is it shadowed by the popular Apache Spark name, it's reference was hidden in a Spring Boot summary... not my favorite family of XML-bloated tools.  Spark is a lightweight web framework for Java 8.  It has modest run-time dependencies -- Jetty and slf4j and four-line hello-world  example -- including imports, but not close curly braces.

Let's go through a somewhat more complex conversation with Spark than "Hello, World" and set up a simple key-value store.

Project Setup

Create a Maven project.  Spark has instructions for Intellij and Eclipse.  You don't need an archetype; just make sure to select Java SDK 1.8.

Salutations, Terrene

We'll implement a simple REST dictionary so that we can show off our vocabulary, or our thesaurus skills, and because we're snooty, we'll "protect" our dictionary with a password.

package org.bredin;

import spark.*
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*

public class Main {
    private static Map<String,String> keyStore = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

    public static void main(String[] args) {
        Spark.before((request, response) -> {
                    if (!"blam".equalsIgnoreCase(request.queryParams("secret"))) {
                        Spark.halt(401, "invalid secret");
                    }
                });
        Spark.get("/get/:key", (request, response) -> readEntry(request, response));
        Spark.get("/put/:key/:value", (request, response) -> writeEntry(request, response));
    }

    public static Object readEntry(Request request, Response response) {
        String key = request.params(":key");
        String value = keyStore.get(key);
        if (value == null) {
            response.status(404);
            return "unknown key " + key;
        } else {
            return value;
        }
    }

    public static Object writeEntry(Request request, Response response) throws UnsupportedEncodingException {
        String key = request.params(":key");
        String value = URLDecoder.decode(request.params(":value"), "UTF-8");
        String oldValue = keyStore.put(key, value);
        if (oldValue == null) return "";
        else return oldValue;
    }
}

OK, it's not as terse as Ruby or Node.js, but it's readable (similar to Express), statically-typed, and integrates with the rest of your JVM.  The real beauty of Spark is in the route definitions and filters -- try approaching that level of conciseness with Spring... or even Jetty and annotations.

Spark provides before() and after() filters, presumably for authentication, logging, forwarding....  executed in the order applied in your code.  Above, there's only an unsophisticated password check.  I've not dug in to discover whether or not Spark exposes enough bells and whistles for Kerberos.

The Spark.get() methods provide conduits for REST into your application.  Spark checks to see that the request parameters are present, returning 404 otherwise, and dispatches your registered handlers.

You can run and test drive the example

$ curl 'localhost:4567/put/foo/What%20precedes%20bar?secret=BLAM'

$ curl localhost:4567/get/foo?secret=BLAM
What precedes bar

Neat!  I've always been uneasy that Jetty's annotations aren't thoroughly checked by the compiler.  DropWizard has loads of dependencies with versioning issues that have tripped me up.