<?xml version="1.0" encoding="UTF-8"?> <groups xmlns="http://www.isdc.ro/wro" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.isdc.ro/wro wro.xsd"> <group name="main"> <css>/resources/**.css</css> <css>/resources/**.less</css> <js>/resources/**.js</js> </group> </groups>



The above "everything in one group" configuration assumes that all style sheets are compatible with each other and the group is applicable to every page. Otherwise said, it is impossible to have different css theme for some pages. It also assumes that all JavaScripts are compatible with each and needed on every page.



The above "everything in one group" configuration assumes that all style sheets are compatible with each other and the group is applicable to every page. Otherwise said, it is impossible to have different css theme for some pages. It also assumes that all JavaScripts are compatible with each and needed on every page. If this is the case, then you have to split style sheets and JavaScripts into multiple groups:

<?xml version="1.0" encoding="UTF-8"?> <groups xmlns="http://www.isdc.ro/wro" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.isdc.ro/wro wro.xsd"> <group name="theme1"> <css>/resources/common/**.css</css> <css>/resources/common/**.less</css> <js>/resources/common/**.js</js> <css>/resources/theme1/**.css</css> <css>/resources/theme1/**.less</css> <js>/resources/theme1/**.js</js> </group> <group name="theme2"> <css>/resources/common/**.css</css> <css>/resources/common/**.less</css> <js>/resources/common/**.js</js> <css>/resources/theme2/**.css</css> <css>/resources/theme2/**.less</css> <js>/resources/theme2/**.js</js> </group> </groups>



Js and Css Tags Resources are listed using js and css tags. Whatever is referenced by js tag is assumed to be JavaScript and whatever is referenced by css tag is assumed to be CSS file.



It is important to keep this distinction clear. JavaScript and CSS files from the same group are served separately and use different pre and post processors.



Both js and css tags can reference resources on classpath, file system, relative to the servlet context or by url. It is also possible to extend them and add new resource locations (database, ...), but such thing is out of scope of this post.

<css>classpath:org/somewhere/style.css</css> <css>file:src/main/webapp/resources/hangman/style.css</css> <css>http://localhost:8080/wicket-wro/resources/hangman/style.css</css>



They both support * wildcard and ** deep wildcard. Wildcard matches only files in specified directory. It does not go into sub-directories. Deep wildcard matches also files in all sub-directories:

