permalink

22

Making Maven Grunt.

Getting Grunt, Yeoman and Bower into your Maven workflow

Introduction

Developers working in agencies often tell us that they want to be able to use modern front-end tooling in their workflow. One set of options this manifests as is using Grunt for their build tasks (instead of Ant, bash or Gradle), Bower for dependency management (instead of Maven or Ivy) and Yeoman for application scaffolding. For personal projects, these tools are straightforward to get setup.

Note: If you are new to this tooling chain, Grunt is a JavaScript task runner which you can think of as Ant for JavaScript. It has a vast ecosystem of predefined tasks, some of which are maintained by the core team and many contributed by the community. There are a number of guides available to help you get started. There’s a beginner’s guide to Grunt, Building a JS library with Grunt.js and Building apps with the Yeoman workflow, all of which are solid recent introductions for beginners.

Complexity however arises when you’re working in a team where some developers focus on the client-side and others on the back-end – it’s a situation that is quite commonplace. More times than not, it’s the

back-end team who will define the build process and end up having to wrangle your front-end code into there. Was Maven chosen for this purpose? A lot of the times the answer to this question is sadly “yes”.

Whilst there are many plugin options for JavaScript processing in the Maven world, these plugins can be a real chore to create, maintain and update. In addition, they often have outdated dependencies – meaning you need to monkey-patch them to work and developers – especially those not coming from a Java background – dislike it.

Note: If you’re using Gradle instead of Maven, Ted Naleid has a guide for calling Grunt tasks from Gradle that’s got you covered.

Tooling options

Our biggest piece of feedback from developers stuck in this situation is:

“We don’t know how to integrate Grunt tooling into our Maven workflow. Even after setting up Node on our dev. box, there’s no documentation about using Maven with Grunt and everyone seems to want it but there are no blessed solutions out there”.

Thinking about this problem at a high-level there are a few options available:

1) Consider your backend and front-end code as completely separate entities. Your backend should just be an API and you can maintain your front-end as a separate client app that consumes the data it provides as a service. In this scenario using completely different tools shouldn’t be too much of a problem. Your front-end engineers can use Grunt/Yeoman/Bower and your back-end team can continue using Maven.

2) Ignore modern tooling altogether. Rather than considering both projects as independent pieces, find or write Maven plugins for building your front and back-end, running tests and managing dependencies as needed. Options include the Rhino based JAWR – which hasn’t been updated in forever and Wro4J – which has an extensive plugin list but is slow compared to Node tooling. The problem with this solution is that whilst it works, you just won’t have the task flexibility as you get with Grunt’s task ecosystem.

3) Use the Maven-exec plugin to call out to Grunt from your existing build process so it can build your front-end code, run tests and so on. You could alternatively use the ant-run plugin together with the ant-exec task (thanks to this SO thread for the tip).

If I was starting work on a completely new web application, I’d always go for option 1). It’s the most clean and because your backend is being treated as an API / data service other apps your company developers could easily use it without having to worry about it being coupled to just one project.

For most other cases, option 3) will allow you to get Grunt into your Maven workflow.

Making Maven Grunt

Speaking of options, here are a few code snippets that will help you get Grunt into your Maven workflow. For option 3), Maven user implemented an ant-run plugin setup for using Grunt that looked a little like this:

<plugin>
  <artifactId>maven-antrun-plugin</artifactId>
  <version>1.7</version>
  <executions>
      <execution>
<phase>generate-sources</phase>
<configuration>
    <target name="building">
        <echo>
<!--  NPM INSTALL  -->
        </echo>
        <exec executable="cmd" dir="${project.basedir}"
        osfamily="windows" failonerror="true">
            <arg line="/c npm config set color false"/>
        </exec>
        <exec executable="bash" dir="${project.basedir}"
        osfamily="unix" failonerror="true">
            <arg line="npm config set color false"/>
        </exec>
        <exec executable="cmd" dir="${project.basedir}"
        osfamily="windows" failonerror="true">
            <arg line="/c npm install"/>
        </exec>
        <exec executable="bash" dir="${project.basedir}"
        osfamily="unix" failonerror="true">
            <arg line="npm install"/>
        </exec>
        <echo>
