2

I have written a small CMS for an application which provides basic site management features like wysiwyg content and template editing. Pages are stored as facelets in the database and presented to the client using a custom url resolver (see last post on https://community.jboss.org/message/495515#495515), which worked really well until I started supporting different page trees for different domains served by the same application (a bit like apache's vhosts).

So http://firstdomain/cms/page.xhtml should lead to a different page than http://seconddomain/cms/page.xhtml. The problem here is, that URLs resolved by my custom DefaultResourceResolver (and any other resolver, too) are cached by JSF only using the path (/cms/page.xhtml). So whatever domain is queried first, provides the cached URL for all requests to the same path, independent of the requested domain.

It took me quite some time to nail this problem down to the caching but now I am stuck. Is there any way to change/override/disable JSF's URL caching to respect the requested domain name?

Update 1: I just read the myfaces implementation of FaceletCacheImpl.java and noticed that URLs are itself the key to their cache and not only their path. This leads to the problem discussed here: Why does java.net.URL's hashcode resolve the host to an IP? - URL's are compared using their IP address and not the hostname. So still, I have to change Facelets caching behaviour.

Update 2: Experimenting with the URL parameters convinced me that they are indeed cached only by their path besides what I've seen in FaceletCacheImpl.java so Update 1 can be ignored.

Community
  • 1
  • 1
Roben
  • 840
  • 9
  • 19

1 Answers1

0

Some more research pointed me to DefaultFaceletFactory.java, which is responsible for calling the registered url resolver and caching the resolved URLs. As the class is final, it was not possible to extend it so I downloaded the latest jsf-facelets source from jboss and modified it directly by appending the current request host to the cache key. This is my replacement for public Facelet getFacelet(String uri):

public static HttpServletRequest getRequest() {
    return (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
}

/**
 * Returns the host name from the current request.
 *
 * @return
 */
public static String getRequestHost() {
    HttpServletRequest request = getRequest();
    if (request == null) {
        return null;
    }
    String result = request.getHeader("x-forwarded-host");
    if (result == null || result.isEmpty()) {
        result = request.getHeader("host");
    }
    return result;
}


/*
 * (non-Javadoc)
 * 
 * @see com.sun.facelets.FaceletFactory#getFacelet(java.lang.String)
 */
public Facelet getFacelet(String uri) throws IOException, FaceletException,
        FacesException, ELException {
    String key = getRequestHost() + ":" + uri;
    URL url = (URL) this.relativeLocations.get(key);
    if (url == null) {
        url = this.resolveURL(this.baseUrl, uri);
        if (url != null) {
            Map newLoc = new HashMap(this.relativeLocations);
            newLoc.put(key, url);
            this.relativeLocations = newLoc;
        } else {
            throw new IOException("'" + uri + "' not found.");
        }
    }
    return this.getFacelet(url);
}

I must admit, this solution is pretty dirty but it gets the job done. Also, there were no changes to the facelets implementation for some time now, so updates shouldn't be a problem.

Roben
  • 840
  • 9
  • 19