<group name="deep_wildcard_goes_into_subdirectories"> <css>/resources/**.css</css> </group> <group name="wildcard_has_to_list_all_subdirectories"> <css>/resources/*.css</css> <css>/resources/hangman/*.css</css> <css>/resources/home/*.css</css> </group>



All resources in the group are minimized by default. If you wish to turn off minimization for some resource, add minimize="false" attribute to js or css tag. Of course, the minimize attribute works only if the minimizing processor is configured as a pre-processor (more on that All resources in the group are minimized by default. If you wish to turn off minimization for some resource, addattribute toortag. Of course, theattribute works only if the minimizing processor is configured as a pre-processor (more on that later ).

<css minimize="false">/resources/**.css</css>



Note: Both js and css tags in the current version 1.4.6 have <css>/**.css</css> does not work. The workaround is to place all resources in some sub-directory and reference that one: <css>/resources/**.css</css> . The bug was already fixed and the next released version will not have this limitation.



Group References Any group can reference resources from other groups using the group-ref tag.



It is used to avoid repetition and place common resources into one group. For example, previous Note: Bothandtags in the current version 1.4.6 have a bug . Neither of them works correctly if it references the project root. For example,does not work. The workaround is to place all resources in some sub-directory and reference that one:. The bug was already fixed and the next released version will not have this limitation.Any group can reference resources from other groups using thetag.It is used to avoid repetition and place common resources into one group. For example, previous multi-group configuration can be rewritten into following form:

<?xml version="1.0" encoding="UTF-8"?> <groups xmlns="http://www.isdc.ro/wro" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.isdc.ro/wro wro.xsd"> <group name="common-resources"> <css>/resources/common/**.css</css> <css>/resources/common/**.less</css> <js>/resources/common/**.js</js> </group> <group name="theme1"> <group-ref>common-resources</group-ref> <css>/resources/theme1/**.css</css> <css>/resources/theme1/**.less</css> <js>/resources/theme1/**.js</js> </group> <group name="theme2"> <group-ref>common-resources</group-ref> <css>/resources/theme2/**.css</css> <css>/resources/theme2/**.less</css> <js>/resources/theme2/**.js</js> </group> </groups>



Using Groups Configured groups are used the same way as any other JavaScript or css resource.



Assuming that the wro filter catches every request for /wro/* and that you have a group named main , you can include all its css resources using:

Configured groups are used the same way as any other JavaScript or css resource.Assuming that the wro filter catches every request forand that you have a group named, you can include all its css resources using:

<link rel="stylesheet" type="text/css" href="wro/main.css">



Similarly, you can include all its js resources the same way as you would an ordinary JavaScript:



<script type="text/javascript" src="wro/main.js"></script>



Note: unless configured Note: unless configured otherwise , wro4j takes requested file name as a group name and a suffix as a resource type. It ignores the path and recognizes only two possible suffixes: .css and .js. Therefore, following snippet loads the same group as previous two:

<script type="text/javascript" src="wro/something/main.js"></script> <link rel="stylesheet" type="text/css" href="wro/something/main.css">



Processors Wro4j applies pre-processors to each file before merging and post-processors to the merged group. As we have not explicitly configured them yet, our project is using the default set.



The list of processors is created in wro manager factory. The default factory supplies reasonable list of default processors, but it is unable to create less compiler. Therefore, we have to: replace the default factory with something more flexible,

configure new factory to use all necessary pre/post-processors,

configure new factory to use less compiler.

First section shows how to replace the default factory and second chapter explains how to configure it. Remaining sections go through all used processors and explain what they do and why we had to use them.



Replace Manager Factory First we have to choose the right manager factory. Manager factory is a class that implements WroManagerFactory interface and wro4j has a lot of them. Most of manager factory implementations serve unit tests, Maven integration, command line integration or other "helper" cases.



Four manager factories are designed to be used in web application: DefaultWroManagerFactory - used if nothing else is configured.

- used if nothing else is configured. CustomWroManagerFactory - meant to be extended. Extend it if no standard factory produces what you need.

- meant to be extended. Extend it if no standard factory produces what you need. ConfigurableWroManagerFactory - able to create all processors from basic wro4j package. Partially configurable in wro.properties file.

- able to create all processors from basic wro4j package. Partially configurable in wro.properties file. ExtensionsConfigurableWroManagerFactory - able to create all processors from wro4j-extensions package. Partially configurable in wro.properties file.

We will use ExtensionsConfigurableWroManagerFactory because it supports all processors we need and is easy to configure.



Create wro.properties file and place it to the WEB-INF directory. Use managerFactoryClassName property to configure wro manager factory class name:

managerFactoryClassName=ro.isdc.wro.extensions.manager.ExtensionsConfigurableWroManagerFactory

Configure Manager Factory Extension configurable manager factory has three configurable properties: uriLocators - uri locators locates resources. They take uri as an input and returns stream. This property has reasonable default value, so we will leave it as it is.

- uri locators locates resources. They take uri as an input and returns stream. This property has reasonable default value, so we will leave it as it is. preProcessors - list of pre-processors. It is empty by default.

- list of pre-processors. It is empty by default. postProcessors - list of post-processors. It is empty by default.

Neither merged .css nor merged .js files work correctly with empty pre-processors list. Simplest working configuration requires three pre-processors: cssUrlRewriting , cssImport and semicolonAppender .



Post-processors are not necessary, but some of them are very useful. We decided to use three post-processors: lessCss , jsMin and cssMin .



Final wro.properties file:

managerFactoryClassName=ro.isdc.wro.extensions.manager.ExtensionsConfigurableWroManagerFactory preProcessors=cssUrlRewriting,cssImport,semicolonAppender postProcessors=lessCss,jsMin,cssMin

Processors are applied in the configured order. Each processor knows whether it should be applied to JavaScript, css or both files. You do not have to worry about wrong type of processor being applied to wrong type of file.



Relative References in CSS Loading css file from the group instead of its original location breaks relative references. Unless import statements and image locations are rewritten, @import statement or referenced images do not work anymore.



Resources references rewriting is done in cssUrlRewriting and cssImport pre-processors. Their order does matter, the css url rewriting processor must go first.



The cssUrlRewriting rewrites relative references to images so they work from the new location. The cssImport does two things: adds imported .css files into the group,

removes the import statement.

Use both cssUrlRewriting and cssImport as pre-processors:

preProcessors=cssUrlRewriting,cssImport

Missing ; in .js Files JavaScript file may miss last semicolon and still be valid JavaScript file. However, once you merge those files together, the missing semicolon may cause a lot of problems.



Use semicolonAppender pre-processor to add missing last ; into JavaScript files.



Add semicolonAppender to pre-processors list:

preProcessors=cssUrlRewriting,cssImport,semicolonAppender

Minimization Strictly speaking, this is not necessary. However, since we already added wro4j into the project and configured it, we can take advantage of its minimization abilities as well.



Use cssMin to minimize css files and jsMin to minimize JavaScript files. Both are able to work as both pre-processors and post-processors.

postProcessors=jsMin,cssMin

Note: jsMin is not the only JavaScript minimizer available in wro4j. Wro4j-extensions contains also Google closure compiler, YUI compression utilities, Dojo Shrinksafe utility and other popular minimizers. All minimizers are listed in the



Less Compiler Less compiler is implemented in LessCssProcessor . It can act as both pre-processor or post-processor. If you use it as a pre-processor the @import statement does not work, so it is better to ignore that option and use it as a post-processor only.



Do not worry about JavaScript files in the same group, LessCssProcessor does not modify them.



Add less compiler to the list of post processors. It make more sense to compile first and minimize later:

postProcessors=lessCss,jsMin,cssMin

Dynamic Model Having files grouped together lowers number of requests, but also adds some additional hustle. The more fine grained groups are, the more likely they break when you add, move, rename or remove files.



If the model is predictable, it can be build dynamically in Java code. If it is done right, wro4j acts transparently to the rest of the application and requires minimal maintennance.



This chapter shows how to create very simple dynamic model: each .css and .less file have its own group and is processed separately. As an additional bonus, it fixes the bug mentioned



Sample Project We took Apache Wicket Hangman example project from the Apache repository and modified it to use wro4j with dynamic model.



Use the StartExamples class to run the project. It will configure and start jetty server with hangman application running on it. The application is then available at http://localhost:8080/wicket-wro/hangman/ url.



The



Filter First, we have to configure WroFilter to catch all requests for .css or .less files. We will not assume that they are all located in some special location.



Add following into web.xml file:

<filter> <filter-name>WebResourceOptimizer</filter-name> <filter-class>ro.isdc.wro.http.WroFilter</filter-class> </filter> <filter-mapping> <filter-name>WebResourceOptimizer</filter-name> <url-pattern>*.less</url-pattern> </filter-mapping> <filter-mapping> <filter-name>WebResourceOptimizer</filter-name> <url-pattern>*.css</url-pattern> </filter-mapping>

The Model Wro4j uses two steps algorithm to create the model: model factory creates initial model,

initial model is transformed into final model using model transformers.

The default model factory creates initial model that corresponds exactly to the content of wro.xml file. Wro4j then applies its only default model transformer to it. The transformer is called wildcard model transformer and expands all resources containing * or ** into list of concrete file resources.



We have to create a model that contains all .css and .less files, so it makes sense to take advantage of the wilcard model transformer. Our initial model has only one group which uses deep wilcard to reference all those files. It corresponds to following xml file: <group name="fake"> <css>/**.css</css> <css>/**.less</css> </group>

Then we use wildcard model transformer to replace wildcards with list of concrete files. Once we have all files in one group, we will use custom model transformer to move each file into separate group.



Initial Model The model must implement WroModel interface and is created by an instance of WroModelFactory . The model contains a list of groups Group and each group contains list of resources Resource :

public class ConfigurationFreeModelFactory implements WroModelFactory { public WroModel create() { WroModel result = new WroModel(); result.addGroup(createAllResourcesGroup()); return result; } private Group createAllResourcesGroup() { Resource allLess = new Resource(); allLess.setType(ResourceType.CSS); allLess.setUri("/**.less"); Resource allCss = new Resource(); allCss.setType(ResourceType.CSS); allCss.setUri("/**.css"); Group group = new Group("fake"); group.addResource(allCss); group.addResource(allLess); return group; } public void destroy() { } }

Model Transformers We use two model transformers: wildcard model transformer to expand wildcards,

custom resources to groups model transformer to create a separate group for each file.

Wildcard model transformer expands wildcards into list of resources. The default wildcard model transformer has a



Find the



Second model transformer creates a separate group for each resource. Each group must have unique name, so we used resource uri as the group name. For example, a style sheet named main.css and located in resources directory is placed into group named /resources/main.css :

Wro4j applies pre-processors to each file before merging and post-processors to the merged group. As we have not explicitly configured them yet, our project is using the default set.The list of processors is created in wro manager factory. The default factory supplies reasonable list of default processors, but it is unable to create less compiler. Therefore, we have to:First section shows how to replace the default factory and second chapter explains how to configure it. Remaining sections go through all used processors and explain what they do and why we had to use them.First we have to choose the right manager factory. Manager factory is a class that implementsinterface and wro4j has a lot of them. Most of manager factory implementations serve unit tests, Maven integration, command line integration or other "helper" cases.Four manager factories are designed to be used in web application:We will usebecause it supports all processors we need and is easy to configure.Create wro.properties file and place it to the WEB-INF directory. Useproperty to configure wro manager factory class name:Extension configurable manager factory has three configurable properties:Neither merged .css nor merged .js files work correctly with empty pre-processors list. Simplest working configuration requires three pre-processors:andPost-processors are not necessary, but some of them are very useful. We decided to use three post-processors:andFinal wro.properties file:Processors are applied in the configured order. Each processor knows whether it should be applied to JavaScript, css or both files. You do not have to worry about wrong type of processor being applied to wrong type of file.Loading css file from the group instead of its original location breaks relative references. Unless import statements and image locations are rewritten,statement or referenced images do not work anymore.Resources references rewriting is done inandpre-processors. Their order does matter, the css url rewriting processor must go first.Therewrites relative references to images so they work from the new location. Thedoes two things:Use bothandas pre-processors:JavaScript file may miss last semicolon and still be valid JavaScript file. However, once you merge those files together, the missing semicolon may cause a lot of problems.Usepre-processor to add missing lastinto JavaScript files.Addto pre-processors list:Strictly speaking, this is not necessary. However, since we already added wro4j into the project and configured it, we can take advantage of its minimization abilities as well.Useto minimize css files andto minimize JavaScript files. Both are able to work as both pre-processors and post-processors.Note: jsMin is not the only JavaScript minimizer available in wro4j. Wro4j-extensions contains also Google closure compiler, YUI compression utilities, Dojo Shrinksafe utility and other popular minimizers. All minimizers are listed in the processors list Less compiler is implemented in. It can act as both pre-processor or post-processor. If you use it as a pre-processor thestatement does not work, so it is better to ignore that option and use it as a post-processor only.Do not worry about JavaScript files in the same group, LessCssProcessor does not modify them.Add less compiler to the list of post processors. It make more sense to compile first and minimize later:Having files grouped together lowers number of requests, but also adds some additional hustle. The more fine grained groups are, the more likely they break when you add, move, rename or remove files.If the model is predictable, it can be build dynamically in Java code. If it is done right, wro4j acts transparently to the rest of the application and requires minimal maintennance.This chapter shows how to create very simple dynamic model: each .css and .less file have its own group and is processed separately. As an additional bonus, it fixes the bug mentioned above We took Apache Wicket Hangman example project from the Apache repository and modified it to use wro4j with dynamic model.Use theclass to run the project. It will configure and start jetty server with hangman application running on it. The application is then available aturl.The sample project is available on Github.First, we have to configure WroFilter to catch all requests for .css or .less files. We will not assume that they are all located in some special location.Add following into web.xml file:Wro4j uses two steps algorithm to create the model:The default model factory creates initial model that corresponds exactly to the content of wro.xml file. Wro4j then applies its only default model transformer to it. The transformer is called wildcard model transformer and expands all resources containing * or ** into list of concrete file resources.We have to create a model that contains all .css and .less files, so it makes sense to take advantage of the wilcard model transformer. Our initial model has only one group which uses deep wilcard to reference all those files. It corresponds to following xml file:Then we use wildcard model transformer to replace wildcards with list of concrete files. Once we have all files in one group, we will use custom model transformer to move each file into separate group.The model must implementinterface and is created by an instance of. The model contains a list of groupsand each group contains list of resourcesWe use two model transformers:Wildcard model transformer expands wildcards into list of resources. The default wildcard model transformer has a bug , so we will replace it with a fixed one.Find the fixed model transformer in our demo project on Github. Since the bug is already fixed in wro4j trunk, we will not show the code here. The solution is only temporary and fixed wildcard model transformer will not be needed after the next wro4j release.Second model transformer creates a separate group for each resource. Each group must have unique name, so we used resource uri as the group name. For example, a style sheet namedand located indirectory is placed into group named

public class ResourcesToGroupsModelTransformer implements Transformer<WroModel> { public WroModel transform(WroModel input) throws Exception { WroModel result = new WroModel(); for (Group group : input.getGroups()) { for (Resource resource : group.getResources()) { Group resourceGroup = toGroup(resource); result.addGroup(resourceGroup); } } return result; } private Group toGroup(Resource resource) { Group resourceGroup = new Group(resource.getUri()); resourceGroup.addResource(resource); return resourceGroup; } }



Group Extractor Group extractor extracts group name and resource type out of incoming request. It also returns whether the group should be minimized and whether it is possible to compose group name and resource type back to the original uri.



Default group extractor takes requested file name as the group name and a suffix as the resource type. It recognizes only two possible suffixes: .css and .js. Our dynamic model uses whole uri as the group name and contains also .less files. As a result, the default wro configuration is unable to find any of our generated groups.



For example, the request for context path/resource/main.css would be resolved as a request for all style sheets inside the main group. As our group is named /resource/main.css , it would not be found. Additionally, we need it to be recognize .less files as a style sheets, but the default implementation discards them as unknown resource types.



A group extractor must implement GroupExtractor interface. Its

Group extractor extracts group name and resource type out of incoming request. It also returns whether the group should be minimized and whether it is possible to compose group name and resource type back to the original uri.Default group extractor takes requested file name as the group name and a suffix as the resource type. It recognizes only two possible suffixes: .css and .js. Our dynamic model uses whole uri as the group name and contains also .less files. As a result, the default wro configuration is unable to find any of our generated groups.For example, the request forwould be resolved as a request for all style sheets inside thegroup. As our group is named, it would not be found. Additionally, we need it to be recognize .less files as a style sheets, but the default implementation discards them as unknown resource types.A group extractor must implementinterface. Its full implementation is available on Github. Most of it is trivial, so following example contains only the most important part:

public class ConfigurationFreeGroupExtractor implements GroupExtractor { private final DefaultGroupExtractor defaultExtractor = new DefaultGroupExtractor(); /** * Everything that follows context path is considered a group name. */ public String getGroupName(HttpServletRequest request) { String contextPath = request.getContextPath(); String uri = getUri(request); if (uri.startsWith(contextPath)) uri = uri.substring(contextPath.length()); return uri; } /** * If the default extractor is unable to find the resource type, * check whether it is a .less file. Less files are considered * style sheets. */ public ResourceType getResourceType(HttpServletRequest request) { ResourceType resourceType = defaultExtractor.getResourceType(request); //if the default extractor could not find the type //check whether it is .less file if (resourceType==null && isLessFile(request)) { resourceType = ResourceType.CSS; } return resourceType; } private boolean isLessFile(HttpServletRequest request) { return request.getRequestURI().toUpperCase().endsWith(".LESS"); } ... }



Wro Manager Factory We have custom model factory, custom group extractor and custom model transformer. The last thing we need is to create and configure custom wro manager factory that produces our new objects.



All wro manager factories are designed in very similar way and it does not matter too much which one we extend. As we have ExtensionsConfigurableWroManagerFactory already configured with needed processors, we will extend this one and override three its methods:

newModelFactory - creates model factory,

- creates model factory, newModelTransformers - creates model transformers,

- creates model transformers, newGroupExtractor - creates group extractor.

New factory is located in org.meri.wro4j.nogroups package: We have custom model factory, custom group extractor and custom model transformer. The last thing we need is to create and configure custom wro manager factory that produces our new objects.All wro manager factories are designed in very similar way and it does not matter too much which one we extend. As we have ExtensionsConfigurableWroManagerFactory already configured with needed processors, we will extend this one and override three its methods:New factory is located inpackage:

public class ConfigurationFreeManagerFactory extends ExtensionsConfigurableWroManagerFactory { @Override protected GroupExtractor newGroupExtractor() { return new ConfigurationFreeGroupExtractor(); } @Override protected WroModelFactory newModelFactory() { return new ConfigurationFreeModelFactory(); } @Override protected List<Transformer<WroModel>> newModelTransformers() { //The super class store the list of model transformers in a private //property. We need both modify that property and return the list of //correct transformers. List<Transformer<WroModel>> modelTransformers = super.newModelTransformers(); //replace default WildcardExpanderModelTransformer with //FixedWildcardExpanderModelTransformer modelTransformers.clear(); addModelTransformer(new FixedWildcardExpanderModelTransformer()); addModelTransformer(new ResourcesToGroupsModelTransformer()); return modelTransformers; } }