(Quick Reference)

15 Resource management with Wicket - Reference Documentation

Authors: Andrea Del Bene, Martin Grigorov, Carsten Hufe, Christian Kroemer, Daniel Bartl, Paul Borș

Version: 6.x

15 Resource management with Wicket

One of the biggest challenge for a web framework is to offer an efficient and consistent mechanism to handle internal resources such as CSS/JavaScript files, picture files, pdf and so on. Resources can be static (like an icon used across the site) or dynamic (they can be generated on the fly) and they can be made available to users as a download or as a simple URL.

In paragraph 4.6 we have already seen how to add CSS and JavaScript contents to the header section of the page. In the first half of this chapter we will learn a more sophisticated technique that allows us to manage static resources directly from code and “pack” them with our custom components.

Then, in the second part of the chapter we will see how to implement custom resources to enrich our web application with more complex and dynamic functionalities.

15.1 Static vs dynamic resources

In Wicket a resource is an entity that can interact with the current request and response and It must implement interface org.apache.wicket.request.resource.IResource. This interface defines just method respond(IResource.Attributes attributes) where the nested class IResource. Attributes provides access to request, response and page parameters objects.

Resources can be static or dynamic. Static resources don't entail any computational effort to be generated and they generally correspond to a resource on the filesystem. On the contrary dynamic resources are generated on the fly when they are requested, following a specific logic coded inside them.

An example of dynamic resource is the built-in class CaptchaImageResource in package org.apache.wicket.extensions.markup.html.captcha which generates a captcha image each time is rendered.

As we will see in paragraph 13.6, developers can build custom resources extending base class org.apache.wicket.request.resource.AbstractResource.

15.2 Resource references

Most of the times in Wicket we won't directly instantiate a resource but rather we will use a reference to it. Resource references are represented by abstract class org.apache.wicket.request.resource.ResourceReference which returns a concrete resource with factory method getResource(). In this way we can lazy-initialize resources loading them only the first time they are requested.

15.3 Package resources

With HTML we use to include static resources in our pages using tags like <script>, <link> or <img>. This is what we have done so far writing our custom panels and pages. However, when we work with a component-oriented framework like Wicket, this classic approach becomes inadequate because it makes custom components hardly reusable. This happens when a component depends on a big number of resources. In such a case, if somebody wanted to use our custom component in his application, he would be forced to know which resources it depends on and make them available.

To solve this problem Wicket allows us to place static resource files into component package (like we do with markup and properties files) and load them from component code.

These kinds of resources are called package resources (a CSS and a JavaScript file in this screenshot):

With package resources custom components become independent and self-contained and client code can use them without worrying about their dependencies.

To load package resources Wicket provides class org.apache.wicket.request.resource.PackageResourceReference.

To identify a package resource we need to specify a class inside the target package and the name of the desired resource (most of the times this will be a file name).

In the following example taken from project ImageAsPackageRes, CustomPanel loads a picture file available as package resource and it displays it in a <img> tag using the built-in component org.apache.wicket.markup.html.image.Image:

HTML:

<html>
<head>...</head>
<body>
<wicket:panel>
	Package resource image: <img wicket:id="packageResPicture"/>
</wicket:panel>
</body>
</html>

Jave Code:

public class CustomPanel extends Panel {

public CustomPanel(String id) { super(id); PackageResourceReference resourceReference = new PackageResourceReference(getClass(), "calendar.jpg"); add(new Image("packageResPicture", resourceReference)); } }

Wicket will take care of generating a valid URL for file calendar.jpg. URLs for package resources have the following structure:

<path to application root>/wicket/resource/<fully qualified classname>/<resource file name>-<ver-<id>>(.file extension)

In our example the URL for our picture file calendar.jpg is the following:

./wicket/resource/org.wicketTutorial.CustomPanel/calendar-ver-1297887542000.jpg

The first part of the URL is the relative path to the application root. In our example our page is already at the application's root so we have only a single-dotted segment. The next two segments, wicket and resource, are respectively the namespace and the identifier for resources seen in paragraph 8.6.4.

The fourth segment is the fully qualified name of the class used to locate the resource and it is the scope of the package resource. In the last segment of the URL we can find the name of the resource (the file name).

As you can see Wicket has automatically appended to the file name a version identifier (ver-1297887542000). When Wicket runs in DEVELOPMENT mode this identifier contains the timestamp in millisecond indicating the last time the resource file was modified. This can be useful when we are developing our application and resource files are frequently modified. Appending the timestamp to the original name we are sure that our browser will use always the last version of the file and not an old, out of date, cached version.

