Oracle JET: JavaScript components for mere mortals?

The final post in this series is about adding a front end to the analytic queries created with jOOQ and published through Spring Boot / MVC throughout the previous posts:

  1. Database centric applications with Spring Boot and jOOQ
  2. Create a Oracle Database Docker container for your Spring Boot + jOOQ application
  3. Take control of your development databases evolution
  4. An HTTP api for analytic queries

Deciding on a frontend technology for web applications

As much as I still like server side rendered HTML, there is some great stuff out there ready to use. I have written about creating a Anguluar 1 front end for a Spring Boot app nearly two years, so I had to research current options.

There’s quite a few buzz around

Alexander Casall did a very good presentation at W-JAX last week which of those techniques is the right choice for you. They both do have very different approaches. ReactJS focuses more on a modern programming paradigm where Angular 2 provides a more complete solution in terms of how one structures an applications, provides dependency injection and so on (On my todo list is an upgrade to above posts, where I will showcase upcoming Spring Framework 5 / Boot 2 with Angular 2).

What both Angular 2 and ReactJS are missing for the use case developed during in this series however, providing fast access to a lot of business relevant data, is a set of curated UI components, especially collections and data visualizations. I know, there is great stuff out there, but all must be carefully selected to work together.

This is where Oracle JET comes in. To quote Oracle:

“It’s a collection of open source JavaScript libraries along with a set of Oracle contributed JavaScript libraries that make it as simple and efficient as possible to build applications […]”

The sales pitch ends up in “consuming and interacting with Oracle products”. Well, you can do that, too.

I think more like a curated set of well proven open source modules of Oracle JET. It basically provides

  • RequireJS for managing used modules inside your scripts
  • Knockout. for providing two-way bindings between models and views
  • jQuery
  • jQuery UI which is the foundation for the great list of UI components included

Getting started with OracleJET

A new Oracle JET application can be created with a Yeoman generator which comes in various flavors: A basic starter which provides only the basic project structure and an empty page, a nav bar starter that has a responsive side bar and a starter that already provides hooks for going native through Cordova.

I personally prefer NetBeans 8.2 for those kind of tasks:



Geertjan has a nice article up, that explains in detail whats new in NetBeans 8.2 for Oracle Jet: https://blogs.oracle.com/geertjan/entry/updates_to_oracle_jet_mooc.

You have several options to use an application generated like that. One is using the provided grunt tasks like grunt build and grunt serve and especially later, when going to production, grunt build:release which does all the black magic of minimizing and optimizing your views and models. Again, you can use NetBeans for those tasks:



I do have a second Oracle JET based application up and running which is the admin backend of my user groups site, available here. This application is build as described above and then deployed to Pivotal CF using the static build pack.

The demo

Checkout the full source michael-simons/DOAG2016 here!

In the demo for DOAG Konferenz und Ausstellung I have chosen a different approach. Consider you want to deploy your analytic query together with an easy to use frontend: All static resources you put into src/main/resources/static are delivered “as is” by the embedded application container of your Spring Boot application, Tomcat being the default as of writing. This is what I did in #8158f5a (the commit is really huge, containing all the small JS files…). The single page application (SPA) is prefixed with “app”, so that I can display my carefully crafted Spring Boot loves jOOQ banner on the entry page before 😉

Note This works quite well, but I wouldn’t want to deliver the huge bulk of those JS, CSS and HTML files this way into production! I wanted to have a single, deployable artifact with a build process I can demonstrate in 90 minutes, even to non JavaScript developers. In production make sure you use the above grunt build:release target before this stuff reaches your customers browser!

Oracle JET uses Knockout for data binding, which is a prominent representative of a Model-View-View Model (MVV) framework.

A model that makes use of the query that provides the cumulative plays of artists by day from the previous post may look like this, see viewModels/artists.js:

