Integrate Gulp on a Maven managed project

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.

Photo credit: Tony lea

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.

gulpgliffy

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

Under OpenShift, you may need to change home directory (npm needs to create files under HOME directory):
>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.

2015_09_23_16_02_46_tomcat7_samerabdelkafi.rhcloud.com_fng_project_apiDoc 2015_09_23_16_03_24_tomcat7_samerabdelkafi.rhcloud.com_ng_swagger_apiDoc

4 thoughts on “Integrate Gulp on a Maven managed project

  1. Thanks for this useful article ,
    But I have an issue using frontend-maven-plugin with openshift.
    The latest version that work with openshift is 0.0.22 -coz the latest versions require mavern 3.1.0 – , and even when I use this version I get another issues.
    So how the application http://tomcat7-samerabdelkafi.rhcloud.com/fng-project/#/users works despite that the code provided in git contains frontend-maven-plugin with version 25.

    If the example has a different code , can you please redirect me to it.

    Thanks in advance

    • Hi,

      You are right.
      To build the project in openshift, I changed to frontend-maven-plugin to 0.0.22.
      Below the plugin configuration in my server.

      Regards.

      <plugin>
          <groupId>com.github.eirslett</groupId>
          <artifactId>frontend-maven-plugin</artifactId>
          <version>0.0.22</version>
          <executions>
              <execution>
                  <id>install node and npm</id>
                  <goals>
                      <goal>install-node-and-npm</goal>
                  </goals>
                  <phase>generate-resources</phase>
                  <configuration>
                      <nodeVersion>v0.12.7</nodeVersion>
                      <npmVersion>3.3.3</npmVersion>
                  </configuration>
              </execution>
              <execution>
                  <id>npm config</id>
                  <goals>
                      <goal>npm</goal>
                  </goals>
                  <phase>generate-resources</phase>
                  <configuration>
                      <arguments>config set cache node\npm-cache --global</arguments>
                  </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>
      
  2. Hi ,

    I want to pass the gulp task as well as a few parameters to it from the pom. But no matter what options I try it isn’t picking it and always executes default. Is there a way to do this?

  3. Hello – looking for solution similar to JAWR but with bundling on mvn compile time and solution you proposed seems good for me. One question I have – is it possible to set bundling to debug mode like in jawr where you can have access to js source code after application is up and running ? Thanks.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s