Fork me on GitHub

Integrating Jawr in a framework

Jawr lends itself to integrating in web frameworks of wider scope. If you have one and want to use Jawr, you probably will want your users to benefit from it instead of just using it for your framework’s needs.

This means that you will probably use Jawr to bundle scripts and CSS to enhance performance. But your framework’s users will probably benefit from using Jawr to bundle and compress their resources with Jawr. Better yet, they could augment the bundles that come with your framework for the tightest integration and lowest response times for pages.

In order to achieve this, Jawr has some hooks to provide multiple configuration files that augment each other. The idea behind this is that you will provide with a base configuration, which defines several standard bundles. Then you provide configuration hooks to add additional config files, in which users define their own bundles and also augment those in your framework.

Two components help in achieving this functionality: ConfigPropertiesAugmenter and MultipleFileConfigSource. The latter is a pre-cooked implementation of the ConfigPropertiesSource interface which you can extend to easily get up and running with this configuration scheme. ConfigPropertiesAugmenter is a lower level component that you can use if extending MultipleFileConfigSource does not suit your needs.

In some cases, you might want to create your own system to render the links and script tags in HTML pages, instead of using the included tag libraries from Jawr. It is actually rather easy to accomplish this, if you follow the instructions at the bottom of this page.

Extending MultipleFileConfigSource

This class loads a configuration file in the usual Jawr way (i.e., loads the config file specified in the configLocation init-param), and then loads further configuration files which augment the base configuration. By default, the additional configuration properties are defined as a parameter in the servlet context, under the key ‘jawr.config.sources’. In the web.xml, configuration would be defined as follows:

        <context-param>
                <param-name>jawr.config.sources</param-name>
                <param-value>file:C:\jawr_A.properties,file:C:\jawr_B.properties</param-value>
        </context-param>

Of course, this is not the best way to achieve this ends. You framework will probably have better ways to configure this parameter, so all you need to do is to extend MultipleFileConfigSource and to override the method that retrieves the parameter value, which has the following signature:

        protected void initAdditionalPropertyBaseNames(ServletContext context); 

This method should initialize a property named ‘propertyBaseNames’ (which is a List object), and add to it the names of the properties files which should override the base configuration. Optionally, you can also initialize another object, this time a Set, named ‘privateConfigProperties’, and add all the names of properties that you don’t wish to let users change or augment.

Once this is done, you only need to register your object as the ConfigPropertiesSource for Jawr, so it will be used instead of the default one. Normally it is done through the servlet configuration:

                <init-param>
                        <param-name>configPropertiesSourceClass</param-name>
                        <param-value>org.myframework.MyConfigPropertiesImplementation</param-value>
                </init-param>

The benefit of overriding MultipleFileConfigSource is that the automatic reloading of configuration will work out of the box. Users will be able to set the time interval to check for changes in the configuration and have Jawr reload whenever changes are made. Also, the syntax to specify the location of config files remains the same as usual (using ‘file:’ as a prefix to load from the filesystem, or no prefix to load from the classpath).

Using ConfigPropertiesAugmenter

Normally, you will not need to use this class directly (being much better to override MultipleFileConfigSource), but it’s important to know its behavior to understand how config augmentation works. So, read on even if you don’t plan on using it. This class has two constructors:

public ConfigPropertiesAugmenter(Properties configProperties);
public ConfigPropertiesAugmenter(Properties configProperties,Set privateConfigProperties);

Both get the base Properties to later augment, and in the second case, a Set of ‘private’ config properties. This set contains names of configuration properties which will not be overwritten or augmented. For instance, if you do not wish to allow users to change the encoding of bundles (which is probably a good idea), you would add ‘jawr.charset.name’ to this Set.

The method which augments configuration is this one:

         public void augmentConfiguration(Properties configToAdd); 

This method will get all properties in configToAdd and overwrite those in the base configuration, except for those which are augmentable. Augmentable properties will be extended. Take for instance a mapping property such as this:

         jawr.js.bundle.one.mappings=/js/someFile.js, /js/someOtherFile.js

Now, in an additional Properties file we have this mapping:

         jawr.js.bundle.one.mappings=/javascript/userProvidedScript.js

Well, after augmentation, the final configuration would look like this:

         jawr.js.bundle.one.mappings=/js/someFile.js, /js/someOtherFile.js, /javascript/userProvidedScript.js

In this example the user augments the mappings of a framework-provided bundle with his own content, thus reducing the total number of required downloads to access his application.

The augmentable properties are the following:

  • Bundle definitions (jawr.[type].bundle.names). Note this property is deprecated as of version 2.7, but you can’t mix configurations with and without it, so be sure to make it clear whether it should be used or not when letting others augment your configuration.
  • Bundle mappings (jawr.[type].bundle.[name].mappings).
  • Composite bundles child members (jawr.[type].bundle.[name].child.names).
  • Custom postprocessors (jawr.custom.postprocessors.names).
  • Generators (jawr.custom.generators).