When instead Wicket is running in DEPLOYMENT mode, the version identifier will contain the MD5 digest of the file instead of the timestamp. The digest is computed only the first time the resource is requested. This perfectly makes sense as static resources don't change so often when our application runs into production environment and when this appends the application is redeployed.

Package resources can be localized following the same rules seen for resource bundles and markup files:

In the example illustrated in the picture above, if we try to retrieve package resource calendar.jpg when the current locale is set to French, the actual file returned will be calendar_fr.jpg.

Using package resources with tag <wicket:link>

In paragraph 8.3 we have used tag <wicket:link> to automatically create links to bookmarkable pages. The same technique can be used also for package resources in order to use them directly from markup file. Let's assume for example that we have a picture file called icon.png placed in the same package of the current page. Under these conditions we can display the picture file using the following markup fragment:

<wicket:link>
   <img src="icon.png"/>
</wicket:link>

In the example above Wicket will populate the attribute src with the URL corresponding to the package resource icon.png. <wicket:link> supports also tag <link> for CSS files and tag <script> for JavaScript files.

15.4 Adding resources to page header section

Wicket comes with interface org.apache.wicket.markup.html.IHeaderContributor which allows components and behaviors (which will be introduced later in paragraph 15.1) to contribute to the header section of their page. The only method defined in this interface is renderHead(IHeaderResponse response) where IHeaderResponse is an interface which defines method render(HeaderItem item) to write static resources or free-form text into the header section of the page.

Header entries are instances of abstract class org.apache.wicket.markup.head.HeaderItem. Wicket provides a set of built-in implementations of this class suited for the most common types of resources. With the exception of PriorityHeaderItem, every implementation of HeaderItem is an abstract factory class:

  • CssHeaderItem: represents a CSS resource. Factory methods provided by this class are forReference which takes in input a resource reference, forUrl which creates an CSS item from a given URL and forCSS which takes in input an arbitrary CSS string and an optional id value to identify the resource.
  • JavaScriptHeaderItem: represents a JavaScript resource. Just like CssHeaderItem it provides factory methods forReference and forUrl along with method forScript which takes in input an arbitrary string representing the script and an optional id value to identify the resource. Method forReference also supports boolean parameter defer which renders the namesake attribute in the script tag (defer attribute indicates that our script must be execute only after the page has loaded).
  • OnDomReadyHeaderItem: it adds JavaScript code that will be executed after the DOM has been built, but before external files (such as picture, CSS, etc...) have been loaded. The class provides a factory method forScript which takes in input an arbitrary string representing the script to execute.
  • OnEventHeaderItem: the JavaScript code added with this class is executed when a specific JavaScript event is triggered on a given DOM element. The factory method is forScript(String target, String event, CharSequence javaScript), where target is the id of a DOM element (or the element itself), event is the event that must trigger our code and javaScript is the code to execute.
  • OnLoadHeaderItem: the JavaScript code added with this class is executed after the whole page is loaded, external files included. The factory method is forScript(CharSequence javaScript).
  • PriorityHeaderItem: it wraps another header item and ensures that it will have the priority over the other items during rendering phase.
  • StringHeaderItem: with this class we can add an arbitrary text to the header section. Factory method is forString(CharSequence string).
  • MetaDataHeaderItem: starting from version 6.17.0, Wicket provides this class to handle meta informations such as <meta> tags or canonical link element. The available factory methods are forLinkTag and forMetaTag which can be used to create respectively a <link> tag or a <meta> one. We can add tag attribute to an existing instance of MetaDataHeaderItem with method addTagAttribute(String attributeName, Object attributeValue). See JavaDoc for further details on this class.

In the following example our custom component loads a CSS file as a package resource (placed in the same package) and it adds it to header section.

public class MyComponent extends Component{

@Override public void renderHead(IHeaderResponse response) { PackageResourceReference cssFile = new PackageResourceReference(this.getClass(), "style.css"); CssHeaderItem cssItem = CssHeaderItem.forReference(cssFile);

response.render(cssItem); } }

15.5 Resource dependencies

Class ResourceReference allows to specify the resources it depends on overriding method getDependencies(). The method returns an iterator over the set of HeaderItems that must be rendered before the resource referenced by ResourceReference can be used. This can be really helpful when our resources are JavaScript or CSS libraries that in turn depend on other libraries.

