General architecture and terminology
WUIC is designed to integrate many architectures. This is possible thanks to the following abstract concepts:
-
Nut: this is an abstraction of your static file providing its type (JS/CSS) and the stream allowing to read it
-
DAO: the Data Access Object pattern is used to define the NutDao interface, allowing to manage nuts
-
Heap: this is a set of nuts that you use to organize them
-
Engine: the engine is a class which can process your nuts with a particular role (caching, aggregating, compressing, etc)
-
Workflow: Associates a heap to a chain of responsibility composed of engines. This way, the workflow describes how you want to process your nuts.
The following diagram shows the architecture used by WUIC to optimize your website on the fly:
Step 1 The browser requires the page corresponding to the link in the address bar on the server. The server can directly serve a HTML page, which can be filtered by the WUIC servlet filter that optimizes it and all its referenced resources. The HTML can also be produced by a processor like JSP or Thymeleaf (which could also be filtered by the WUIC servlet filter). WUIC tags for those libraries can also be called during template processing to generate links pointing to optimized resources.
Step 2 A workflow is executed according to the information associated to the requested page. For now, just remember that in WUIC, a workflow describes the way you want to process a set of statics. The workflow could be created on the fly by the servlet filter or a workflow referenced manually inside a template (through JSP or Thymeleaf).
Step 2a: the invoked workflow corresponds at least to the resources referenced in the HTML page, and possibly to the HTML page itself if the servlet filter is installed.
Step 3 When a workflow needs to be executed, WUIC looks at first for the statics to be processed. They are provided by an associated heap. A heap is also identified by an ID and just contains a set of paths representing the statics to be loaded.
Step 4 The heap just has the paths representing the statics, but it can’t open the stream to them itself. It consequently uses an associated DAO which, thanks to a given path, can provide access to the stream. Remember: the DAO will produce a nut. Nut is the term that will be used everywhere in WUIC to represent your static (or you also called a resource).
Step 5 Once the nuts have been returned by the heap, then a chain of responsibility composed of engines is called to process the nuts. Each engine has a particular purpose. There is an engine for caching, compressing, inspecting or aggregating nuts.
Step 6 Once nuts have been processed by engines, they are returned by the invoker (a servlet, a filter or a tag processor).
Step 7 The result is sent to the browser. If the result contains the optimized version of the required page, then its content is written to the HTTP response. Otherwise, the result is sent under the form of a link added to the HTML page which points to the WUIC servlet. Note that the resource can also be pushed to the client if HTTP/2 is enabled.
Step 8 When the page is fetched by the browser, additional resources (JS, CSS, etc) that have been processed by WUIC could be referenced as links. The links are used to submit a new HTTP request to the WUIC servlet.
Step 9 The WUIC servlet extracts the requested name of the result entry for the specified workflow to be written to the HTTP response.
Step 2b: when a particular resource is requested, the WUIC servlet executes the workflow like in step 2 to retrieve the content to write. However, you can be sure that the workflow will be executed pretty fast, as a result already exists in the internal cache.
Dependency management
Reduce dependencies
WUIC tries to reduce as much as possible the dependencies of the project. However, we know that we will still depend of some third party libraries because of the complexity of the feature and/or the well known API they provide.
Artifacts are organized in two major repositories:
-
core: minimal configurations to get essential features in J2SE/JEE environment. It is very light and comes only with two dependencies in runtime scope.
-
extensions: additional features implemented on top of third-party libraries.
You can see in action design decisions explained bellow by taking a look at the pom.xml
of the project and our samples.
Core dependencies
SLF4J for logging
We decided to use the famous SLF4J
API as logger facade in the WUIC project. Every features are based on the wuic-core
artifact which comes this dependency: org.slf4j:sl4j-api
As explained by the SL4FJ
documentation, you will have to add in your project the concrete logging API and its corresponding SLF4J
binder you want to use.
Google GSON
The GSON project is used in several areas of the project to parse/write GSON objects.
Google Protocol Buffer
The Protocol Buffer project is used in Closure source map implementation (see bellow).
Provided JEE dependencies
Finally, the core repository also provide JEE supports. In addition to the wuic-core
dependency, those artifacts declare additional dependencies in provided scope because they’re already provided by the servlet container.
Extensions
Some protocols supports are included in the core (like HTTP) because they are not based on any external project.
For others like FTP or SSH, we needed to use some projects like JSCH
or commons-net
making the protocols easy to use. Each protocol support which requires additional dependencies comes in a separate module you need to include.
The issue is the same for processors. For instance, aggregation is provided by core but minification is enabled only when you add an extension which comes with third party library like YUICompressor
.
If you want to use an extension which comes with specific dependencies, you’ll have to put it in your pom.xml :
<dependency>
<groupId>com.github.wuic.extension</groupId>
<artifactId>${wuic-extension-name}</artifactId>
<version>${wuic-extension.version}</version>
</dependency>
Folks code
ASL Annotation Detector
ASL Annotation Detector is a very light project packaged in a single JAR. It’s used by wuic-core
to detect automatically extensions added by the user by scanning the classpath. This library’s code is currently embedded in WUIC as it fixes a bug on JBoss/Wildfly not resolved in upstream. Anyway, the code will be totally removed in a future release as we plan to use ServiceLoader
instead.
Jetty Path Map
We embed the jetty implementation of servlet path specification. We just need a few useful classes so it was not relevant to add a transitive dependency to this project. Moreover the source code is not concerned by changes as it is quite stable now.
Closure Sourcemap
Source map is a core aspect of WUIC which heavily rely on it to provide debugging support. We embed the closure-compiler implementation of source map specification. Only the source map implementation was required so it was not necessary to make a dependency to the entire project. Moreover the source code is quite stable, so we don’t have to heavily update the package with upstream changes.
Configuration structure
Purpose
Organizing, processing and serving web assets is related to a lot of technical considerations and settings. This document helps to understand how WUIC organizes its configuration points to address those several issues.
Key components
The central component used inside WUIC is the Context. It knows:
-
How statics are organized (nuts inside heaps)
-
How statics are processed (workflows)
It’s built thanks to a ContextBuilder. This builder could be configured with several ContextBuilderConfigurator. The wuic.xml
file is for instance read by a ContextBuilderConfigurator.
Building the context is automated and managed transparently by the WuicFacade. Its state also contains additional information regarding how the process result is served to client. The facade could be built with a WuicFacadeBuilder. The web.xml
can declare several facade settings to let a ContextListener bootstrap WUIC.
Conclusion
You can delegate facade instantiation to a servlet listener in your web.xml and configure the underlying WUIC context with a wuic.xml
file. You can also complete or replace configuration with Java Config
. Take a look at the different API references to see details.