<!-- GRUNT  -->
        </echo>
        <exec executable="cmd" dir="${project.basedir}"
        osfamily="windows" resultproperty="cmdresult">
            <arg line="/c grunt --no-color >
            grunt.status "/>
        </exec>
        <exec executable="bash" dir="${project.basedir}"
        osfamily="unix" resultproperty="cmdresult">
            <arg line="grunt --no-color > grunt.status"/>
        </exec>
        <loadfile property="grunt.status"
        srcFile="grunt.status"/>
        <echo>${grunt.status}</echo>
        <delete file="grunt.status" quiet="true"/>
        <condition property="cmdsuccess">
            <equals arg1="${cmdresult}" arg2="0"/>
        </condition>
        <fail unless="cmdsuccess"/>
    </target>
</configuration>
          <goals>
              <goal>run</goal>
          </goals>
      </execution>
  </executions>
</plugin>

For the Maven-exec sample, the following should help you get setup:

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>exec-maven-plugin</artifactId>
  <version>1.2.1</version>
  <executions>
    <execution>
      <phase>prepare-package</phase>
      <goals>
        <goal>exec</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <executable>grunt</executable>
  </configuration>
</plugin>

You can customise your Maven-exec setup to run a shell script that calls out to any custom Grunt build profiles (e.g grunt build, grunt build:prod --force, grunt build:dev --force) and so on. As mentioned earlier, your only real requirement is that Node needs to be available on your box in order for Grunt (and other tools) to be run from your Maven setup. That’s it.

Handling WAR Files

Okay, so we can now integrate Grunt into our Maven workflow but that isn’t the complete story. You still ultimately need to be able to produce a WAR file – a JAR file used to distribute a collection of JavaServer Pages, servlets, static Web pages (HTML/CSS/JS) and other resources that together constitute a Web application in a Java driven web app.

For reference, GitHub user ahabra put together a boilerplate for Maven projects wishing to use Grunt which includes:

  1. Maven project which produces war file
  2. Maven dependencies for: junit, commons-lang3, guava-13
  3. The web app directory includes a JavaScript directory for your scripts (/js)
  4. Third-party JS files are in js/vendor, include: jquery, underscore.js, namespace.js, tstring.js
  5. Jasmine specs in test/js/jasmine-specs
  6. Gruntfile.js configured to lint all the js code and run jasmine specs.

Not to shabby. A prescribed Maven + Grunt workflow is also where some users have found the Yeoman Maven plugin useful.

Like Maven-exec, it helps you run grunt from your Maven build but also ensures that Bower gets all of your needed dependencies too. With just a small amount of configuration you can use all of the modern tools we introduced earlier without too much extra work.

Using the plugin is fairly easy. First, declare the plugin:

<plugin>
    <groupId>com.github.trecloux</groupId>
    <artifactId>yeoman-maven-plugin</artifactId>
    <version>0.1</version>
    <executions>
        <execution>
            <goals>
                <goal>build</goal>
            </goals>
        </execution>
    </executions>
</plugin>

You’ll then want to add the yeoman dist directory to your WAR file.

<plugin>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.3</version>
            <configuration>
        <webResources>
            <resource>
                <directory>yo/dist</directory>
            </resource>
        </webResources>
    </configuration>
</plugin>

And finally configure the clean plugin in order to delete generated directories.

<plugin>
    <artifactId>maven-clean-plugin</artifactId>
    <version>2.5</version>
    <configuration>
        <filesets>
            <fileset>
                <directory>yo/dist</directory>
            </fileset>
            <fileset>
                <directory>yo/.tmp</directory>
            </fileset>
            <fileset>
                <directory>yo/app/components</directory>
            </fileset>
            <fileset>
              <directory>yo/node_modules</directory>
            </fileset>
        </filesets>
    </configuration>