For example we can use this method to ensure that a custom reference to JQueryUI library will find JQuery already loaded in the page:

Url jqueyuiUrl = Url.parse("https://ajax.googleapis.com/ajax/libs/jqueryui/" + 
                                                                 "1.10.2/jquery-ui.min.js");

UrlResourceReference jqueryuiRef = new UrlResourceReference(jqueyuiUrl){ @Override public Iterable<? extends HeaderItem> getDependencies() { Application application = Application.get(); ResourceReference jqueryRef = application.getJavaScriptLibrarySettings(). getJQueryReference();

return Arrays.asList(JavaScriptHeaderItem.forReference(jqueryRef)); } };

Please note that in the code above we have built a resource reference using a URL to the desired library instead of a package resource holding the physical file.

The same method getDependencies() is defined also for class HeaderItem.

15.6 Aggregate multiple resources with resource bundles

One of the best practices to make our web application faster and reduce its latency is to reduce the number of requests to the server to load page resources like JavaScript or CSS files. To achieve this goal some JavaScript-based build tools (like Grunt) allow to merge multiple files used in a page into a single file that can be loaded in a single request. Wicket provides class org.apache.wicket.ResourceBundles to aggregate multiple resource references into a single one. A resource bundle can be declared during application initialization listing all the resources that compose it:

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

getResourceBundles().addJavaScriptBundle(WicketApplication.class, "jqueryUiJs", jqueryJsReference, jqueryUiJsReference);

getResourceBundles().addCssBundle(WicketApplication.class, "jqueryUiCss", jqueryCssReference, jqueryUiCssReference);

}

To declare a new resource bundle we need to provide a scope class (WicketApplication.class in our example) and an unique name. Now, when one of the resources included in the bundle is requested, the entire bundle is rendered instead.

A specific resource reference can not be shared among different resource bundles (i.e. it can be part of only one bundle).

15.7 Put JavaScript inside page body

Some web developers prefer to put their <script> tags at the end of page body instead of inside the <head> tags:

<html>

<head> //no <script> tag here… </head>

<body> … <script> //one or more <script> tags at the end of the body </script> </body> </html>

In Wicket we can achieve this result providing a custom IHeaderResponseDecorator to a our application and using Wicket tag <wicket:container/> to indicate where we want to render our scripts inside the page. Interface IHeaderResponseDecorator defines method IHeaderResponse decorate(IHeaderResponse response) which allows to decorate or add funtionalities to Wicket IHeaderResponse. Our custom IHeaderResponseDecorator can be registered in the application with method setHeaderResponseDecorator. Anytime Wicket creates an instance of IHeaderResponse, it will call the registered IHeaderResponseDecorator to decorate the header response.

In the example project ScriptInsideBody we can find a custom IHeaderResponseDecorator that renders CSS into the usual <head> tag and put JavaScricpt header items into a specific container (tag <wicket:container/>) Wicket already comes with class JavaScriptFilteredIntoFooterHeaderResponse which wraps a IHeaderResponse and renders in a given container all the instances of JavaScriptHeaderItem. The following code is taken from the Application class of the project:

//…
    @Override
    public void init()
    {
       setHeaderResponseDecorator(new JavaScriptToBucketResponseDecorator("footer-container"));
    }

/** * Decorates an original IHeaderResponse and renders all javascript items * (JavaScriptHeaderItem), to a specific container in the page. */ static class JavaScriptToBucketResponseDecorator implements IHeaderResponseDecorator {

private String bucketName;

public JavaScriptToBucketResponseDecorator(String bucketName) { this.bucketName = bucketName; }

@Override public IHeaderResponse decorate(IHeaderResponse response) { return new JavaScriptFilteredIntoFooterHeaderResponse(response, bucketName); }

}

As you can see in the code above the "bucket" that will contain JavaScript tags is called "footer-container". To make a use of it the developer have to add a special component called HeaderResponseContainer in his page:

add(new HeaderResponseContainer("someId", "filterName"));

Please note that HeaderResponseContainer's needs also a name for the corresponding header response's filter. The markup of our page will look like this:

<html>

<header> //no <script> tag here… </header>

<body> <!-- here we will have our JavaScript tags --> <wicket:container wicket:id="someId"/> </body> </html>

The code of the home page is the following:

public HomePage(final PageParameters parameters) {
        super(parameters);

add(new HeaderResponseContainer("footer-container", "footer-container")); }

