Package org.apache.myfaces.component.html.util

Source Code of org.apache.myfaces.component.html.util.AddResource$AdditionalHeaderInfoToRender

/*
* Copyright 2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.myfaces.component.html.util;

import java.io.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.ResourceBundle;
import java.util.Set;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.renderkit.html.HTML;
import org.apache.myfaces.renderkit.html.HtmlResponseWriterImpl;
import org.apache.myfaces.renderkit.html.HtmlRendererUtils;

/**
* This is a utility class to render link to resources used by custom components.
* Mostly used to avoid having to include <script src="..."></script>
* in the head of the pages before using a component.
*
* @author Sylvain Vieujot (latest modification by $Author: mmarinschek $)
* @version $Revision: 291865 $ $Date: 2005-09-27 05:07:57 -0400 (Tue, 27 Sep 2005) $
*/
public final class AddResource {
    protected static final Log log = LogFactory.getLog(AddResource.class);

    private static final String COMPONENTS_PACKAGE = "org.apache.myfaces.custom.";

    private static final String RESOURCE_VIRTUAL_PATH = "/faces/myFacesExtensionResource";

    private static final String ADDITIONAL_HEADER_INFO_REQUEST_ATTRUBITE_NAME = "myFacesHeaderResource2Render";

    private AddResource() {
        //no object creation allowed (util clazz)
    }

    // Methodes to Add resources

    /**
     * Adds the given Javascript resource to the document body.
     */
    public static void addJavaScriptHere(Class componentClass, String resourceFileName, FacesContext context, UIComponent component) throws IOException{

        addJavaScriptHere(componentClass, null, resourceFileName, context, component);

    }

    private static void addJavaScriptHere(
            Class componentClass, String baseDirectory, String resourceFileName, FacesContext context, UIComponent component)
            throws IOException
    {

        ResponseWriter writer = context.getResponseWriter();

        writer.startElement(HTML.SCRIPT_ELEM, component);
        writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR,HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT,null);

        if(baseDirectory != null)
        {
            writer.writeURIAttribute(HTML.SRC_ATTR,
                    getResourceBasePath(baseDirectory)+resourceFileName, null);
        }
        else
        {
            writer.writeURIAttribute(HTML.SRC_ATTR,
                    getResourceMappedPath(componentClass, resourceFileName, context),
                    null);
        }

