You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ba...@apache.org on 2005/11/11 12:14:13 UTC
svn commit: r332518 - in
/myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util:
AddResource.java MyFacesResourceHandler.java MyfacesResourceLoader.java
ResourceHandler.java ResourceLoader.java
Author: baranda
Date: Fri Nov 11 03:14:05 2005
New Revision: 332518
URL: http://svn.apache.org/viewcvs?rev=332518&view=rev
Log:
Fixes MYFACES-810. Thanks to Simon Kitching
Modified:
myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/AddResource.java
myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/MyFacesResourceHandler.java
myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/MyfacesResourceLoader.java
myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/ResourceHandler.java
myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/ResourceLoader.java
Modified: myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/AddResource.java
URL: http://svn.apache.org/viewcvs/myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/AddResource.java?rev=332518&r1=332517&r2=332518&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/AddResource.java (original)
+++ myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/AddResource.java Fri Nov 11 03:14:05 2005
@@ -48,6 +48,24 @@
* emit references to javascript/css/etc which are bundled in the component's
* jar file. Special URLs are generated which the ExtensionsFilter will later
* handle by retrieving the specified resource from the classpath.
+ * <p>
+ * The special URL format is:
+ * <pre>
+ * {contextPath}/faces/myFacesExtensionResource/
+ * {resourceLoaderName}/{cacheKey}/{resourceURI}
+ * </pre>
+ * Where:
+ * <ul>
+ * <li> {contextPath} is the context path of the current webapp
+ * <li> {resourceLoaderName} is the fully-qualified name of a class which
+ * implements the ResourceLoader interface. When a browser app sends a request
+ * for the specified resource, an instance of the specified ResourceLoader class
+ * will be created and passed the resourceURI part of the URL for resolving to the
+ * actual resource to be served back. The standard MyFaces ResourceLoader
+ * implementation only serves resources for files stored beneath path
+ * org/apache/myfaces/custom in the classpath but non-myfaces code can provide their
+ * own ResourceLoader implementations.
+ * </ul>
*
* @author Sylvain Vieujot (latest modification by $Author$)
* @version $Revision$ $Date$
@@ -90,7 +108,7 @@
*/
private static final Map _addResourceMap = new HashMap();
- /*
+ /**
* Internal factory method.
* <p>
* Return an instance of AddResource keyed by context path, or create one
@@ -204,6 +222,11 @@
}
/**
+ * 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
*/
protected void validateResourceHandler(ResourceHandler resourceHandler)
@@ -216,6 +239,9 @@
}
/**
+ * Given a Class object, verify that the instances of that class
+ * implement the ResourceLoader interface.
+ *
* @param resourceloader
*/
protected void validateResourceLoader(Class resourceloader)
@@ -442,6 +468,37 @@
return getResourceUri(context, sb.toString(), withContextPath);
}
+ /**
+ * Return a value used in the {cacheKey} part of a generated URL for a
+ * resource reference.
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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 <i>without</i> a webapp redeploy will not be seen by browsers.
+ */
protected long getCacheKey(FacesContext context)
{
// cache key is hold in application scope so it is recreated on redeploying the webapp.
@@ -484,8 +541,7 @@
int posStartClassName = uri.indexOf(classNameStartsAfter) + classNameStartsAfter.length();
int posEndClassName = uri.indexOf(PATH_SEPARATOR, posStartClassName);
- String className;
- className = uri.substring(posStartClassName, posEndClassName);
+ String className = uri.substring(posStartClassName, posEndClassName);
int posEndCacheKey = uri.indexOf(PATH_SEPARATOR, posEndClassName + 1);
String resourceUri = null;
if (posEndCacheKey + 1 < uri.length())
@@ -619,11 +675,14 @@
{
if (beforeBodyPosition != -1)
{
+ // The input html has a body start tag, but no head tags. We therefore
+ // need to insert head start/end tags for our content to live in.
addHeaderTags = true;
headerInsertPosition = beforeBodyPosition;
}
else
{
+ // neither head nor body tags in the input
log.warn("Response has no <head> or <body> tag:\n" + originalResponse);
}
}
@@ -633,6 +692,7 @@
if(afterBodyContentInsertPosition >=0)
{
+ // insert all the items that want to go immediately after the <body> tag.
HtmlBufferResponseWriterWrapper writerWrapper = HtmlBufferResponseWriterWrapper.
getInstance(writer);
Modified: myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/MyFacesResourceHandler.java
URL: http://svn.apache.org/viewcvs/myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/MyFacesResourceHandler.java?rev=332518&r1=332517&r2=332518&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/MyFacesResourceHandler.java (original)
+++ myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/MyFacesResourceHandler.java Fri Nov 11 03:14:05 2005
@@ -18,8 +18,10 @@
import javax.faces.context.FacesContext;
/**
+ * Class whose instances represent a resource inside the tomahawk jarfile
+ * which a custom component needs to tell a browser to fetch.
+ *
* @author Mathias Broekelmann
- *
*/
public class MyFacesResourceHandler implements ResourceHandler
{
@@ -27,17 +29,30 @@
private final String _resource;
/**
- * @param myfacesCustomComponent
- * @param resource
+ * Constructor.
+ *
+ * @param myfacesCustomComponent is a class that must be in package
+ * org.apache.myfaces.custom. The resource to be served will be
+ * located relative to this class in the classpath. Note that code
+ * wishing to serve resources from other locations in the classpath
+ * must write a custom ResourceHandler implementation.
+ *
+ * @param resource is the name of a file that can be found in dir
+ * "resource/{resourceName} relative to the location of the specified
+ * component class in the classpath.
*/
- public MyFacesResourceHandler(Class myfacesCustomComponent, String resource)
+ public MyFacesResourceHandler(Class myfacesCustomComponent, String resourceName)
{
validateCustomComponent(myfacesCustomComponent);
_myfacesCustomComponent = myfacesCustomComponent;
- _resource = resource;
+ _resource = resourceName;
}
/**
+ * Return a Class object which can decode the url generated by this
+ * class in the getResourceUri method and use that info to locate
+ * the resource data represented by this object.
+ *
* @see org.apache.myfaces.component.html.util.ResourceHandler#getResourceLoaderClass()
*/
public Class getResourceLoaderClass()
@@ -45,6 +60,13 @@
return MyFacesResourceLoader.class;
}
+ /**
+ * Verify that the base class for the resource lookup is in the
+ * org.apache.myfaces.custom package.
+ *
+ * @param myfacesCustomComponent is the base component for the lookup.
+ * @throws IllegalArgumentException if the class is not in the expected package.
+ */
protected void validateCustomComponent(Class myfacesCustomComponent)
{
if (!myfacesCustomComponent.getName().startsWith(
@@ -57,15 +79,24 @@
}
/**
+ * Return a URL that the browser can later submit to retrieve the resource
+ * handled by this instance.
+ * <p>
+ * The emitted URL is of form:
+ * <pre>
+ * {partial.class.name}/{resourceName}
+ * </pre>
+ * where partial.class.name is the name of the base class specified in the
+ * constructor, and resourceName is the resource specified in the constructor.
+ *
* @see org.apache.myfaces.component.html.util.ResourceHandler#getResourceUri(javax.faces.context.FacesContext)
*/
public String getResourceUri(FacesContext context)
{
String className = _myfacesCustomComponent.getName();
StringBuffer sb = new StringBuffer();
- sb
- .append(className.substring(MyFacesResourceLoader.ORG_APACHE_MYFACES_CUSTOM
- .length() + 1));
+ sb.append(className.substring(
+ MyFacesResourceLoader.ORG_APACHE_MYFACES_CUSTOM.length() + 1));
sb.append("/");
if (_resource != null)
{
Modified: myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/MyfacesResourceLoader.java
URL: http://svn.apache.org/viewcvs/myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/MyfacesResourceLoader.java?rev=332518&r1=332517&r2=332518&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/MyfacesResourceLoader.java (original)
+++ myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/MyfacesResourceLoader.java Fri Nov 11 03:14:05 2005
@@ -44,6 +44,19 @@
private static long lastModified = 0;
+ /**
+ * Get the last-modified time of the resource.
+ * <p>
+ * 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.
+ * <p>
+ * 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)
@@ -67,7 +80,22 @@
}
/**
- * @see org.apache.myfaces.component.html.util.ResourceLoader#serveResource(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.String)
+ * Given a URI of form "{partial.class.name}/{resourceName}", locate the
+ * specified file within the current classpath and write it to the
+ * response object.
+ * <p>
+ * 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}.
+ * <p>
+ * 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.
+ * <p>
+ * @see org.apache.myfaces.component.html.util.ResourceLoader#
+ * serveResource(javax.servlet.http.HttpServletRequest,
+ * javax.servlet.http.HttpServletResponse, java.lang.String)
*/
public void serveResource(HttpServletRequest request, HttpServletResponse response,
String resourceUri) throws IOException
@@ -123,9 +151,7 @@
}
/**
- * @param request
- * @param response
- * @param in
+ * Copy the content of the specified input stream to the servlet response.
*/
protected void writeResource(HttpServletRequest request, HttpServletResponse response,
InputStream in) throws IOException
@@ -145,18 +171,29 @@
}
}
+ /**
+ * Output http headers telling the browser (and possibly intermediate caches) how
+ * to cache this data.
+ * <p>
+ * 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)
{
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());
}
+ /**
+ * 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)
{
@@ -179,6 +216,7 @@
return Thread.currentThread().getContextClassLoader().loadClass(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 + "."))
Modified: myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/ResourceHandler.java
URL: http://svn.apache.org/viewcvs/myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/ResourceHandler.java?rev=332518&r1=332517&r2=332518&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/ResourceHandler.java (original)
+++ myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/ResourceHandler.java Fri Nov 11 03:14:05 2005
@@ -20,39 +20,57 @@
import javax.servlet.http.HttpServletResponse;
/**
- *
- * @author Mathias Broekelmann
+ * Represents a single resource that a component in a page needs a
+ * browser to fetch. This class helps generate the URI that is emitted
+ * into the page, and specifies the class that should be invoked to
+ * handle the request for that URI when the browser makes it.
*
+ * @author Mathias Broekelmann
*/
public interface ResourceHandler
{
/**
- * Returns the resource loader class which is used to load the resource
+ * Return a Class object whose instance can decode the url generated
+ * by this class in the getResourceUri method and use that info to
+ * locate the resource data represented by this object. When a
+ * browser requests the data in the URL generated by this class
+ * and its callers, an instance of the returned class shall be
+ * created to decode the remainder of the url and serve the
+ * resource.
*
- * @return a class which implements org.apache.myfaces.component.html.util.ResourceLoader
+ * @return a class which implements
+ * org.apache.myfaces.component.html.util.ResourceLoader
+ *
* @see ResourceLoader
*/
- Class getResourceLoaderClass();
+ public Class getResourceLoaderClass();
/**
- * Returns the uri part which is used by the resourceloader to identify the resource to load.
+ * Returns the uri part which is used by the resourceloader to
+ * identify the resource to load. This URI will be interpreted
+ * by an instance of the class returned by getResourceLoaderClass.
*
- * @param context
- * @return the returned resource uri will be passed as the resourceUri parameter for
- * the ResourceLoader.serveResource method (omitting request parameters)
* @see ResourceLoader#serveResource(HttpServletRequest, HttpServletResponse, String)
*/
- String getResourceUri(FacesContext context);
+ public String getResourceUri(FacesContext context);
/**
- * must be implemented to avoid loading the same resource multiple times.
+ * Must always be implemented when equals is overridden.
*
* @see java.lang.Object#hashCode()
*/
public int hashCode();
/**
- * must be implemented to avoid loading the same resource multiple times.
+ * Must be implemented to avoid loading the same resource multiple times.
+ * <p>
+ * When the same component is used multiple times in a page and that
+ * component needs an external resource such as a script, multiple calls
+ * will be made to the AddResource methods for the same resource. The
+ * AddResource class will create an instance of this class for each such
+ * call. However if there is already a ResourceHandler instance existing
+ * which is "equal" to the newly created one then a duplicate will not
+ * be queued for output.
*
* @see java.lang.Object#equals(java.lang.Object)
*/
Modified: myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/ResourceLoader.java
URL: http://svn.apache.org/viewcvs/myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/ResourceLoader.java?rev=332518&r1=332517&r2=332518&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/ResourceLoader.java (original)
+++ myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/util/ResourceLoader.java Fri Nov 11 03:14:05 2005
@@ -22,6 +22,9 @@
import javax.servlet.http.HttpServletResponse;
/**
+ * A class which can interpret the URI generated by a corresponding ResourceHandler
+ * implementation, locate that resource and write it to the servlet response stream.
+ *
* @author Mathias Broekelmann (latest modification by $Author$)
* @version $Revision$ $Date$
*/
@@ -37,5 +40,6 @@
*
* @throws IOException
*/
- void serveResource(HttpServletRequest request, HttpServletResponse response, String resourceUri) throws IOException;
-}
\ No newline at end of file
+ public void serveResource(HttpServletRequest request,
+ HttpServletResponse response, String resourceUri) throws IOException;
+}