</plugin>

Note: If you happen to use Jetty, the the Java HTTP web server, Patrick Grimard has a good write-up about setting it up with the Yeoman Maven plugin that you might find useful.

Dependency management

Next, let’s talk a little more about dependency management in your app using Maven. When working on an app with a Java backend and a HTML/JS/CSS front-end Bower can actually help keep your project more maintainable.

Instead of having to check-in your front-end dependencies into the src/main/webapp directory, your bower.json file can be checked into your repository instead. Once again using the Maven exec plugin, Bower can be run as a part of your Maven build. This avoids the need to check-in your front-end.

From xebia:

<plugin>
   <groupId>org.codehaus.mojo</groupId>
   <artifactId>exec-maven-plugin</artifactId>
   <executions>
     <execution>
     <phase>generate-sources</phase>
     <goals>
      <goal>exec</goal>
     </goals>
     </execution>
   </executions>
   <configuration>
     <executable>bower</executable>
     <arguments>
      <argument>install</argument>
     </arguments>
     <workingDirectory>${basedir}/src/main/webapp
</workingDirectory>
   </configuration>
 </plugin>

Middleware

Another question that might arise is how your back-end web server, such as Tomcat might fit in as middleware.

When attempting to make remote XHR calls to your web server (which may be running on a different port) you may find them failing as your browser will refuse to run an XHR to a different origin. One solution to this is adding a proxy within your local server to handle same-origin restriction. The grunt-connect-proxy provides a HTTP proxy as middleware for the grunt-contrib-connect plugin.

After installing the module, your Grunt server will proxy requests to your back-end as follows:

var proxySnippet
  = require('grunt-connect-proxy/lib/utils').proxyRequest ;
// Modify the connect configuration task:
// add proxies section and insert 'proxySnippet'
// in the middleware
   connect : {
     proxies : [
       {
         context : '/rest' ,
         host : 'localhost' ,
         Port : 8080 ,
         https : false ,
         changeOrigin : false
       }
     ]
     options : {
       port : 9000 ,
       hostname : 'localhost'
     },
     livereload : {
       options : {
         middleware : function ( connect ) {
           return [
             proxySnippet ,
             lrSnippet ,
             mountFolder ( connect , . 'tmp' )
             mountFolder ( connect , yeomanConfig . app )
           ];
         }
       }
     }
    }
  }
// Modifify the server task to coincide with
// the configureProxies step
 grunt.registerTask ('server' , [
   'clean: server' ,
   'coffee dist' ,
   'compass: server' ,
   'configureProxies' ,
   'livereload-start',
   'connect: livereload' ,
   'open' ,
   'watch'
 ]);

Conclusions

Whilst I’m by no means a Maven expert, I hope you’ve found some of the tips in this write-up useful.

As a community, I think we can do better. Creating better boilerplates, Yeoman generators and docs for the Maven + Grunt workflow would greatly help a number of developers. We need engineers with experience in these tools to help us work on open-source solutions to these problems. It’s necessary for modern tooling to really have an impact in the real world.

That said, If you’re working on a project where the front-end and back-end are somewhat coupled, using Maven as your primary build system with binaries like Grunt and Bower called out as needed is completely feasible. For fresh projects, keeping these two pieces distinctly separate comes with the flexibility to use whatever tooling chain you want, including just using Grunt for your front-end.

Further reading

