Demonstration of Java8 running SparkJava web application with ReactJS via Nashorn and tested with Retrofit
JavaScript HTML Java CSS
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
docs
src
.gitignore
Gruntfile.js
LICENSE
README.md
build.gradle
package.json
pom.xml

README.md

SparkJava Retrofit React Demonstration

A Java 8 web application using lightweight frameworks and ReactJS for the UI

Basic Idea

A Java 8 process with an embedded rest-api and web application, where the web application user interface is constructed using the React JavaScript components via the Nashorn engine with access to Java services.

Dependencies

SparkJava and Guava keep the overall dependencies lightweight (not even a single spring library here).

Also using Quava and a choice of Gradle or Maven for Java builds.

Taking it for a spin

Make sure you have JDK 1.8.0_05-b13 or above.

command description
gradle build to compile and run the tests
mvn clean install to compile and run the tests
grunt watch compile jsx into js resource (from src/main/jsx into src/main/js)
DemoService Start Web Application http://localhost:4545

Alternatively run the gradle installApp command to create a runnable distribution package in build/install/sparkjava-retrofit-react and run the appropriate script in the bin folder.

These dials are using a JavaScript charting library but are rendered using ReactJS with data from Java:

JavaScript charts with data from Java

These tables are rendered using ReactJS with data from Java, one table uses a Java service with direct access to the data store, the other Java service calls the rest-api from within Java to obtain the same data:

ReactJS Tables

the ?skip=2&limit=4 parameters on the request URL are passed into ReactJS and back to Java services

License

MIT license

Overview

The REST api

The rest-api provides a simple (yet richer than the usual todo list) task data model encapsulated behind a TaskService interface (the typical Data-Access-Object pattern). For the purpose of this exercise the tasks are seeded from a small sample of data and kept in-memory. There are only a couple of read operations defined as the main focus is to render data. However, this Data-Access-Object implementation could be swapped out for any Java based database or messaging connection and enriched to include fully functional CRUD (create, read, update, delete) functionality.

See RestfulController

The User Interface

The first version of Bootstrap-Admin-Theme is purely a static copy of the original with the dashboard sidebar changed to provide the menu of demonstration links. Useful for showing how rich static content can be rendered easily with SparkJava.

SideBar

The second version is the Dashboard content rendered by ReactJS, with a minimal HTML template to keep the surrounding page <script> and <head> tags separate.

Setting up Nashorn with ReactJS

Npm is used to download JavaScript packages. Grunt is used to compile jsx and to create a bundle.js of all Javascript needed for Nashorn. This keeps the Java Nashorn loading code simple.

Java to setup Nashorn

        nashorn = (NashornScriptEngine) new ScriptEngineManager().getEngineByName("nashorn");
		...
        Bindings bindings = new SimpleBindings();
        bindings.put("JavaTaskService", taskService);
        nashorn.setBindings(bindings, ScriptContext.GLOBAL_SCOPE);
        ...
        nashorn.eval(new InputStreamReader(this.getClass().getResourceAsStream("/webapp/bundle.min.js")));

There are three steps above:

  1. Create the Nashorn JavaScript engine instance
  2. Inject any Java services you want your JavaScript to access
  3. Load the pre-created bundle.js

Creating the Nashorn engine instance is simple enough, although there is a little bit of nashorn-polyfill to be aware of (this gets included in the bundle.js).

The ability to use 'Dependency Injection' into your JavaScript makes it possible to access any Java services you have while rendering a page. There may need to be some data type conversions but this is a very powerful option and avoids trying to find JavaScript drivers for everything.

JavaScript to call back into Java services

    loadTaskDataFromJavaDirectly: function () {
        return Java.from(JavaTaskService.getTaskList(this.props.skip, this.props.limit));
    },

Lastly we leverage npm and grunt to create a bundle.js containing ReactJS and any other libraries you wish to have access to. Note: this bundle.js is a simple Grunt concatenation bundle using uglify, not a webpack or browserify bundle.

The Flow

Calling the rest-api will perform all operations in Java; the rest-api converts the incoming endpoint into calls to the data-access-object. However, the web application calls take the following route:

  1. java8 SparkJava calls a rendering function in the Nashorn JavaScript engine with properties from the Java process (mapped from the request query parameters)
  2. nashorn starts rendering the component graph using React.renderToString
  3. nashorn calls any Java services for data at the time of rendering
  4. java8 service can do anything Java can do and return results to the React component renderer
  5. nashorn completes and returns the rendered content to java8
  6. java8 maps the title and content into a HTML template and returns the response

Java8 React Diagram

Testing

This entire project is one big test so the only unit tests focus on testing the rest-api using Retrofit. Retrofit allows you to define a simple interface for your rest-api and leave the plumbing up to the Retrofit library. A real gem!

Example Java Retrofit RESTful api interface (See RestfulService for full version)

public interface RestfulService {

    @GET("/api/v1/tasks")
    RestResponse<List<Task>> getTasks(
            @Query("skip") int skip,
            @Query("limit") int limit
    );
}

Example Java test using Retrofit (See RestfulControllerTest)

    RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint("http://localhost:4545")
                .build();
    RestfulService taskService = restAdapter.create(RestfulService.class);
    RestResponse<List<Task>> response = taskService.getTasks(1,4);
    List<Task> tasks = response.content;

This project was built and tested on a Mac laptop, using Chrome, with JDK 1.8.0_05-b13. It has not been tested/built on any Windows or Linux platforms but should work providing you have a Java 8 installed.