define(['ojs/ojcore', 'knockout', 'jquery', 'moment', 'ojs/ojselectcombobox', 'ojs/ojchart', 'ojs/ojdatetimepicker'],
        function (oj, ko, $, moment) {
            function artistsContentViewModel() {
                var self = this;
 
                // The array containing all artists from api
                // Use the ojCombobox to implement server side filtering instead
                // of my brute force approach getting all artists
                self.allArtists = ko.observableArray([]);
 
                // We need this to store and get the selected artists...
                self.selectedArtists = ko.observableArray([]);
 
                // Load the artists data
                ko.computed(function () {
                    $.ajax({
                        url: "/api/artists",
                        type: 'GET',
                        dataType: 'json',
                        success: function (data, textStatus, jqXHR) {
                            self.allArtists.removeAll();
                            for (var i = 0, len = data.records.length; i < len; i++) {
                                self.allArtists.push({value: data.records[i][0], label: data.records[i][1]});
                            }
                            self.selectedArtists.removeAll();
                        }});
                }, this);
 
                self.fromValue = ko.observable('2016-01-01');
                self.toValue = ko.observable('2016-03-31');
 
                self.areaSeriesValue = ko.observableArray([]);
                self.areaGroupsValue = ko.observableArray([]);
                self.dataCursorValue = ko.observable('off');
 
                self.topNTracksSeriesValue = ko.observableArray([]);
                self.topNAlbumsSeriesValue = ko.observableArray([]);
 
                self.chartLabel = function (dataContext) {
                    return dataContext.series;
                };
 
                var updateCharts = function () {
                    self.areaSeriesValue.removeAll();
                    self.areaGroupsValue.removeAll();
                    self.dataCursorValue('off');                                        
                    self.topNTracksSeriesValue.removeAll();
                    self.topNAlbumsSeriesValue.removeAll();
 
                    if (self.selectedArtists().length === 0) {
                        return;
                    }
 
                    var dateRange = {
                        from: self.fromValue(),
                        to: self.toValue()
                    };
 
                    $.ajax({
                        url: "/api/artists/" + self.selectedArtists().join() + "/cumulativePlays",
                        type: 'GET',
                        data: dateRange,
                        dataType: 'json',
                        success: function (data, textStatus, jqXHR) {
                            if (data.records.length === 0) {
                                return;
                            }
 
                            // Need the artists labels for the series
                            var artists = self.allArtists().filter(function (elem) {
                                return self.selectedArtists.indexOf(elem.value) >= 0;
                            }).map(function (elem) {
                                return elem.label;
                            });
 
                            // Make it nice and compute continous days
                            var firstDay = moment(data.records[0][0]);
                            var lastDay = moment(data.records[data.records.length - 1][0]);
                            var days = [];
                            while (firstDay.isSameOrBefore(lastDay)) {
                                days.push(firstDay.toDate());
                                firstDay.add(1, 'd');
                            }
 
                            // Create groups and series from array of records                            
                            // This groups the records by day
                            var hlp = data.records.reduce(function (acc, cur) {
                                var key = moment(cur[0]).toDate();
                                if (acc[key]) {
                                    acc[key][cur[1]] = cur[2];
                                } else {
                                    acc[key] = {};
                                    $.each(artists, function (i, val) {
                                        acc[key][val] = val === cur[1] ? cur[2] : null;
                                    });
                                }
                                return acc;
                            }, {});
 
                            // This collects the items by artists
                            var series = artists.map(function (label) {
                                var data = [];
                                var prev = 0;
                                $.each(days, function (i, value) {
                                    var cur = hlp[value] && hlp[value][label] || prev;
                                    data.push(cur);
                                    prev = cur;
                                });
                                return {name: label, items: data};
                            });
 
                            self.areaGroupsValue(days);
                            self.areaSeriesValue(series);
                            self.dataCursorValue('on');
                        }
                    });
 
 
                    $.when(
                            $.ajax({
                                url: "/api/artists/" + self.selectedArtists().join() + "/topNTracks",
                                type: 'GET',
                                data: dateRange
                            }),
                            $.ajax({
                                url: "/api/artists/" + self.selectedArtists().join() + "/topNAlbums",
                                type: 'GET',
                                data: dateRange
                            })
                    )
                   .done(function (tracks, albums) {
                        var hlp = [];
                        for (var i = 0, len = tracks[0].records.length; i < len; i++) {
                            hlp.push({name: tracks[0].records[i][1], items: [tracks[0].records[i][2]]});
                        }
                        self.topNTracksSeriesValue(hlp);   
                        hlp = [];
                        for (var i = 0, len = albums[0].records.length; i < len; i++) {
                            hlp.push({name: albums[0].records[i][0], items: [albums[0].records[i][1]]});
                        }
                        self.topNAlbumsSeriesValue(hlp);   
                    });
 
 
                };
 
                self.optionChanged = function (event, data) {
                    if ("value" !== data.option) {
                        return;
                    }
                    updateCharts();
                };
            }
            return new artistsContentViewModel();
        });