        writer.endElement(HTML.SCRIPT_ELEM);
    }

    /**
     * Adds the given Javascript resource to the document body, without passing UIComponent.
     */
    public static void addJavaScriptHere(Class componentClass, String resourceFileName, FacesContext context) throws IOException{
        addJavaScriptHere(componentClass, resourceFileName,context,null);
    }

    /**
     * Adds the given Javascript resource to the document body, without passing UIComponent.
     */
    public static void addJavaScriptHere(String baseDirectory, String resourceFileName, FacesContext context) throws IOException{
        addJavaScriptHere(null, baseDirectory, resourceFileName,context,null);
    }

    /**
     * Adds the given Javascript resource to the document Header.
     * If the script is already has already been referenced, it's added only once.
     */
    public static void addJavaScriptToHeader(Class componentClass, String resourceFileName, FacesContext context){
        addJavaScriptToHeader(componentClass, resourceFileName, false, context);
    }

    /**
     * Adds the given Javascript resource to the document Header.
     * If the script is already has already been referenced, it's added only once.
     */
    public static void addJavaScriptToHeader(Class componentClass, String resourceFileName, boolean defer, FacesContext context){
        AdditionalHeaderInfoToRender jsInfo =
            new AdditionalHeaderInfoToRender(AdditionalHeaderInfoToRender.TYPE_JS, componentClass, resourceFileName, defer);
        addAdditionalHeaderInfoToRender(context, jsInfo );
    }

    /**
     * Adds the given Javascript resource to the document Header.
     * If the script is already has already been referenced, it's added only once.
     */
    public static void addJavaScriptToHeader(String baseDirectory, String resourceFileName, FacesContext context){
        addJavaScriptToHeader(baseDirectory, resourceFileName, false, context);
    }

    /**
     * Adds the given Javascript resource to the document Header.
     * If the script is already has already been referenced, it's added only once.
     */
    public static void addJavaScriptToHeader(String baseDirectory, String resourceFileName, boolean defer, FacesContext context){
        AdditionalHeaderInfoToRender jsInfo =
            new AdditionalHeaderInfoToRender(AdditionalHeaderInfoToRender.TYPE_JS, baseDirectory, resourceFileName, defer);
        addAdditionalHeaderInfoToRender(context, jsInfo );
    }

    /**
     * Adds the given Style Sheet to the document Header.
     * If the style sheet is already has already been referenced, it's added only once.
     */
    public static void addStyleSheet(Class componentClass, String resourceFileName, FacesContext context){
        AdditionalHeaderInfoToRender cssInfo =
            new AdditionalHeaderInfoToRender(AdditionalHeaderInfoToRender.TYPE_CSS, componentClass, resourceFileName);
        addAdditionalHeaderInfoToRender(context, cssInfo );
    }

    /**
     * Adds the given Style Sheet to the document Header.
     * If the style sheet is already has already been referenced, it's added only once.
     */
    public static void addStyleSheet(String baseDirectory, String resourceFileName, FacesContext context){
        AdditionalHeaderInfoToRender cssInfo =
            new AdditionalHeaderInfoToRender(AdditionalHeaderInfoToRender.TYPE_CSS, baseDirectory, resourceFileName);
        addAdditionalHeaderInfoToRender(context, cssInfo );
    }

    /**
     * Adds the given Style Sheet to the document Header.
     * If the style sheet is already has already been referenced, it's added only once.
     */
    public static void addInlineStyleToHeader(String inlineStyle, FacesContext context){
        AdditionalHeaderInfoToRender cssInfo =
            new AdditionalHeaderInfoToRender(AdditionalHeaderInfoToRender.TYPE_CSS_INLINE, inlineStyle);
        addAdditionalHeaderInfoToRender(context, cssInfo );
    }

    /**
     * Adds the given Script to the document Header.
     * If the style sheet is already has already been referenced, it's added only once.
     */
    public static void addInlineScriptToHeader(String inlineScript, FacesContext context){
        AdditionalHeaderInfoToRender scriptInfo =
            new AdditionalHeaderInfoToRender(AdditionalHeaderInfoToRender.TYPE_JS_INLINE, inlineScript);
        addAdditionalHeaderInfoToRender(context, scriptInfo );
    }

    /**
     * Get the Path used to retrieve an internal resource for a custom component.
     * Example : You can use this to initialize javascript scripts so that they know the path of some other resources
     * (image, css, ...).
     * <code>
     *   AddResource.addJavaScriptOncePerPage(HtmlCalendarRenderer.class, "popcalendar.js", facesContext,
     *     "jscalendarSetImageDirectory("+AddResource.getResourceMappedPath(HtmlCalendarRenderer.class, "DB", facesContext)+")");
     * </code>
     *
     * Note : set context to null if you want the path after the application context path.
     */
    public static String getResourceMappedPath(Class componentClass, String resourceFileName, FacesContext context){
        String contextPath = null;
        if( context != null ){
            contextPath = context.getExternalContext().getRequestContextPath();
        }

        return getResourceMappedPath(
                getComponentName(componentClass),
                resourceFileName,
                contextPath);
    }

    public static String getResourceBasePath(String baseDirectory)
    {
        return baseDirectory+(baseDirectory.endsWith("/")?"":"/");
    }

    public static String getResourceBasePath(Class componentClass, FacesContext context){
        String contextPath = null;
        if( context != null ){
            contextPath = context.getExternalContext().getRequestContextPath();
        }

        return getResourceBasePath(
                getComponentName(componentClass),
                contextPath);
    }

    public static String getResourceBasePath(String componentName, String contextPath)
    {
        String returnString = RESOURCE_VIRTUAL_PATH+"/"+componentName+'/'+getCacheKey();
        return (contextPath == null) ? returnString : contextPath + returnString;
    }

    protected static String getResourceMappedPath(String componentName, String resourceFileName, String contextPath){
       return getResourceBasePath(componentName,contextPath)+'/'
               +resourceFileName;
    }

    private static long getCacheKey(){
        // A 100 sec. delay between 2 deployement should be enough
        // and helps reduce the URL length.
        return getLastModified() / 100000;
    }

    private static long lastModified = 0;
    private static long getLastModified(){
        if( lastModified == 0 ){
            final String format = "yyyy-MM-dd HH:mm:ss Z"; // Must match the one used in the build file
            final String bundleName = AddResource.class.getName();
            ResourceBundle resources = ResourceBundle.getBundle( bundleName );
            String sLastModified = resources.getString("lastModified");
            try {
                lastModified = new SimpleDateFormat(format).parse( sLastModified ).getTime();
            } catch (ParseException e) {
                lastModified = new Date().getTime();
                log.error("Unparsable lastModified : "+sLastModified);
            }
        }

        return lastModified;
    }

    public static boolean isResourceMappedPath(HttpServletRequest request){
        return request.getRequestURI().indexOf( RESOURCE_VIRTUAL_PATH ) != -1;
    }

    /**
     * Decodes the path to return the requested componentName & resourceFileName
     * String[0] == componentName
     * String[1] == resourceFileName
     */
    private static String[] getResourceInfoFromPath(HttpServletRequest request){
        String uri = request.getRequestURI();
        String componentNameStartsAfter = RESOURCE_VIRTUAL_PATH+'/';

        int posStartComponentName = uri.indexOf( componentNameStartsAfter )+componentNameStartsAfter.length();
        int posEndComponentName = uri.indexOf("/", posStartComponentName);
        String componentName = uri.substring(posStartComponentName, posEndComponentName);

        // Skip cache key
        int posStartResourceFileName = uri.indexOf("/", posEndComponentName+1)+1;

        String resourceFileName = uri.substring(posStartResourceFileName);

        return new String[]{componentName, resourceFileName};
    }

    protected static String getComponentName(Class componentClass){
        String name = componentClass.getName();
        if( ! name.startsWith(COMPONENTS_PACKAGE) ){
            log.error("getComponentName called for non extension component : "+name+"\n"+
                    "For security reasons, only components member of the "+COMPONENTS_PACKAGE+" are allowed to add ressources.");
            return null;
        }

        name = name.substring( COMPONENTS_PACKAGE.length() );

        return name;
    }

    static Class getComponent(String componentName) throws ClassNotFoundException{
        return Class.forName( COMPONENTS_PACKAGE+componentName );
    }

    static private InputStream getResource(String componentName, String resourceFileName) {
        Class component;
        try {
            component = getComponent(componentName);
        } catch (ClassNotFoundException e) {
            log.error("Class not found for component "+componentName);
            return null;
        }
        while( resourceFileName.startsWith(".") || resourceFileName.startsWith("/") || resourceFileName.startsWith("\\") )
                resourceFileName = resourceFileName.substring(1);

        return component.getResourceAsStream( "resource/"+resourceFileName );
    }

    static public void serveResource(HttpServletRequest request, HttpServletResponse response) throws IOException{
        String[] resourceInfo = getResourceInfoFromPath(request);
        String componentName = resourceInfo[0];
        String resourceFileName = resourceInfo[1];

        log.debug("Serving resource "+resourceFileName+" for component "+componentName);

        String lcResourceFileName = resourceFileName.toLowerCase();

        if( lcResourceFileName.endsWith(".js") )
            response.setContentType("text/javascript");
        else if( lcResourceFileName.endsWith(".css") )
            response.setContentType("text/css");
        else if( lcResourceFileName.endsWith(".gif") )
            response.setContentType("image/gif");
        else if( lcResourceFileName.endsWith(".png") )
            response.setContentType("image/png");
        else if( lcResourceFileName.endsWith(".jpg") || lcResourceFileName.endsWith(".jpeg") )
            response.setContentType("image/jpeg");
        else if( lcResourceFileName.endsWith(".xml"|| lcResourceFileName.endsWith(".xsl") )
            response.setContentType("text/xml"); // XSL has to be served as XML.

        InputStream is = getResource(componentName, resourceFileName);
        if( is == null ){
            throw new IOException("Unable to find resource "+resourceFileName+" for component "+componentName+
                    ". Check that this file is available in the classpath in sub-directory /resource of the component-directory.");
        }

        response.setDateHeader("Last-Modified", getLastModified());

        // Set browser cache to a week.
        // There is no risk, as the cache key is part of the URL.
        Calendar expires = Calendar.getInstance();
        expires.add(Calendar.DAY_OF_YEAR, 7);
        response.setDateHeader("Expires", expires.getTimeInMillis());

        OutputStream os = response.getOutputStream();
        int c;
        while ((c = is.read()) != -1)
            os.write(c);

        os.close();
    }

    // Header stuffs

    private static Set getAdditionalHeaderInfoToRender(HttpServletRequest request){
        Set set = (Set) request.getAttribute(ADDITIONAL_HEADER_INFO_REQUEST_ATTRUBITE_NAME);
        if( set == null ){
            set = new LinkedHashSet();
            request.setAttribute(ADDITIONAL_HEADER_INFO_REQUEST_ATTRUBITE_NAME, set);
        }

        return set;
    }

    private static void addAdditionalHeaderInfoToRender(FacesContext context, AdditionalHeaderInfoToRender info){

        //todo: fix this to work in PortletRequest as well
        HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
        Set set = getAdditionalHeaderInfoToRender( request );
        set.add( info );
    }

    static public boolean hasAdditionalHeaderInfoToRender(HttpServletRequest request){
        return request.getAttribute(ADDITIONAL_HEADER_INFO_REQUEST_ATTRUBITE_NAME) != null;
    }

    /**
     * Add the resources to the &lt;head&gt; of the page.
     * If the head tag is missing, but the &lt;body&gt; tag is present, the head tag is added.
     * If both are missing, no resources is added.
     *
     * TODO : Change the ordering so that the user header CSS & JS override MyFaces' ones.
     */
    static public void writeWithFullHeader(HttpServletRequest request,
                                           ExtensionsResponseWrapper responseWrapper,
                                           HttpServletResponse response) throws IOException{

        String originalResponse = responseWrapper.toString();

        // try the most common cases first
        boolean addHeaderTags = false;
        int insertPosition = originalResponse.indexOf( "</head>" );

        if( insertPosition < 0 ){
            insertPosition = originalResponse.indexOf( "</HEAD>" );

            if( insertPosition < 0 ){
                insertPosition = originalResponse.indexOf( "<body" );
                addHeaderTags = true;

                if( insertPosition < 0 ){
                    insertPosition = originalResponse.indexOf( "<BODY" );
                    addHeaderTags = true;

                    if( insertPosition < 0 ){
                        // the two most common cases head/HEAD and body/BODY did not work, so we try it with lowercase
                       String lowerCase = originalResponse.toLowerCase(response.getLocale());
                       insertPosition = lowerCase.indexOf( "</head>" );

                       if( insertPosition < 0 ){
                           insertPosition = lowerCase.indexOf( "<body" );
                           addHeaderTags = true;
                       }
                    }
                }
            }

            if( insertPosition < 0 ){
                log.warn("Response has no <head> or <body> tag:\n"+originalResponse);
                insertPosition = -1;
            }
        }

        PrintWriter writer = response.getWriter();

        if( insertPosition > 0 )
            writer.write( originalResponse.substring(0, insertPosition) );
        if(insertPosition>=0 && addHeaderTags )
            writer.write("<head>");

        if(insertPosition>=0)
        {
            for(Iterator i = getAdditionalHeaderInfoToRender(request).iterator(); i.hasNext() ;){
                AdditionalHeaderInfoToRender headerInfo = (AdditionalHeaderInfoToRender) i.next();
                headerInfo.writeString(request.getContextPath(),writer,
                        HtmlRendererUtils.selectContentType(request.getHeader("accept")),null);
            }
        }

        if(insertPosition>=0 && addHeaderTags)
            writer.write("</head>");

        writer.write( insertPosition > 0 ? originalResponse.substring(insertPosition) : originalResponse );
    }

    private static class AdditionalHeaderInfoToRender{
        static final int TYPE_JS = 0;
        static final int TYPE_CSS = 1;
        static final int TYPE_CSS_INLINE = 2;
        static final int TYPE_JS_INLINE = 3;

        public int type;
        public boolean deferJS = false;
        public String componentName;
        public String baseDirectory;
        public String resourceFileName;
        public String inlineText;

        public AdditionalHeaderInfoToRender(int infoType, String baseDirectory, String resourceFileName) {
            this.type = infoType;
            this.baseDirectory = baseDirectory;
            this.resourceFileName = resourceFileName;
        }

        public AdditionalHeaderInfoToRender(int infoType, String baseDirectory, String resourceFileName, boolean defer) {
            if( defer && infoType != TYPE_JS )
                log.error("Defer can only be used for scripts.");
            this.type = infoType;
            this.baseDirectory = baseDirectory;
            this.resourceFileName = resourceFileName;
            this.deferJS = defer;
        }

        public AdditionalHeaderInfoToRender(int infoType, Class componentClass, String resourceFileName) {
            this.type = infoType;
            this.componentName = getComponentName(componentClass);
            this.resourceFileName = resourceFileName;
        }

        public AdditionalHeaderInfoToRender(int infoType, Class componentClass, String resourceFileName, boolean defer) {
            if( defer && infoType != TYPE_JS )
                log.error("Defer can only be used for scripts.");
            this.type = infoType;
            this.componentName = getComponentName(componentClass);
            this.resourceFileName = resourceFileName;
            this.deferJS = defer;
        }

        public AdditionalHeaderInfoToRender(int infoType, String inlineText) {
            if( infoType != TYPE_CSS_INLINE  && infoType != TYPE_JS_INLINE)
                log.error("This constructor only supports TYPE_CSS_INLINE or TYPE_JS_INLINE");
            this.type = infoType;
            this.inlineText = inlineText;
        }

        public int hashCode() {
            return (componentName+((char)7)
                    +resourceFileName+((char)7)
                    +(type+""+((char)7))
                    +(inlineText+""+((char)7))
                    +(deferJS+"")).hashCode();
        }

        public boolean equals(Object obj) {
            if( !(obj instanceof AdditionalHeaderInfoToRender) )
                return false;
            AdditionalHeaderInfoToRender toCompare = (AdditionalHeaderInfoToRender) obj;

            if( type != toCompare.type || deferJS != toCompare.deferJS )
                return false;

            if( componentName == null ){
                if( toCompare.componentName != null )
                    return false;
            }else if( ! componentName.equals(toCompare.componentName) )
                return false;

            if( resourceFileName == null ){
                if( toCompare.resourceFileName != null )
                    return false;
            }else if( ! resourceFileName.equals(toCompare.resourceFileName) )
                return false;

            if( inlineText == null )
                return toCompare.inlineText == null;

            return inlineText.equals(toCompare.inlineText);
        }

        public void writeString(String contextPath, Writer writer, String contentType,
                                String characterEncoding) throws IOException
        {

            HtmlResponseWriterImpl responseWriter = new HtmlResponseWriterImpl(
                    writer,contentType,characterEncoding);

            responseWriter.writeText("\n",null);

            switch (type) {
                case TYPE_JS:
                    responseWriter.startElement(HTML.SCRIPT_ELEM,null);

                    if(baseDirectory != null)
                    {
                        responseWriter.writeAttribute(HTML.SRC_ATTR,
                            getResourceBasePath(baseDirectory)+resourceFileName,
                            null);
                    }
                    else
                    {
                        responseWriter.writeAttribute(HTML.SRC_ATTR,
                            getResourceMappedPath(componentName, resourceFileName, contextPath),null);
                    }

                    if(deferJS)
                        responseWriter.writeAttribute("defer","true",null);
                    responseWriter.writeAttribute(HTML.SCRIPT_TYPE_ATTR,HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT,null);
                    responseWriter.endElement(HTML.SCRIPT_ELEM);
                    break;
                case TYPE_CSS:
                    responseWriter.startElement("link",null);
                    responseWriter.writeAttribute("rel","stylesheet",null);

                    if(baseDirectory != null )
                    {
                        responseWriter.writeAttribute(
                                HTML.HREF_ATTR,
                                baseDirectory+(baseDirectory.endsWith("/")?"":"/")+resourceFileName,
                                null);
                    }
                    else
                    {
                        responseWriter.writeAttribute(HTML.HREF_ATTR,
                            getResourceMappedPath(componentName, resourceFileName, contextPath),null);
                    }

                    responseWriter.writeAttribute(HTML.TYPE_ATTR,"text/css",null);
                    responseWriter.endElement("link");
                    break;

                case TYPE_CSS_INLINE:
                    responseWriter.startElement(HTML.STYLE_ELEM,null);
                    responseWriter.writeAttribute("rel","stylesheet",null);
                    responseWriter.writeAttribute(HTML.TYPE_ATTR,"text/css",null);
                    responseWriter.writeText(inlineText,null);
                    responseWriter.endElement(HTML.STYLE_ELEM);
                    break;
                case TYPE_JS_INLINE:
                    responseWriter.startElement(HTML.SCRIPT_ELEM,null);
                    responseWriter.writeAttribute(HTML.SCRIPT_TYPE_ATTR,HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT,null);
                    responseWriter.writeText(inlineText,null);
                    responseWriter.endElement(HTML.SCRIPT_ELEM);
                    break;
                default:
                    log.warn("Unknown type:"+type);
            }
        }
    }
}
TOP

Related Classes of org.apache.myfaces.component.html.util.AddResource$AdditionalHeaderInfoToRender

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.