Jump into Java microframeworks, Part 1: Introduction

Go extra lightweight, with Java microframeworks Spark, Ninja, and Play

For true programmers, there is an inherent joy in programming. Our goal is to maximize the time that we spend in the zone, and our tools should help us do just that. No matter what the craft, the ideal tool is an extension of your mind and limbs: without drawing attention to itself, it humbly enables you to do your work better.

In the world of Java web development, such a tool would be described as lightweight. Java microframeworks have recently emerged as the newest and leanest of frameworks yet. Rather than consolidate a massive feature set, as did Java EE, or even a lighter MVC framework like Spring, microframeworks put the programmer at the center of a coherent set of increasingly powerful, lightweight tools.

This article is the first in a four-part series. We'll start with an overview of three popular Java microframeworks, Spark, Ninja, and Play. Comparing the frameworks will give you a high-level view of what makes microframeworks tick and where they differentiate. You'll also get started with quick setup instructions and a sample application for each framework. In the next three articles we'll dive into the frameworks individually, building a more complex application that highlights the strongest features of each one.

Now let's take our first look at the Ninja framework.

download
Source code for the Ninja demo app. Created by Matthew Tyson for JavaWorld.

Ninja: Full stack, high scalability

Ninja installs quickly and painlessly. Just download Ninja from Github and run the provided Maven archetype by typing:

mvn archetype:generate -DarchetypeGroupId=org.ninjaframework -DarchetypeArtifactId=ninja-servlet-archetype-simple

You will be prompted for your group and artifact IDs. Once the the download is complete, you can generate a project for your IDE with a simple Maven command, for example: mvn eclipse:eclipse. Import your new project, and you're ready to code!

To import the project in Eclipse, go to File, and select Import, as shown in Figure 1.

Java Micro Frameworks, Part 1.

Figure 1. Eclipse project import, Step 1

Next, select Existing Projects into Workspace, as shown in Figure 2:

Java Micro Frameworks, Part 1.

Figure 2. Eclipse project import, Step 2

Select Browse and choose the directory where your project is located. Recall that Maven created a directory based on your project name, which in my case is /micro-ninja. Select OK.

Java Micro Frameworks, Part 1.

Figure 3. Eclipse project import, Step 3

Your project should now appear in the main pane, already selected with a checkbox, as shown in Figure 4.

Java Micro Frameworks, Part 1.

Figure 4. Eclipse project import, Step 4

When you hit Finish, the fully formed Ninja project should import into your workspace. We'll follow these same steps to import the projects for Spark and Play later on.

When you crack open the Ninja project's navigation pane, you might wonder if you're really looking at a microframework. Ninja's curated set of open-source libraries places it in the category of a full-stack microframework.

A package of interest is Guice. Guice is an IOC (Inversion of Control) library for connecting application dependencies in a de-coupled style. If you're familiar with Spring, Guice performs the same role as Spring's core IOC library.

Dev mode

Drop back to the command-line for a moment, and run: mvn ninja:run. Now open your browser to localhost:8080. A simple app is already running; and moreover, it is hot-deploying your code.

You can set the port for dev mode with the command:

mvn ninja:run -Dninja.port=9090

In the Project menu, make sure that Eclipse is set to "Build Project Automatically," as shown in Figure 5. When Eclipse compiles your classes, Ninja will watch for them and make the changes live. Ninja also watches the static resources directory.

Java Micro Frameworks, Part 1.

Figure 5. Eclipse project import, Step 5

To be sure it's working, let's run a quick test. Open the file /src/main/java/conf/messages.properties, and make the change shown in Listing 1.

Listing 1. messages.properties



...

# This file is utf-8

header.title=Hello, JavaWorld!

header.home=BAM!

hello.world=Hello, JavaWorld!

...



Reload the page and you'll see the new message: "Hello, JavaWorld!" Ninja's built-in internationalization support and template system inserted it automatically. We'll take a closer look at Ninja's template library in the next section.

Ninja views with FreeMarker

Open up index.ftl.html, which is the page that renders our localhost:8080 request. Consider the first couple of lines in Listing 2:

Listing 2. index.ftl.html




<#import "../layout/defaultLayout.ftl.html" as layout> 

<@layout.myLayout "Home page">



FreeMarker is importing a template, and then invoking it. In the template's header.ftl.html you'll see a simple HTML template. Now look at this line:


<a class="navbar-brand" href="/">${i18n("header.title")}</a>

This is a template token, which will eventually be replaced by a model value. You'll remember that when we changed the value in messages.properties, the value was reflected in the generated index.html. Ninja's internationalization feature, denoted as i18n above, automatically inserts the value from the message file.

Model values and controllers

Now let's try a more interesting way to get values into the template pages, by setting them in the model.

First, we'll add the following line to our index.ftl.html page:


Favorite Beatle: ${simplePojo.favBeatle}

Next, we need to set the simplePojo.favBeatle value. We do that in Ninja's routes file, Routes.java, which defines all the handlers for each URL. Note that we're able to define our routes in pure Java, with no XML or JSON config at all.

Routes.java

In Routes.java, you'll see the root URL of the application is configured with a controller. Listing 3 has the relevant line. Note that in Eclipse, you can quickly find this file with ctrl-shift-t, and the start type "Routes."

Listing 3. A Ninja route