Notice in the first line how you import Oracle JET modules by the means of RequireJS. The defined function than provides a nested function (artistsContentViewModel()) that creates the actual view model. The view model than is passed to the view.

Interesting stuff to see are the ko.observable which are the data bindings. In this demo I make every HTTP call through the jQuery api. There are probably better ways for tables, lists and such, have a look at those scripts, they use the oj.Collection api for the cool table component to achieve the same without the dirty details.

Coming back to my example. The model fields areaSeriesValue and areaGroupsValue go directly into a snippet that I basically copy and pasted from Line with Area Chart component:

<h2>Cumulative plays per artist and day</h2>
<div id='chart-container'>
    <div id="lineAreaChart" style="max-width:1024px;width:100%;height:320px;" data-bind="ojComponent: {
        component: 'ojChart', 
        type: 'lineWithArea', 
        series: areaSeriesValue, 
        groups: areaGroupsValue, 
        timeAxisType: 'enabled',
        animationOnDisplay: 'on',
        animationOnDataChange: 'on',            
        stack: 'on',             
        hoverBehavior: 'dim',
        zoomAndScroll: 'live',
        overview: {rendered: 'off'},
        dataCursor: dataCursorValue
     }"></div>                  
</div>

This may not be fancy, but it does several things pretty well: It’s accessible, fully translated and easy understandable on the user side and easy enough to implement on developer side.

The cookbook

The Cookbook is really valuable. It presents all the components in great detail, the links to the api are up to date and it even gives you hints on where and when to use a certain type of visualization. With it at hand I created the frontend for DOAG2016 in about three days:



Verdict

If you want to be always bleeding edge – which I can fully understand – don’t go near Oracle JET. You won’t like it. If you have to suit a projects needs, presenting lots of data, often visualized and don’t have time or the will to figure all the ways you can setup a JavaScript application yourself, give OracleJET a try.

I find the basic architecture well thought through, highly extensible (I did it with several Spring collections apis and more) and excellent documented.

One of the best things is, from a Java developers perspective actually, is the fact, that it doesn’t want to hide JavaScript and HTML code and fragments behind Java code, which brings in too much abstraction according to my experience. You can achieve a lot by using for example GWT based stuff, but the moment you have to create your own components, you basically have to learn the interns of the chosen framework, JavaScript, HTML and CSS… The later 3 are somewhat standards, which is why I like to have them in my toolbelt anyway.

Summary

In 5 posts I walked you from setting up a skeleton Spring Boot project that provides a database connection or connection pool and a jOOQ context to creating an Oracle Database inside a container for developing and integration purposes to advanced analytic queries and in the end, showed one way to provide a UI. After 5 posts we end with a decent looking, database centric but not database dependent web applications.

In all steps I opted for the tools that make my life as a developer easier, facilitate the features I already payed for and help my customer to achieve their business goals.

There are several possible deployment scenarios: Run the application on Pivotal CF, get yourself a PostgreSQL database from the marketplace. You just have to push it. Put your frontend on some CDN. Or also on CF with the static build pack.

If you need an Oracle database, maybe Amazon RDS is the right choice for you. It doesn’t take much effort to run a Spring Boot application on Amazon EC2.

Do you prefer getting your Oracle Database right from the vendor? I once was in a talk by both Bruno and Josh who deployed a Spring Boot application to the Oracle Cloud.

Regarding your database: I still love coding in Java. Even more so with Java 8. But why not using parts of all the awesome analytic functions my database, for example PostgreSQL and Oracle, offer? Both support fantastic spatial queries, XML and JSON. Maybe you already have relational database knowledge in your company: Use it! Get rid of bad practices putting everything into the database, but use it for what it was designed for. Don’t reinvent the wheel.

Last but not least: If you have the time and ambition, try out new features (not only in the front end). Implement stuff yourself. It’s fine. Learn. But if you have a project goal, maybe a prepackaged structure for a client side web application is enough and something like OracleJET is of value to you as well.

