Introduction
Imagine you want to build an application with a Java or Clojure backend, exposing services via REST. The services are consumed by a JavaScript application using AngularJS.
In article, I summarize the experience I made to develop, test, build and release applications with mixed technologies. I will give you an introduction into JavaScript tooling and share my experience how to integrate the GUI with a Java or Clojure backend.
JavaScript build tool ecosystem
The JavaScript ecosystem is amazing. You will find well designed tools, each one with a defined but limited responsibility, which allows to combine them into powerful tool chains. It is enlightening coming from a Java Maven background. The Java world has a tendency to create things like ‘the coffee making, water distributing, ice machine, soap bubble making bicycle’.
Task runner
A task runner can execute tasks, define dependencies between them, run them in parallel or sequential but very important they don’t know much about the tasks themselves. Gulp Grunt and Brunch fall into this category.
Combined with plugins the task runner can do anything you need to do with a JavaScript web application.
Here is an extract of a task definition using Gulp. It compiles Sass style sheets into normal CSS and send a desktop notification, if it succeeds or fails.
// load plugins var gulp = require('gulp'), minifycss = require('gulp-minify-css'), notify = require('gulp-notify'), source = require('vinyl-source-stream'), streamify = require('gulp-streamify'), uglify = require('gulp-uglify'), sass = require('gulp-ruby-sass'); // define a task gulp.task('styles-sass', function () { return gulp.src(paths.styles_sass) .pipe(sass({ style: 'expanded' })) .on("error", notify.onError(function (error) { return "CSS problem: " + error.message; })) .pipe(gulp.dest('dist/dev/styles')) .pipe(notify({ message: 'Saas styles task complete' })); });
A typical JavaScript build file consists of small tasks. Whenever you need to achieve something, just search for a plugin using something like “Grunt minify CSS” or “Gulp compress JavaScript”.
To describe the three task executors shortly: Grunt has the largest plugin collection, Gulp is more efficient has it uses pipes to pass the output from one step into the next, Brunch is the youngest and makes heavy use of conventions over configurations, which makes it very easy to setup but on the other hand sometimes harder to understand and to adapt. After trying out each of them, I choose Gulp. But this is a matter of personal preferences. Each tool has its shiny sites.
Dependency management
The Maven or Clojars equivalent in the JavaScript world are NPM and Bower. Both help dealing with fetching dependencies. NPM provides dependencies in a format suitable for JavaScript backend application. Combined with a tool like Browserify which packs the dependency into a single file, it can be used for JavaScript in the browser as well. Bower is already intended to be used with Browser applications. Both are viable solutions.
You will probably always use NPM to install all other tools needed for development.
Writing JavaScript modules is not trivial, as you need to target both NPM and Bower. It is worth to invest some time understanding how they work. Especially in larger GUI applications you need a form of modularization to avoid naming conflicts.
Scaffolding
There is a good chance that you run into Yeoman as well. It helps to quickly start building a JavaScript application. If you want to have an AngularJS application including build script, test runner and an application template, just type:
yeoman init angular
Alternatively, there is large choice of boilerplate tempates.
Development
For development there is a list of typical tasks for a JavaScript application.
- Quality check the JavaScript code using JSHint or JSLint
- Package all required libraries
- Concatenate all your JavaScript code
- Transform your Saas or LESS style sheets into normal CSS
- Copy fonts, HTML files and images
- Execute unit and end to end tests
- Automatically reload the browser if anything has changed
All this can be achieved with plugins for Gulp, Grunt or Brunch. The automated test execution, reloading and quality check are very useful. Every time you save a file, you get a feedback fractions of a second later.
To get an impression, how to configure such a build file, I found it helpful to develop it step by step from scratch and then compare it to existing solutions.
- AngularJS template
- Templates created by yeoman
Next thing is how to integrate the JavaScript GUI with a deployed Java or Clojure backend.
A very good way to achieve this is using a JavaScript web server with integrated proxy. It is a normal web server serving requests from a defined directory. In addition a URL like /api can be defined to be forwarded to a backend running under its own port.
Why not using backend tools to build the JavaScript application?
Gradle and even Maven has extensions to build JavaScript applications. The answer is simple. If you go swimming what do you pick: flippers or a bucket of heavy stones.
The JavaScript build tools are much more advanced, used by many more people and are very easy to adapt to your own needs.
Packaging for Production
For production CSS, JavaScript and libraries needs to be minimized. If the backend and the GUI is packaged as single application, the JavaScript application is copied into the web directories of the backend project.
In the past, I achieved this by adding a task to the Gulp or Grunt build. The reason, I am controlling this from the JavaScript and not the backend build script, is that the JavaScript task runners are way easier to adapt. Neither of tools like Maven or Gradle (Java world) or leiningen (Clojure world) is as flexible.
Releasing
Typical tasks to release a project are
- run production build for backend and GUI
- run tests
- remove snapshot suffix for production release in backend and GUI
- commit and tag version
- upload application to a repository (Nexus etc)
- bump up version in backend and GUI
- commit with new version
I found this part a bit tricky. Releasing can be both controlled from the task runner and the backend build script or even a third script controlling both. I used the backend build script in the past to do the actual release, but triggered the JavaScript task runner before doing this from an outside script.
Other solution, I encountered:
- Rake (The smart Ruby buildtool) to control both JavaScript and backend build
- Control everything from the JavaScript task runner
- Control everything from the Java or Clojure build tool. I did this once with Gradle in a Java project.
Summary
This overview should get you started easier in building mixed builds. Feel free to sent me comments with your own experiences.