In the world of Java web development, such a tool would be described as lightweight. Java micro frameworks 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, micro frameworks 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 micro frameworks, Spark, Ninja, and Play. Comparing the frameworks will give you a high-level view of what makes micro frameworks 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.
Ninja installs quickly and painlessly. Just download Ninja from Github and run the provided Maven archetype by typing:
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.
Next, select Existing Projects into Workspace, as shown in Figure 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.
Your project should now appear in the main pane, already selected with a checkbox, as shown in Figure 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 micro framework. Ninja's curated set of open-source libraries places it in the category of a full-stack micro framework.
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.
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:
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.
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.
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.
Open up index.ftl.html, which is the page that renders our localhost:8080 request. Consider the first couple of lines in Listing 2:
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:
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.
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:
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.
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."
Let's unpack this line:
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.
To get our new index page working, just make it looks like this:
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.
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.
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.
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.
cd into our freshly minted project dir and add Listing 7 to your pom.xml using a text editor.
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.
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.
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.
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:
This method uses the Route object to define the output of the route. Listing 10 shows the Route interface's one and only method (it's important that it be the one and only).
Now look back at the arrow (->) lamdba notation in Listing 8. You can see now that what it does it to provide an interface implementation in a single line of code. Nice! This is radical stuff for Java (unless you count anonymous interfaces), but is commonplace in JavaScript and other languages.
So item 3 is saying: You want an implementation of the Route.handle method Well here's an implementation that takes two arguments, and returns a String: "Hello, JavaWorld."
If you wanted to make use of the arguments within the lambda body, you could do as I've done in Listing 11:
And then restart the server by stopping the Spark main process in Eclipse (see Figure 6), and refreshing the browser with a query param: http://localhost:4567/jwsongwriter=paul_simon. You should see your songwriting choice reflected in the browser output.
In this short example we have accessed the req argument from inside the lambda expression, and you've also had your first look at the Spark request model. Next let's take a look at how Spark handles views.
Of course, we don't want to generate our responses inline in code (if we did, we'd still be writing CGI). Spark supports a wide range of view technologies via plugins. Thymeleaf is a templating system that is often underrated, so let's see how it handles Spark's views.
Start by adding the spark-thymeleaf bridge to your POM, as shown in Listing 12.
Next, create a new mapping in App.java:
This mapping basically says: when a GET request hits /thyme, handle it with the defined objects. In this case, the second argument is a ModelAndView, and we are passing it a map and a name, in this case "template". The third argument is the TemplateEngine implementation that we pulled in with Maven, ThymeleafTemplateEngine.
Spark will take the Map data, the template, and the template engine, and combine them into a rendered view.
The default is for the TemplateResolver to find your templates on the classpath under /templates, with an .html extension. For expedience, we'll just add a /templates directory under src/main/resources, then add that to the source classpath in Eclipse. The steps for that are: project-->properties-->Java Build Path-->Source-->Add Folder, as in Figure 8.
As our final step, stop App.java and run it again. View the http://localhost:4567/thyme location and you'll see one of the greats of Rock and Roll's second generation displayed.
You might observe that it took a little more doing to engage the templating library in Spark than in Ninja. That is because Spark includes fewer libraries out of the box, and allows a broader choice for templating solutions. Spark is extremely lean, and is ideal for creating small RESTful APIs or small web apps with a variety of front-end technology choices.
We've sailed through short demos for Ninja and Spark, and now I personally am excited to take a look at Play, the venerable grandfather of the group. Play is known as a Scala framework, but it's also a high quality Java micro framework, based on the same RVC model as the others profiled here.
Installing Play is different from the others, because you need to first download and install Typesafe Activator (there are other ways, but this is the easiest). Unlike the other two micro frameworks profiled here, Play offers a complete replacement to Java tooling like Maven, so you'll find you have to learn more conventions to use it effectively.
For the download, the minimal version should be fine. Note that it will resolve dependencies for you. To install it, unzip Activator into a directory and put it on your system path.
Next, create a new project, by typing the following at the command-line: activator new micro-play play-java. This will build a simple Java project we can use to start from, called "micro-play".
Now cd into your project (/micro-play) and run the interactive console by typing activator. We can run it with run. This starts our app in dev mode, with auto update enabled. You can now see it running at: http://localhost:9000/. (Sometimes it takes a minute or two to be ready, but after that changes are loaded on the fly.)
You can edit your project files with Notepad++ or VI, but I usually like to use an IDE like Eclipse. It used to be possible to run activator eclipse out of the box, but now you have to update the build config – or you can just run the UI and do it there. We'll take that approach.
First, Ctrl-d to stop the running server, and then type exit to drop back to the command-line. Type: activator ui. Now open: http://127.0.0.1:8888/app/micro-play-1/ (it should open automatically for you), and you will be looking at your projects configuration page in the UI. Click Code View & open in IDE in the main content window, as seen in Figure 7. Hit Create Eclipse Project as seen in Figure 8, then return to the command-line, and kill off the UI process (ctrl-c). You can now import the project into Eclipse. If you would like to get the dependency sources, run activator eclipse with-source=true from your project directory.
You'll notice that Play has dropped in a lot of packages; in this regard it's more akin to Ninja than Spark.
Now type activator run, and you're ready to start coding.
After unpacking Ninja and Spark, you'll know that Play must include a centralized routing file. Look at conf/routes. Here again we have the method, path, handler pattern – this time in a plain text format, as seen in Listing 13.
At a glance, we can see that our GET root URL calls are going to be handled by package controllers, class Application, and method index(). Notice that the index() method for Application looks like Listing 14.
So the index() method returns a Result object, and the method uses the ok helper class to return a successful HTTP result, where the content is specified by the argument. As an alternative example, if we wanted to send a 401 unauthorized HTTP status, we could use unauthorized("no") - you can see this in the sample code for this article, mapped to the /no URL path.
The argument passed to the ok method is the body of the response, and it makes use of the views.html.index.render() call. That call resolves a template, similar to how Ninja and Spark work. Play has the ability to swap in various templating technologies; but for now, let's take a quick look at the default Scala template.
Listing 14 shows the views/index.scala.html file:
It's important to note that the Scala template actually compiles into a Scala class. That is why we executed the template as a method call earlier.
Here's some detail about the Scala template syntax in Listing 14:
A look at the main.scala.html template will show a very simple HTML layout that displays the first message parameter and the second template parameter (String and HTML types, respectively).
To wrap up our first look at Play, let's expose a model in the view. We'll create a new class called models.Group, which we'll use to model a musical group. Put the new class in /app/models. Give it a private String member name, with getters and setters and a constructor that takes the member, as seen in Listing 15. We can make this class a persistent class later on, but for now it's just a POJO.
Now jump back into Application.java and update the index() method as shown in Listing 16.
Listing 16 gives us the seven degrees of separation between Bob Marley and The Eagles (meaning that each band is related to the next somehow), in the form of a List, which is passed into the view-render method as a second argument. Next, we need to update the index view (index.scala.html) to accept another argument and do something with it. Listing 17 shows that.
In Listing 17, we've added an argument called groups and given it a List<Group> type -- notice the slight variation of using square brackets instead of angle brackets for the generic definition. Then we used a @for loop to just iterate over the collection and display the names.
In this overview we've unpacked three Java micro frameworks at a brisk pace. In my opinion, Spark is the quickest and easiest to get into, followed by Ninja, and finally Play. That being said, moving into non-trivial features and enterprise quality of service will reveal a lot more of the thinking behind each framework and what each has to offer. Different levels and types of REST/JSON support, enterprise middleware integration, persistence, testing support, and production deployment are all key differentiators between these frameworks.
Next up, keep an eye out for three deep dives, where we'll explore the mechanics of each framework in a real-world enterprise application development scenario.
Until then, happy coding.