Workaround for LOAD_NONE resource loading strategy in RichFaces 4.1

Version 2

    Introduction

     

    The resource loading strategy in RichFaces 4 is different from that in RichFaces 3 -- see the developper guide for details.

    In the RichFaces 4 implementation, no built-in LOAD_NONE strategy has yet been provided (you can vote for the issue RF-11514).  This article documents a workaround, explaining how to achieve a similar result.

     

     

    Concept of the Workaround

     

    For developers which would like to use LOAD_NONE resource loading strategy,

    there is workaround based on implementation of backing classes for StylesheetRenderer and ScriptRenderer,

    which are responsible for rendering component resources.

     

    These classes are putting key for given resource to the context map (FacesContext.getAttributes())

    in order to remember that given resource has been already loaded.

     

    Thus workaround is based simply on putting resource keys for resources that you don't want to load to context map before the view is rendered.

     

    Sample implementation

     

    The sample implementation consists of registering PreRenderViewEvent listener in faces-config.xml and listener implementation.

     

    In order to customize which resources should not be loaded, look for shouldNotBeLoaded(ResourceKey resourceKey) method.

     

    <faces-config>
        ...
        <application>
            ...
            <system-event-listener>
                <system-event-listener-class>org.richfaces.demo.common.DoNotLoadSelectedRichFacesResources</system-event-listener-class>
                <system-event-class>javax.faces.event.PreRenderViewEvent</system-event-class>
            </system-event-listener>
        </application>
    </faces-config>
    

     

     

    /*
     * JBoss, Home of Professional Open Source.
     * Copyright 2010, Red Hat, Inc., and individual contributors
     * as indicated by the @author tags. See the copyright.txt file in the
     * distribution for a full listing of individual contributors.
     *
     * This is free software; you can redistribute it and/or modify it
     * under the terms of the GNU Lesser General Public License as
     * published by the Free Software Foundation; either version 2.1 of
     * the License, or (at your option) any later version.
     *
     * This software is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
     * Lesser General Public License for more details.
     *
     * You should have received a copy of the GNU Lesser General Public
     * License along with this software; if not, write to the Free
     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
     */
    package org.richfaces.demo.common;
    
    
    import java.io.InputStream;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.Properties;
    import java.util.concurrent.atomic.AtomicReference;
    
    
    import javax.faces.component.UIViewRoot;
    import javax.faces.context.FacesContext;
    import javax.faces.event.AbortProcessingException;
    import javax.faces.event.PreRenderViewEvent;
    import javax.faces.event.SystemEvent;
    import javax.faces.event.SystemEventListener;
    
    
    import org.richfaces.resource.ResourceKey;
    
    
    import com.sun.faces.renderkit.html_basic.ScriptRenderer;
    import com.sun.faces.renderkit.html_basic.StylesheetRenderer;
    
    
    /**
     * <p>
     * Satisfies that links for selected RichFaces won't be put to renderer view.
     * </p>
     *
     * <p>
     * Resource which should not be loaded are selected by condition in {@link #shouldNotBeLoaded(ResourceKey, String)}.
     * </p>
     *
     * @author Lukas Fryc
     */
    public class DoNotLoadSelectedRichFacesResources implements SystemEventListener {
    
    
        private static final String STATIC_MAPPING_FILE = "META-INF/richfaces/staticResourceMapping/Static.properties";
    
    
        private AtomicReference<Properties> propertiesRef = new AtomicReference<Properties>();
    
    
        @Override
        public void processEvent(SystemEvent event) throws AbortProcessingException {
            if (event instanceof PreRenderViewEvent) {
                putSelectedResourceKeysToContextMap();
            }
        }
    
    
        @Override
        public boolean isListenerForSource(Object source) {
            return source instanceof UIViewRoot;
        }
    
    
        /**
         * <p>
         * Adds all keys of resources which meets {@link #shouldNotBeLoaded(ResourceKey, String)} condition to context map.
         * </p>
         *
         * <p>
         * Adding resource key to context map ensures that {@link ScriptRenderer} {@link StylesheetRenderer} won't render the link
         * to page.
         * </p>
         */
        private void putSelectedResourceKeysToContextMap() {
            FacesContext facesContext = FacesContext.getCurrentInstance();
    
    
            loadStaticResourceMappingProperties();
    
    
            for (Entry<Object, Object> entry : propertiesRef.get().entrySet()) {
                ResourceKey resourceKey = ResourceKey.create((String) entry.getKey());
    
    
                if (shouldNotBeLoaded(resourceKey)) {
                    putResourceKeyToContextMap(facesContext, resourceKey);
                }
            }
        }
    
    
        /**
         * Loads configuration of static resource mapping which lists all RichFaces resources.
         */
        private void loadStaticResourceMappingProperties() {
            if (propertiesRef.get() == null) {
                Properties loadedProperties = new Properties();
                InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(STATIC_MAPPING_FILE);
                try {
                    loadedProperties.load(inputStream);
                } catch (Exception e) {
                    throw new RuntimeException("Unable to load resource-mapping configuration file", e);
                }
                propertiesRef.compareAndSet(null, loadedProperties);
            }
        }
    
    
        /**
         * <p>
         * Adds resource key to context map.
         * </p>
         *
         * <p>
         * Adding resource key to context map ensures that {@link ScriptRenderer} {@link StylesheetRenderer} won't render the link
         * to page.
         * </p>
         *
         * @param facesContext the current {@link FacesContext} instance
         * @param resourceKey the key of the resource
         */
        private void putResourceKeyToContextMap(FacesContext facesContext, ResourceKey resourceKey) {
            Map<Object, Object> contextMap = facesContext.getAttributes();
    
    
            String resourceName = resourceKey.getResourceName();
            String libraryName = resourceKey.getLibraryName();
            String key = resourceName + libraryName;
            if (!contextMap.containsKey(key)) { // stylesheets (with this name + library) will not be rendered multiple
                                                // times per request
                contextMap.put(key, Boolean.TRUE);
            }
            if (libraryName == null || libraryName.isEmpty()) { // also store this in the context map with library as
                                                                // "null"
                libraryName = "null";
                key = resourceName + libraryName;
                if (!contextMap.containsKey(key)) {
                    contextMap.put(key, Boolean.TRUE);
                }
            }
        }
    
    
        /**
         * Returns true if given resourceKey should not be loaded for current view.
         *
         * @param resourceKey the key of the resource
         * @return true if given resourceKey should not be loaded for current view; false otherwise
         */
        private boolean shouldNotBeLoaded(ResourceKey resourceKey) {
            String resourceName = resourceKey.getResourceName();
            return resourceName.endsWith(".js") || resourceName.endsWith(".css") || resourceName.endsWith(".ecss");
        }
    }