All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.myfaces.renderkit.html.util.NonBufferingAddResource Maven / Gradle / Ivy

Go to download

JSF components and utilities that can be used with any JSF implementation. This library is compatible with both JSF1.1 and JSF1.2; however for JSF1.2 users there is an alternative build of Tomahawk available that takes advantage of JSF1.2 features to offer some additional benefits.

There is a newer version: 1.1.14
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.renderkit.html.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.shared_tomahawk.config.MyfacesConfig;
import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlResponseWriterImpl;
import org.apache.myfaces.shared_tomahawk.util.ClassUtils;

import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;

/**
 * @since 1.1.7
 * @author Martin Marinschek
 */
public class NonBufferingAddResource implements AddResource {

    protected static final String PATH_SEPARATOR = "/";
    protected String _contextPath;

    private static final String RESOURCES_CACHE_KEY = AddResource.class.getName() + ".CACHE_KEY";

    private String resourceVirtualPath;

    protected Log log = LogFactory.getLog(NonBufferingAddResource.class);

    /**
     * the context path for the web-app.
* You can set the context path only once, every subsequent set will throw an SecurityException */ public void setContextPath(String contextPath) { if (_contextPath != null) { throw new SecurityException("context path already set"); } _contextPath = contextPath; } // Methods to add resources /** * Insert a [script src="url"] entry at the current location in the response. * The resource is expected to be in the classpath, at the same location as the * specified component + "/resource". *

* Example: when customComponent is class example.Widget, and * resourceName is script.js, the resource will be retrieved from * "example/Widget/resource/script.js" in the classpath. */ public void addJavaScriptHere(FacesContext context, Class myfacesCustomComponent, String resourceName) throws IOException { addJavaScriptHere(context, new MyFacesResourceHandler(myfacesCustomComponent, resourceName)); } /** * Insert a [script src="url"] entry at the current location in the response. * * @param uri is the location of the desired resource, relative to the base * directory of the webapp (ie its contextPath). */ public void addJavaScriptHere(FacesContext context, String uri) throws IOException { writeJavaScriptReference(context, getResourceUri(context, uri), true, false); } protected static void writeJavaScriptReference(FacesContext context, String resourceUri, boolean encoding, boolean defer) throws IOException{ ResponseWriter writer = context.getResponseWriter(); String src = null; if(encoding) { src=context.getExternalContext().encodeResourceURL(resourceUri); } else { src = resourceUri; } writeJavaScriptReference(defer, writer, src); } protected static void writeJavaScriptReference(HttpServletResponse response, ResponseWriter writer, String resourceUri, boolean encoding, boolean defer) throws IOException { String src = null; if(encoding) { src=response.encodeURL(resourceUri); } else { src = resourceUri; } writeJavaScriptReference(defer, writer, src); } private static void writeJavaScriptReference(boolean defer, ResponseWriter writer, String src) throws IOException { writer.startElement(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_ELEM, null); writer.writeAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_TYPE_ATTR, org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null); if(defer) { writer.writeAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_ELEM_DEFER_ATTR, "true", null); } writer.writeURIAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SRC_ATTR, src, null); writer.endElement(HTML.SCRIPT_ELEM); } protected static void writeStyleReference(FacesContext context, String resourceUri) throws IOException { ResponseWriter writer = context.getResponseWriter(); writer.startElement(HTML.LINK_ELEM, null); writer.writeAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.REL_ATTR, org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.STYLESHEET_VALUE, null); writer.writeAttribute(HTML.HREF_ATTR, context.getExternalContext().encodeResourceURL(resourceUri), null); writer.writeAttribute(HTML.TYPE_ATTR, HTML.STYLE_TYPE_TEXT_CSS, null); writer.endElement(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.LINK_ELEM); } protected static void writeStyleReference(HttpServletResponse response, ResponseWriter writer, String resourceUri) throws IOException { writer.startElement(HTML.LINK_ELEM, null); writer.writeAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.REL_ATTR, org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.STYLESHEET_VALUE, null); writer.writeAttribute(HTML.HREF_ATTR, response.encodeURL(resourceUri), null); writer.writeAttribute(HTML.TYPE_ATTR, HTML.STYLE_TYPE_TEXT_CSS, null); writer.endElement(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.LINK_ELEM); } protected static void writeInlineScript(ResponseWriter writer, String inlineScript) throws IOException { writer.startElement(HTML.SCRIPT_ELEM, null); writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null); writer.writeText(inlineScript, null); writer.endElement(HTML.SCRIPT_ELEM); } protected static void writeInlineStylesheet(ResponseWriter writer, String inlineStyle) throws IOException { writer.startElement(HTML.STYLE_ELEM, null); writer.writeAttribute(HTML.REL_ATTR, HTML.STYLESHEET_VALUE, null); writer.writeAttribute(HTML.TYPE_ATTR, HTML.STYLE_TYPE_TEXT_CSS, null); writer.writeText(inlineStyle, null); writer.endElement(HTML.STYLE_ELEM); } public void addJavaScriptHerePlain(FacesContext context, String uri) throws IOException { writeJavaScriptReference(context, getResourceUri(context, uri), false, false); } /** * Insert a [script src="url"] entry at the current location in the response. * * @param context The current faces-context * @param resourceHandler is an object which specifies exactly how to build the url * that is emitted into the script tag. Code which needs to generate URLs in ways * that this class does not support by default can implement a custom ResourceHandler. * @throws IOException */ public void addJavaScriptHere(FacesContext context, ResourceHandler resourceHandler) throws IOException { validateResourceHandler(resourceHandler); writeJavaScriptReference(context, getResourceUri(context, resourceHandler), true, false); } public void addResourceHere(FacesContext context, ResourceHandler resourceHandler) throws IOException { validateResourceHandler(resourceHandler); String path = getResourceUri(context, resourceHandler); ResponseWriter writer = context.getResponseWriter(); writer.write(context.getExternalContext().encodeResourceURL(path)); } /** * Verify that the resource handler is acceptable. Null is not * valid, and the getResourceLoaderClass method must return a * Class object whose instances implements the ResourceLoader * interface. * * @param resourceHandler handler to check */ protected void validateResourceHandler(ResourceHandler resourceHandler) { if (resourceHandler == null) { throw new IllegalArgumentException("ResourceHandler is null"); } validateResourceLoader(resourceHandler.getResourceLoaderClass()); } /** * Given a Class object, verify that the instances of that class * implement the ResourceLoader interface. * * @param resourceloader loader to check */ protected void validateResourceLoader(Class resourceloader) { if (!ResourceLoader.class.isAssignableFrom(resourceloader)) { throw new FacesException("Class " + resourceloader.getName() + " must implement " + ResourceLoader.class.getName()); } } public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position, ResourceHandler resourceHandler) { try { writeJavaScriptReference(context,getResourceUri(context,resourceHandler),true,false); } catch (IOException e) { throw new IllegalStateException(e.getMessage()); } } public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position, Class myfacesCustomComponent, String resourceName) { try { writeJavaScriptReference(context,getResourceUri(context,new MyFacesResourceHandler( myfacesCustomComponent, resourceName)),true,false); } catch (IOException e) { throw new IllegalStateException(e.getMessage()); } } public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position, Class myfacesCustomComponent, String resourceName, boolean defer) { try { writeJavaScriptReference(context,getResourceUri(context,new MyFacesResourceHandler( myfacesCustomComponent, resourceName)),true,true); } catch (IOException e) { throw new IllegalStateException(e.getMessage()); } } public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position, String uri) { try { writeJavaScriptReference(context,getResourceUri(context,uri),true,false); } catch (IOException e) { throw new IllegalStateException(e.getMessage()); } } public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position, String uri, boolean defer) { try { writeJavaScriptReference(context,getResourceUri(context,uri),true,true); } catch (IOException e) { throw new IllegalStateException(e.getMessage()); } } /** * * @param context * @param javascriptEventName * @param addedJavaScript * * @deprecated */ public void addJavaScriptToBodyTag(FacesContext context, String javascriptEventName, String addedJavaScript) { throw new UnsupportedOperationException("not supported anymore - use javascript to register your body-event-handler directly"); } public void addJavaScriptAtPosition(FacesContext context, ResourcePosition position, ResourceHandler resourceHandler, boolean defer) { try { writeJavaScriptReference(context,getResourceUri(context,resourceHandler),true,defer); } catch (IOException e) { throw new IllegalStateException(e.getMessage()); } } public void addJavaScriptAtPositionPlain(FacesContext context, ResourcePosition position, Class myfacesCustomComponent, String resourceName) { try { writeJavaScriptReference(context,getResourceUri(context,new MyFacesResourceHandler(myfacesCustomComponent, resourceName)),false,false); } catch (IOException e) { throw new IllegalStateException(e.getMessage()); } } public void addStyleSheet(FacesContext context, ResourcePosition position, Class myfacesCustomComponent, String resourceName) { try { writeStyleReference(context,getResourceUri(context,new MyFacesResourceHandler(myfacesCustomComponent, resourceName))); } catch (IOException e) { throw new IllegalStateException(e.getMessage()); } } public void addStyleSheet(FacesContext context, ResourcePosition position, String uri) { try { writeStyleReference(context,getResourceUri(context,uri)); } catch (IOException e) { throw new IllegalStateException(e.getMessage()); } } public void addStyleSheet(FacesContext context, ResourcePosition position, ResourceHandler resourceHandler) { try { writeStyleReference(context,getResourceUri(context,resourceHandler)); } catch (IOException e) { throw new IllegalStateException(e.getMessage()); } } public void addInlineStyleAtPosition(FacesContext context, ResourcePosition position, String inlineStyle) { try { writeInlineStylesheet(context.getResponseWriter(), inlineStyle); } catch (IOException e) { throw new IllegalStateException(e.getMessage()); } } public void addInlineScriptAtPosition(FacesContext context, ResourcePosition position, String inlineScript) { try { writeInlineScript(context.getResponseWriter(), inlineScript); } catch (IOException e) { throw new IllegalStateException(e.getMessage()); } } public String getResourceUri(FacesContext context, Class myfacesCustomComponent, String resource, boolean withContextPath) { return getResourceUri(context, new MyFacesResourceHandler(myfacesCustomComponent, resource), withContextPath); } public String getResourceUri(FacesContext context, Class myfacesCustomComponent, String resource) { return getResourceUri(context, new MyFacesResourceHandler(myfacesCustomComponent, resource)); } /** * Get the Path used to retrieve an resource. */ public String getResourceUri(FacesContext context, ResourceHandler resourceHandler) { String uri = resourceHandler.getResourceUri(context); if (uri == null) { return getResourceUri(context, resourceHandler.getResourceLoaderClass(), true); } return getResourceUri(context, resourceHandler.getResourceLoaderClass(), true) + uri; } /** * Get the Path used to retrieve an resource. */ public String getResourceUri(FacesContext context, ResourceHandler resourceHandler, boolean withContextPath) { String uri = resourceHandler.getResourceUri(context); if (uri == null) { return getResourceUri(context, resourceHandler.getResourceLoaderClass(), withContextPath); } return getResourceUri(context, resourceHandler.getResourceLoaderClass(), withContextPath) + uri; } /** * Get the Path used to retrieve an resource. */ public String getResourceUri(FacesContext context, String uri) { return getResourceUri(context, uri, true); } /** * Get the Path used to retrieve an resource. */ public String getResourceUri(FacesContext context, String uri, boolean withContextPath) { if (withContextPath) { return context.getApplication().getViewHandler().getResourceURL(context, uri); } return uri; } /** * Get the Path used to retrieve an resource. * @param context current faces-context * @param resourceLoader resourceLoader * @param withContextPath use the context-path of the web-app when accessing the resources * * @return the URI of the resource */ protected String getResourceUri(FacesContext context, Class resourceLoader, boolean withContextPath) { StringBuffer sb = new StringBuffer(200); sb.append(MyfacesConfig.getCurrentInstance(context.getExternalContext()).getResourceVirtualPath()); sb.append(PATH_SEPARATOR); sb.append(resourceLoader.getName()); sb.append(PATH_SEPARATOR); sb.append(getCacheKey(context)); sb.append(PATH_SEPARATOR); return getResourceUri(context, sb.toString(), withContextPath); } /** * Return a value used in the {cacheKey} part of a generated URL for a * resource reference. *

* Caching in browsers normally works by having files served to them * include last-modified and expiry-time http headers. Until the expiry * time is reached, a browser will silently use its cached version. After * the expiry time, it will send a "get if modified since {time}" message, * where {time} is the last-modified header from the version it has cached. *

* Unfortunately this scheme only works well for resources represented as * plain files on disk, where the webserver can easily and efficiently see * the last-modified time of the resource file. When that query has to be * processed by a servlet that doesn't scale well, even when it is possible * to determine the resource's last-modified date from servlet code. *

* Fortunately, for the AddResource class a static resource is only ever * accessed because a URL was embedded by this class in a dynamic page. * This makes it possible to implement caching by instead marking every * resource served with a very long expiry time, but forcing the URL that * points to the resource to change whenever the old cached version becomes * invalid; the browser effectively thinks it is fetching a different * resource that it hasn't seen before. This is implemented by embedding * a "cache key" in the generated URL. *

* Rather than using the actual modification date of a resource as the * cache key, we simply use the webapp deployment time. This means that all * data cached by browsers will become invalid after a webapp deploy (all * the urls to the resources change). It also means that changes that occur * to a resource without a webapp redeploy will not be seen by browsers. * * @param context the current faces-context * * @return the key for caching */ protected long getCacheKey(FacesContext context) { // cache key is hold in application scope so it is recreated on redeploying the webapp. Map applicationMap = context.getExternalContext().getApplicationMap(); Long cacheKey = (Long) applicationMap.get(RESOURCES_CACHE_KEY); if (cacheKey == null) { cacheKey = new Long(System.currentTimeMillis() / 100000); applicationMap.put(RESOURCES_CACHE_KEY, cacheKey); } return cacheKey.longValue(); } public boolean isResourceUri(ServletContext servletContext, HttpServletRequest request) { String path; if (_contextPath != null) { path = _contextPath + getResourceVirtualPath(servletContext); } else { path = getResourceVirtualPath(servletContext); } //fix for TOMAHAWK-660; to be sure this fix is backwards compatible, the //encoded context-path is only used as a first option to check for the prefix //if we're sure this works for all cases, we can directly return the first value //and not double-check. try { if(request.getRequestURI().startsWith(URLEncoder.encode(path,"UTF-8"))) return true; } catch (UnsupportedEncodingException e) { log.error("Unsupported encoding UTF-8 used",e); } return request.getRequestURI().startsWith(path); } private String getResourceVirtualPath(ServletContext servletContext) { if(resourceVirtualPath == null) { resourceVirtualPath = servletContext.getInitParameter(MyfacesConfig.INIT_PARAM_RESOURCE_VIRTUAL_PATH); if(resourceVirtualPath == null) { resourceVirtualPath = MyfacesConfig.INIT_PARAM_RESOURCE_VIRTUAL_PATH_DEFAULT; } } return resourceVirtualPath; } private Class getClass(String className) throws ClassNotFoundException { Class clazz = ClassUtils.classForName(className); validateResourceLoader(clazz); return clazz; } public void serveResource(ServletContext context, HttpServletRequest request, HttpServletResponse response) throws IOException { String pathInfo = request.getPathInfo(); String uri = request.getContextPath() + request.getServletPath() + (pathInfo == null ? "" : pathInfo); String classNameStartsAfter = getResourceVirtualPath(context) + '/'; int posStartClassName = uri.indexOf(classNameStartsAfter) + classNameStartsAfter.length(); int posEndClassName = uri.indexOf(PATH_SEPARATOR, posStartClassName); String className = uri.substring(posStartClassName, posEndClassName); int posEndCacheKey = uri.indexOf(PATH_SEPARATOR, posEndClassName + 1); String resourceUri = null; if (posEndCacheKey + 1 < uri.length()) { resourceUri = uri.substring(posEndCacheKey + 1); } try { Class resourceLoader = getClass(className); validateResourceLoader(resourceLoader); ((ResourceLoader) resourceLoader.newInstance()).serveResource(context, request, response, resourceUri); // Do not call response.flushBuffer buffer here. There is no point, as if there // ever were header data to write, this would fail as we have already written // the response body. The only point would be to flush the output stream, but // that will happen anyway when the servlet container closes the socket. // // In addition, flushing could fail here; it appears that Microsoft IE // hasthe habit of hard-closing its socket as soon as it has received a complete // gif file, rather than letting the server close it. The container will hopefully // silently ignore exceptions on close. } catch (ResourceLoader.ClosedSocketException e) { // The ResourceLoader was unable to send the data because the client closed // the socket on us; just ignore. } catch (ClassNotFoundException e) { log.error("Could not find class for name: " + className, e); response.sendError(HttpServletResponse.SC_NOT_FOUND, "Could not find resourceloader class for name: " + className); } catch (InstantiationException e) { log.error("Could not instantiate class for name: " + className, e); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Could not instantiate resourceloader class for name: " + className); } catch (IllegalAccessException e) { log.error("Could not access class for name: " + className, e); response.sendError(HttpServletResponse.SC_FORBIDDEN, "Could not access resourceloader class for name: " + className); } catch (Throwable e) { log.error("Error while serving resource: " + resourceUri + ", message : " + e.getMessage(), e); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } public void parseResponse(HttpServletRequest request, String bufferedResponse, HttpServletResponse response) throws IOException { throw new UnsupportedOperationException("non-buffering add resource is not buffering."); } public void writeMyFacesJavascriptBeforeBodyEnd(HttpServletRequest request, HttpServletResponse response) throws IOException { throw new UnsupportedOperationException("non-buffering add resource is not buffering."); } public void writeWithFullHeader(HttpServletRequest request, HttpServletResponse response) throws IOException { throw new UnsupportedOperationException("non-buffering add resource is not buffering."); } public void writeResponse(HttpServletRequest request, HttpServletResponse response) throws IOException { throw new UnsupportedOperationException("non-buffering add resource is not buffering."); } public boolean requiresBuffer() { return false; } public void responseStarted() { } public void responseFinished() { } public boolean hasHeaderBeginInfos() { return false; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy