Supported server and known issues

WUIC is designed to be deployed in any Servlet 3 compatible server running on top of Java 6 and above:

  • Jetty since 8.0

  • Tomcat since 7.0

  • GlassFish since 3.0

  • JBoss since 6.0

  • WebLogic since 12.0

  • Undertow since 1.0

However, some features have a limited support on some specific version. Known issues are identified with a link to the details and workaround in the table below.

Feature/Servlet Container Jetty Tomcat GlassFish JBoss WebLogic Undertow (Wildfly)
Unknown MIME type in Filter OK OK Test needed! Test needed! Test needed! Before 1.1 (Wildfly 8.1)
Best effort, warmup, polling and async version computation Since 9.3.0.M2 OK Test needed! Test needed! Test needed! Before 1.2

Unknown MIME type in Filter

Because of UNDERTOW-268 issue, WUIC’s servlet filter will fails if you deploy it on Undertow 1.0 (which is the default HTTP server in Wildfly 8.0 and 8.1).

A workaround exist: create a filter that force the mime type on resources filtered by WUIC. For instance:

/**
 * Workaround for https://issues.jboss.org/browse/UNDERTOW-268 issue.
 *
 * @author Guillaume DROUET
 * @since 0.5.0
 * @version 1.0
 */
public class MimeTypeFilter implements Filter {

