2

I'm using RichFaces 3.3.1 and I am trying to figure out if there is a way to host the RichFaces CSS and Javascript as static resources from another web server like Apache or Nginx.

I've tried setting the org.ajax4jsf.RESOURCE_URI_PREFIX initialization parameter in the web.xml to the other web server but the URI was still relative to the web application.

I also tried extracting the two Javascript files from the RichFaces jar

  • framework.pack.js
  • ui.pack.js

added the following to the web.xml.

<context-param>
    <param-name>org.richfaces.LoadScriptStrategy</param-name>
    <param-value>NONE</param-value>
</context-param>

and then in the xhtml included the Javascript files from the other server. Unfortunately many of the RichFaces components did not work after I did this.

Any other ideas how this might be done? Has anyone had any success doing this with JSF component frameworks?

3
  • I have tried to do just about everything you did for the Primefaces component framework to no success. I have no idea why however. If you figure this out I would love to find out how. Commented May 25, 2011 at 15:34
  • Mark -- can you update to Richfaces 4 Final? In v4 they support static resource replacement out of the box since that's what they had to do to host the demo on Google app engine. Commented May 25, 2011 at 23:50
  • 1
    @maple_shaft there is a post on the PrimeFaces forum that indicates this can be be done primefaces.prime.com.tr/forum/viewtopic.php?f=3&t=4331 Commented May 26, 2011 at 14:21

1 Answer 1

3

I realize this isn't the answer in version 3.3 but if you can updgrade to Richfaces 4 final then they have this nifty little feature where you can add this to web.xml:

<context-param>
    <param-name>org.richfaces.staticResourceLocation</param-name>
    <param-value>#{resourceLocation}</param-value>
</context-param>

and then in META-INF you can include a static resource mapping file. The full path/filename is:

META-INF/richfaces/static-resource-mappings.properties

The contents of which are like:

jquery.js=https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js
richfaces.js=https://mystaticimageserver.com/img/richfaces.js

EDIT:

One other thing I do sometimes (In JSF 2) is just suppress the version of a resource that a library provider includes. Then I just create a static version and include it myself in my facelets template or wherever it makes sense in your project. It's less elegant but it works well if you are combining resources for performance or the like.

To do this I first define some customer renderers for Script and Stylesheet by adding this to faces-config.xml:

faces-config.xml

<render-kit>
    <renderer>
        <component-family>javax.faces.Output</component-family>
        <renderer-type>javax.faces.resource.Script</renderer-type>
        <renderer-class>com.davemaple.jsf.resource.ScriptRenderer</renderer-class>
    </renderer>
    <renderer>
        <component-family>javax.faces.Output</component-family>
        <renderer-type>javax.faces.resource.Stylesheet</renderer-type>
        <renderer-class>com.davemaple.jsf.resource.StylesheetRenderer</renderer-class>
    </renderer>
</render-kit>

Then I add some files or Classes I want to suppress to web.xml:

web.xml

<context-param>
    <param-name>com.davemaple.jsf.resource.suppressedScriptResourceClasses</param-name>
    <param-value></param-value>
</context-param>

<context-param>
    <param-name>com.davemaple.jsf.resource.suppressedScripts</param-name>
    <param-value>javax.faces:jsf.js :jquery.js :richfaces.js :richfaces-event.js :richfaces-base-component.js org.richfaces:message.js org.richfaces:richfaces-csv.js</param-value>
</context-param>

<context-param>
    <param-name>com.davemaple.jsf.resource.suppressedStylesheetResourceClasses</param-name>
    <param-value></param-value>
</context-param>

<context-param>
    <param-name>com.davemaple.jsf.resource.suppressedStylesheets</param-name>
    <param-value>org.richfaces:msg.ecss</param-value>
</context-param>

ScriptRenderer.java

/**
 * A Customer ScriptRenderer that allows specific resources to be  
 * suppressed and managed manually (CDN, Static Resource Server, Combining resources)
 *
 */
public class ScriptRenderer extends ScriptStyleBaseRenderer {

    private static final String DELIMITER = " ";
    private static final String LIBRARY_DELIMITER = ":";

    private static final String SUPRESSED_CLASSES_INIT_PARAM = "com.davemaple.jsf.resource.suppressedScriptResourceClasses";
    private final List<Class<?>> supressedClasses = new ArrayList<Class<?>>();

