Wicket in Action

Wicket 6 resource management

06 July 2012, by martin-g

Several improvements in the header contribution functionality have been made in Wicket 6 to make it more flexible.

Header items

The major API break is that now IHeaderResponse can render only instances of HeaderItem. In previous versions IHeaderResponse had many #renderXyz() methods for each and every combination of CSS or JavaScript attributes, like media, IE condition, charset, url, id, etc. The new version of IHeaderResponse has just one #render(HeaderItem) and the developer should use the most specific implementation of HeaderItem.

For example:

AnyComponent/AnyBehavior.java:

@Override
public void renderHead(IHeaderResponse response) {
	super.renderHead(response);

	response.render(new CssContentHeaderItem(someCss, someId, optionalCondition));
	response.render(CssHeaderItem.forReference(new SomeCssResourceReference()));
	response.render(JavaScriptHeaderItem.forScript(someScript, someId));
}

JavaScriptHeaderItem and CssHeaderItem provide factory methods for all default implementations of JavaScript and CSS related header items.

Dependency management

Resource references take advantage of the new HeaderItem's and use them to specify on what other resources they depend. The most trivial example is when a JavaScriptResourceReference used to contribute a jQuery plugin needs to declare that it depends on jQuery. This way the component/behavior needs to contribute only the resources it really depends on and Wicket cares to deliver the dependencies first and then the dependants.

public class MyResourceReference extends JavaScriptResourceReference {
	public ResourceReferenceA() {
		super(ResourceReferenceA.class, "my.js");
	}

	public Iterable<? extends HeaderItem> getDependencies() {
		List<HeaderItem> dependencies = new ArrayList<HeaderItem>();

		Url dojoGoogleCdn = Url.parse("https://ajax.googleapis.com/ajax/libs/dojo/1.7.2/dojo/dojo.js");
		ExternalUrlResourceReference externalUrlResourceReference = new ExternalUrlResourceReference(dojoGoogleCdn);
		dependencies.add(JavaScriptHeaderItem.forReference(externalUrlResourceReference));

		dependencies.add(CssHeaderItem.forReference(new CssResourceReference(ResourceReferenceA.class, "a.css")));

		return dependencies;
	}
}

With the example above Wicket will render first the <script> to load Dojo and the <link> to load a.css before rendering the <script> for my.js.

Resource bundles

Another new feature in Wicket 6 is that several resources can be combined together in one before delivering it to the browser.

One way to do this is to use WebApplication's ResourceBundles:

public void init() {
	super.init();

	getResourceBundles()
		.addJavaScriptBundle(ResourceManagementApplication.class, "bundle.js",
			new ResourceReferenceA(),
			new ResourceReferenceB(),
			new ResourceReferenceC()
		);
}

Another way is by extending ConcatResourceBundleReference and using it as any other ResourceReference.

The bundle (with all resources) will be contributed if at least one of its resources is contributed by a component or behavior.

Resource replacement

Sometimes a third-party library or even Wicket itself adds dependencies you
would rather replace by something else. For example, your application might
depend on a more recent version of a JavaScript library or you want to use a
CDN rather than a packaged resource. For this, Wicket 6 supports replacement
resources. A replacement resource is a resource that will replace any
occurence of some other resource throughout the application. It does not
matter if the replaced resource is added in third party code, in your code or
even as a depedency of another resource.

The following code replaces any occurence of DojoResourceReference by the
version hosted on the Google APIs CDN:

MyApplication.java:

@Override
public void init() {
  super.init();
  
  addResourceReplacement(
    DojoResourceReference.get(),
    new UrlResourceReference(Url.parse("https://ajax.googleapis.com/ajax/libs/dojo/1.7.3/dojo/dojo.js"))
  );

Positioning of contributions

Filtered items

A not well know feature in Wicket is that the application can provide custom IHeaderResponseDecorator and filter the header contributions. A common use case is when the application needs to render all JavaScripts in the footer of the page and leave CSSs in the header.
This is made a bit simpler now by introducing FilteredHeaderItem.

To make a use of it the developer have to add a special component in his page:

add(new HeaderResponseContainer("someId", filterName));
<wicket:container wicket:id="someId"/>

And later contribute FilteredHeaderItems in any component or behavior:

@Override
public void renderHead(IHeaderResponse response) {
	super.renderHead(response);

	response.render(new FilteredHeaderItem(wrappedItem, filterName));
}

Priority items

Since Wicket 1.5 components do their header contribution before the page they are contained in. The reason is that the application may use a third party component library and the page should be able to override any contribution made by the components. A better design is if the components may be configured what to contribute but sometimes this is not possible. The same is valid for component inheritence - the specialization (the child component) contributes after the base component.
Thanks to the HeaderItems with Wicket 6 it is possible to give some header contributions bigger priority than the others when there is such need. By default Wicket provides PriorityHeaderItem which is an item that is contributed before any other non-priority item. If the application needs finer control then it may register a custom comparator with IResourceSettings#setHeaderItemComparator(Comparator)

response.render(new PriorityHeaderItem(wrappedItem));

Demo application

A demo application showing all these features in action can be found at Martin Grigorov's GitHub repository. Run it locally and check the produced markup for the pages. There are comments inside the code describing the demonstrated features.

Additionally the Resource aggregation example has been updated to use the new features.

-->