    /**
     * {@inheritDoc}
     */
    @Override
    public void init(final FilterConfig filterConfig) throws ServletException {
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void doFilter(final ServletRequest servletRequest,
                         final ServletResponse servletResponse,
                         final FilterChain filterChain)
            throws IOException, ServletException {
        filterChain.doFilter(servletRequest, servletResponse);
        servletResponse.setContentType("text/html");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void destroy() {
    }
}

Best effort, warmup, polling and async version computation

When a Nut is created with the RequestDispatcherNutDao component, the path is resolve with the ServletContext#getResource(String) method, which works fine in any servlet container.

However, the content is dynamic (generated by a Servlet or a JSP), you must tell WUIC to use RequestDispatcher#include(HttpServletRequest,HttpServletResponse) thanks to the c.g.wuic.dao.useIncludeForPathPattern that accepts a regex matching the desired path. The problem is that following references explain that any attempt to use a RequestDispatcher outside the HTTP request/response lifecycle and/or in another thread will fail:

This lead to the following limitations when you use the RequestDispatcherNutDao, which is the default DAO in the HtmlParserFilter:

  • Warmup strategy must be set to NONE (default setting)

  • Version number must be computed synchronously (done asynchronously by default)

  • Nut polling must be disabled (default setting)

  • Cache engines must disable best effort (default setting)

Considering those points, you should be ok if you configure your wuic.properties like this when you use RequestDispatcherNutDao:

c.g.wuic.computeVersionAsynchronously=false
c.g.wuic.engine.bestEffort=false

Error codes

WUIC tries to clearly identify an error with a specific exception and a specific error code. When an exception is thrown, you will see in the logs the code which is described here.

If you have any suggestion about a more detailed code to be provided by WUIC, please contact us.

Caching mechanism

Caching is a critical issue because it is often the most effective way to improve performances. However, implementing a cache mechanism becomes tricky when we want to expose fresh data to the users.

WUIC helps developer to expose statics while taking advantage from caching on both server-side and client-side. It also takes care of cache eviction when statics need to be refreshed. This page introduces how WUIC does the trick.

Caching on server side

When a workflow is executed on server side, a set of statics are retrieved to be converted to treatable nuts. The result is added to a cache which could be managed behind the scene by a memory map or then EhCache if you configured it.

Then, any other execution of the workflow will be faster because WUIC will use the cache to retrieve processed nuts.

To guarantee nuts freshness, you can obviously configure the cache with some settings to set a time to live or a time to idle. However, this simple solution has its limitation. Once you deployed your new statics, they will be ignored until the cache has expired. Moreover, cache duration becomes a big problem since you may configure a high value (say 1 day) because you want that your application uses the cache as much as possible.

A better solution would be the polling feature. Here a thread will check any changes on set of statics at a configured interleave. Nothing prevents you to poll your statics more regularly, say every 10 minutes.

Tip
it becomes interesting when you realize that WUIC triggers an event internally to evict the cache entry when any associated nut corresponds to an updated static.

Caching on client side

The HTTP protocol provides several directives to order caching to the browser. We often talk about the cache control header. The max-age value defines how long the static should be kept before checking for new update. The same problem on the server appears: you don’t know how long the user will load an old version from browser cache until its expiry. Consequently, you may probably set a short value. When the statics expires, the browser sends a HTTP request to the server to know if the data is fresh or not. If data is fresh, server responds with a status code 304 - not modified and an empty body. However, it sends the fresh data in the body. What is unfortunate here is that the browser could poll the server synchronously and, at the end, see that nothing changed. WUIC does not use the cache control for that reason.

The HTTP response should instead contains a "expires" directive which tells the browser to keep the statics for a very long time. Since cache entry is associated the path URL of the static, WUIC has just to modify this path to evict the cache. How it does the trick? By adding in the URL a version number identifying all nuts state processed by the workflow. When nuts are changed, it could be detected on redeployment or during a polling operation. Both cases will result in a different version number which will change the static URL, which does not corresponds to any cache entry on the browser. This is the way WUIC prevent any unnecessary HTTP request on the server while maintaining fresh statics loaded by the browser.

Tip
with HTTP/2 server-push is always initiated and stopped as soon as the browser has detected that the resource is actually cached. With resource-hint, the browser will check first that resource is not cached before creating a new HTTP request.

Version strategy

Three strategies exist for computing the version number:

  • Last modification timestamp: by default WUIC uses the last modification date associated to the resource on the disk. It’s fast, but if your build process touch the file, it will lead to a different version number while the content has not changed.

  • CRC32 content hash: by setting the value true to the c.g.wuic.dao.contentBasedVersionNumber property, WUIC will read the entire content to obtain a CRC32 checksum. It’s slower but you have the guarantee to keep intact the browser cache if you don’t update the file between two releases.

  • Fixed version number: if you set a value for the c.g.wuic.fixedVersionNumber property, WUIC will always use this value as version number. You can for instance use a version of your release. This strategy will lead to a browser cache refresh each time you release a new version. It requires to change the version yourself but this strategy allows to modified URL that can’t be resolved. For instance, if WUIC can’t resolve a background url in a CSS file, it does not process the referenced image. However it can at least add the fixed version number as a parameter to the URL (for instance ?versionNumber=20150808).

Best effort

By default at runtime, any cache Engine executes the entire workflow when the desired resources are not in the cache. This operation takes time for the first user that retrieves its page. You can consider that it won’t be a problem since only the first user will be concerned. However, WUIC comes with some possibilities to optimize this case.

First, WUIC can populate the cache for any Nut explicitly declared in your configuration point (for instance in your wuic.xml file) when your server starts. This could be achieved with the following context-param in your web.xml file:

    <context-param>
        <param-name>c.g.w.wuicWarmupStrategy</param-name>
        <param-value>SYNC</param-value>
    </context-param>

You can specify the ASYNC value instead of SYNC if you want that WUIC populates the cache in parallel of other components deployment to reduce startup time.

If WUIC discovers Nut on the fly by filtering HTML page with the HtmlParserFilter component, it can’t know what to cache until a client sends the first HTTP request. In that case, WUIC can do its best effort to deliver a response faster by applying synchronously only the mandatory operations.

You can enable this feature like that on your cache engine in your wuic.properties file:

c.g.wuic.engine.bestEffort=true

Mandatory operations are:

  • CSS inspection to rewrite relative URL because they are served from a different location

  • Image inspection to generate a sprite if configured

  • Conversion of extended WEB language like Typescript

In best effort mode, all other operations (aggregation, compression) will be done asynchronously when the process result is not cached and the user will get its response faster. Of course, the result won’t be optimized but you’ll still improve response time in that case. Finally, once the asynchronous job will be done, cache will be updated and next requests will get transparently the full process result as configured.

Feature comparison

Choose the right tool

WUIC provides several features to optimize your statics. Choosing the right feature is important because they will have a different impact on your application in terms of:

  • Performances: statics are optimized but an overhead can exist

  • Maintainability: how you integrate the tool has an impact when you maintain your application

  • Scalability: some compared feature can limit the power of WUIC, reducing the possibilities if you want to enhance your application

This document shows those differences to help you make the best choice.

Features overview

  • No-Tag: just refer URL mapped to the WUIC servlet in your <script>, <link> and <img> elements when you write the HTML page

  • Per-Page-Configuration with JSP or Thymeleaf

  • Startup-Configuration (for instance with configuration in wuic.xml) with JSP or Thymeleaf support to write imports

  • Servlet-Filter

  • Build-Time

Feature Performance Maintain Scalable
No-Tag Good Limited Limited
Full front-end development with no server-side page generation. It becomes necessary to re-deploy your application each time a new version needs to be published. You need also to evict by yourself browser cache.
Startup-Configuration Good Good Good
Server-side page generation with all configurations in one location (wuic.xml)
Per-Page-Configuration Limited Good Excellent
Server-side page generation with more configurations added in each page. Perfect when having a lot of pages with dedicated statics: it's more easy to declare them in the single page using it instead of declaring them in a big wuic.xml file. Configuration is discovered when user loads the page. "c.g.wuic.facade.multipleConfigInTagSupport" setting set to false helps to reduce response time.
Servlet-Filter Limited Excellent Limited
Legacy pages that takes too much time to migrate to use tags. Statics are discovered when the user loads the page. Best effort mode can help to reduce response time.
Build-Time Excellent Limited Good
Not necessary to reload statics at runtime. It becomes necessary to re-deploy your application each time a new version needs to be published.