Monday, January 26, 2015

Vert.x with gulp.js

Vert.x is often put forward as a polyglot alternative to node.js that runs on the JVM. A read through the vert.x javascript docs indicates that javascript is a first-class language in vert.x, and both node.js and vert.x use an event-driven, non-blocking I/O programming model. But to what degree will a node programmer feel at home in writing a vert.x application?

In this blog post I will look at using gulp, a node.js build tool, to build a vert.x 2 module.

Platform Installation

Before proceeding, be sure to have both the vert.x and node.js platforms installed. Vert.x will provide the run-time for our application, and node.js will provide us with the build environment for our project. Refer to the vert.x install docs and the node and npm install docs for further details.

Project layout

The gulp.js build tool has us apply transformations to streams of our source code, and as such doesn’t dictate how we structure our source code within our project. The structure I chose is as follows:

.
    ├── gulpfile.js
    ├── node_modules
    │   └── ...
    ├── package.json
    ├── src
    │   ├── app.js
    │   ├── mod.json
    │   └── ...
    ├── tasks
    │   ├── vertx.gulp.js
    │   └── zip.gulp.js
    └── vertx_modules
        └── ...

The package.json file manages the npm dependencies for our gulp.js build, where those dependencies are stored in the node_modules folder. The gulpfile.js file is our gulp build file, and incorporates individual build tasks defined in the tasks folder. The src folder contains our the source for our vert.x module, and finally the vertx_modules folder contains the vert.x modules on which our application depends.

The gulp build

The gulp build file (gulpfile.js) is pretty straightforward; the top-level gulp file is used to configure the project, with individual tasks defined in separate files. These files are then included using the require statement.

gulpfile.js
process.env.VERTX_MODS = 'vertx_modules';
    
    var gulp = require('gulp');
    
    var opts = {
      module: {
        group: 'ca.bleathem',
        artifact: 'demo',
        version: '0.0.1'
      },
      paths: {
        src: 'src/**/*',
        dist: 'dist'
      }
    };
    
    opts.module.name = opts.module.group + '~'
                     + opts.module.artifact + '~'
                     + opts.module.version + '.zip';
    opts.paths.cp = 'src';
    
    require('./tasks/vertx.gulp.js')(gulp, opts);
    require('./tasks/zip.gulp.js')(gulp, opts);
    
    gulp.task('default', ['vertx']);

Notice the VERTX_MODS environment variable is set in the gulpfile. Using the build file to programtically set environment variable depending on the deployment target (production/development) can be a powerful technique. Here we set VERTX_MODS to store vertx modules in a folder paralleling the node.js modules.

The *.gulp.js build files containing the individual gulp task definitions are stored in the gulp sub folder.

...
    ├── tasks
    │   ├── vertx.gulp.js
    │   └── zip.gulp.js
    ...

Let’s explore these vertx and zip tasks further.

The vert.x gulp task

The gulp plugin guidelines recommend not creating a plugin for a task that can "be done easily with an existing node module". To this end, we’ll start by seeing how far we can by leveraging the abilities of node to spawn a child process. Below is a gulp task that runs the vert.x module that is our sample application:

vertx.gulp.js
var spawn = require('child_process').spawn
      , gutil = require('gulp-util');
    
    module.exports = function(gulp, opts) {
      gulp.task('vertx', [], function(done) {
        var child = spawn('vertx', ['runmod', opts.module.name, '-cp', opts.paths.cp ], {cwd: process.cwd()}),
            stdout = '',
            stderr = '';
    
        child.stdout.setEncoding('utf8');
        child.stdout.on('data', function (data) {
            stdout += data;
            gutil.log(data.slice(0, data.length - 1));
        });
    
        child.stderr.setEncoding('utf8');
        child.stderr.on('data', function (data) {
            stderr += data;
            gutil.log(gutil.colors.red(data.slice(0, data.length - 1)));
            gutil.beep();
        });
    
        child.on('close', function(code) {
            gutil.log('Done with exit code', code);
            done();
        });
      });
    };