    private static final String SUPRESSED_SCRIPTS_INIT_PARAM = "com.davemaple.jsf.resource.suppressedScripts";
    private final List<String> suppressedScriptNames = new ArrayList<String>();

    /**
     * Constructor
     */
    public ScriptRenderer() {
        super();

        String suppressedClasses = 
            FacesContext.getCurrentInstance().getExternalContext().getInitParameter(SUPRESSED_CLASSES_INIT_PARAM);

        if (suppressedClasses != null
                && !suppressedClasses.isEmpty()) {
            for (String suppressedClassString : suppressedClasses.split(DELIMITER)) {
                try {
                    this.supressedClasses.add(Class.forName(suppressedClassString));
                } catch (ClassNotFoundException ex) {
                }
            }
        }

        String suppressedScripts = 
            FacesContext.getCurrentInstance().getExternalContext().getInitParameter(SUPRESSED_SCRIPTS_INIT_PARAM);

        if (suppressedScripts != null
                && !suppressedScripts.isEmpty()) {
            for (String suppressedScript : suppressedScripts.split(DELIMITER)) {
                this.suppressedScriptNames.add(suppressedScript);
            }
        }        
    }

    /**
     * Returns a boolean indicating if the component should
     * be encoded and thus rendered
     * 
     * @param component
     * @return isSuppressed
     */
    protected boolean isSuppressed(UIComponent component) {

        if (this.supressedClasses.contains(component.getClass())) {
            return true;
        }

        if (component.getAttributes().containsKey("name")
                && component.getAttributes().containsKey("library")) {

            String key = component.getAttributes().get("library") + LIBRARY_DELIMITER + component.getAttributes().get("name");

            if (this.suppressedScriptNames.contains(key)) {
                return true;
            }
        }

        if (component.getAttributes().containsKey("name")
                && !component.getAttributes().containsKey("library")) {

            String key = LIBRARY_DELIMITER + component.getAttributes().get("name");

            if (this.suppressedScriptNames.contains(key)) {
                return true;
            }
        }

        return false;
    }

    @Override
    protected void startElement(ResponseWriter writer, UIComponent component) throws IOException {
        writer.startElement("script", component);
        writer.writeAttribute("type", "text/javascript", "type");
    }

    @Override
    protected void endElement(ResponseWriter writer) throws IOException {
        writer.endElement("script");
    }

    @Override
    public void encodeBegin(FacesContext context, UIComponent component) throws IOException {

        if (!this.isSuppressed(component)) {
            super.encodeBegin(context, component);
        }
    }


    @Override
    public void encodeEnd(FacesContext context, UIComponent component) throws IOException {

        if (!this.isSuppressed(component)) {

            Map<String, Object> attributes = component.getAttributes();
            Map<Object, Object> contextMap = context.getAttributes();

            String name = (String) attributes.get("name");
            String library = (String) attributes.get("library");

            String key = name + library;

            if (null == name) {
                return;
            }

            // Ensure this script is not rendered more than once per request
            if (contextMap.containsKey(key)) {
                return;
            }
            contextMap.put(key, Boolean.TRUE);

            // Special case of scripts that have query strings
            // These scripts actually use their query strings internally, not
            // externally
            // so we don't need the resource to know about them
            int queryPos = name.indexOf("?");
            String query = null;
            if (queryPos > -1 && name.length() > queryPos) {
                query = name.substring(queryPos + 1);
                name = name.substring(0, queryPos);
            }

            Resource resource = context.getApplication().getResourceHandler()
                    .createResource(name, library);

            ResponseWriter writer = context.getResponseWriter();
            this.startElement(writer, component);

            String resourceSrc;
            if (resource == null) {
                resourceSrc = "RES_NOT_FOUND";
            } else {
                resourceSrc = resource.getRequestPath();
                if (query != null) {
                    resourceSrc = resourceSrc
                            + ((resourceSrc.indexOf("?") > -1) ? "+" : "?") + query;
                }
            }

            writer.writeURIAttribute("src", resourceSrc, "src");
            this.endElement(writer);
            super.encodeEnd(context, component);

        }
    }

}

ScriptRenderer.java

/**
 * A Customer StylesheetRenderer that allows specific resources to be  
 * suppressed and managed manually (CDN, Static Resource Server, Combining resources)
 *
 */
public class StylesheetRenderer extends ScriptStyleBaseRenderer {

