Fork me on GitHub

Bundle preprocessing

One of the key features of Jawr is its ability to bundle resources. The process of bundling involves the following steps:

  • Resolve the bundle mapping definition
  • Aggregate resources
  • Minify them
  • Apply custom post processors
  • Generate GZIP and non GZIP version

By default, this process is done at server startup. This bundling process could take some time, when the application contains a large number of bundles.

Since Jawr 3.0, we have added a feature to preprocess the Jawr bundles. The bundle preprocessing is almost the same as the standard processing, except that it could be done during a build.

We have added 2 properties:

  • jawr.use.bundle.mapping : This flag indicates if during the server startup we should use the data of the preprocessed bundles instead of performing the bundling process. The preprocessed bundles will be found in the Jawr working directory. (See below)
  • jawr.working.directory : This property defines where the jawr working directory is. By default, it’s jawrTmp in the web application working directory.

Use bundle mapping property

When the property jawr.use.bundle.mapping is set to true, Jawr will try to use specific files, which contain all the data about the bundles (full mapping paths, bundle hash code …). These files are searched in the working directory (jawrWrkDir/jawr-(js/css/img)-mapping.properties ). If these files are not found, Jawr will create them in the working directory.

So if you have this property set to true, and you start a fresh application with an empty working directory, the first time, Jawr will process the bundle normally and it will create the mapping files. Then the next time, when you restart your application, Jawr will use the mappings files and skip the bundling process, this will make the startup time of the web application faster.

Note: The full image mapping file located at jawrWrkDir/jawr-img-mapping.properties, will be generated from the image resources defined with the jawr.image.resources property and the CSS images used in the CSS bundles. So for an image which is referenced by an image tag, and which is not part of the image mapping, the hashcode will be generated at runtime, and put in cache.

Jawr working directory

We have added the option to define explicitly the location of the working directory. By default the working directory is set in webAppWrkDir/jawrTmp/

The user could define the working directory location somewhere else on the file system using a value, which is prefixed by **file://**. For example:

        jawr.working.directory=file://D:/temp/jawr/

The user could also define the working directory inside the web application itself. In that case, the value should be a relative path from the web application root directory. For example:

        jawr.working.directory=/jawrContent/

But in that case, Jawr will not be able to create the temporary files in the web application. So this means that the user will have to create the temporary files, and put them in the web application.

Bundle Processor

This class is responsible for preprocessing the bundles, creating the Jawr working directory and the CDN directory.

In the working directory, you will have the content of Jawr working directory generated by Jawr.

In the CDN directory, you will have all the data for the CDN.

An Ant task and a maven plugin have been created to make it easy to integrate into the build process. For more information about the usage, please check the documentation for the maven plugin and the Ant task.

To implement this feature, we have decided to simulate the server startup.

This means that we are faking the application server startup:

  • We read the web.xml file
  • We initialize the servlets
  • Then we launch the bundling process.

The main method of the bundle processor is the process method. This method takes 5 parameters:

  • baseDirPath the web application base directory path
  • tmpDirPath the temporary directory path
  • destDirPath the destination directory path, where the result will be stored
  • springConfigFiles the comma separated list of spring configuration files, which will be used to initialize the Jawr spring controller. This property accepts any spring resource location definition like classpath:…, file:…, etc
  • servletsToInitialize the list of comma separated names of servlets to initialize
  • generateCdnFiles the flag indicating if we should generate the CDN files or not
  • keepUrlMapping The flag indicating if we want to keep the jawr URL mapping or if we rewrite it to remove resource hashcode.

Limitations

As we have seen, the bundle preprocessing has almost the same behaviour as the standard bundle processing. To implement this feature, we have decided to simulate the server startup. By default we will only initialize the Jawr servlets. You could add in the servletsToInitialize the list of servlets to initialize.

As said above, we only fake a server startup, which means that we don’t initialize the entire web application context (filters, servlets …). This could be a problem if you are initializing things, in the standard server startup, which are used later in Jawr. These elements could be missing. This is why we have added the parameter servletsToInitialize to be able to initialize other servlets, which you could initialize data used later by Jawr.

Spring MVC support

As the bundle processor simulates the server startup, the idea is to only initialize the Jawr component. So with the springConfigFiles parameter, we can choose exactly which beans must be instanciated, in order to initialize the Jawr component.

For Spring MVC, Jawr provides the JawrSpringController which generates the bundles and handles the request. By default, if you don’t provide the spring configuration files to the bundle processor, it will search for the configuration file associated to your spring dispatcher servlet. For example:

        <servlet>
              <servlet-name>dispatcher</servlet-name>
              <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
              <load-on-startup>1</load-on-startup>
        </servlet>

With the below configuration, the bundle processor will search for a configuration file named dispatcher-servlet.xml. Here dispatcher is the name of the servlet.

If in your spring MVC configuration file, you don’t have only the JawrSpringController defined (which is generally the case), we advise you to create a separate file containing only the JawrSpringController declaration, and use the import tag to include it in your spring configuration. In that case, you must pass the spring configuration file containing the JawrSpringController declaration as an argument to the bundle processor.

If your controller uses dynamic properties, you can define these dynamic properties in another file, and pass to the bundle processor the files defining the properties and the JawrSpringController.

For example, if you have the following JawrSpringController declaration :

        <!-- Base Jawr config -->
        <bean id="jawrJsController"  class="net.jawr.web.servlet.JawrSpringController" >
                <!-- This must match the servlet-mapping to which the spring servlet is bound -->
                <property name="mapping" value="/spring/"/>

                <!-- This must match the controller mapping prefix to which the 
                        jawr controllers are bound at the urlMapping bean below -->
                <property name="controllerMapping" value="/jawrJS/"/>
                <property name="configuration">
                        <props>
                                <prop key="jawr.gzip.on">{gzip.resource}</prop>
                        </props>
                </property>
                <property name="configLocation" value="/jawr-springMVC.properties" />
        </bean>

In the configuration above, you will notice that the “jawr.gzip.on” property is set with the value of “gzip.resource”. If you have a such configuration, you will probably have the declaration of your property in another file. And in that case you will pass the file defining the gzip.resource property to the bundle processor, so the Jawr component initialization can be initialized properly.

CDN support

One of the goals of this feature is to provide a better support for CDN. In the CDN directory, generated by the BundleProcessor, you will find all the bundles which are accessible in production and in debug modes.

To define the URL which will contain the Jawr bundles on the CDN, you must specify the context path, which should be used like below:

        jawr.url.contextpath.override=http://myCDN.com/myApplication
        jawr.url.contextpath.ssl.override=https://myCDN.com/myApplication

It is also possible to use the resources files from the CDN even in debug mode using:

        jawr.url.contextpath.override.used.in.debug.mode=true

Apache support

The bundle processor also generates a configuration file, containing the rewrite settings for the httpd.conf file of apache. This file named jawr-apache-httpd.conf is generated in the CDN folder.

Limitations

As explained above, the bundle preprocessing uses the predefined mapping described in the full mapping files. This means that in debug mode, if you update a resource file, which is already part of a bundle, the change will be taken in account.

BUT if you add a new resource file to a bundle mapping, this resource file will never be taken into account, unless you clean your working directory and start your application again.

Note also that, you could switch from debug mode to production mode, without launching the bundling process, but you have to keep in mind that the production bundles are the ones which have been processed during the bundling phase. This means that if you have modified a resource file, since the latest bundling process, you will have to launch the bundling process again to have this updated resource file in your production bundle.