org.apache.myfaces.renderkit.html.util.MyFacesResourceLoader Maven / Gradle / Ivy
Show all versions of tomahawk Show documentation
/*
* 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.
*
* @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
{
// is = componentClass.getResourceAsStream(resource);
//if (is == null)
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
{
if(is!=null)
is.close();
}
}
/**
* 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);
}
}
finally
{
out.close();
}
}
/**
* 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);
}
}
}