Using front-end frameworks for a java web application like AngularJs and Bootstrap needs some caution to manage JavaScript and Css dependencies.
There are two tools family for front-end libraries management. The Java ones like Wro4j and Jawr witch have the advantage to integrate easily to Java project management softs like maven and grails and don’t need any extra knowledge.
The second ones are JavaScript tools like Grunt, Bower and Gulp. Those tools have a great success and have the advantage to be used for front-end frameworks development.
As a result, you will face less problems where managing front-end dependencies and optimizing web resources.
This this post presents a solution to use Maven, Bower and Gulp to manage front-end libraries for a java web project.
The source code of this post is available in this link and you can test it here.
Add frontend maven Plugin
Frontend plugin install node and npm and It lets you run Bower and Gulp.
Add the xml bellow to your project pom.xml file to configure “frontend-maven-plugin”.
<build> <plugins> <plugin> <groupId>com.github.eirslett</groupId> <artifactId>frontend-maven-plugin</artifactId> <version>0.0.20</version> <executions> <execution> <id>install node and npm</id> <goals> <goal>install-node-and-npm</goal> </goals> <phase>generate-resources</phase> <configuration> <nodeVersion>v0.10.26</nodeVersion> <npmVersion>1.4.3</npmVersion> </configuration> </execution> <execution> <id>npm install</id> <goals> <goal>npm</goal> </goals> <phase>generate-resources</phase> </execution> <execution> <id>bower install</id> <goals> <goal>bower</goal> </goals> <configuration> <arguments>install</arguments> </configuration> </execution> <execution> <id>gulp build</id> <goals> <goal>gulp</goal> </goals> <phase>generate-resources</phase> </execution> </executions> </plugin> </plugins> </build>
Add front-end dependencies
Bower works by fetching and installing front-end packages. For more information about bower visit the website bower.io.
Add a bower.json file with front-end dependencies in the root directory of your maven project (next to the pom.xml file).
{ "name": "example", "version": "0.0.1", "dependencies": { "jquery": "~2.1.3", "angular": "1.4.5", "angular-route": "1.4.5", "angular-resource": "1.4.5", "angular-sanitize": "1.4.5", "bootstrap": "3.3.4", "bootstrap-material-design": "0.3.0", "angular-swagger-ui": "~0.2.3" }, "private": true }
Add Glup module dependencies
Add package.json file with the content bellow.
{ "name": "example", "version": "0.0.1", "dependencies": { "bower": "~1.3.12", "gulp": "^3.9.0", "gulp-concat": "^2.6.0", "gulp-concat-vendor": "0.0.4", "gulp-uglify": "^1.3.0", "main-bower-files": "^2.9.0", "gulp-minify-css" : "^1.2.1", "gulp-notify":"2.2.0", "gulp-inject": "1.5.0", "gulp-run-sequence": "0.3.2", "stream-series": "0.1.1", "gulp-gzip": "1.2.0", "gulp-clone": "1.0.0" }, "jspm": { "dependencies": { "jquery": "github:jquery/jquery@^2.1.3" }, "devDependencies": { "traceur": "github:jmcriffey/bower-traceur@0.0.88", "traceur-runtime": "github:jmcriffey/bower-traceur-runtime@0.0.88" } }, "scripts": { "prebuild": "npm install", "build": "gulp" } }
Add a Gulp execution file
Create the Gulp execution file gulpfile.js with the content bellow.
var gulp = require('gulp'); var concat = require('gulp-concat'); var concatVendor = require('gulp-concat-vendor'); var uglify = require('gulp-uglify'); var minify = require('gulp-minify-css') var mainBowerFiles = require('main-bower-files'); var inject = require('gulp-inject'); var runSequence = require('gulp-run-sequence'); var gzip = require('gulp-gzip'); var clone = require('gulp-clone'); var series = require('stream-series'); var vendorJs; var vendorCss; gulp.task('lib-js-files', function () { vendorJs = gulp.src(mainBowerFiles('**/*.js'),{ base: 'bower_components' }) .pipe(concatVendor('lib.min.js')) .pipe(uglify()) .pipe(gulp.dest('src/main/webapp/resources/vendor/js')); vendorJs.pipe(clone()) .pipe(gzip()) .pipe(gulp.dest('src/main/webapp/resources/vendor/js')); }); gulp.task('lib-css-files', function () { vendorCss = gulp.src(mainBowerFiles('**/*.css'), {base: 'bower_components'}) .pipe(concat('lib.min.css')) .pipe(minify()) .pipe(gulp.dest('src/main/webapp/resources/vendor/css')); vendorCss.pipe(clone()) .pipe(clone()) .pipe(gzip()) .pipe(gulp.dest('src/main/webapp/resources/vendor/css')); }); gulp.task('index', function () { var target = gulp.src("src/main/webapp/index.html"); var sources = gulp.src(['src/main/webapp/resources/js/*.js', 'src/main/webapp/resources/css/*.css'], {read: false}); return target.pipe(inject(series(vendorJs, vendorCss, sources), {relative: true})) .pipe(gulp.dest('src/main/webapp')); }); gulp.task('copyFonts', function() { gulp.src(mainBowerFiles('**/dist/fonts/*.{ttf,woff,woff2,eof,svg}')) .pipe(gulp.dest('src/main/webapp/resources/vendor/fonts')); }); // Default Task gulp.task('default', function () { runSequence('lib-js-files', 'lib-css-files', 'index', 'copyFonts'); });
The gulp script has 4 tasks:
- lib-js-files : concat, uglify and compress all vendor javascript dependencies
- lib-css-files : concat, minify and compress all vendor css dependencies
- index : inject the dependencies to file
- copyFonts : copy fonts to vendor/font directory to keep css references
The graph bellow presents gulp tasks flows.
Configure Spring to resolve Gzip files
In this step, we configure Spring MVC framework to look for Gzip files for the application web resources.
@Configuration @EnableWebMvc // Extra config annotations public class MvcConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/vendor/**") .addResourceLocations("/resources/vendor/") .setCachePeriod(0) .resourceChain(true) .addResolver(new GzipResourceResolver()) .addResolver(new PathResourceResolver()); } // Extra config here }
Build and deploy your application
Build your application by running :
mvn clean install
>HOME=$OPENSHIFT_DATA_DIR
If everything is well done, your project directory will have the structure described below.
project +-- bower_components ** +-- node ** +-- node_module ** +-- src | +-- main | | +-- java | | +-- resources | | \-- webapp | | +-- resources | | | +-- css | | | +-- js | | | \-- vendor ** | | | +-- css | | | | +-- lib.min.css | | | | \-- lib.min.css.gz | | | +-- fonts | | | | +-- glyphicons-halflings-regular.svg | | | | +-- .... | | | \-- js | | | +-- lib.min.js | | | \-- lib.min.js.gz | | +-- WEB-INF | | \-- index.html | +-- test +-- target +-- bower.json +-- gulpfile.js +-- package.json \-- pom.xml
Finally, deploy and run the application. The sample application of this post is deployed on this link.
The two pictures bellow present Chrome DevTools to compare network performance of the optimized web application which has a load time of 2.74s and the same application without resource optimization which has a load time of 3.37s.