You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by sk...@apache.org on 2008/06/24 13:19:44 UTC

svn commit: r671131 - in /myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet: AbstractStylesheet.java StylesheetRenderer.java TextResourceFilter.java TextResourceFilterProvider.java

Author: skitching
Date: Tue Jun 24 04:19:43 2008
New Revision: 671131

URL: http://svn.apache.org/viewvc?rev=671131&view=rev
Log:
TOMAHAWK-1266. Report an exception if an absolute path is used together with "inline" or "filtered" options.
Also add more documentation.

Modified:
    myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/AbstractStylesheet.java
    myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/StylesheetRenderer.java
    myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/TextResourceFilter.java
    myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/TextResourceFilterProvider.java

Modified: myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/AbstractStylesheet.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/AbstractStylesheet.java?rev=671131&r1=671130&r2=671131&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/AbstractStylesheet.java (original)
+++ myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/AbstractStylesheet.java Tue Jun 24 04:19:43 2008
@@ -44,6 +44,16 @@
 
     /**
      * URL for CSS-file.
+     * <p>
+     * If this path starts with a slash, then the webapp context path will be prepended to it.
+     * This makes it simple to reference stylesheets at an absolute path within the webapp.
+     * A value like "/styles/style.css" becomes "/webappname/styles/style.css".
+     * <p>
+     * If this path does not start with a slash, then it is output unaltered. This supports
+     * absolute urls ("http://host/path/style.css"). It also supports having css files relative
+     * to the current page ("style.css" or "styles/style.css") but this needs to be used with
+     * care as the standard JSF postback/internal-forward navigation style can cause browsers
+     * to use an inappropriate base url when resolving relative references. 
      * 
      * @JSFProperty
      *   required="true"
@@ -51,23 +61,29 @@
     public abstract String getPath();
 
     /**
-     * Inline the stylesheet file content as in contrast to referencing it as .
+     * Inline the stylesheet file content as in contrast to referencing it as a link.
+     * <p>
+     * The file referenced by the path attribute is loaded, and its content is written
+     * to the page wrapped in an &lt;script&gt; tag.
+     * <p>
+     * When this option is enabled, the path property must contain an absolute path
+     * within the current webapp. External urls ("http://*") and paths relative to the
+     * current page are not supported.
      * 
      * @JSFProperty
      *   defaultValue = "false"
-     * @return true if the styles are inlined to the jsp file
      */
     public abstract boolean isInline();
 
     /**
-     * true|false. When true, any EL expression in the stylesheet will 
-     * be evaluated and replaced by its string representation on the 
-     * first access. The stylesheet will be processed only once. 
-     * Every subsequent request will get a cached view.
+     * Cause EL expressions in the stylesheet to be evaluated.
+     * <p>
+     * When true, any EL expression in the stylesheet will be evaluated and replaced
+     * by its string representation on the first access. The stylesheet will be
+     * processed only once. Every subsequent request will get a cached view.
      * 
      * @JSFProperty
      *   defaultValue = "false"
-     * @return true if the stylesheet should be filtered before sending to the browser
      */
     public abstract boolean isFiltered();
 

Modified: myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/StylesheetRenderer.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/StylesheetRenderer.java?rev=671131&r1=671130&r2=671131&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/StylesheetRenderer.java (original)
+++ myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/StylesheetRenderer.java Tue Jun 24 04:19:43 2008
@@ -20,6 +20,7 @@
 
 import java.io.IOException;
 
+import javax.faces.FacesException;
 import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
 import javax.faces.context.ResponseWriter;
