Introducing WebJars – Web Libraries as Managed Dependencies

Update: I’ve created a Spring MVC WebJars example.
Update 2: Ukrainian translation here – http://softdroid.net/vvedennya-webjars by Eclipse Android.
Update 3: WebJars.org has been officially launched! Learn more.

Our web apps are using more and more web libraries like jQuery, Backbone.js and Twitter Bootstrap. The traditional way to use those libraries is to locate & download the JavaScript and CSS source then just copy it into a project. To me this resembles how we used to just copy JAR files into a project’s WEB-INF/lib dir. But why not do with web libraries like we now do with Java libraries and specify them as managed dependencies? This allows us to declaratively set the version, use a consistent version across an application, and easily deal with transitive dependencies. Then we just need web frameworks that can serve static assets from JAR files and we are good to go! Luckily Play 2 and Dropwizard both have out-of-the-box support for this. So I decided to give it a try…

I packaged up some JavaScript and CSS web libraries into JARs, put them into a Maven repo and it worked! And thus the WebJars project was born!

Lets look at an example for how to use the Twitter Bootstrap WebJar in a Play 2 app. First we need to add the WebJars Maven repo to the Play 2 dependency resolvers and specify Bootstrap as a dependency. For Play 2 this is done in the “project/Build.scala” file. Here is one for my Play 2 WebJars Demo project:

import sbt._
import Keys._
import PlayProject._
 
object ApplicationBuild extends Build {
 
    val appName         = "play2_webjars_demo"
    val appVersion      = "1.0-SNAPSHOT"
 
    val appDependencies = Seq(
      "com.github.twitter" % "bootstrap" % "2.0.2"
    )
 
    val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA).settings(
      resolvers += "webjars" at "http://webjars.github.com/m2"
    )
 
}

Now to use Bootstrap in a Scala template, we can just do:

<link rel='stylesheet' media='screen' href='@routes.Assets.at("stylesheets/bootstrap.min.css")'>

The “@routes.Assets.at” thing just gets a reverse route for the URL that will serve the “bootstrap.min.css” file from the Bootstrap WebJar. Note: It’s not necessary in Play 2 to use the reverse routing but it is a type safe way to get a URL. Based on the default routes in Play 2 the route to that file will be “/assets/javascripts/bootstrap.min.js” which we could have used instead.

Now here is the super cool part… The Bootstrap WebJar depends on jQuery so now I can also just use jQuery:

<script type='text/javascript' src='@routes.Assets.at("javascripts/jquery.min.js")'></script>

That is super simple and now I’m managing my web libraries as dependencies!

Note: Play 2 actually puts a copy of jQuery in the default project template but hopefully for Play 2.1 they will pull it out and instead use the jQuery WebJar. In my demo project I’ve removed that copy of jQuery because it’s no longer needed.

If you want to use WebJars with Dropwizard then first setup the Maven “pom.xml” file with the WebJars repo and add the dependency:

    <repositories>
        <repository>
            <id>webjars</id>
            <url>http://webjars.github.com/m2</url>
        </repository>
    </repositories>
 
    <dependencies>
        <dependency>
            <groupId>com.github.twitter</groupId>
            <artifactId>bootstrap</artifactId>
            <version>2.0.2</version>
        </dependency>
    </dependencies>

Then add an “AssetBundle” that will serve static assets in the “public” directory on the “/public” URL path:

    addBundle(new AssetsBundle("/public/", 0, "/public"));

Now you can load the static assets in a web page:

    <link rel='stylesheet' media='screen' href='/public/stylesheets/bootstrap.min.css'>
    <script type='text/javascript' src='/public/javascripts/jquery.min.js'></script>

Get the full source code for the Dropwizard WebJars demo on GitHub.

I’m sure there are other Java web frameworks that support WebJars, so if you know of one, let me know and I’ll try to create more demo projects.

Right now there are just a couple WebJars in the repository but if you’d like to see others then create a new issue on GitHub and I’ll build a new WebJar.

Let me know what you think about WebJars. Thanks!

