BENJAMINWINTERBERG

Using Backbone.js with Nashorn

April 07, 2014

This example demonstrates how to use Backbone.js models with the Java 8 Nashorn Javascript Engine. First released in March 2014 as part of Java SE 8, Nashorn extends Javas capabilities by running javascript code natively on the JVM. For java web developers Nashorn might be especially useful for re-using existing client-side code on the java server. Traditionally Node.js was in a clear advantage, but Nashorns possibilities might close the gap to the JVM.

When working with modern javascript MVC frameworks like Backbone.js for HTML5 front-ends, more and more code moves from the server back-end to the web front-end. This approach can greatly increase the user experience because you safe a lot of server-roundtrips when using business logic from your views.

Backbone enables you to define model classes which can be bound to views (e.g. HTML forms). Backbone keeps track of updating the model when the user interacts with the UI and vice versa. It also aids you by synchronizing your model with the server, e.g. by calling the appropriate method of your REST handler on the server side. So you end up implementing business logic in your front-end code, leaving your server model responsible for persisting data.

Reusing backbone models on the server side is quite easy with Nashorn, as the following example will demonstrate. Before we start make sure you're familiar with writing javascript for the Nashorn Engine by reading my Nashorn Tutorial.

The Java Model

First, we define a domain class Product in java code. This class might be used for CRUD database operations (saving to and loading from a datasource). Keep in mind that this class is a dumb Java Bean without any business logic applied, because we want our front-end to be capable of executing the business logic right from the UI.

class Product {
    String name;
    double price;
    int stock;
    double valueOfGoods;
}

The Backbone Model

Now we define the backbone model as the counter-part of our java bean. The backbone model Product uses the same data-structure as the java bean, since this is the data we might want to persist on the java server.

The backbone model also implements the business logic: The method getValueOfGoods calculates the value of all products by multiplying stock with price. Each time stock or price changes the property valueOfGoods must be re-calculated.

var Product = Backbone.Model.extend({
    defaults: {
        name: '',
        stock: 0,
        price: 0.0,
        valueOfGoods: 0.0
    },

    initialize: function() {
        this.on('change:stock change:price', function() {
            var stock = this.get('stock');
            var price = this.get('price');
            var valueOfGoods = this.getValueOfGoods(stock, price);
            this.set('valueOfGoods', valueOfGoods);
        });
    },

    getValueOfGoods: function(stock, price) {
        return stock * price;
    }
});

Since the backbone model doesn't use any Nashorn language extensions, we can safely use the same code both on the client (Browser) and on the server (Java) side.

Keep in mind that I deliberately chose a really simple function for demonstrating purposes only. Real business logic should be more complex.

Putting both together

The next goal is to re-use the backbone model from Nashorn, e.g. on the java server. We want to achieve the following behavior: bind all properties from the java bean to the backbone model; calculate the property valueOfGoods; pass the result back to java.

First, we create a new script to be evaluated solely by Nashorn, so we can safely use Nashorn extensions here:

load('http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js');
load('http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone-min.js');
load('product-backbone-model.js');

var calculate = function(javaProduct) {
    var model = new Product();
    model.set('name', javaProduct.name);
    model.set('price', javaProduct.price);
    model.set('stock', javaProduct.stock);
    return model.attributes;
};

The script first loads the relevant external scripts Underscore and Backbone (Underscore is a pre-requirement for Backbone) and our Product backbone model as defined above.

The function calculate accepts a Product java bean, binds all properties to a newly created backbone Product and returns all attributes of the model back to the caller. By setting the properties stock and price on the backbone model, property valueOfGoods will automatically be calculated due to the event handler registered in the models initialize constructor function.

Finally, we call the function calculate from java:

Product product = new Product();
product.setName("Rubber");
product.setPrice(1.99);
product.setStock(1337);

ScriptObjectMirror result = (ScriptObjectMirror)
    invocable.invokeFunction("calculate", product);

System.out.println(result.get("name") + ": " + result.get("valueOfGoods"));
// Rubber: 2660.63

We create a new Product java bean and pass it to the javascript function. As a result the method getValueOfGoods will be triggered, so we can read the property valueOfGoods from the returning object.

Conclusion

Reusing existing javascript libraries on the Nashorn Engine is quite easy. Backbone is great for building complex HTML5 front-ends. In my opinion Nashorn and the JVM now is a great alternative to Node.js, since you can make use of the whole Java eco-system in your Nashorn codebase, such as the whole JDK API and all available libraries and tools. Keep in mind that you're not tight to the Java Language when working with Nashorn - think Scala, Groovy, Clojure or even pure Javascript via jjs.

The runnable source code from this article is hosted on GitHub (see this file). Feel free to fork the repository or send me your feedback via Twitter.