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;
+}