Resource Identification
According to the JSF spec, a legal resource identifier consists of these parts. In effect, everything except for the resourceName is optional:
Ideally, our various resources would use this system so that they are not cached between updates and they could, potentially, be localized. For example, the autocomplete script file resource is currently stored as:
Assuming we release a 3.2.0 version of ICEfaces, the resource location could be changed to:
META-INF/resources/en/icefaces.ace/3_2_0/autocompleteentry/autocompleteentry.js/1_0.js
The spec is a bit unclear about how flat the library and resource paths have to be. It may be the case that slashes are not allowed in either one and that a library is mandatory. In other words autocompleteentry/autocompleteentry.js would be illegal and need to be switched to just autocompleteentry.js. There is some work ongoing for JSF 2.2 that is related to http://java.net/jira/browse/JAVASERVERFACES-2401.
The rules around version formatting only provided limited flexibility on what we can put in there:
So no using anything but numbers and underscores. We should discuss whether we think both the library versioning and the resource versioning are useful to us. The localization we could simply add as "en" for now and then if we need to different one, we already are positioned to provide it.
Resource Caching
The point of doing this is to allow a release of ICEfaces where our resources would be distinctly identified as to their version as well as preventing caching between our own releases. The work involved would be to:
- change our existing directory structures to match the standard
- automate the required processes so that the handling of the version numbers is relatively transparent during the build process
However, doing this does not help with caching between deployments of the same release as the URL would be the same. For that, JSF provides the ProductionStage.Development setting. When that parameter is set, the JSF ResourceImpl class (at least for Mojarra) has the following logic:
public Map<String, String> getResponseHeaders() {
if (isResourceRequest()) {
if (responseHeaders == null)
responseHeaders = new HashMap<String, String>(6, 1.0f);
long expiresTime;
FacesContext ctx = FacesContext.getCurrentInstance();
if (ctx.isProjectStage(ProjectStage.Development)) {
expiresTime = new Date().getTime();
} else {
expiresTime = new Date().getTime() + maxAge;
}
...
For Production mode, you can set the maxAge of the resources using another parameter. But in Development mode, the resources are designed to expire immediately and should therefore (in theory) be retrieved from the server every time.
ICEfaces has a strategy where we add a request parameter, "v=hashcode()" - with the hashcode being recalculated between deployments. This doesn't quite align with what JSF is doing so we should discuss how we want that to work as well.
Deryk:
{ externalContext.setResponseHeader("Expires", Util.HTTP_DATE.format(options.expiresBy)); }There is both a "library" version and "resource" version for resources. An example of using both is provided in the JSF spec and shows the final rendered resource to look like:
basic/2_3/script.js/1_3_4.js
Using the versions feature of JSF could help. At least with regards to resource changes between releases but I'm not sure we bundle any of our resources with a version number. For example, when I look at the ACE resources, no versioning is provided that I can see.
The spec also states that:
"An implementation (of a Resource Handler) may perform caching of the resource metadata to improve performance if the ProjectStage is ProjectStage.Production."
This implies that setting ProjectStage to Development could/should prevent caching. Looking at some of our resource handling code (for example DynamicResourceDispatcher), I do see that we are setting some heading related to caching:
externalContext.setResponseHeader("ETag", encode(resource));
externalContext.setResponseHeader("Cache-Control", "public");
externalContext.setResponseHeader("Content-Type", options.mimeType);
externalContext.setResponseHeader("Last-Modified",
Util.HTTP_DATE.format(options.lastModified));
if (options.expiresBy != null)
But I don't see any mechanism that might allow it to be configurable in any way or whether we honour the Development setting (at least yet, it would take me some more time to dig through it). I would have thought that if the date changed on the file then the "Last-Modified" header would have changed. Unless you go backwards with the releases - that might not trigger a reload of the cached resource if the date was actually older.