@Override public void renderHead(IHeaderResponse response) { response.render(JavaScriptHeaderItem.forReference(new PackageResourceReference(getClass(), "javasciptLibrary.js")));

response.render(OnEventHeaderItem.forScript("'logo'", "click", "alert('Clicked me!')")); }

Looking at the code above you can note that our page adds two script to the header section: the first is an instance of JavaScriptHeaderItem and will be rendered in the HeaderResponseContainer while the second will follow the usual behavior and will be rendered inside <head> tag.

15.8 Header contributors positioning

Starting from version 6.15.0 we can specify where header contributors must be rendered inside <head> tag using the placeholder tag <wicket:header-items/>:

<head>
  <meta chartset="UTF-8"/>
  <wicket:header-items/>
  <script src="my-monkey-patch-of-wicket-ajax.js"></script>
</head>

With the code above all header contributions done by using IHeaderResponse in your Java code or the special <wicket:head> tag will be put between the <meta> and <script> elements, i.e. in the place of <wicket:header-items/>.

This way you can make sure that some header item is always before or after the header items managed by Wicket.

<wicket:header-items/> can be used only in the page's <head> element and there could be at most one instance of it.

15.9 Custom resources

In Wicket the best way to add dynamic functionalities to our application (such as csv export, a pdf generated on the fly, etc...) is implementing a custom resource. In this paragraph as example of custom resource we will build a basic RSS feeds generator which can be used to publish feeds on our site (project CustomResourceMounting). Instead of generating a RSS feed by hand we will use Rome framework and its utility classes.

As hinted above in paragraph 13.1, class AbstractResource can be used as base class to implement new resources. This class defines abstract method newResourceResponse which is invoked when the resource is requested. The following is the code of our RSS feeds generator:

