Use the servlet filter
This tutorial shows how you can setup WUIC with minimal configuration to filter any HTML page and serve the optimized result.
Prerequisites
You need to install first:
-
JDK 1.6
-
Maven 3
-
Servlet container (we advise to use an already tested server)
Purpose
We are going to setup the minimal configuration to process your statics on the fly. In this example, we consider the following statics to be processed at the root of your maven project:
. |--src |--main |--webapp `index.html |--css `-- foo.css |--js `--foo.js
In our index.html
file, we refer foo.css
and foo.js
as external resources using <script>
and <link>
elements.
Choosing the right dependencies
Define in your pom.xml a property 'wuic.version' with the latest release available on maven central We use SLF4J and WUIC comes with the API as dependency. Please refer to the documentation to import the logger you want.
First, import the servlet support for WUIC.
<dependency>
<groupId>com.github.wuic</groupId>
<artifactId>wuic-servlet</artifactId>
<version>${wuic.version}</version>
</dependency>
In our example, we want to minify CSS, Javascript and HTML code. We can add the htmlcompressor
and yuicompressor
extensions for that:
<dependency>
<groupId>com.github.wuic.extensions</groupId>
<artifactId>wuic-htmlcompressor</artifactId>
<version>${wuic.version}</version>
</dependency>
<dependency>
<groupId>com.github.wuic.extensions</groupId>
<artifactId>wuic-yuicompressor</artifactId>
<version>${wuic.version}</version>
</dependency>
Configure your web.xml
WUIC is bootstrapped automatically thanks to a 'ContextListener` detected by the servlet container. However, you need to specify which resources you want to optimize (here the index.html
file). This could be done by installing the WUIC servlet Filter
:
<filter>
<filter-name>htmlParserFiler</filter-name>
<filter-class>com.github.wuic.servlet.HtmlParserFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>htmlParserFiler</filter-name>
<url-pattern>/index.html</url-pattern>
</filter-mapping>
The optimized HTML will refer optimized CSS and Javascript scripts that need to be served at a specific location. To serve processed nuts in our servlet container, you need to declare the WUIC Servlet
inside your web.xml
file.
<servlet>
<description>WUIC servlet</description>
<servlet-name>wuic</servlet-name>
<servlet-class>com.github.wuic.servlet.WuicServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
Choose your servlet mapping for WUIC, for instance:
<servlet-mapping>
<servlet-name>wuic</servlet-name>
<url-pattern>/wuic/*</url-pattern>
</servlet-mapping>
That’s it
Open you browser display the index.html
file. You will see that your HTML code is now minified. Open the referenced JS and CSS scripts, they are also minified!
Tip | According to the resource-hints specification, you will see in the HTTP response for the HTML page that one Link header for each external resources as been added. Thus the browser will be able to download those external resources without the need to fetch the HTML page before. |
Tip | The first time your page is displayed, the server takes time before responding. This is because WUIC optimizes the page synchronously. After that, the result is added to a memory cache and future HTTP requests will be treated faster. We can take control over the cache implementation and add some configurations to display an already optimized page even the first time the page is loaded, but we will see it later. |
Tip | If you reload the page, you will see that resources are loaded from the browser cache. WUIC has sent a far expiry header to the HTTP response in order to put resources in the browser cache. Don’t worry about cache busting, external resources URL contain a version number that WUIC updates when change are detected. We can also take control over version number computation, see details here. |
JSP / Thymeleaf processing
We have seen how we can optimize a plain HTML page with the WUIC Filter
. If you use a template processor, this tutorial shows how you can inject referenced resources optimized by WUIC in your page. Two sections describe how to do that for good old JSP
users and for turned Thymeleaf
users.
In our examples, we will consider two JS resources foo.js
and bar.js
and two CSS resources foo.css
and bar.css
. Those four files are stored in src/main/resources
to be exposed in the root of the classpath at runtime.
Tip | in those tutorials the WUIC Filter could be use to optimize the generated HTML. However, you might not install it if you don’t see the value of just minifying the HTML code. |
JSP tag
Use the maven dependency
You need to add the following dependency in your pom.xml to enable the JSP support:
<dependency>
<groupId>com.github.wuic</groupId>
<artifactId>wuic-tag</artifactId>
<version>${wuic.version}</version>
</dependency>
Configure resource location in JSP
First we need to declare the resources to be injected. In your JSP, you can use the tag wuic-config
to create a heap called statics
that contains nuts corresponding to your statics.
<%@ taglib prefix="wuic-conf" uri="http://www.github.com/wuic/xml-conf" %>
<wuic-conf:xml-configuration>
<wuic>
<heaps>
<heap id="statics">
<nut-path>js/foo.js</nut-path>
<nut-path>js/bar.js</nut-path>
<nut-path>css/foo.css</nut-path>
<nut-path>css/bar.css</nut-path>
</heap>
</heaps>
</wuic>
</wuic-conf:xml-configuration>
Tip | by default, WUIC retrieves resources relatively to the root of the classpath. We will see later how we can take control over the location of your resources. |
Tip | we can configure resource location outside the JSP. This will be the topic of a subsequent tutorial. |
Create <script> and <link> elements
Now we have to inject the <script>
and <link>
element where we want. Just use the wuic:html-import
tag to inject the processed nuts in the page:
<%@ taglib prefix="wuic" uri="http://www.github.com/wuic" %>
<wuic:html-import workflowId="statics"/>
Thymeleaf processor
Use the maven dependency
You need to add the following dependency in your pom.xml to enable the thymeleaf support:
<dependency>
<groupId>com.github.wuic</groupId>
<artifactId>wuic-thymeleaf</artifactId>
<version>${wuic.version}</version>
</dependency>
Use the dialect
Use the WUIC dialect:
templateEngine.setDialect(new WuicDialect());
Create your template
First we need to declare the resources to be injected. In your template, you can use the tag wuic-config
to create a heap called statics
that contains nuts corresponding to your statics.
<wuic:config>
<wuic>
<heaps>
<heap id="statics">
<nut-path>js/foo.js</nut-path>
<nut-path>js/bar.js</nut-path>
<nut-path>css/foo.css</nut-path>
<nut-path>css/bar.css</nut-path>
</heap>
</heaps>
</wuic>
</wuic:config>
Tip | by default, WUIC retrieves resources relatively to the root of the classpath. We will see later how we can take control over the location of your resources. |
Tip | we can configure resource location outside the template. This will be the topic of a subsequent tutorial. |
Then in your head element, just use the attribute processor import
to inject the processed nuts in the page:
<head wuic:import="statics">
That’s it
Now just run the application and load the page. Both JSP and Thymeleaf tutorials lead to the same result. You will see that one combined JS and one combined CSS have been imported!
Tip | we have configured WUIC with an XML structure supported by WUIC. You can find a deep description of all configurable elements via XML here. By the way, we will see progressively all the possibilities offered by the XML configuration step by step in the subsequent tutorials. |
Custom DAOs
You can configure precisely how WUIC should access nuts by defining properties in custom DAOs. This tutorial shows how to change the base path in a classpath.
In the previous tutorials we used custom a JSP tag and Thymeleaf processor to configure WUIC. Here we will use a wuic.xml
file which needs to be placed at the root of your classpath (src/main/resources
).
Specifying custom DAOs
If default DAOs are not configured as you want, then you can declare a new configuration like this:
<nut-dao-builders>
<nut-dao-builder id="myDao">
<properties>
<property key="c.g.wuic.dao.basePath">/scripts</property>
</properties>
</nut-dao-builder>
</nut-dao-builders>
Here we declare a DAO for classpath accesses which will retrieve any nut in /scripts. So, you may have something like that:
<?xml version="1.0"?>
<wuic>
<nut-dao-builders>
<nut-dao-builder>
<properties>
<property key="c.g.wuic.dao.basePath">/scripts</property>
</properties>
</nut-dao-builder>
</nut-dao-builders>
<heaps>
<heap id="css">
<nut-path>css/foo.css</nut-path>
</heap>
<heap id="js">
<nut-path>js/foo.js</nut-path>
</heap>
</heaps>
</wuic>
This configuration manages two files in your classpath: /scripts/css/foo.css
and /scripts/js/foo.js
!
Tip | many components that you will discover through the documentation support equivalent properties. For instance, you can set the property c.g.wuic.basePath for both ClasspathNutDao (classpath access) and DiskNutDao (filesystem access). If you want to set the same value for a given property in all components, then you can drop a wuic.properties file in src/main/resources in order to locate it in the classpath root. In this property file, you can add all the properties and their value. For example, to specify the use of a wildcard * in the paths to resolve, write something like this: |
# wuic.properties # this property will be set for all components that support it c.g.wuic.dao.wildcard=true
Tip | by default the DAO load resources from the classpath if you don’t specify the type attribute in the nut-dao-id-builder . Writing <nut-dao-builder> is equivalent to <nut-dao-builder type="ClasspathNutDaoBuilder"> . You can find all the supported DAO with a deep description of this API here |
Important notes
Caution | any nuts referenced by the declared nut should be accessible from its associated DAO. Common mistake is for instance to import an image in 'background' rule inside a CSS with a path not relative to the DAO’s base path associated to the CSS nut. |
For instance, this configuration won’t work:
-
DAO with base path
/css
-
Nut with path
foo.css
-
An image referenced in
foo.css
withurl('image/foo.png')
-
A structure like this:
|_css/foo.css
|_image/foo.png
Because the base path of the DAO associated to foo.js
is /css
, the image referenced in foo.css
will be expected to be found at /css/image/foo.png
.
Finally, it is better to define for instance a base path called '/statics' and to have a structure like this:
|_ statics/css/foo.css
|_ statics/image/foo.png
Caution | another reason to specify a particular base path is to make the file research faster. If you let the default base path, a large set of unnecessary files could be scanned. |
Additional process
Behind each process operation, an engine is run. WUIC is fully modular and use engines provided out of the box. However, caching or compressing are some tasks you may want to delegate to external APIs like YUICompressor or EhCache. We will introduce how to do this with WUIC for those two APIs.
Specifying your engines
First of all, you have to put the right dependencies in your project. In this tutorial, we will replace the default cache implementation based on a memory Map
by the EHCache
support.
<dependency>
<groupId>com.github.wuic.extensions</groupId>
<artifactId>wuic-ehcache</artifactId>
<version>${wuic.version}</version>
</dependency>
And that’s it! WUIC is able to auto-detect any extension added to the classpath and use it when processing nuts!
You’ll see in your logs that EhCache is now used to cache the results.
Configure your engines
With a little bit more of work, you can also configure the available properties of your engines.
The following configuration disables the cache:
<engine-builders>
<engine-builder type="EhCacheEngineBuilder">
<properties>
<property key="c.g.wuic.engine.cache">false</property>
</properties>
</engine-builder>
</engine-builders>
Another solution is to add c.g.wuic.engine.cache=false
in a wuic.properties
file located in the classpath root. Note that with this approach the state will change for any component supporting the c.g.wuic.engine.cache
property.
You will find all the available engines and the possible properties here.
Tip | for each engine, WUIC creates an instance used by default when some nuts are processed. The ID always match the naming convention wuicDefault[engine-builder-name] . For instance, the EHCache engine is called EhCacheEngine so the builder’s ID will be wuicDefaultEhCacheEngineBuilder . If the engine-builder-id attribute is not set in the engine-builder element, a default instance is used. So writing <engine-builder type="EhCacheEngineBuilder" engine-builder-id="wuicDefaultEhCacheEngineBuilder"> is equivalent to <engine-builder type="EhCacheEngineBuilder"> . |
Adding sprites
Applying sprites best practice
You can easily apply your sprites in css. However, combining your images takes time. This tutorial shows how WUIC helps developers to generate sprites.
Caution | WUIC can pack a set of PNG image only. Other format like JPEG will be just ignored by the ImageAggregatorEngine . Consequently if you want to include image in a format other than PNG you will need to convert them before. |
The heap to gather your images
Declare all your images in a classic heap:
<heap id="img" dao-builder-id="imageDao">
<nut-path>/foo.png</nut-path>
<nut-path>/bar.png</nut-path>
</heap>
Import CSS sprites
If you have imported the underlying img
workflow in your page, then you can assume that a CSS stylesheet with img_foo
and img_bar
classes are available so you can display each image by applying the associated class to any DOM element:
<span class="img_foo"></span>
...
<span class="img_bar"></span>
Find a sample here.
Using Javascript sprites
For developers using a HTML5 framework like SceneGraph, you can specify the generation of javascript sprites.
<engine-builders>
<engine-builder id="wuicDefaultImageAggregatorEngineBuilder" type="ImageAggregatorEngineBuilder">
<properties>
<property key="c.g.wuic.engine.spriteProviderClassName">javascript</property>
</properties>
</engine-builder>
</engine-builders>
Where you finally import your img
workflow, you can use in Javascript WUIC_SPRITE['img_foo']
and WUIC_SPRITE['img_bar']
to get an object with following properties:
-
x: x-position from the left inside the merged image
-
y: y-position from the top inside the merged image
-
w: the image width
-
y: the image height
-
url: the image URL
For SceneGraph users, you can use this factory and generate a CGSGNodeImage
like this:
var imageFactory = new WUICCGSGNodeImageFactory("img");
var node = imageFactory.create('img_foo');
...
A sample can be found here.
Monitor changes
Feature
WUIC offers to users the possibility to load external resources (configurations and nuts). This tutorial shows how to enable a polling mechanism that detects any modification.
Polling wuic.xml
In your wuic.xml
file, add the following attribute to root element:
<wuic polling-interval-seconds="10">
...
</wuic>
Every 10 seconds, WUIC checks if the file has been modified. If it is the case, then the configuration is reloaded without need redeploy your application or restart server.
To specify a different location of wuic.xml
file, you can use following init-param
in web.xml
:
<context-param>
<param-name>c.g.w.wuicXmlPath</param-name>
<param-value>file:/my_app/wuic.xml</param-value>
</context-param>
Polling nuts
You can enable polling on a particular DAO like this:
<nut-dao-builder type="DiskNutDaoBuilder">
<properties>
<property key="c.g.wuic.dao.basePath">file:/my_app/statics</property>
<property key="c.g.wuic.dao.pollingInterval">3600</property>
</properties>
</nut-dao-builder>
Each hour, all nuts creates with the DAO will be polled. If any change is detected, then both browser and server caches are evicted to reload them.
Logging
Common loggers
WUIC uses slf4j
as logging facade, which allows you to plug any supported logger behind the scene. All log levels are used by the library:
-
DEBUG
logs are very verbose and should be activated only for debug purpose -
INFO
should be activated when you want to make sure that WUIC is active -
WARN
logs detect something you should not expect, while they don’t mean the static won’t be delivered to the client, hey should be always activated -
ERROR
logs mean that WUIC won’t deliver the statics successfully.
Special loggers
In addition WUIC provides special loggers using TRACE
level:
-
com.github.wuic.Logging.TIMER
will log all measured amount of time taken by process executions -
com.github.wuic.Logging.POLLING
will log all polling operations when you monitor nuts or configuration file
As an example, a configuration with spring-boot
could be:
logging.level.com.github.wuic.Logging.TIMER: TRACE
logging.level.com.github.wuic.Logging.POLLING: TRACE
Process at build time
Purpose
Processing at build time could be a great solution for different use cases:
-
Statics are not served from your application server.
-
Application server can’t reach the server that serves statics to upload it.
-
You don’t use any Java application server but you use maven to package your application.
-
You don’t need to reload some configuration/statics at runtime and you want to reduce as much as possible the resources consumed at runtime by WUIC on your application server.
There are one benefit and one concern of build time solution:
-
Benefit: build time processing will turn to 0 the overhead induced by WUIC at runtime
-
Concern: no polling to reload both configuration and nuts at runtime
Configure your pom.xml
Options
Both task and maven plugin allow to configure the following options:
-
xml
: thewuic.xml
file location (which is configured as usual) -
properties
: thewuic.properties
file location (which is optional) -
output
: base path where results are written by the plugin -
charset
: by defaultUTF-8
, change it if you want to write processed text content (scripts, SVG, etc) with a different encoding -
contextPath
: the context path of the web server that will serve the generated statics (which is optional)
You will see in the next sections how they can be declared.
With Maven plugin
You can use the static-helper-maven-plugin
that is commonly configured like that in the build section of your pom.xml
:
<plugin>
<groupId>com.github.wuic.plugins</groupId>
<artifactId>static-helper-maven-plugin</artifactId>
<version>${project.version}</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>process</goal>
</goals>
</execution>
</executions>
<configuration>
<xml>src/main/resources/wuic.xml</xml>
<properties>src/main/resources/wuic.properties</properties>
<relocateTransformedXml>true</relocateTransformedXml>
<output>${build.finalName}</output>
</configuration>
</plugin>
The maven plugin has the following specific options:
-
relocateTransformedXml
: when set to true, after statics have been processed, the plugin will move a transformedwuic.xml
file to your out directory with some metadata files. This will allow the servlet to serve the process result from the application server by picking information from classpath.
With ANT integration
WUIC support ant
integration. For instance, you can configure the maven-antrun-plugin
like that:
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<phase>prepare-package</phase>
<configuration>
<target name="wuic-target">
<property name="wuic-jar" value="${maven.dependency.com.github.wuic.wuic-core.jar.path}"/>
<property name="wuic-output" value="${project.build.directory}/${project.build.finalName}" />
<ant antfile="${basedir}/build.xml">
<target name="wuic-task"/>
</ant>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
In your build.xml
file, just add:
<project>
<target name="wuic-task">
<taskdef name="wuic" classname="com.github.wuic.WuicTask" classpath="${wuic-jar}"/>
<wuic xml="src/main/resources/wuic-ant.xml"
properties="target/classes/wuic-ant.properties"
relocateTransformedXmlTo="${wuic-output}/WEB-INF/classes"
output="${wuic-output}"
/>
</target>
</project>
The task has the following specific options:
-
relocateTransformedXmlTo
: if defined, a location where the transformedwuic.xml
file some metadata must be written. This directory should be added to your classpath at runtime to allow the servlet to serve the process result from the application server by picking information from classpath.
Configure
Configure for pure HTML pages
If your application just contains .html
files, then you can filter them with WUIC and then directly serve them.
Configure your wuic.xml
by adding just a heap that resolves the HTML pages:
<heap id="html" dao-builder-id="htmlClasspathNutDaoBuilder">
<nut-path>.*.html</nut-path>
</heap>
Then, according to the output location, you will find the transformed HTML files. The extracted scripts are also copied. All files are stored under a directory named with the version number. This way you can deploy the directories in your application server to serve new paths not in the browser cache when content files change.
The website serves statics generated with a simple "mvn clean install" and is a good example or the approach. Check the source here.
Tip | if your statics are not served by the Servlet provided by WUIC, both cache and link headers won’t be set in the HTTP response for the HTML page. In that case, WUIC will modify the HTML to respectively insert a (cache manifest file and a link tag to the content. |
Tip | if your statics are going to served by the Servlet container , the ContextListener will automatically install a Filter that optimize each resource by enabling the browser cache. It will also GZIP the content if the client supports it. |
Configure for templating usage
If you use templating project like JSP
or Thymeleaf
, then you can build your application as usual and then use the maven plugin to create a file added to the classpath. At runtime, WUIC will read this file which refers statics generated at build time.
Examples
JSP
You have the sample here.
Also take a look at this sample, which performs optimizations at runtime but can be configured at build-time if you run mvn clean package -Pbuild-time
.
Full static
wuic.github.io is processed by WUIC. You can check the sources in a branch here. The publish.sh
script run WUIC and copy results in master branch.