The bulk of the above listing deals with re-directing and formatting the output of the vert.x child process. The invocation of the spawn function is the interesting part, and is where we pass our arguments to the vert.x process. In our case we want to run the module that is our sample project, and we set the vert.x classpath to our source folder to allow for on-the-fly code changes.

Invoking the build via the command gulp vertx will start vert.x, running the module in our project.

The zip gulp task

The distribution format for vert.x is a wonderfully simple zip format. This makes it easy to use a the gulp-zip plugin to zip up the file and create a bundle for our module.

vertx.gulp.js
var zip = require('gulp-zip');
    
    module.exports = function(gulp, opts) {
      return gulp.task('zip', function() {
        return gulp.src(opts.paths.src)
          .pipe(zip(opts.module.name))
          .pipe(gulp.dest(opts.paths.dist));
      });
    };

The above source transformation is a trivial one. Those familiar with gulp will recognize we could easily add additional stream transformations here, eg. compiling coffescript, minifying client code, compiling sass etc.

On to vert.x 3

The above build works well for vert.x 2. However vert.x 3 is around the corner and introduces many changes. The changes relevant to our gulp build include:

  1. Vert.x 3 will do away with modules and flatten the classpath across verticals. This will directly affect how we structure our source code and invoke vert.x from our gulpfile.

  2. Vert.x 3 will also resolve packaged verticles from npm, which will align nicely with our npm-based build approach.

Stay tuned for a new post addressing a gulp.js build targeting vert.x 3.


Thursday, November 29, 2012

Polyglot Widgets

JBoss Developer Framework

The JBoss JDF project shows Java EE developers how to build state-of-the-art applications using the JBoss implementations of the Java EE stack. Specifically, the JDF View Frameworks section identifies a number of alternative approaches one can take when developing the view layer of your application. We in the RichFaces project have been working towards better supporting this effort by redesigning our JSF component architecture to allow the javascript part of our components (what we call our “widgets”) to be used independent of JSF, either in a standalone manner or coupled with another web framework.

By isolating the client-behaviour into widgets, we are able to achieve both a faster turnaround on new JSF component development, while at the same time enabling users to achieve a uniform look and feel in their heterogeneous application environments. To demonstrate the impact of this, I’ve prepared a demo application that demonstrates using our standalone javascript widgets in JSF with RichFaces, GWT with Errai, and a plain HTML 5 page with Aerogear.

Polyglot widgets demo

polyglot-widgets

The polyglot-widgets demo takes the RichFaces Sandbox pickList component and builds a GWT and plain HTML5 application using the standalone javascript pickList widget.

Some key points of the demo to call out:

Consistent L&F

Imagine writing “polyglot” web applications with both a consistent Look & Feel, and access to the rich set of enterprise-grade widgets that you’ve come to expect from the RichFaces project. In the demo, the same RichFaces Sandbox pickList widget is used in a JSF, GWT and HTML 5 application. The widget and the demo pages themselves use the Bootstrap project for styling, which is what enables us to achieve a consistent L&F across the pages backed by different web technologies. This common L&F is taken one step further to deliver a consistent user experience by using the same javascript widget implementation across all the demo pages.

CDI Programming model

In the demo, we use the pickList component to make a selection on one of the demo pages, then click the submit button. On navigation to one of the other demo pages, you’ll notice the state persisted. This demonstrates how we can leverage CDI to provide a common programming model across all our web frameworks.

Integrated Ajax Push

Open each page of the demo in a separate window to witness selection updates synchronizing the pages in real time. Taking advantage of RichFaces push, the Errai CDI bus, and HTML 5 Server-Sent-Events (via the Atmosphere project) in each of the respective frameworks provides incredible power in keeping our “polyglot” web-apps in a coherent state.

Screencast

I created a screencast of the demo, to make it easier to see the above points in action. Watch the screencast below, then head off to play with the demo yourself. Even better, fork the demo on github, and see what cool things you can do with it.

Next Steps…

I put this demo together as a proof-of-concept to help me illustrate what I mean when I talk about “standalone widgets” and polyglot/poly-framework applications. The RichFaces team will ramp up development on these new standalone widgets as we wrap up our RichFaces 4.3 effort and shift gears into RichFaces 5. So stay tuned for further development in this area!