public class RSSProducerResource extends AbstractResource {

@Override protected ResourceResponse newResourceResponse(Attributes attributes) { ResourceResponse resourceResponse = new ResourceResponse(); resourceResponse.setContentType("text/xml"); resourceResponse.setTextEncoding("utf-8");

resourceResponse.setWriteCallback(new WriteCallback() { @Override public void writeData(Attributes attributes) throws IOException { OutputStream outputStream = attributes.getResponse().getOutputStream(); Writer writer = new OutputStreamWriter(outputStream); SyndFeedOutput output = new SyndFeedOutput(); try { output.output(getFeed(), writer); } catch (FeedException e) { throw new WicketRuntimeException("Problems writing feed to response..."); } } });

return resourceResponse; } // method getFeed()… }

Method newResourceResponse returns an instance of ResourceResponse representing the response generated by the custom resource. Since RSS feeds are based on XML, in the code above we have set the type of the response to text/xml and the text encoding to utf-8.

To specify the content that will be returned by our resource we must also provide an implementation of inner class WriteCallback which is responsible for writing content data to response's output stream. In our project we used class SyndFeedOutput from Rome framework to write our feed to response. Method getFeed() is just an utility method that generates a sample RSS feed (which is an instance of interface com.sun.syndication.feed.synd.SyndFeed).

Now that we have our custom resource in place, we can use it in the home page of the project. The easiest way to make a resource available to users is to expose it with link component ResourceLink:

add(new ResourceLink("rssLink", new RSSProducerResource()));

In the next paragraphs we will see how to register a resource at application-level and how to mount it to an arbitrary URL.

15.10 Mounting resources

Just like pages also resources can be mounted to a specific path. Class WebApplication provides method mountResource which is almost identical to mountPage seen in paragraph 8.6.1:

@Override
public void init() {
  super.init();
  //resource mounted to path /foo/bar
  ResourceReference resourceReference = new ResourceReference("rssProducer"){
     RSSReaderResource rssResource = new RSSReaderResource();
     @Override
     public IResource getResource() {
	return rssResource;
  }};
  mountResource("/foo/bar", resourceReference);
}

With the configuration above (taken from project CustomResourceMounting) every request to /foo/bar will be served by the custom resource built in the previous paragraph.

Parameter placeholders are supported as well:

@Override
public void init() {
  super.init();
  //resource mounted to path /foo with a required indexed parameter
  ResourceReference resourceReference = new ResourceReference("rssProducer"){
     RSSReaderResource rssResource = new RSSReaderResource();
     @Override
     public IResource getResource() {
	return rssResource;
  }};
  mountResource("/bar/${baz}", resourceReference);
}

15.11 Shared resources

Resources can be added to a global registry in order to share them at application-level. Shared resources are identified by an application-scoped key and they can be easily retrieved at a later time using reference class SharedResourceReference. The global registry can be accessed with Application's method getSharedResources. In the following excerpt of code (taken again from project CustomResourceMounting) we register an instance of our custom RSS feeds producer as application-shared resource:

//init application's method
  @Override
  public void init(){
    RSSProducerResource rssResource = new RSSProducerResource();
    // …
    getSharedResources().add("globalRSSProducer", rssResource);    
  }

Now to use an application-shared resource we can simply retrieve it using class SharedResourceReference and providing the key previously used to register the resource:

add(new ResourceLink("globalRssLink", new SharedResourceReference("globalRSSProducer")));

The URL generated for application shared resources follows the same pattern seen for package resources:

./wicket/resource/org.apache.wicket.Application/globalRSSProducer

The last segment of the URL is the key of the resource while the previous segment contains the scope of the resource. For application-scoped resources the scope is always the fully qualified name of class Application. This should not be surprising since global resources are visible at application level (i.e. the scope is the application).

Package resources are also application-shared resources but they don't need to be explicitly registered.

Remember that we can get the URL of a resource reference using method urlFor(ResourceReference resourceRef, PageParameters params ) available with both class RequestCycle and class Component.

15.12 Customizing resource loading

Wicket loads application's resources delegating this task to a resource locator represented by interface org.apache.wicket.core.util.resource.locator.IResourceStreamLocator. To retrieve or modify the current resource locator we can use the getter and setter methods defined by setting interface IResourceSettings:

//init application's method
  @Override
  public void init(){   
    //get the resource locator 
    getResourceSettings().getResourceStreamLocator();
    //set the resource locator    
    getResourceSettings().setResourceStreamLocator(myLocator);
  }

The default locator used by Wicket is class ResourceStreamLocator which in turn tries to load a requested resource using a set of implementations of interface IResourceFinder. This interface defines method find(Class class, String pathname) which tries to resolve a resource corresponding to the given class and path.

The default implementation of IResourceFinder used by Wicket is ClassPathResourceFinder which searches for resources into the application class path. This is the implementation we have used so far in our examples. However some developers may prefer storing markup files and other resources in a separate folder rather than placing them side by side with Java classes.

To customize resource loading we can add further resource finders to our application in order to extend the resource-lookup algorithm to different locations. Wicket already comes with two other implementations of IResourceFinder designed to search for resources into a specific folder on the file system. The first is class Path and it's defined in package org.apache.wicket.util.file. The constructor of this class takes in input an arbitrary folder that can be expressed as a string path or as an instance of Wicket utility class Folder (in package org.apache.wicket.util.file). The second implementation of interface IResourceFinder is class WebApplicationPath which looks into a folder placed inside webapp's root path (but not inside folder WEB-INF).

Project CustomFolder4MarkupExample uses WebApplicationPath to load the markup file and the resource bundle for its home page from a custom folder. The folder is called markupFolder and it is placed in the root path of the webapp. The following picture illustrates the file structure of the project:

As we can see in the picture above, we must preserve the package structure also in the custom folder used as resource container. The code used inside application class to configure WebApplicationPath is the following:

@Override
public void init()
{
	getResourceSettings().getResourceFinders().add(
			new WebApplicationPath(getServletContext(), "markupFolder"));
}

Method getResourceFinders() defined by setting interface IResourceSettings returns the list of resource finders defined in our application. The constructor of WebApplicationPath takes in input also an instance of standard interface javax.servlet.ServletContext which can be retrieved with WebApplication's method getServletContext().

By default, if resource files can not be found inside application classpath, Wicket will search for them inside “resources” folder. You may have noted this folder in the previous picture. It is placed next to the folder “java” containing our source files:

This folder can be used to store resource files without writing any configuration code.

15.13 Summary

In this chapter we have learnt how to manage resources with the built-in mechanism provided by Wicket. With this mechanism we handle resources from Java code and Wicket will automatically take care of generating a valid URL for them. We have also seen how resources can be bundled as package resources with a component that depends on them to make it self-contained.

Then, in the second part of the chapter, we have built a custom resource and we have learnt how to mount it to an arbitrary URL and how to make it globally available as shared resource.

Finally, in the last part of the paragraph we took a peek at the mechanism provided by the framework to customize the locations where the resource-lookup algorithm searches for resources.