Thank you so much for your attention and reading so far. The demo project and the talk have been in the making for about 6 months. We actually use most of it inside new projects (not the OracleJET part yet) and it was time well invested researching that stack.

| Comments (5) »

14-Nov-16


An HTTP api for analytic queries

This post has been featured on This Week in Spring – November 8, 2016 and I’m so happy to be referred as “friend of the Spring community” 🙂

So, you have read through the previous parts of this series (1, 2 and 3) and you now have a Spring Boot project with jOOQ enabled, an Oracle Database with a simple schema containing musical data. What can you do with it?

Content

  1. An introductory example
  2. Window functions with jOOQ
  3. Common table expressions, window functions and Java expressions combined
  4. Summary and use cases

In the previous post I had a simple select, getting all artists from my library ordered by name. For queries like that I wouldn’t bother investigating into jOOQ. I have written extensively about my use of Hibernate and summarized my thoughts in this interview. In short: You can come very far with JPA / Hibernate, you can even create portable JPQL queries that support some analytics, as Thorben shows. Thorben also has a nice little tool that helps deciding wether JPA / Hibernate is the right tool for the job, checkout it out: Should you use JPA for your next project?

Imagine the following use cases based on this schema:


doag2016_schema
  • I want a list of top n albums by a list of artists ordered by play count
  • I want a list of artists and how many times I played a track by them cumulative by day
  • Given a specific months, I want a top n list of tracks played in this month ordered by play count and also including the change in result to the previous month

You can try all examples yourself by cloning this repository: github.com/michael-simons/DOAG2016 and running those steps:

./mvnw docker:start
./mvnw clean package
./mvnw spring-boot:run

Please read through the links at the top of this post to grok each step.

Read the complete article »

| Comments (5) »

02-Nov-16


Take control of your development databases evolution

So you learned how to setup a Spring Boot project that supports jOOQ in this post and then created a Docker container on your local host that provides you with an Oracle Database instance that you’re ready to use in your project but the instance doesn’t have any tables, data or procedures yet.

You could login into the database via SQL*Plus and create tables manually. And so would your colleague on her machine.

It doesn’t matter if you use an ORM or a code generator at this point, the day you’re gonna move to your production database, stuff will break. One of your team forgot to apply his scripts to production and you are screwed. Either your JPA mappings are broken, SQL queries fail or jOOQs generated code doesn’t match the database.

In this part of the series you’ll learn how to solve this challenge and which workflow we use for database centric applications with jOOQ.

Content

  1. Database migrations with Flyway and Maven
  2. Generation of a Java based database schema
  3. Database migrations revised
  4. Summary

Read the complete article »

| Comments (2) »

31-Oct-16



Database centric applications with Spring Boot and jOOQ

Abstract

As the german conference DOAG K+A is approaching very fast, I have to finalize my talk. As I prepared my demo already several months ago (on the one hand good, it’s done, on the other hand bad, I have to rehearse it), I might as well write about it.

This is gonna be a serious of posts that will provide one solution on how to do advanced reporting using jOOQ and basically mapping queries on HTTP endpoints with Spring Boot. We also gonna add a nice frontend to the mix based on Oracle JET.

I used NetBeans throughout the demo. It’s a highly versatile IDE that not only helped me a lot creating the buildfile for maven, but also supports me in managing my Oracle Datebase inside a Docker container.

There will be two “bonus” topics: Creating an Oracle Database inside a Docker container and using the Oracle Maven repositories for an “original” copy of the Oracle JDBC driver.

Be aware that some things maybe feels very simple to you if you are an experienced Spring Boot developer, but remember that this demo is developed for an audience that probably has little knowledge of Spring Boot or even Java.

As I’m gonna add posts to this series, I’ll push the commits to this repository: michael-simons/DOAG2016, planing to have it fully populated by the end of November.

Content

  1. Part 1: Database centric applications with Spring Boot and jOOQ (this post)
  2. Part 2: Create a Oracle Database Docker container for your Spring Boot + jOOQ application
  3. Part 3: Take control of your development databases evolution
  4. Part 4: An HTTP api for analytic queries
  5. Part 5: Oracle JET: JavaScript components for mere mortals?

Read the complete article »

| Comments (7) »

28-Oct-16