Bundles are the core functionality of Jawr. A bundle is a group of resources joined together in a single file that has an associated logical name.
Common misconception: Note that you are not required to add each and every resource you want compressed to a bundle. Standalone resources can also be referred by a tag library and they will be compressed/gzipped as well (see the orphan resources section below for more information).
To create a bundle the bare minimum you will need is a name and a group of resources. For instance, the following lines in your descriptor file would create a bundle named /bundles/fooBundle.js:
jawr.js.bundle.foo.id=/bundles/fooBundle.js jawr.js.bundle.foo.mappings=/js/foo.js, /js/bar.js
Note that the property jawr.js.bundle.foo.id is declaring two different identifiers: the identifier foo, which is used within other property declarations (such as jawr.js.bundle.foo.mappings), and a path identifier for script tags (/bundles/fooBundle.js).
The newly created bundle would be the union of foo.js and bar.js. Once created, you could then use any of three ways to import the whole bundle to your pages:
Id of the bundle: <jwr:script src="/bundles/fooBundle.js"/> Path to foo.js: <jwr:script src="/js/foo.js"/> Path to bar.js <jwr:script src="/js/bar.js"/>
As you can see, a bundle can be invoked by using either its id or the path to any of its members. The page link rendered will be the same in all the three sample cases:
<script type="text/javascript" src="/myWarContext/prefix/bundles/fooBundle.js" ></script>
Normally, it would be a best practice to use the resource names directly, since:
This holds true even if you use several members of a bundle on the same page. Jawr will only include the same bundle once per page, even when several tags match it. On the other hand, if a bundle conveys a single general functionality (such as validation), then it is probably best to invoke it by id.
It is recommended that you use a virtual prefix for bundle ids (such as ‘/bundles/’). The prefix should not correspond to any actual directory in the root resources dir, to avoid namespace collisions.
When defining a bundle ID, you must ensure that your bundle ID has the right suffix ( “.js” or “.css” ). Jawr doesn’t allow you to define bundle whose the ID starts with ‘/WEB-INF’ and ‘/META-INF’.
So valid bundle IDs are :
jawr.js.bundle.foo.id=/bundles/fooBundle.js ... jawr.css.bundle.bar.id=/bundles/barBundle.css
There are three ways to map resources:
Note :
If you use a mapping like “/js/lib/” or “/js/lib/**”, by default Jawr will sort the resources in the directory by alphabetical order.
You can add any combination of the three types of mapping to your bundles. for instance, let’s add another bundle to the previous example. We will name this bundle /bundles/bazBundle.js. It will contain every file under /js/baz/ and any of its subdirs, a script located at /js/someScript.js, and every file directly under /js/someSubdir/.
jawr.js.bundle.foo.id=/bundles/fooBundle.js jawr.js.bundle.foo.mappings=/js/foo.js, /js/bar.js jawr.js.bundle.baz.id=/bundles/bazBundle.js jawr.js.bundle.baz.mappings=/js/baz/**, /js/someScript.js, /js/someSubdir/
For details on how the resources are ordered within the bundle, refer to the source ordering manual page.
When defining the bundle mapping, Jawr allows you to define mapping containing files which have the right suffix ( “.js” or “.css” ) depending on your bundle, except for generated resource, where there is no restriction on the suffix.
There are some alternative ways to get resources into bundles. These are meant to include resources that are not in the WAR file structure: some as resources that are available only through the classpath, and others as somehow generated content. This will be useful if your application uses a library that has its own javascript or CSS files. To add these a special syntax is used in the mapping definition, which consists in using a specific pattern (for example : a prefix followed by a colon ‘:’).
jawr.js.bundle.foo.id=/bundles/fooBundle.js # Map to a script in the com.mycompany.myapp package and to another from the WAR jawr.js.bundle.foo.mappings=jar:/com/mycompany/myapp/foo.js, /js/bar.js
The full documentation for the available generators and instructions on how to create your own are here.
Global bundles are useful for including common libraries and modules used throughout you site. Any bundle defined as global will always be included before any other bundle. Most of the time you will not need to explicitly invoke it since Jawr will automatically include all global bundles before any particular bundle in a page. So when you add a link to whichever bundle in your page, links will be created to all global bundles and then one more for the bundle you specified.
To define a global bundle you would do as follows:
jawr.js.bundle.lib.id=/lib.js jawr.js.bundle.lib.mappings=/js/lib/** jawr.js.bundle.lib.global=true jawr.js.bundle.lib.order=1
We set the global parameter to true and then we specified an order number. The order value is only needed if you define more than one global bundle and you need a specific order of inclusion.
Every HTML link that Jawr creates has a section of its path dedicated to versioning. Jawr will serve its resources using aggressive caching headers, in order to keep clients from requesting the same bundle over and over. But, when you make changes you want to be sure that every client will download the new version. Thus, the best way to do this is to actually change the URL for the resource whenever the resource changes.
While in the past the prefix for a bundle had to be specified manually, now Jawr creates this prefix automatically, based upon the hash of the bundle contents. Since the generation has repeatable results, this prefix remains the same until you make a change to any member of the bundle, even across redeployments.
Postprocessors are filters that Jawr applies to resources and bundles during startup. Check the postprocessors manual page for more info.
Each bundle you define can override the global postprocessing options. This is useful for example if you only want to minify a subset of your files.
To change the global postprocessor chain for a bundle, specify the bundlepostprocessors and/or the filepostprocessors attribute:
jawr.js.bundle.foo.bundlepostprocessors=license jawr.js.bundle.foo.fileprocessors=none
In this example, the bundle postprocessor for the foo bundle is set to license, meaning no minification will occur. And for the file-by-file postprocessor, it is specified that none will be used.
A composite bundle is made up of several bundles that may have different configuration attributes. For example, you might have a pre minified javascript library and need to bundle it along some of your own scripts. You can could then create a composite bundle in which your scripts are minified but the library is not.
Another use case for composites is to have part of a bundle appear in debug mode but not in production, such as loggers and the like.
Creating a composite is just like creating any other bundle, only instead of the mappings, you specify the child bundles that make the composite up using the child.names attribute. You also need to specify that the bundle is a composite by setting the composite attribute to true:
jawr.js.bundle.comp.id=/bundles/composite.js jawr.js.bundle.comp.global=true jawr.js.bundle.comp.composite=true jawr.js.bundle.comp.child.names=foo, bar ...
For this composite, we declared two children: foo and bar. The child bundles for a composite differ from normal bundles in that they are not declared with an id property, and they cannot be declared as global. Otherwise, every other attribute can be specified individually, such as postprocessors and the debugnever and debugonly attributes:
jawr.js.bundle.foo.mappings=/js/foo.js, /js/bar.js jawr.js.bundle.foo.debugnever=true jawr.js.bundle.bar.mappings=/js/baz/**, /js/someScript.js, /js/someSubdir/ jawr.js.bundle.bar.bundlepostprocessors=none
With such mapping, we specify that, when in development mode, the composite will not contain the files mapped by the foo bundle, and that the baz bundle will not use any postprocessor even if there were globally defined ones.
Since version 3.8, it is possible to declare nested composite bundle. For example, you can reference a composite bundle inside another composite bundle, like below :
jawr.js.bundle.libs.id=/bundles/libs.js jawr.js.bundle.libs.composite=true jawr.js.bundle.libs.child.names=angular jawr.js.bundle.angular.id=/bundles/angular.js jawr.js.bundle.angular.composite=true jawr.js.bundle.angular.child.names=\ angular_prd,\ angular_dev jawr.js.bundle.angular_prd.debugnever=true jawr.js.bundle.angular_prd.mappings=\ /webjars/angularjs/*/angular.min.js,\ /webjars/angularjs/*/angular-animate.min.js,\ /webjars/angularjs/*/angular-cookies.min.js,\ /webjars/angularjs/*/angular-route.min.js jawr.js.bundle.angular_dev.debugonly=true jawr.js.bundle.angular_dev.mappings=\ /webjars/angularjs/*/angular.js,\ /webjars/angularjs/*/angular-animate.js,\ /webjars/angularjs/*/angular-cookies.js,\ /webjars/angularjs/*/angular-route.js
Since the version 3.2, Jawr allows you to define the dependencies for your bundles. This means that if your bundle defines dependencies to other bundles, when you include your bundle in a page, Jawr will add the dependencies in your page before referencing your bundle, if these dependencies are not already included. To define your dependencies, you need to use dependencies property in your bundle definition.\ This property contains a list of comma separated bundle names, on which the bundle depends. You can define dependencies to a bundle or to a composite bundle.
jawr.js.bundle.foo.id=/bundles/fooBundle.js jawr.js.bundle.foo.mappings=/js/foo.js, /js/bar.js jawr.js.bundle.foo.dependencies=baz,bar jawr.js.bundle.baz.id=/bundles/bazBundle.js jawr.js.bundle.baz.mappings=/js/baz/**, /js/someScript.js, /js/someSubdir/ jawr.js.bundle.bar.id=/bundles/barBundle.js jawr.js.bundle.bar.mappings=/js/bar/**, /js/someScript1.js, /js/someSubdir1/
With such mapping, if you include the bundle /bundles/fooBundle.js in your page, if the bundles /bundles/bazBundle.js and /bundles/barBundle.js are not included, then Jawr will automatically add the reference to the missing dependencies.
<head> <jwr:script src="/bundles/fooBundle.js"/> </head>
In our example, this will generate:
<script type="text/javascript" src="/myWarContext/prefix/bundles/bazBundle.js" ></script> <script type="text/javascript" src="/myWarContext/prefix/bundles/barBundle.js" ></script> <script type="text/javascript" src="/myWarContext/prefix/bundles/fooBundle.js" ></script>
The dependency resolution algorithm can be summed up as follows:
The dependencies are included before the bundle, and there are included in the same order as the one defined in the dependencies declaration.
And if a dependency bundle has also a dependency in that case, the bundle dependencies will be included before the dependency itself.
jawr.js.bundle.foo.id=/bundles/fooBundle.js jawr.js.bundle.foo.mappings=/js/foo.js, /js/bar.js jawr.js.bundle.foo.dependencies=baz,bar jawr.js.bundle.baz.id=/bundles/bazBundle.js jawr.js.bundle.baz.mappings=/js/baz/**, /js/someScript.js, /js/someSubdir/ jawr.js.bundle.baz.dependencies=qux jawr.js.bundle.bar.id=/bundles/barBundle.js jawr.js.bundle.bar.mappings=/js/bar/**, /js/someScript1.js, /js/someSubdir1/ jawr.js.bundle.qux.id=/bundles/quxBundle.js jawr.js.bundle.qux.mappings=/js/qux/**, /js/someScript2.js, /js/someSubdir2/
With the mapping above, this is how Jawr will resolve the dependencies of the bundle foo:
So the inclusion order, resulting from the dependency resolution is the following:
bar1, baz, bar, foo
Global bundle
It is unnecessary to reference a global bundle in your dependencies, because it will be automatically be added in any case. It is also important to note that you can’t define dependencies for global bundles. This property is not allowed for this kind of bundle. To define the order of the global bundle, you can use the order property. Please check the descriptor syntax doc for more information.
Circular dependencies
The circular dependencies are not allowed. So if you define dependencies, where a bundle appears twice in the dependency chain, a net.jawr.web.exception.BundleDependencyException will be thrown during the bundling process. You will see below an example of circular dependency where /bundles/fooBundle.js appears twice in the dependency chain.
jawr.js.bundle.foo.id=/bundles/fooBundle.js jawr.js.bundle.foo.mappings=/js/foo.js, /js/bar.js jawr.js.bundle.foo.dependencies=baz,bar jawr.js.bundle.baz.id=/bundles/bazBundle.js jawr.js.bundle.baz.mappings=/js/baz/**, /js/someScript.js, /js/someSubdir/ jawr.js.bundle.bar.id=/bundles/barBundle.js jawr.js.bundle.bar.mappings=/js/bar/**, /js/someScript1.js, /js/someSubdir1/ awr.js.bundle.bar.dependencies=foo
External bundles are used to reference resources, which are not handle by Jawr. You can still use the jawr tag library to An external bundle is a bundle with a specific URL to use in debug mode and in production mode. For example, in the following mapping :
jawr.js.bundle.external.id=/bundles/externalBundle.js jawr.js.bundle.external.debugURL=http://mycomp/js/foo.js jawr.js.bundle.external.productionURL=http://mycomp/js/foo.min.js
External bundles have some restrictions :
In Jawr terminology, orphan resources are those not mapped to any bundle after initial configuration. The ‘jawr.[js or css].bundle.basedir’ property lets Jawr know where to look for orphan resources. After all bundles are created, Jawr scans this location and its subdirs to find non mapped resources (if the property is not specified, Jawr will search from the web app root dir).
You may choose not to define any bundle at all, so all resources will be orphan. Typically, you would want this if you want Jawr to act as a traditional (yet powerful) resources compressor.
Warning: When working with Jawr, bear in mind that all bundles need to be referenced using the tag library or the javascript link generator for non-JSP pages. Something that usually fools unaware Jawr users is the fact that using regular <script> or <link> tags with orphans will work in debug mode. However, when going into production mode, things start breaking badly because the URLs remain the same in both modes and they should actually contain extra path information for production.
You can configure Jawr to do one of three things with orphans:
If the path of an orphan file accidentally matches the name of any of your bundles an exception will be thrown since bundles can not share the same path. Therefore it is recommended to reserve some namespace prefix for bundles, such as /bundles/.
# For js resources. CSS is the same but properties start with jawr.css.* jawr.js.factory.use.singlebundle=true # Use any name you want jawr.js.factory.singlebundle.bundlename=/bundles/allInOne.js
Note that you may actually define some bundles and then use this feature if that suits you. Also, the usual ordering scheme will apply to the orphans bundle. Check the source ordering manual for more info.
In some special cases you might want to have bundles served from a static URL when in production. To achieve this you would specify the production URL using the productionURL:
jawr.js.bundle.foo.productionURL=http://someserver.com/myscript.js
This would be useful when you use libraries which are publicly served by the author’s CDN, such as the YUI library. You could work on your app offline using your local files, with debug mode on, and then in production your users will download from the CDN elsewhere, thus reducing your required bandwith. Needles to say, you must take good care to be sure that your local files contain the same code as the publicly served ones.