Obtaining Facelets templates/files from an external filesystem or database
Asked Answered
T

1

7

I am able to successfully get this to work with the template in my app:

<ui:decorate template="/WEB-INF/templates/mytemplate.xhtml">

I am also able to move template to /META-INF/templates/mytemplate.xhtml of a JAR and get this to work:

<ui:decorate template="/templates/mytemplate.xhtml">

I would actually like to put this file onto filesystem (or database for that matter). How can I achieve this? I found plenty of things related to com.sun.facelets.impl.DefaultResourceResolver, but I don't think that is actually related to override the serving of the template. It is not trying resolve a URL, it is simply trying to get the file somehow on the classpath.

Tarttan answered 8/11, 2012 at 15:37 Comment(0)
T
13

If you're already on JSF 2.2, you can do this by providing a custom ResourceHandler wherein you return the desired view resource in createViewResource().

public class FaceletsResourceHandler extends ResourceHandlerWrapper {

    private ResourceHandler wrapped;

    public FaceletsResourceHandler(ResourceHandler wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public ViewResource createViewResource(FacesContext context, final String name) {
        ViewResource resource = super.createViewResource(context, name);

        if (resource == null) {
            resource = new ViewResource() {
                @Override
                public URL getURL() {
                    try {
                        return new File("/some/base/path", name).toURI().toURL();
                    } catch (MalformedURLException e) {
                        throw new FacesException(e);
                    }
                }
            };
        }

        return resource;
    }

    @Override
    public ResourceHandler getWrapped() {
        return wrapped;
    }

}

Which is registered in faces-config.xml as below:

<application>
    <resource-handler>com.example.FaceletsResourceHandler</resource-handler>
</application>

Or if you're not on JSF 2.2 yet, then use ResourceResolver instead.

public class FaceletsResourceResolver extends ResourceResolver {

    private ResourceResolver parent;

    public FaceletsResourceResolver(ResourceResolver parent) {
        this.parent = parent;
    }

    @Override
    public URL resolveUrl(String path) {
        URL url = parent.resolveUrl(path); // Resolves from WAR.

        if (url == null) {
            try {
                url = new File("/some/base/path", path).toURI().toURL();
            } catch (MalformedURLException e) {
                throw new FacesException(e);
            }

        }

        return url;
    }

}

Which is registered in web.xml as below:

<context-param>
    <param-name>javax.faces.FACELETS_RESOURCE_RESOLVER</param-name>
    <param-value>com.example.FaceletsResourceResolver</param-value>
</context-param>

Regardless of the way, in order to provide the resource from the database, you'd either save/cache them on (temp) disk file system so you can provide the URL just via File, or invent a custom protocol such as db:// and provide a custom URLStreamHandlerFactory and URLStreamHandler implementation to perform the actual job of streaming from the DB. You can find a kickoff example here Registering and using a custom java.net.URL protocol.

Tetrachloride answered 8/11, 2012 at 19:55 Comment(3)
Hi BalusC..This is helped get me started. I seems to work fine with getting actual .xhtml pages. But it doesn't seem to be working with using the template. I edit the question to show the current error I am now getting.Tarttan
The file apparently doesn't exist. Extend the kickoff example to use the File#exists() test and return null when it doesn't.Tetrachloride
I figured out the issue...Your solution does work. But it depends on how I define the template. For example if I use template="myfile.xhtml" it will fail. But if I do template="/myfile.xhtml" it will get into the ResourceResolver.Tarttan

© 2022 - 2024 — McMap. All rights reserved.