All the other properties will simply be overwritten.

Documenting your configuration

Once you have Jawr setup this way, the best thing you can do is to document how to use Jawr, and to specify which bundles you are using and how to augment them, in order to get the most out of Jawr usage.

Replicating the tag library behavior

If your framework has anything other than JSP or JSF as the HTML rendering system, then you won’t be able to use the tag libraries that Jawr provides. Luckily, it is quite easy to replicate the behavior using the same API used by the taglib. The only functional requirement is to have access to an HttpServletRequest instance and to the current ServletContext when generating the output. If you have a request and access to the ServletContext, Jawr will be able to generate the HTML for you.

Note that you can get the ServletContext using request.getSession().getServletContext(), but this may not always be desirable since it grants the request a session id.

So, if you are ready to get your hands dirty, first be sure you know how the tag libraries work and the parameters they receive so you understand the APIs described below. The component which generates the output is an instance of the BundleRenderer interface; Jawr provides two implementations of this interface. Obviously there’s one to render script tags and another to render CSS link tags. The renderers are:

net.jawr.web.resource.bundle.renderer.JavascriptHTMLBundleLinkRenderer
net.jawr.web.resource.bundle.renderer.CSSHTMLBundleLinkRenderer

The constructor of both classes requires an instance of a ResourceBundlesHandler component, which you will get from the ServletContext instance using one of two keys, each belonging to a resource type:

ResourceBundlesHandler rsHandler = (ResourceBundlesHandler) 
        servletContext.getAttribute(ResourceBundlesHandler.JS_CONTEXT_ATTRIBUTE);

or:

ResourceBundlesHandler rsHandler = (ResourceBundlesHandler) 
        servletContext.getAttribute(ResourceBundlesHandler.CSS_CONTEXT_ATTRIBUTE);

These ResourceBundlesHandler are created by Jawr during the server startup procedure and set as context attributes using the string keys shown above. If any of these are null, it means that something went wrong while Jawr was initializing. Once you have this reference, it is easy to create an instance of the renderers:

BundleRenderer myJsRenderer = new JavascriptHTMLBundleLinkRenderer(rsHandler, true); 
BundleRenderer myCSSRenderer = new CSSHTMLBundleLinkRenderer(rsHandler, true, "screen");

As you noticed, the constructors receive additional parameters: a boolean and, in the case of the CSS renderer, a String. The boolean corresponds to the tag library parameter ‘useRandomParam’, and the string (which may be null) is the ‘media’ param for the CSS tag. Refer to the taglib docs for further explanation of what these do.

Once you have a proper instance of BundleRenderer, there is just one method to invoke, so the only thing left to do is to gather the required parameters and make the call. Let’s review the API:

public void renderBundleLinks(  String requestedPath, 
                                BundleRendererContext bundleRendererCtx,
                                Writer out ) throws IOException;

The parameters represent the following data:

  • requestedPath corresponds to the taglib’s src attribute, so it may be a bundle id or the path to a bundle member.
  • bundleRendererCtx the context of the renderer. To obtain a BundleRendererContext, you could use the following utility method :

    BundleRendererContext ctx = RendererRequestUtils.getBundleRendererContext(request, renderer);
    

This object contains :

  • contextPath is obviously the request context path, as retrieved from request.getContextPath().
  • variantKey is used for the i18n scripts. To retrieve this you need to perform a somewhat long call using the ResourceBundlesHandler instance you retrieved from the servlet context:

        String variantKey = rsHandler.getConfig().getLocaleResolver().resolveLocaleCode(request);
    
  • includedBundles is a Set which holds a log of written tags during the request so that no tag is written out twice. You don’t actually need to handle its creation or to hold a reference. Instead rely on a utility method that handles everything:

        Set includedBundles = RendererRequestUtils.getAddedBundlesLog(request);
    
  • useGzip determines whether to use the gzip compressed version of the scripts, based on Jawr configuration and on the request headers. Again, a utility method will take care of everything. You will need to reuse the ResourceBundlesHandler instance once more for this one:

        boolean useGzip = RendererRequestUtils.isRequestGzippable(request,rsHandler.getConfig());
    
  • out is a Writer instance into which the tags will be written. If you just need a string it is perfectly okay to use a StringWriter instance for this purpose. Note that Jawr will not flush nor close this Writer.

And this basically covers it. If you want a sample, check out the net.jawr.web.taglib package sources (Jawr sources are available for download and also the SVN is publicly accessible). In this package are all the current implementations of tag libraries used by Jawr, so you can probably get ideas from there.