This entry was posted in Dropwizard, Java, Play Framework, WebJars. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.
  • Scott Frederick

    This looks interesting James.

    Grails is another JVM-based web framework that supports something like this. Grails has a Resources plugin (http://grails.org/plugin/resources) that helps manage static web resources. Other plugins build on Resources to manage dependencies on jquery, jquery-ui, bootstrap, and other libraries.

    It seems like a Resources plugin to WebJars bridge would make some sense, and should be easy to implement.

    • http://www.jamesward.com James Ward

      Yes it is!  Do you know if it can serve static assets from a JAR on the classpath?

      • Scott Frederick

         Sorry, I pressed the submit button too soon on my comment.

        The Resources-based plugins package static assets in a Grails plugin format. The resources get unpacked into the project structure when the plugin is installed. Again, it seems like using the Resources plugin as a way to support WebJars in Grails should be do-able.

        • http://www.jamesward.com James Ward

          Awesome.  I’ll have to give it a try.  Thanks!

  • http://twitter.com/paulecci Paulecci

    Well guys, Apache Wicket is supporting this feature since first release in 2005 .. 

    • http://www.jamesward.com James Ward

      Awesome!  Can you point me to an example so that I can create a little demo app?

      • http://twitter.com/paulecci Paulecci

        Hi James, 

        You may want to give a look here http://www.wicket-library.com/wicket-examples/

        Best

        • http://www.jamesward.com James Ward

          Do you know which one of those has an example of serving static assets from a JAR?

          • http://twitter.com/paulecci Paulecci

            Check it this http://www.wicket-library.com/wicket-examples/images/

            And look for the PackageResourceRerefence in the Wicket javadocs

          • http://www.jamesward.com James Ward

            On that link I get:
            java.lang.OutOfMemoryError: PermGen space

            :(

    • http://timperrett.com/ Timothy Perrett

      Yeah, and Lift borrowed it from Wicket in late 06 / early 07, and all the external plugins for Lift (widgets etc) all use this technique. 

      • http://www.jamesward.com James Ward

        That is great to hear!  Is there already a repo that Lift users use to get common web libraries from?

        • http://timperrett.com/ Timothy Perrett

          Lift ships with a range of JS+CSS deps; they’re just sat in the core lib at the moment, but many people write external ones that plug into the asset resolution system. If you’re interested, take a look at ResourceServer in the code base, or Lift in Action, page 164 if applicable. Cheers :-)

          • http://www.jamesward.com James Ward

            Awesome!  Thanks Timothy.  I’ll have to create a simple Lift demo that uses WebJars.

  • Nraychaudhuri

    Interesting. I like the idea

  • http://yobriefca.se James Hughes

    Will this solution resolve the correct version of, say, jQuery when declared as a dependency?  Imagine I had Twitter Bootstrap and some arcane old jQuery plugin.  Now these 2 things have dependencies on two different versions of jQuery (the old plugin is locked to a very specific version) – will these things naturally co-exist or would there be some work involved in making this happen?

    • http://www.jamesward.com James Ward

      Yup!  That is exactly how the Java dependency managers work.  Right now I’ve made Twitter Bootstrap 2.0.2 depend on jQuery 1.7.1.  But if you want to use jQuery 1.5.1 (right now the only other version in the repo), then you can explicitly set it as a dependency and it will override the transitive dependency.

      • http://yobriefca.se James Hughes

        What happens then if I reference,

        @routes.Assets.at(“javascripts/jquery.min.js”)

        in my view?  What one does it pull in?  How do you pull both in if they are named the same in the jar?

        • http://www.jamesward.com James Ward

          That would reference which ever version the dependency manager has resolved.  The current implementation doesn’t allow you to use multiple versions of a web library in a single project.

          • http://yobriefca.se James Hughes

            Ah right gotcha.  I’d imagine this is a bit of an edge case anyhow but just wanted to be sure.

          • http://www.jamesward.com James Ward

            I thought about organizing things different so that you could do this.  I could make the artifactId for jQuery contain the major.minor version, like “jquery-1.7″.  I’d love to hear feedback on whether or not people think that approach would work better for them.

          • http://yobriefca.se James Hughes

            OK so I’ve seen about 2 projects in my entire career that mixed different versions of libraries and both were “legacy” projects undergoing migration.  In these cases you’d never have both under dependency management anyway.

            Soooooo with that in mind is the effort of changing strategy actually worth it?  I’d say yes but then I don’t know how much effort is involved!

  • Frank Wienberg

    James, thanks a lot for your great initiative! As you know, we at Jangaroo have been using Maven for (generated) JavaScript for quite a while. Independent of your project (which I stumbled upon only yesterday), we just started utilizing a Java feature new in Servlet 3, which standardizes how to package Web resources inside JARs. Inspired by your post / project, I just blogged about our approach, and would be happy if we could consolidate both projects!
    http://blog.jangaroo.net/2012/06/managing-javascript-libraries-with.html 

    • http://www.jamesward.com James Ward

      Hey Frank!  That is fantastic stuff!  I wasn’t aware of that feature of Servlet 3.  Very cool!

      I’d love to work with you guys on this.  I think there are two places we could work together right away:
      1) Would love to get you involved in defining the organization of WebJars: https://github.com/webjars/webjars.github.com/issues/13
      2) I’d love to get these into Maven Central like you’ve done.  Maybe you could help with that part?
      https://github.com/webjars/webjars.github.com/issues/24

      I love how we always think alike!  :)

      -James

      • Frank Wienberg

        It’s always a pleasure :-)
        I just commented on both issues!

  • Pingback: Edge Caching With Play 2, Heroku, and CloudFront()

  • Pingback: WebJars Officially Launched!()



  • View James Ward's profile on LinkedIn