22 Comments

  1. It is nice to see an example of how grunt ecosystem can be integrated into maven based projects.

    I have one small amendment:
    ” Wro4J – which has an extensive plugin list but is slow compared to Node tooling”
    That is not quite true. Wro4j uses various processors, just like grunt uses plugins. When comparing a node based plugin with a Rhino based processor, node.js is a clear winner. But it doesn’t mean that a node based processor cannot be implemented as well. For instance most recent versions of wro4j has node based processors like: nodeCoffeeScript, nodeLessCss, nodeTypeScript. Also there is an alternative to nodeLessCss based on less4j library which uses ANTLR to compile less to css which is even faster to node based less plugin.

    Here are few resources which clarify this aspect:
    http://axelhzf.com/blog/2012-05-28-grunt-vs-wro4j.html
    https://code.google.com/p/wro4j/wiki/IsWro4jSlow

    • Hey Alex,

      Thanks for sharing the clarifications about Wro4J!. I wasn’t aware it was possible to implement Node based processors with it for CS or Less. I’ll have to read up on this a little further.

      Cheers!

  2. Thanks for the nice writeup. I have been using Yo toolset for my personal projects for quite sometime. Now, we want to use it at workplace along with Maven. This will be a good starting point for us. Have a couple of questions related to this:

    1. Bower dependencies. From the bower site: “N.B. If you aren’t authoring a package that is intended to be consumed by others (e.g., you’re building a web app), you should always check installed packages into source control.”. Any reasons for checking in the dependencies. I would prefer to go with your approach of checking in only the bower.json file.

    2. Proxy Middleware. In our typical setup, we will have a HTTP server (Apache) proxying, rewriting the incoming requests to underlying Tomcat instances. Looks like we can do the same with this middleware. Do you recommend using this middleware in production scenario? How mature is the plugin?

    Thanks.

    • Good questions!

      1) In the end it comes down to personal choice.

      Those that do like checking in their dependencies do so to prevent bad dependencies from breaking their app or the remote being down (which may in fact prevent deployment). This could happen even with the presence of a build step as you probably don’t test on each build. There’s also the reasoning that multiple developers may have different versions of a dep and having them committed makes sure everyone stays on the same version. Sindre (on the Yeoman team) likes this as it means you can diff to ensure nothing malicious was introduced into your dependency tree.

      Those that prefer to just keep around their bower.json file might just be running `bower install` as a part of their continuous integration. Your CI might just pull in your dependencies, run all the tests and if it happens that everything works, deploy the exact copy generated onto your staging system. The only real downside to this approach is that running `bower install` on each deployment might be slow and your CI could potentially have to support the entire tooling chain.

      2) I haven’t run into any issues using the plugin but don’t understand quite what you mean. You want to proxy as a part of production?

      • Regarding #1: I keep asking the same question for node modules also. I used to check in node modules till I ran into environment specific compilation issues with modules. In Bower, however, this is not a point to worry about. Checking in bower components might help removing the external dependency (on remote site) for every build in CI system. This will also give some comfort to people from maven background where we can have de-centralized local repositories.

        >> You want to proxy as a part of production?
        Yes, I want to use this proxy in production and mimic the behavior of a HTTP server in front of Tomcat.

        Thanks.

        • grunt-connect-proxy was created as a development proxy to save front-end devs the pain of having to setup a true proxy server like nginx or apache.

          For production, I would recommend strongly that you use one of the production grade proxies like nginx or apache. Alternatively, grunt-connect-proxy is based on the http-proxy library from nodejitsu, which is definitely used in production for several copmanies.

  3. I’m assuming these ideas are still dependant on node.js being installed on the host box.

    We have the issue where the clients build server does not have node, and can’t/won’t/unwilling to install it.

    So we are having to replicate our grunt builds with hacky maven/ant alternatives… it’s not good.

  4. Thanks for the article! I wound up putting all the tasks into the exec-maven-plugin and it seems to work out pretty well.

    Here’s the plugin portion I used to execute npm install and bower install on every compile (I added bower to devDependencies and a bower-install script):

    org.codehaus.mojo
    exec-maven-plugin
    1.2.1

    npm
    generate-sources

    exec

    npm

    install

    bower
    generate-sources

    exec

    npm

    run-script
    bower-install

  5. Thanks Addy! think this is very relevant for a lot of people out there, who silently wrangle with Java & Maven-based backends and “modern” JS front-ends, trying to have Maven do JS test-running, optimization & minification, and packaging, all using various plugins.

    This usually has one or both of the following effects:

    You end up being dependent on a particular plugin that does aaalmost just what you need, so you end up forking it and modify it yourself to make it a better fit for your scenario. Then you have to maintain it alongside your project, costing resources and causing headaches.

    You end up being dependent on a particular plugin to the extend that you build your workflow (or even worse, your architecture) around it, and the maintainer of the plugin abandons it. Then you either enlist as a maintainer or hope that someone else does.

    Either way, you’re locking your project into a set of external dependencies that you can’t control, which is far from good.

    I agree with you, it is much much better to face the fact that Maven is old and not optimal for JS-heavy apps. Isolate the frontend workflow and use appropriate tooling. If its too late for that, extending Maven with Grunt like you explain here is a perfectly viable middle-of-the-road solution.

    Thanks for writing this up and gathering useful resources!

  6. Pingback: Making Maven Grunt. | Nuixen Technology

  7. Excellent post, indeed.

    We want to follow your “option 1″ recommendation for future projects with a mandatory (grrr) Java-based and Maven-built (of course) back-end, but there is one small detail missing to achieve the full “divorce” between front- and back-end. How do you recommend to bootstrap initial application data (model data, I mean), both for development and production? In previous projects, our index.html page served by the (Rails) back-end embedded this initial data, but that would probably not be the case in option 1.

    Thanks a lot!

    • Guillermo Grau

      In order to separate the front- and back- end build processes, you will need to be able to move static assets (that is, the results of the frontend build) into a directory that your back-end serves.

      1. The back-end application is set up to serve static assets from a known location (e.g. app-home/resources/).
      2. The front-end build process (i.e. grunt and company) will compile and deploy the static assets to that know location.

      Maven does not need to get involved, the backend build process doesn’t need to be complicated by adding front-end dependencies, and we don’t need to rebuild the backend application anytime a front-end asset is changed.

      • If you can, set up your back-end application with REST endpoints that provide the data you are looking for. Your front-end application can hit them as needed to get the data it is looking forward.

        Anyways, this is just one way of doing it, and assumes that your front-end application can get all the data it needs by hitting endpoint on your back-end application.

  8. After adding grunt into maven we occasionally run into this error:

    Running “concurrent:dist” (concurrent) task
    Reading target file…
    New Shard Created.
    Warning: Running “htmlmin:dist” (htmlmin) task
    Warning: Unable to create directory “/dist” (Error code: EEXIST). Use –force to continue.

    Aborted due to warnings. Use –force to continue.

  9. Thank you, Addy — great writeup, very helpful! :)

    One *tiny* typo / nit in the prose above:

    “… other apps your company developers could easily use it”

    should be

    “… other apps your company develops could easily use it”

    HTH
    C

  10. You might want to checkout http://jhipster.github.io/ which I have released 1 month ago, and which is getting quite popular.
    It’s a Yeoman generator, that aims to make Maven and Grunt work fine together.
    There are many ideas in common to what you explain in your blog, for the “build” part. It’s also going on step further, as the idea is also to generate AngularJS code that works fine with back-end Java REST services.

  11. Very helpful post, but I had small problems with running the ant-run tasks on Ubuntu.

    The problem is that the executable is “bash”, whereas it should be npm/bower/grunt, i.e.:

  12. I build big rich client-side applications against services written in Java. In the past we have had to install node onto our CI server to use the Maven exec plugin with Grunt for client-side build and testing of our API’s via frisbie.js .

    In the past getting sandboxed versioned Node containers has been complicated, and I’ve been looking at LXC and Docker to alleviate some of the discomfort.

    I recently read about this Maven plugin that installs Node.
    https://github.com/eirslett/frontend-maven-plugin
    For certain use-cases it certainly seems promising.

Leave a Reply

Required fields are marked *.