    private static final String DELIMITER = " ";
    private static final String LIBRARY_DELIMITER = ":";

    private static final String SUPRESSED_CLASSES_INIT_PARAM = "com.davemaple.jsf.resource.suppressedStylesheetResourceClasses";
    private final List<Class<?>> supressedClasses = new ArrayList<Class<?>>();

    private static final String SUPPRESSED_STYLESHEETS_INIT_PARAM = "com.davemaple.jsf.resource.suppressedStylesheets";
    private final List<String> suppressedStylesheets = new ArrayList<String>();

    /**
     * Constructor
     */
    public StylesheetRenderer() {
        super();

        String suppressedClasses = 
            FacesContext.getCurrentInstance().getExternalContext().getInitParameter(SUPRESSED_CLASSES_INIT_PARAM);

        if (suppressedClasses != null
                && !suppressedClasses.isEmpty()) {
            for (String suppressedClassString : suppressedClasses.split(DELIMITER)) {
                try {
                    this.supressedClasses.add(Class.forName(suppressedClassString));
                } catch (ClassNotFoundException ex) {
                }
            }
        }

        String suppressedStylesheets = 
            FacesContext.getCurrentInstance().getExternalContext().getInitParameter(SUPPRESSED_STYLESHEETS_INIT_PARAM);

        if (suppressedStylesheets != null
                && !suppressedStylesheets.isEmpty()) {
            for (String suppressedStylesheet : suppressedStylesheets.split(DELIMITER)) {
                this.suppressedStylesheets.add(suppressedStylesheet);
            }
        }        
    }

    /**
     * Returns a boolean indicating if the component should
     * be encoded and thus rendered
     * 
     * @param component
     * @return isSuppressed
     */
    protected boolean isSuppressed(UIComponent component) {

        if (this.supressedClasses.contains(component.getClass())) {
            return true;
        }

        if (component.getAttributes().containsKey("name")
                && component.getAttributes().containsKey("library")) {

            String key = component.getAttributes().get("library") + LIBRARY_DELIMITER + component.getAttributes().get("name");

            if (this.suppressedStylesheets.contains(key)) {
                return true;
            }
        }

        if (component.getAttributes().containsKey("name")
                && !component.getAttributes().containsKey("library")) {

            String key = LIBRARY_DELIMITER + component.getAttributes().get("name");

            if (this.suppressedStylesheets.contains(key)) {
                return true;
            }
        }

        return false;
    }

    @Override
    protected void startElement(ResponseWriter writer, UIComponent component)
            throws IOException {
        writer.startElement("style", component);
        writer.writeAttribute("type", "text/css", "type");
    }

    @Override
    protected void endElement(ResponseWriter writer) throws IOException {
        writer.endElement("style");
    }

    @Override
    protected String verifyTarget(String toVerify) {
        return "head";
    }

    @Override
    public void encodeBegin(FacesContext context, UIComponent component) throws IOException {

        if (!this.isSuppressed(component)) {
            super.encodeBegin(context, component);
        }
    }


    @Override
    public void encodeEnd(FacesContext context, UIComponent component) throws IOException {

        if (!this.isSuppressed(component)) {
            Map<String, Object> attributes = component.getAttributes();
            Map<Object, Object> contextMap = context.getAttributes();

            String name = (String) attributes.get("name");
            String library = (String) attributes.get("library");
            String key = name + library;

            if (null == name) {
                return;
            }

            // Ensure this stylesheet is not rendered more than once per request
            if (contextMap.containsKey(key)) {
                return;
            }
            contextMap.put(key, Boolean.TRUE);

            Resource resource = context.getApplication().getResourceHandler()
                    .createResource(name, library);

            ResponseWriter writer = context.getResponseWriter();
            writer.startElement("link", component);
            writer.writeAttribute("type", "text/css", "type");
            writer.writeAttribute("rel", "stylesheet", "rel");
            writer.writeURIAttribute("href",
                    ((resource != null) ? resource.getRequestPath()
                            : "RES_NOT_FOUND"), "href");
            writer.endElement("link");
            super.encodeEnd(context, component);
        }
    }

}
Sign up to request clarification or add additional context in comments.

2 Comments

Yeah it looks like the answer is to update to RichFaces 4. RichFaces 3.3 is using the Java2D API to dynamically create the resources.
It's worth the upgrade if you can manage it. The client side validation integration is pretty sick.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.