@@ -41,7 +42,7 @@
 public class StylesheetRenderer extends HtmlRenderer
 {
     public void encodeEnd(FacesContext context, UIComponent component)
-        throws IOException
+        throws IOException, FacesException
     {
 
         if ((context == null) || (component == null))
@@ -50,43 +51,41 @@
         }
         Stylesheet stylesheet = (Stylesheet) component;
         ResponseWriter writer = context.getResponseWriter();
-        
-        //A path starting with / or not is the same for this component,
-        //because ctx.getExternalContext().getResourceAsStream(file);
-        //or ServletContext.getResourceAsStream() specifies that
-        //ALL resources must start with '/'
-        String path = stylesheet.getPath(); 
-        if (path.startsWith("/"))
-        {
-            path = path.substring(1);
-        }    
+
+        String path = stylesheet.getPath();
 
         if (stylesheet.isInline())
         {
             //include as inline css
+            
+            if (!path.startsWith("/"))
+            {
+                throw new FacesException("Inline stylesheets require absolute resource path");
+            }
+
             writer.startElement("style", component);
             writer.writeAttribute("type", "text/css", null);
             if (stylesheet.getMedia() != null)
             {
                 writer.writeAttribute("media", stylesheet.getMedia(), null);
             }
-            //writer.writeText("<!--\n", null);
 
             Object text;
             if (stylesheet.isFiltered())
             {
+                // Load, filter and cache the resource. Then return the cached data.
                 ResourceInfo info = TextResourceFilter.getInstance(context).getOrCreateFilteredResource(context, path); 
                 text = info.getText();
             }
             else
             {
-                text = RendererUtils.loadResourceFile(context,'/'+ path);
+                // Just load the data (not cached)
+                text = RendererUtils.loadResourceFile(context, path);
             }
             if (text != null)
             {
                 writer.writeText(text, null);
             }
-            //writer.writeText("\n-->", null);
             writer.endElement("style");
         }
         else
@@ -103,8 +102,32 @@
             String stylesheetPath;
             if (stylesheet.isFiltered())
             {
+                if (!path.startsWith("/"))
+                {
+                    throw new FacesException("Filtered stylesheets require absolute resource path");
+                }
+
+                // Load, filter and cache the resource
                 TextResourceFilter.getInstance(context).getOrCreateFilteredResource(context, path);
-                stylesheetPath = AddResourceFactory.getInstance(context).getResourceUri(context, TextResourceFilterProvider.class, path, true);
+                
+                // Compute a URL that goes via the tomahawk ExtensionsFilter and the
+                // TextResourceFilterProvider to fetch the resource from the cache.
+                //
+                // Unfortunately the getResourceUri(context, Class, String, bool) api below is
+                // really meant for serving resources out of the Tomahawk jarfile, relative to
+                // some class that the resource belongs to. So it only expects to receive
+                // relative paths. We are abusing it here to serve resources out of the
+                // webapp, specified by an absolute path. So here, the leading slash is
+                // stripped off and in the TextResourceFilterProvider a matching hack
+                // puts it back on again. A better solution would be to write a custom
+                // ResourceHandler class and pass that to the getResourceUri method...
+                // TODO: fixme
+                String nastyPathHack = path.substring(1);
+                stylesheetPath = AddResourceFactory.getInstance(context).getResourceUri(
+                        context,
+                        TextResourceFilterProvider.class,
+                        nastyPathHack,
+                        true);
             }
             else
             {

Modified: myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/TextResourceFilter.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/TextResourceFilter.java?rev=671131&r1=671130&r2=671131&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/TextResourceFilter.java (original)
+++ myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/TextResourceFilter.java Tue Jun 24 04:19:43 2008
@@ -37,7 +37,9 @@
 /**
  * Loads, filters and then caches any resource available to the webapp.
  * <p>
- * The resource can then be retrieved from the cache when desired.
+ * The resource can then be retrieved from the cache when desired. In
+ * particular, it can be retrieved via a URL that invokes the Tomahawk
+ * ExtensionsFilter and TextResourceFilterProvider classes.
  * <p>
  * The "filtering" process looks for any strings of form #{...} in the
  * resource, and executes it as an EL expression. The original expression
@@ -139,6 +141,9 @@
      * <p>
      * If the resource is not already in the cache (due to an earlier call to
      * getOrCreateFilteredResource) then null is returned.
+     * <p>
+     * The path param is a simple key that must match the value passed to
+     * an earlier call to getOrCreateFilteredResource.
      */
     public ResourceInfo getFilteredResource(String path)
     {
@@ -155,14 +160,16 @@
      * <p>
      * Note: This method is not synchronized for performance reasons (the map is).
      * The worst case is that we filter a resource twice the first time which is not
-     * a problem
+     * a problem.
+     * <P>
+     * The path param must start with a slash, and is interpreted as a path relative
+     * to the webapp root (not the current page).
      */
     public ResourceInfo getOrCreateFilteredResource(FacesContext context, String path) throws IOException
     {
-        if (path.startsWith("/"))
+        if (!path.startsWith("/"))
         {
-            // the resource loader do not use the leading "/", so strip it here
-            path = path.substring(1);
+            throw new IllegalArgumentException("Path must start with a slash, but was: " + path);
         }
 
         ResourceInfo filteredResource = getFilteredResource(path);
@@ -174,7 +181,7 @@
         //Tomcat ASF Bugzilla � Bug 43241
         //ServletContext.getResourceAsStream() does not follow API spec
         //ALL resources must start with '/' 
-        String text = RendererUtils.loadResourceFile(context,'/' + path);
+        String text = RendererUtils.loadResourceFile(context, path);
         if (text == null)
         {
             // avoid loading the errorneous resource over and over again

Modified: myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/TextResourceFilterProvider.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/TextResourceFilterProvider.java?rev=671131&r1=671130&r2=671131&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/TextResourceFilterProvider.java (original)
+++ myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/custom/stylesheet/TextResourceFilterProvider.java Tue Jun 24 04:19:43 2008
@@ -37,23 +37,31 @@
  */
 public class TextResourceFilterProvider implements ResourceProvider
 {
+    // Hack note: a slash has to be prefixed to the resource value here because
+    // we are abusing the AddResource API in StylesheetRenderer; see comments
+    // in StylesheetRenderer.encodeEnd for details.
+
     public boolean exists(ServletContext context, String resource)
     {
+        resource = "/" + resource; // hack
         return TextResourceFilter.getInstance(context).getFilteredResource(resource) != null;
     }
 
     public int getContentLength(ServletContext context, String resource) throws IOException
     {
+        resource = "/" + resource; // hack
         return TextResourceFilter.getInstance(context).getFilteredResource(resource).getSize();
     }
 
     public long getLastModified(ServletContext context, String resource) throws IOException
     {
+        resource = "/" + resource; // hack
         return TextResourceFilter.getInstance(context).getFilteredResource(resource).getLastModified();
     }
 
     public InputStream getInputStream(ServletContext context, String resource) throws IOException
     {
+        resource = "/" + resource; // hack
         return new ByteArrayInputStream(
             TextResourceFilter.getInstance(context).getFilteredResource(resource).getText().getBytes(
                 getEncoding(context, resource)