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

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

Go to download

JSF components and utilities that can be used with any JSF implementation. This library is based on the JSF1.1 version of Tomahawk, but with minor source code and build changes to take advantage of JSF2.1 features. A JSF2.1 implementation is required to use this version of the Tomahawk library.

The 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.util.ClassUtils;

import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.ResourceBundle;
import java.net.HttpURLConnection;

/**
 * A ResourceLoader capable of fetching resources from the classpath,
 * but only for classes under package org.apache.myfaces.custom.
 * 

* The URI is expected to contain two pieces of information: the * tomahawk class the resource is associated with, and a relative path * from that class to the resource. * * @author Mathias Broekelmann (latest modification by $Author$) * @version $Revision$ $Date$ */ public class MyFacesResourceLoader implements ResourceLoader { protected static final Log log = LogFactory.getLog(MyFacesResourceLoader.class); static final String ORG_APACHE_MYFACES_CUSTOM = "org.apache.myfaces.custom"; private static long lastModified = 0; /** * Get the last-modified time of the resource. *

* Unfortunately this is not possible with files inside jars. Instead, the * MyFaces build process ensures that there is a file AddResource.properties * which has the datestamp of the time the build process was run. This method * simply gets that value and returns it. *

* Note that this method is not related to the generation of "cache key" * values by the AddResource class, nor does it affect the caching behaviour * of web browsers. This value simply goes into the http headers as the * last-modified time of the specified resource. */ 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.warn("Unparsable lastModified : " + sLastModified); } } return lastModified; } /** * Given a URI of form "{partial.class.name}/{resourceName}", locate the * specified file within the current classpath and write it to the * response object. *

* The partial class name has "org.apache.myfaces.custom." prepended * to it to form the fully qualified classname. This class object is * loaded, and Class.getResourceAsStream is called on it, passing * a uri of "resource/" + {resourceName}. *

* The data written to the response stream includes http headers * which define the mime content-type; this is deduced from the * filename suffix of the resource. *

* @see org.apache.myfaces.renderkit.html.util.ResourceLoader#serveResource(javax.servlet.ServletContext, * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.String) */ public void serveResource(ServletContext context, HttpServletRequest request, HttpServletResponse response, String resourceUri) throws IOException { String[] uriParts = resourceUri.split("/", 2); String component = uriParts[0]; if (component == null || component.trim().length() == 0) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid request"); log.error("Could not find parameter for component to load a resource."); return; } Class componentClass; String className = ORG_APACHE_MYFACES_CUSTOM + "." + component; try { componentClass = loadComponentClass(className); } catch (ClassNotFoundException e) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); log.error("Could not find the class for component " + className + " to load a resource."); return; } String resource = uriParts[1]; if (resource == null || resource.trim().length() == 0) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "No resource defined"); log.error("No resource defined component class " + className); return; } InputStream is = null; try { ResourceProvider resourceProvider; if (ResourceProvider.class.isAssignableFrom(componentClass)) { try { resourceProvider = (ResourceProvider) componentClass.newInstance(); } catch (InstantiationException e) { response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Unable to instantiate resource provider for resource " + resource + " for component " + component); log.error("Unable to instantiate resource provider for resource " + resource + " for component " + component, e); return; } catch (IllegalAccessException e) { response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Unable to instantiate resource provider for resource " + resource + " for component " + component); log.error("Unable to instantiate resource provider for resource " + resource + " for component " + component, e); return; } } else { resourceProvider = new DefaultResourceProvider(componentClass); } if (!resourceProvider.exists(context, resource)) { response.sendError(HttpServletResponse.SC_NOT_FOUND, "Unable to find resource " + resource + " for component " + component + ". Check that this file is available " + "in the classpath in sub-directory " + "/resource of the package-directory."); log.error("Unable to find resource " + resource + " for component " + component + ". Check that this file is available " + "in the classpath in sub-directory " + "/resource of the package-directory."); } else { // URLConnection con = url.openConnection(); long lastModified = resourceProvider.getLastModified(context, resource); if (lastModified < 1) { // fallback lastModified = getLastModified(); } long browserDate = request.getDateHeader("If-Modified-Since"); if (browserDate > -1) { // normalize to seconds - this should work with any os lastModified = (lastModified / 1000) * 1000; browserDate = (browserDate / 1000) * 1000; if (lastModified == browserDate) { // the browser already has the correct version response.setStatus(HttpURLConnection.HTTP_NOT_MODIFIED); return; } } int contentLength = resourceProvider.getContentLength(context, resource); String contentEncoding = resourceProvider.getEncoding(context, resource); is = resourceProvider.getInputStream(context, resource); defineContentHeaders(request, response, resource, contentLength, contentEncoding); defineCaching(request, response, resource, lastModified); writeResource(request, response, is); } } finally { // nothing to do here.. } } /** * Copy the content of the specified input stream to the servlet response. */ protected void writeResource(HttpServletRequest request, HttpServletResponse response, InputStream in) throws IOException { ServletOutputStream out = response.getOutputStream(); try { byte[] buffer = new byte[1024]; for (int size = in.read(buffer); size != -1; size = in.read(buffer)) { out.write(buffer, 0, size); } out.flush(); } catch(IOException e) { // This happens sometimes with Microsft Internet Explorer. It would // appear (guess) that when javascript creates multiple dom nodes // referring to the same remote resource then IE stupidly opens // multiple sockets and requests that resource multiple times. But // when the first request completes, it then realises its stupidity // and forcibly closes all the other sockets. But here we are trying // to service those requests, and so get a "broken pipe" failure // on write. The only thing to do here is to silently ignore the issue, // ie suppress the exception. Note that it is also possible for the // above code to succeed (ie this exception clause is not run) but // for a later flush to get the "broken pipe"; this is either due // just to timing, or possibly IE is closing sockets after receiving // a complete file for some types (gif?) rather than waiting for the // server to close it. We throw a special exception here to inform // callers that they should NOT flush anything - though that is // dangerous no matter what IOException subclass is thrown. log.debug("Unable to send resource data to client", e); throw new ResourceLoader.ClosedSocketException(); } } /** * Output http headers telling the browser (and possibly intermediate caches) how * to cache this data. *

* The expiry time in this header info is set to 7 days. This is not a problem as * the overall URI contains a "cache key" that changes whenever the webapp is * redeployed (see AddResource.getCacheKey), meaning that all browsers will * effectively reload files on webapp redeploy. */ protected void defineCaching(HttpServletRequest request, HttpServletResponse response, String resource, long lastModified) { response.setDateHeader("Last-Modified", lastModified); Calendar expires = Calendar.getInstance(); expires.add(Calendar.DAY_OF_YEAR, 7); response.setDateHeader("Expires", expires.getTimeInMillis()); //12 hours: 43200 = 60s * 60 * 12 response.setHeader("Cache-Control", "max-age=43200"); response.setHeader("Pragma", ""); } /** * Output http headers indicating the mime-type of the content being served. * The mime-type output is determined by the resource filename suffix. */ protected void defineContentHeaders(HttpServletRequest request, HttpServletResponse response, String resource, int contentLength, String contentEncoding) { String charset = ""; if (contentEncoding != null) { charset = "; charset=" + contentEncoding; } if (contentLength > -1) { response.setContentLength(contentLength); } if (resource.endsWith(".js")) response.setContentType( org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT + charset); else if (resource.endsWith(".css")) response.setContentType( org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.STYLE_TYPE_TEXT_CSS + charset); else if (resource.endsWith(".gif")) response.setContentType("image/gif"); else if (resource.endsWith(".png")) response.setContentType("image/png"); else if (resource.endsWith(".jpg") || resource.endsWith(".jpeg")) response.setContentType("image/jpeg"); else if (resource.endsWith(".xml") || resource.endsWith(".xsl")) response.setContentType("text/xml"); // XSL has to be served as XML. } protected Class loadComponentClass(String componentClass) throws ClassNotFoundException { return ClassUtils.classForName(componentClass); } // NOTE: This method is not being used. Perhaps it can be removed? protected void validateCustomComponent(Class myfacesCustomComponent) { if (!myfacesCustomComponent.getName().startsWith(ORG_APACHE_MYFACES_CUSTOM + ".")) { throw new IllegalArgumentException( "expected a myfaces custom component class in package " + ORG_APACHE_MYFACES_CUSTOM); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy