Introduction to Java microframeworks

Jump into Java microframeworks, Part 2: Ninja

Build a functional Person application with Ninja

The ingenuity, creativity, and plain hard work that software developers put into advancing new technologies is truly astounding. This series looks at three shining examples of that output, the Java microframeworks Ninja, Spark, and Play. In Part 1, I explained what makes Java microframeworks both familiar and different from traditional web application frameworks, even relatively lightweight ones like Spring. I also presented quick demos highlighting each framework's respective features. In the next three articles you'll learn a lot more about each of these frameworks, starting with Ninja.

Ninja: A high-quality RVC stack

I previously explained that while I use the term Java microframework, what I am really referring to is an increasingly popular Java framework style that is similar to MVC, but tightly focused on the minimum elements of a web application: routing requests, rendering views, and processing requests in a controller. Whereas MVC is more directly concerned with separation of concerns (with respect to the model, view, and controllers), RVC-style frameworks prioritize exposing the capabilities of routing, views, and controllers to the developer in a convenient and powerful toolset.

If you don't have Ninja set up in your development environment already, please refer to Part 1 for instructions and programming basics. You will also need the updated source code for the demo application, which you can get below. This tutorial will pick up where we left off last time, adding persistence, logging, a functional UI, and other features that you would need in a working Ninja application.

Source code for "Jump into Java microframeworks, Part 2: Ninja." Created by Matthew Tyson for JavaWorld.

Ninja demo: Persistence

Our first step for developing a real-world Ninja application is to get its persistence model set up. We'll start with a model class. The example application generates a list of musicians and bands, so our first model class will be a Person, which could be a musician, producer, or songwriter. Recall that after we built our Maven Ninja archetype in Part 1, the project included the Java Persistence API (javax.persistence) interfaces, as well as Hibernate as the ORM implementation provider. The Person model currently looks like what you see in Listing 1.

Listing 1. /src/main/java/models/

package models;

import javax.persistence.Entity;

import javax.persistence.GeneratedValue;

import javax.persistence.GenerationType;

import javax.persistence.Id;


public class Person {



    Long id;


	private String firstName;


	private String lastName;

	public String getFirstName() {

		return firstName;


	public void setFirstName(String firstName) {

		this.firstName = firstName;


	public String getLastName() {

		return lastName;


	public void setLastName(String lastName) {

		this.lastName = lastName;



Note that the @Entity annotation marks the class as a managed persistent one. The @Id and @GeneratedValue annotations specify the primary key field and the strategy for generating it. In JPA, all members are persisted by default unless marked otherwise; therefore, the name fields will be persisted. I added the @Column notations in order to be able to specify names in a way that is compatible with RDBMS.

Now consider the infrastructure required to make this entity work. We'll need three things:

  1. A database for storage
  2. A JPA configuration
  3. A bit of Ninja config

Ninja supports anything that JPA can, using Hibernate as the provider, so you have your choice of virtually any major SQL database. For the example app I'll use MariaDB, a MySQL fork developed by some of MySQL's original designers. MariaDB installs and operates much like MySQL.

Once you've selected and installed a database, add a schema called ninja_app using the create command like so: create database ninja_app. Don't forget to also add your chosen database connector to your pom.xml! In this case I added the mysql-connector-java dependency.

Next, we'll include a JPA configuration file, so our app knows how to connect to the database.

JPA configuration

Listing 2 displays the contents of our persistence.xml file, and reveals itself to be a typical JPA config.

Listing 2. /src/main/java/META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8"?>

<persistence xmlns=""




    <!-- Database settings for development and for tests -->

    <persistence-unit name="devdb" transaction-type="RESOURCE_LOCAL">



            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>

            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />

            <property name="hibernate.show_sql" value="true" />

            <property name="hibernate.format_sql" value="true" /> 

            <!-- Connection Pooling settings -->

            <property name="hibernate.connection.provider_class"

                value="org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider" />

            <property name="hibernate.c3p0.max_size" value="100" />

            <property name="hibernate.c3p0.min_size" value="0" />

            <property name="hibernate.c3p0.acquire_increment" value="1" />

            <property name="hibernate.c3p0.idle_test_period" value="300" />

            <property name="hibernate.c3p0.max_statements" value="0" />

            <property name="hibernate.c3p0.timeout" value="100" />      




The code in Listing 2 is a mostly generic configuration, targeting a MySQL instance. The key part for us is the configuration profile (aka persistence-unit) named devdb. Also note that the transaction-type is RESOURCE_LOCAL, meaning we're using local transactions.

Selecting an environment

Ninja allows you to select the configuration values for different environments. Supported environments are dev, prod, and test. Left to default, the environment resolves to production. You can select all configuration values with this mechanism, including things like the persistence unit and logging, which I'll demonstrate in a bit.

As an example, you could create different persistence units in your persistence.xml and reference them in application.conf, as shown below. Notice the %<environment name> notation for referencing the environments.   # will be used when no mode is set (or prod)   # will be used when running in prod mode     # will be used when running in dev mode   # will be used when running in test mode

The last piece of the puzzle is to set the active environment. If you don't set it yourself, it will resolve to prod; otherwise, you can set the ninja.mode system property to select any of the supported environments.

Ninja config

Next we'll connect our persistence model to Ninja. Open the /src/main/java/confg/application.conf file and append Listing 3.

Listing 3. Additions to /src/main/java/confg/application.conf

# dev db mysql




# Active connection


Here we are telling Ninja what URL, username, and password to use, along with the persistence_unit to use. Remember that we defined the persistence unit with devdb in Listing 2.

Next we'll save a Person. We'll begin by mapping a URL, which will be a REST endpoint. URL mapping in Ninja happens in the file, as shown in Listing 4.

Listing 4. Adding a Person POST endpoint to src/main/java/conf/

router.GET().route("/assets/webjars/{fileName: .*}").with(AssetsController.class, "serveWebJars");
router.GET().route("/assets/{fileName: .*}").with(AssetsController.class, "serveStatic");

router.POST().route("/person").with(ApplicationController.class, "createPerson"); // Add this line!

In Part 1 we developed a simple ApplicationController. Now we'll revisit that in order to add a createPerson() method. This will give us a view into how Ninja interfaces with the JPA infrastructure. Listing 5 has the additions for the controller.

Listing 5. Additions to /src/main/java/controllers/

package controllers;

import javax.persistence.EntityManager;

import models.Person;







public class ApplicationController {


	Provider<EntityManager> entitiyManagerProvider;


	Router router;


	public Result createPerson(Person person){

		EntityManager entityManager = entitiyManagerProvider.get();


		return Results.json().render("{}");



Recall that Ninja uses Guice for dependency injection and persistence management. Here you can see it in action. Note the two @Inject annotations, which let us wire in objects by type: First, we inject Provider<EntityManager>, which provides an implementation of javax.persistence.EntityManager -- the main entry point for Guice's persistence support. Second, we inject a Router, which will let us take advantage of Ninja's programmatic routing.

Finally, take a look at the createPerson() method. The @Transactional annotation is very important! It tells Guice that the method should be instrumented for persistence. Without it, nothing interesting will happen. Our next task is to get an instance of EntityManager and call persist() on our Person object.

But wait, how did we get a Person instance?

Parsing request data

When Ninja detects a request, it will automatically populate the handler argument with appropriate values. In this case, you'll recall from Part 1, we want to send a JSON request body in our POST request. Besides the JSON itself, we need a content-type header for application/json. Without that header, Ninja will not populate the argument.

To test things out, I'll use the Postman app, which I highly recommend for simulating API requests. Using Postman, we can generate a POST request with the body and headers shown in Listing 6.

Listing 6. Create a Person header and body

Content-Type: application/json

{"firstName": "John", "lastName":"Lennon"}

When the request arrives at createPerson(), Ninja will notice the content type header is JSON. It will then stuff the appropriate values from the body of the request into the Person object, which is the argument to our createPerson method. Ninja is transparently converting from JSON to a Java object based on the content header. Pretty convenient.

Populating the database

You're almost ready to hit Send on the PostMan UI and watch it work! Before you do that, though, you will need a table in your database to receive the object data. Below is the schema DDL.

Listing 7. DDL to create the Person table

create table person (first_name varchar (200), last_name varchar (200), id int not null auto_increment primary key);

Now try sending the POST request to /person. When you check the database (select * from person), you should find the new Person waiting for you.

Routing requests and reverse routing

Before we conclude this quick introduction to persistence in Ninja, take a look back at Listing 5. I'll highlight how we handled the response there, and introduce you to some Ninja routing capabilities.

In the last line of Listing 5, you'll see return Results.json().render("{}");. That line sends an HTTP 200 OK response back with an empty JSON body. That works for the AJAX design of our demo app, but say you wanted to redirect the request to another endpoint, passing in everything as a fresh request? As an example, say you wanted to do some processing in the createPerson() method, and then redirect the request to the index() handler. The handler would then be enabled to return a response as if it were handling the request.

For that use case you'd need what is called a reverse route: reverse because you go from the handler method to get the route. To do this, we would replace our return value with:

return Results.redirect(router.getReverseRoute(ApplicationController.class, "index"));

which would achieve a redirect to /. The router.getReverseRoute method takes the handler endpoint and discovers the route URL, and Result.redirect issues the redirect for us.

You can inject the router object that holds getReverseRoute() into your controllers as shown in Listing 8. Just set a member on the Controller class, with the Router type, and use the Guice @Inject annotation. Ninja will then set its own router.

Listing 8. Injecting the router into a controller

Router router;

1 2 Page 1
View Comments
Join the discussion
Be the first to comment on this article. Our Commenting Policies