Spring integration
Spring resource handling
Spring
4.1 exposes a pluggable API allowing to specify custom web resources resolution and transformation. WUIC provides an extension that comes with:
-
a PathResourceResolver that process your workflow and wraps the resulting nut in a spring resource.
-
a VersionStrategy to build public URLs with WUIC which includes in the URL a CRC32 content based hash or the last-modified timestamp regarding your DAO configuration
Maven dependency
First you need to add to your pom.xml
the maven dependency:
<dependency>
<groupId>com.github.wuic.extensions</groupId>
<artifactId>wuic-spring</artifactId>
<version>${wuic.version}</version>
</dependency>
Integrate WUIC resolvers
Spring
offers an easy way to configure resource handling with Java Config.
First, declare your @Configuration
class as usual.
@Configuration
@EnableWebMvc
public class MyWebConfig extends WebMvcConfigurerAdapter {
}
Then create the WuicFacade
to expose it as a bean in the ApplicationContext
.
@Autowired
private ApplicationContext applicationContext;
@Bean
public WuicFacade wuicFacade() throws WuicException {
return new WuicFacadeBuilder()
.contextPath("/resources/")
.wuicXmlPath(getClass().getResource("/wuic.xml"))
.build();
}
Tip | you can completely ignore the use of wuic.xml and configure WUIC with Java Config. |
Finally, just use the handy WuicHandlerMapping
to inject to spring a bean handling resources with WUIC. Core resolvers are WuicPathResourceResolver
and WuicVersionStrategy
, but you can create additional implementations.
@Bean
public SimpleUrlHandlerMapping handleWuicResources(final ServletContext servletContext,
final ResourceUrlProvider resourceUrlProvider,
final WuicFacade wuicFacade) {
final ResourceResolver versionResourceResolver =
new VersionResourceResolver().addVersionStrategy(new WuicVersionStrategy(), "/**/*");
final ResourceResolver pathResourceResolver =
new WuicPathResourceResolver(applicationContext.getBean(WuicFacade.class));
return new WuicHandlerMapping(applicationContext,
servletContext,
resourceUrlProvider,
wuicFacade,
Arrays.asList(versionResourceResolver, pathResourceResolver),
Collections.<ResourceTransformer>emptyList());
}
You can also override addResourceHandlers
method to configure resolvers/transformers outside WUIC.
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
final ResourceResolver myResolver = new MyResourceResolver();
registry.addResourceHandler("/other/**")
.resourceChain(true)
.addResolver(myResolver);
}
Full example
A full example is available here. In addition, it shows how you can mix built-in spring resolvers with WUIC and how to handle public URLs with Thymeleaf
.
AngularJS
Feature
WUIC provides out of the box an AngularJS
support and optimize the templateUrl
option by default! This topic gives you the details.
Writing your directives
AngularJS is MV* framework which advises the use of custom directives. As described in the documentation:
Best Practice: Unless your template is very small, it’s typically better to break it apart into its own HTML file and load it with the templateUrl
option.
This means that when AngularJS
will instantiate the directive, an additional HTTP request will be executed to load the template content. This leads to two issues:
-
the user must waits the HTTP request is done to see the content displayed
-
you should take care of client cache, which must be burst each time you deploy a new version with a different template
Inlining
WUIC takes care of templateUrl occurrences in javascript code and replace it with a template option.
For example, imagine you have the following directory tree:
. |--app |--myFeature |--directive `-- myDirective.js |--template `-- myTemplate.html
myTemplate.html
:
<h1>My Template Title</h1>
In your myDirective.js
file, you can refer myTemplate.html with an URL relative to your script location:
myModule.directive('myDirective', function() {
return {
templateUrl: '../template/myTemplate.html'
};
});
When WUIC parses myTemplate.js
, you will obtain something like that:
myModule.directive('myDirective', function() {
return {
template: '<h1>My Template Title</h1>'
};
});
WUIC solves two issues:
-
You save the additional HTTP request
-
As each file processed by WUIC is served with a different URL each time the content change, you make sure the client cache is burst when you update the template
Best effort
Note that in best effort mode, WUIC serves the content as fast as possible and just rewrite the template URL, without running inlining process.
Natural Templating
If you want to mix natural templating and WUIC’s AngularJS
optimization feature, you must read this important page.
Typescript integration
The tsc command
Typescript
could be installed in several ways. If you are a Node.JS
user, then you will be able to compile your source files with the 'tsc' command:
Examples: tsc hello.ts tsc --out file.js file.ts tsc @args.txt
Many options are available, like specifying the ECMAScript version or the sourcemap generation.
WUIC comes with an extension which is able to compile typescript files. The extension works in two modes:
-
executing the tsc command, which is faster but requires to install the module with Node.JS
-
executing a compiler wrapper on top of rhino, which is slower but crossplatform
Configuration
First of all, you need to add the dependency to your project:
<dependency>
<groupId>com.github.wuic.extensions</groupId>
<artifactId>wuic-typescript</artifactId>
<version>${wuic.version}</version>
</dependency>
Then, you can write your HTML page by referencing your scripts:
<script src="greeter.ts"></script>
<script src="use.greeter.ts"></script>
Then you can just include the HTML page in the WUIC process, for instance by declaring it in your wuic.xml file or just filtering it with the servlet filter.
When WUIC transforms the HTML page, it replaces the initial referenced typescript file by the compiled javascript file:
<script src="/wuic/56321344/myWorkflow/aggregate.ts.js"></script>
WUIC also serves a sourcemap file referenced in the compiled javascript.
Crossplatform configuration
By default, you don’t need to install Node.JS to compile your typescript source files with WUIC. The extension takes advantage from the tsc wrapper project, which is run with trireme, a Node.JS
compatibility layer for the JVM.
However, because trireme uses Rhino
to executes javascript code, the compilation is slow, even with the optimizations provided by the project. This is not really a problem because compilation is performed at build time or when the server starts at runtime, but in development you need to compile each time you change your sources. In this case you might need to enable command line execution.
tsc command line execution
The tsc command line compiles your sources 10x faster than rhino-based compiler and could be very useful when you need to recompile your scripts in development.
Before switching to this mode, you need to install the typescript compiler:
npm install -g typescript
Then, just set the c.g.wuic.engine.useNodeJs
setting to true in the TypescriptConverterEngineBuilder
. For instance, if you configure engines in your wuic.xml
, you will write this:
<engine-builders>
<engine-builder type="TypescriptConverterEngineBuilder">
<properties>
<property key="c.g.wuic.engine.useNodeJs">true</property>
</properties>
</engine-builder>
</engine-builders>
Note: you can filter the value with maven to set this setting to true only in a development profile. When you’ll go in production, the setting will be false and you won’t need to ask your ops to install Node.JS
Command lines execution
Purpose
Sometimes you have a command line tool that just does a part of the work as you want. When using WUIC, you can be in a situation where you would like to integrate the result of any command line execution inside the WUIC workflow. For instance, let’s imagine that you have a command line that converts LESS
files to CSS
.
This tutorial will show you how to configure this kind of command line to be executed by WUIC.
Command line configuration
WUIC relies on a special engine to execute command line called CommandLineConverterEngine
. By default, this engine does nothing until you configure it to specifies which NutType
need to be processed by the engine and which NutType
is expected as the result of command line execution.
To specify to the NutType
, you need to configure the c.g.wuic.engine.inputNutType
and c.g.wuic.engine.outputNutType
properties. For instance, you can do it in your wuic.properties
file like this:
c.g.wuic.engine.inputNutType=LESS c.g.wuic.engine.outputNutType=CSS
The value must be a supported NutType
name.
At this moment, the engine will be activated and validate the command line to execute. This command line also have to be configured with a dedicated property.
c.g.wuic.engine.command=mycmd param1 param2
WUIC will use this command line to specify where the result and the associated source map should be generated according to a set of files to be converted. When analyzing the command line, WUIC will need to identify three mandatory tokens:
-
%paths%
: the token to replace with the input files location -
%outPath%
: the token to replace with the result file location -
%sourceMap%
: the token to replace with the source map file location
WUIC will reject your command line if those three tokens are not defined. A valid command looks like this:
c.g.wuic.engine.command=mycmd %paths% --sourcemap %sourceMap% > %outPath%
When executing the command line, WUIC will use this pattern to contextualize a command. For instance, the command that is actually run could looks like this: mycmd /path/to/foo.less /path/to/bar.less --sourcemap /path/to/aggregate.css.map > /path/to/aggregate.css
Tip | Optionally, you can define the %basePath% token in your command line to let WUIC use a command which looks like mycmd %paths% --baseDir %basePath% --sourcemap %sourceMap% > %outPath% to execute a command which will looks like mycmd foo.less bar.less --baseDir /path/to --sourcemap /path/to/aggregate.css.map > /path/to/aggregate.css |
Tip | by default all file paths are separated by a space, but you can configure it thanks to the c.g.wuic.engine.pathSeparator property. |
If you need to include any additional file in the directory where the command line is executed, you can use the c.g.wuic.engine.libraries
property to specify all files path to be copied separated by a semi-colon. The files paths path must corresponds to a resource available in from the classpath.
c.g.wuic.engine.libraries=/path/to/lib1;/path/to/lib2;/path/to/lib3
Tip | if any token (paths, out paths, source map path or base path) need to be specified in a separate file, you can add it to the file and then consider it as a library to be copied by WUIC. During copy, WUIC will take care of any token to be replaced. If a mandatory token is found in one library, WUIC won’t fails if this token is not in the configured command. |
Using NodeJS with NPM command line
When you need to run npm
on top of NodeJs
as a command line run by WUIC, you must be sure that npm
will be recognized in the working directory. To provide integration facilities of npm
command line execution, you can install in your pom.xml
file an extension provided by WUIC:
<dependency>
<artifactId>wuic-extensions</artifactId>
<groupId>com.github.wuic.extensions</groupId>
<version>${wuic.version}</version>
</dependency>
This extension provides an inspector automatically detected and installed by WUIC that relies on the frontend-plugin-core module to download NodeJs
and NPM
in the working directory of any CommandLineConverterEngine
.
In your classpath, you just have to create a file called /com.github.wuic.nodejs.NodeJsInspector.config.json
that provides the information required by the frontend-plugin-core
. The minimal configuration that you need to provide must contain the NodeJs
and NPM
version to be used. This configuration looks like this:
{ "nodeVersion": "v0.10.30", "npmVersion": "1.4.21" }
Caution | the nodeVersion value must starts with v character, while it’s not required for npmVersion . |
According to the documentation here, you can also consider to specify additional configurations:
-
nodeDownloadRoot
: a different URL forNodeJs
archives location -
npmDownloadRoot
: a different URL forNPM
archives location -
proxies
: an array of proxies to use where each element is an object which looks like{"id":xxx, "protocol": xxx, "host": xxx, "username": xxx, "password": xxx, "nonProxyHosts": xxx}
Enable HTTP/2
Purpose
This tutorial shows how you can enable HTTP/2 in your server to let WUIC use this protocol.
HTTP/2 protocol specifies the server-push
feature that allows to send over the HTTP response additional resources that the client should require one he fetched the response. For instance, if a browser fetches a HTML content, it will load additional resources like CSS or Javascript files. Actually, we don’t wait the browser performs a new request and we push those resources earlier.
This method is speculative because we don’t know that the client will really load the resource. For instance, if the resource is in the browser cache, then the push
will be canceled.
Tip | by default, WUIC uses resource-hints to let the browser fetch the resources once the HTTP response is sent to the client. This is slower than HTTP/2 server-push but not speculative: if the resource is in the browser cache no unnecessary download will occur. If you enable HTTP/2, then server-push will be used instead of resource-hints . |
Java server
Java server and even servlet containers don’t implement any standard API for HTTP/2. This means that each server comes with its own API that you need to specifically implement. WUIC hides those API and comes with a support for a specific server under the form of a JAR file.
Servlet containers
For any servlet container, you must install the com.github.wuic.servlet.HtmlParserFilter
to let WUIC
initiate a server-push
for resources associated to the filtered HTML page.
Jetty
First you need to install HTTP/2 with Jetty
. This requires the version 9.3+ and the JDK 8. You will find a complete sample in this pom.xml.
Then, you just need to add the dependency corresponding to the Jetty
support for WUIC:
<dependency>
<groupId>com.github.wuic.extensions</groupId>
<artifactId>http2-jetty</artifactId>
<version>${project.version}</version>
</dependency>
You can run your application and see that HTTP/2 is transparently enabled. To see if everything is fine, we advise to check the session activity for your host by opening link:chrome://net-internals/#spdy in chrome.
Any WUIC sample can be run with HTTP/2. Read the README.md
for details!
HTTP/2 and nghttpx
With nghttpx installed as a proxy, WUIC can initiate a HTTP/2 server-push
without any extra HTTP/2 installation in your java server!
This interesting and alternative technique is to let a nghttpx
proxy to negotiate a HTTP/2 connection with browsers and initiate server-push
according to the header specified in the HTTP response sent from the back-end. nghttpx
pushes resources referenced in the <link>
header found in the proxied HTTP response. See their documentation for more details here.
The good news is that WUIC specifies by default <link>
headers in the HTTP response when a HTML page is filtered by the com.github.wuic.servlet.HtmlParserFilter
. Consequently you just have to install this listener in your webapp without the need to configure HTTP/2 in your servlet container.
Natural templating with AngularJS
Purpose
This part explain an important design consideration when you want to mix natural templating, WUIC and AngularJS
. In fact, WUIC reads templateUrl
option relatively to the script location declaring the directive, while in a pure HTML page, AngularJS
loads it relatively to the page location. This chapter gives details about the workaround you can use to mix natural templating in development and server-side WUIC optimization in production.
Design
Natural templating considers you should not process your page on server side before it could be served to the client. WUIC makes it possible since you can write a pure HTML page and then optimize it with an engine installed by default. The simplest way to optimize the page in this case is to use the servlet filter provided by WUIC.
Demonstration
If you want to directly display the HTML page in the browser, your templates will be loaded relatively to this page. For instance, if we have the following structure:
. |--app `-- index.html |--myFeature |--directive `-- myDirective.js |--template `-- myTemplate.html
You will write your directive like this:
myModule.directive('myDirective', function() {
return {
templateUrl: 'myFeature/template/myTemplate.html'
};
});
Everything will work fine. Then if you want to use the servlet filter provided by WUIC. When you deploy into production, WUIC will try to detect templateUrl
options in the javascript code. In our example, WUIC will read the URL myFeature/template/myTemplate.html
relatively to the script myDirective.js
, which leads to bad URL computation (/app/myFeature/myDirective/myFeature/template/myTemplate.html
does not exists!). In that case, WUIC will log a warning and won’t change the statement. Your application won’t be broken but no optimization will be done!
So if you want to see your template optimized, you should write your directive like this:
myModule.directive('myDirective', function() {
return {
templateUrl: '../template/myTemplate.html'
};
});
The problem is that your template won’t work when you display your page without filtering it with WUIC. In fact, because the URL is relative to the page location, the browser will try to load /app/../template/myTemplate.html
, which does not exists! The solution is to provide a function which takes in parameter the relative URL and computes the correct absolute path like WUIC does. This could be done with a function returning the location of the current executed script. You can bind this function to the declared module:
var myModule = angular.modue('myModule');
/**
* Gets the directory path (url without file name) from the latest loaded script.
* This function could be used to register script location when they get executed.
* In fact the returned object exposes a getChild function which returns a given path
* concatenated to the script directory path.
*
* @method getPathRelativeToScript
* @returns {Object} the directory path
*/
myModule.getLastScriptPathDirectory = function () {
var scripts = document.getElementsByTagName('script');
var path = scripts[scripts.length - 1].src.split('?')[0]; // remove any ?query
var dir = path.split('/').slice(0, -1).join('/') + '/'; // remove last filename part of path
return {
getChild : function(path) {
return dir + path;
}
}
};
Then in your myDirective.js
file, you can use this function like that:
1. myModule.constant('myDirectiveUrl', myModule.getLastScriptPathDirectory());
2. myModule.directive('myDirective', function(myDirectiveUrl) {
3. var getChild = myDirectiveUrl.getChild;
return {
4. templateUrl: getChild('../template/myTemplate.html')
};
});
-
We declare the AngularJS constant which to keep the script location.
-
We inject the constant into the directive.
-
The constant is an object providing a getChild function.
-
We use the getChild function to get the correct absolute URL.
The last thing we have to do is to configure WUIC to not extract getChild(…)
as an URL and instead ignore the wrapping function. This could be done by configuring the JavascriptInspectorEngine
in your wuic.xml
file:
<?xml version="1.0" encoding="UTF-8"?>
<wuic>
<engine-builders>
<engine-builder id="wuicDefaultJavascriptInspectorEngineBuilder" type="JavascriptInspectorEngineBuilder">
<properties>
<property key="c.g.wuic.engine.wrapPattern">getChild(%s)</property>
</properties>
</engine-builder>
</engine-builders>
</wuic>
That’s it! you can now develop without any server deployment and then let WUIC optimize your scripts in production!