5

I would like to load dynamic resource (generated JavaScript, technically configuration in JSON) as dynamic resource, using h:outputScript tag. I'm aware I can do that via writing my own servlet and load it via normal tag scripts, by I would prefer the "JSF-way" of loading resouces.

Is it possible, and how?

Danubian Sailor
  • 1
  • 38
  • 145
  • 223

1 Answers1

7

Yes, it's possible. You can achieve this by using a specific resource library name and have a custom ResourceHandler intercept on it and return a custom Resource when a resource of the specific library is been requested.

E.g.

<h:outputScript library="dynamicJs" name="some.js" />

with

public class DynamicJsResourceHandler extends ResourceHandlerWrapper {

    public DynamicJsResourceHandler(ResourceHandler wrapped) {
        super(wrapped);
    }

    @Override
    public Resource createResource(String resourceName, String libraryName) {
        if ("dynamicJs".equals(libraryName)) {
            return new DynamicJsResource(resourceName);
        } else {
            return super.createResource(resourceName, libraryName);
        }
    }

}

and

public class DynamicJsResource extends Resource {

    private String resourceName;

    public DynamicJsResource(String resourceName) {
        this.resourceName;
    }

    @Override
    public String getRequestPath() {
        // TODO: return "/context/javax.faces.resource/" + resourceName + ".xhtml?ln=dynamicJs";
    }

    @Override
    public URL getURL() {
        // TODO: return new URL("http://localhost:8080" + getRequestPath());
    }

    @Override
    public Map<String, String> getResponseHeaders() {
        // TODO: return desired HTTP response headers. 
    }

    @Override
    public InputStream getInputStream() throws IOException {
        // TODO: return InputStream by resourceName.
    }

    @Override
    public boolean userAgentNeedsUpdate(FacesContext context) {
        // TODO: return true when resource has been modified in server side.
    }

}

To get it to run, register it as follows in faces-config.xml:

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

If you happen to use JSF utility library OmniFaces, then you can also save the boilerplate code and extend from its org.omnifaces.resourcehandler.DynamicResource instead so you only need to implement getInputStream(). If you don't use OmniFaces, then you can always use its source code as guidance.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Is it possible to register multiple resource handlers, each supporting one library, or that functionality must be merged into single implementation? – Danubian Sailor Feb 06 '13 at 12:36
  • Yes. You can have 0-* `` entries in ``. They're initialized in the order they appear in the XML. – BalusC Feb 06 '13 at 12:37
  • OK, but now I'm by implementation I need to provide Resource implementation to DynamicJsResource, or copy the very long code from ResourceImpl... – Danubian Sailor Feb 12 '13 at 15:01
  • I'm not sure why you need to copy the default impl code. It reads the resource from deploy folder and that's not what you want. – BalusC Feb 12 '13 at 15:10
  • 1
    I need to implement such methods as getURL and getRequestPath with no idea how should I do that... PrimeFaces are overgoing it somehow by overloading the method handleResourceRequest(FacesContext context), but by me it was not functioning, instead I got error "Resource referenced by resourceName messages and libraryName i18n not found in call to ResourceHandler.createResource. It will be silenty ignored." – Danubian Sailor Feb 12 '13 at 15:35
  • 1
    The DynamicJsResource class cannot extend from ResourceWrapper in your example, because there is really nothing to wrap. – Daniel Beer Oct 24 '17 at 14:11