router.GET().route("/").with(ApplicationController.class, "index");



Let's unpack this line:

  1. Router.GET means that we are talking about GET requests (i.e., the HTTP GET method).
  2. .route("/") denotes the root URL.
  3. .with(ApplicationController.class, "index") simply says (via a bit of behind-the-scenes reflection): when the URL is matched, send the request to the class (first parameter) using the method (second method).

Next we need to take a look at the ApplicationController.index() method, because that is what will be called to set up the model for the view. Remember, we are making sure that simplePojo.favBeatle will resolve to our preferred Englishman.

Listing 4 shows how this is done.

Listing 4. ApplicationController.index() updated



public Result index() {

        return Results.html();

    }

// …

public static class SimplePojo {

        public String content;

    }



To get our new index page working, just make it looks like this:

Listing 5. A Ninja route




public Result index() {

    	SimplePojo simplePojo = new SimplePojo();

    	simplePojo.favBeatle = "George";

        return Results.html().render(simplePojo);

    }

// ...

public static class SimplePojo {

        public String content;

        public String favBeatle;

    }



In Listing 5 we've instantiated a SimplePojo class, setting its favBeatle member and then rendering a response with the simplePojo instance, which is exposed to the view template. (Note that favBeatle is a public member. I'm sure some purists would frown on that design decision, but it does help us avoid getter/setter resolution kruft.)

This is fairly similar to old fashioned JavaServer Pages and Expression Language template variable resolution. simplePojo is just a value object used to ferry objects to the view. Since we are in dev mode, we can just reload the browser page and view our musical choice.

Notes about Ninja

You've had a quick look at Ninja, and we'll go much more in-depth with this framework in the next article in this series. For now, just note that Ninja is a stateless architecture. All session information is intended to be stored client-side, and the framework has support for that. This in principle means that scaling is as simple as adding more Ninja nodes to your cluster, without concern for sticky sessions.

For this quick demo, we kind of backed into Ninja's request processing architecture, which consists of routes, views, and controllers. We started at the view and moved into routing and controllers. This pattern is repeated in the other frameworks. As I mentioned earlier, I call these types of frameworks RVC frameworks -- the routes, views, and controllers architecture being their core commonality.

Spark 2: Extremely lightweight RVC for Java 8 and up

Now let's turn our attention to Spark. Not to be confused with Apache's big data processing engine, this Spark is the most lightweight of our three RVC frameworks. It draws inspiration from the Ruby framework, Sinatra.

download
Source code for the Spark demo. Created by Matthew Tyson for JavaWorld.

We'll use Spark 2, the latest version as of this writing, for our demo. Spark 2 doesn't support any Java versions below Java 8, so be sure to install the most recent Java update if you want to follow along. It might seem extreme to drop support for Java 7 but it does keep the project very tight. Moreover, Java 8 is a real winner, and it's great to see how Spark integrates some of its standout features. We'll explore Spark's support for lambda expressions when the time comes.

For now, just as we did with Ninja, start by creating a new Maven project, as shown in Listing 6. In this case, there is no Spark specific archetype, so we'll just use a generic Maven archetype.

Listing 6. Creating a new Spark project



mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

cd into our freshly minted project dir and add Listing 7 to your pom.xml using a text editor.

Listing 7. Adding Spark core to the Maven POM



<dependency>

    <groupId>com.sparkjava</groupId>

    <artifactId>spark-core</artifactId>

    <version>2.3</version>

</dependency>

As we did for Ninja, create an Eclipse project (mvn eclipse:eclipse) and import it as described before. After importing the project to Eclipse, you'll notice a significantly smaller number of libraries than you saw for Ninja. This project holds just the core Spark package, some Jetty libraries for the server, two logging jars, and some websocket support libs. Spark is really compact -- and just wait till you see how fast you can start handling requests.

Next, in Eclipse, go to the App.java file that Maven added to our project. Notice that this is actually a Java class with a public static voice main() method -- a standalone executable class. We'll commandeer that for our main class. We can add routes right here in the main class. Update the class as shown in Listing 8.

Listing 8. Adding a Spark route



package org.mtyson;

import spark.Spark;

public class App {

    public static void main( String[] args ){

    	Spark.get("/jw", (req, res) -> "Hello JavaWorld");

    }

}

Now run the app -- in eclipse, you can right-click and select run as, or you can use the alt-shift-x and then the the j key -- and yes, it's really that simple. The above get() method call invokes an embedded Jetty server to host the app. Spark really is micro.

java microframeworksp1 fig6

Figure 6. Executing the Spark main class in Eclipse

Spark request mapping and Java 8 lambdas

Let's take a minute to dissect Spark's mapping from Listing 8. It isn't unlike Ninja's Routes.java file, but it has its own character.

  1. Spark.get() defines the HTTP method.
  2. "/jw" defines the URL path.
  3. You provide the handler.

Items 1 and 2 are probably obvious, but item 3 might be new to you. Since Java 8 is still fairly fresh, and functional programming is one of its coolest new language features, I'll take a moment to introduce this form of the Java lambda expression.

First, take a look at the message signature for the Spark.get() called in Listing 8:

Listing 9. Spark.get() method signature




public static void get(final String path, final Route route)



Related:
1 2 Page 1
Shop Tech Products at Amazon