myfaces-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From lu4...@apache.org
Subject svn commit: r891494 - in /myfaces/core/trunk/impl/src/main/java/org/apache/myfaces: application/DefaultResourceHandlerSupport.java application/ResourceHandlerSupport.java resource/ResourceImpl.java resource/ResourceUtils.java
Date Thu, 17 Dec 2009 00:42:20 GMT
Author: lu4242
Date: Thu Dec 17 00:42:18 2009
New Revision: 891494

URL: http://svn.apache.org/viewvc?rev=891494&view=rev
Log:
MYFACES-2460 Add Resource Headers and allow EL Expressions only on css files

Added:
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/resource/ResourceUtils.java
Modified:
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/DefaultResourceHandlerSupport.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/ResourceHandlerSupport.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/resource/ResourceImpl.java

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/DefaultResourceHandlerSupport.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/DefaultResourceHandlerSupport.java?rev=891494&r1=891493&r2=891494&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/DefaultResourceHandlerSupport.java
(original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/DefaultResourceHandlerSupport.java
Thu Dec 17 00:42:18 2009
@@ -24,6 +24,7 @@
 import javax.faces.context.FacesContext;
 
 import org.apache.myfaces.application.DefaultViewHandlerSupport.FacesServletMapping;
+import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
 import org.apache.myfaces.resource.ClassLoaderResourceLoader;
 import org.apache.myfaces.resource.ExternalContextResourceLoader;
 import org.apache.myfaces.resource.ResourceLoader;
@@ -37,7 +38,14 @@
  */
 public class DefaultResourceHandlerSupport implements ResourceHandlerSupport
 {
-    
+
+    /**
+     * Set the max time in miliseconds set on the "Expires" header for a resource.
+     * (default to one week in miliseconds or 604800000) 
+     */
+    @JSFWebConfigParam(since="2.0", defaultValue="604800000")
+    public static final String RESOURCE_MAX_TIME_EXPIRES = "org.apache.myfaces.RESOURCE_MAX_TIME_EXPIRES";
+
     /**
      * Identifies the FacesServlet mapping in the current request map.
      */
@@ -45,7 +53,16 @@
         DefaultResourceHandlerSupport.class.getName() + ".CACHED_SERVLET_MAPPING";
     
     private ResourceLoader[] _resourceLoaders;
+    
+    private Long _startupTime;
+    
+    private Long _maxTimeExpires;
         
+    public DefaultResourceHandlerSupport()
+    {
+        _startupTime = System.currentTimeMillis();
+    }
+
     public String calculateResourceBasePath(FacesContext facesContext)
     {        
         FacesServletMapping mapping = getFacesServletMapping(facesContext);
@@ -208,4 +225,33 @@
             }
         }
     }
+
+    public long getStartupTime()
+    {
+        return _startupTime;
+    }
+    
+    public long getMaxTimeExpires()
+    {
+        if (_maxTimeExpires == null)
+        {
+            String time = FacesContext.getCurrentInstance().getExternalContext().getInitParameter(RESOURCE_MAX_TIME_EXPIRES);
+            if (time != null && time.length() > 0)
+            {
+                try
+                {
+                    _maxTimeExpires = Long.parseLong(time);
+                }
+                catch (NumberFormatException e)
+                {
+                    _maxTimeExpires = 604800000L;
+                }
+            }
+            else
+            {
+                _maxTimeExpires = 604800000L;
+            }
+        }
+        return _maxTimeExpires;
+    }
 }

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/ResourceHandlerSupport.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/ResourceHandlerSupport.java?rev=891494&r1=891493&r2=891494&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/ResourceHandlerSupport.java
(original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/ResourceHandlerSupport.java
Thu Dec 17 00:42:18 2009
@@ -71,4 +71,18 @@
      */
     String getMapping();
     
+    /**
+     * Return the time when the app started. This is useful to set the
+     * "Last-Modified" header in some specific cases.
+     * 
+     * @return
+     */
+    long getStartupTime();
+    
+    /**
+     * Return the time that should be set on "Expires" header in a resource.
+     * 
+     * @return
+     */
+    long getMaxTimeExpires();
 }

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/resource/ResourceImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/resource/ResourceImpl.java?rev=891494&r1=891493&r2=891494&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/resource/ResourceImpl.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/resource/ResourceImpl.java Thu
Dec 17 00:42:18 2009
@@ -22,13 +22,21 @@
 import java.io.InputStream;
 import java.io.PushbackInputStream;
 import java.net.URL;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
+import java.util.TimeZone;
 
 import javax.el.ELContext;
 import javax.el.ValueExpression;
+import javax.faces.application.ProjectStage;
 import javax.faces.application.Resource;
 import javax.faces.application.ResourceHandler;
 import javax.faces.context.FacesContext;
@@ -82,9 +90,9 @@
     {
         String contentType = getContentType();
 
-        return ("text/css".equals(contentType) || 
+        return ("text/css".equals(contentType)/* || 
             "text/javascript".equals(contentType) || 
-            "application/x-javascript".equals(contentType) );
+            "application/x-javascript".equals(contentType)*/ );
     }
 
     private class ValueExpressionFilterInputStream extends InputStream
@@ -211,9 +219,44 @@
     @Override
     public Map<String, String> getResponseHeaders()
     {
-        // TODO: Read the HTTP documentation to see how we can enhance this
-        //part. For now, use always an empty map. 
-        return Collections.emptyMap();
+        FacesContext facesContext = FacesContext.getCurrentInstance();
+        
+        if (facesContext.getApplication().getResourceHandler().isResourceRequest(facesContext))
+        {
+            Map<String, String> headers = new HashMap<String, String>();
+            
+            long lastModified;
+            try
+            {
+                lastModified = ResourceUtils.getResourceLastModified(this.getURL());
+            }
+            catch (IOException e)
+            {
+                lastModified = -1;
+            }
+            
+            // Here we have two cases: If the file could contain EL Expressions
+            // the last modified time is the greatest value between application startup and
+            // the value from file.
+            if (this.couldResourceContainValueExpressions() &&
+                    lastModified < _resourceHandlerSupport.getStartupTime())
+            {
+                lastModified = _resourceHandlerSupport.getStartupTime();
+            }
+
+            if (lastModified >= 0)
+            {
+                headers.put("Last-Modified", ResourceUtils.formatDateHeader(lastModified));
+                headers.put("Expires", ResourceUtils.formatDateHeader(System.currentTimeMillis()+_resourceHandlerSupport.getMaxTimeExpires()));
+            }
+            
+            return headers;
+        }
+        else
+        {
+            //No need to return headers 
+            return Collections.emptyMap();
+        }
     }
 
     @Override
@@ -225,8 +268,61 @@
     @Override
     public boolean userAgentNeedsUpdate(FacesContext context)
     {
-        // TODO: When and How we can return safely false?
+        // RFC2616 says related to If-Modified-Since header the following:
+        //
+        // "... The If-Modified-Since request-header field is used with a method to 
+        // make it conditional: if the requested variant has not been modified since 
+        // the time specified in this field, an entity will not be returned from 
+        // the server; instead, a 304 (not modified) response will be returned 
+        // without any message-body..."
+        // 
+        // This method is called from ResourceHandlerImpl.handleResourceRequest and if
+        // returns false send a 304 Not Modified response.
+        
+        String ifModifiedSinceString = context.getExternalContext().getRequestHeaderMap().get("If-Modified-Since");
+        
+        if (ifModifiedSinceString == null)
+        {
+            return true;
+        }
+        
+        Long ifModifiedSince = ResourceUtils.parseDateHeader(ifModifiedSinceString);
+        
+        if (ifModifiedSince == null)
+        {
+            return true;
+        }
+        
+        Long lastModified;
+        try
+        {
+            lastModified = ResourceUtils.getResourceLastModified(this.getURL());
+        }
+        catch (IOException exception)
+        {
+            lastModified = -1L;
+        }
+        
+        if (lastModified >= 0)
+        {
+            if (this.couldResourceContainValueExpressions() &&
+                    lastModified < _resourceHandlerSupport.getStartupTime())
+            {
+                lastModified = _resourceHandlerSupport.getStartupTime();
+            }
+            
+            // If the lastModified date is lower or equal than ifModifiedSince,
+            // the agent does not need to update.
+            // Note the lastModified time is set at milisecond precision, but when 
+            // the date is parsed and sent on ifModifiedSince, the exceding miliseconds
+            // are trimmed. So, we have to compare trimming this from the calculated
+            // lastModified time.
+            if ( (lastModified-(lastModified % 1000)) <= ifModifiedSince)
+            {
+                return false;
+            }
+        }
+        
         return true;
     }
-
 }

Added: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/resource/ResourceUtils.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/resource/ResourceUtils.java?rev=891494&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/resource/ResourceUtils.java (added)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/resource/ResourceUtils.java Thu
Dec 17 00:42:18 2009
@@ -0,0 +1,137 @@
+/*
+ * 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.resource;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class ResourceUtils
+{
+    // TODO: In tomcat and jetty it is implemented a Flyweight pattern when converting
+    // date headers. For now it is better keep this stuff simple.
+    private static final String HTTP_RESPONSE_DATE_HEADER =
+        "EEE, dd MMM yyyy HH:mm:ss zzz";
+    
+    private static final String[] HTTP_REQUEST_DATE_HEADER = {
+            "EEE, dd MMM yyyy HH:mm:ss zzz", "EEEEEE, dd-MMM-yy HH:mm:ss zzz",
+            "EEE MMMM d HH:mm:ss yyyy" };
+    
+    private static TimeZone __GMT = TimeZone.getTimeZone("GMT");
+
+    public static String formatDateHeader(long value)
+    {
+        SimpleDateFormat format = new SimpleDateFormat(
+                HTTP_RESPONSE_DATE_HEADER,
+                Locale.US);
+        format.setTimeZone(__GMT);
+        return format.format(new Date(value));
+    }
+    
+    public static Long parseDateHeader(String value)
+    {
+        Date date = null;
+        for (int i = 0; (date == null) && (i < HTTP_REQUEST_DATE_HEADER.length);
i++)
+        {
+            try
+            {
+                SimpleDateFormat format = new SimpleDateFormat(
+                        HTTP_REQUEST_DATE_HEADER[i], Locale.US);
+                format.setTimeZone(__GMT);
+                date = format.parse(value);
+            }
+            catch (ParseException e)
+            {
+                ;
+            }
+        }
+        if (date == null)
+        {
+            return null;
+        }
+        return new Long(date.getTime());
+    }
+    
+    //Taken from trinidad URLUtils
+    public static long getResourceLastModified(URL url) throws IOException
+    {
+        if ("file".equals(url.getProtocol()))
+        {
+            String externalForm = url.toExternalForm();
+            // Remove the "file:"
+            File file = new File(externalForm.substring(5));
+
+            return file.lastModified();
+        }
+        else
+        {
+            return getResourceLastModified(url.openConnection());
+        }
+    }
+
+    //Taken from trinidad URLUtils
+    public static long getResourceLastModified(URLConnection connection) throws IOException
+    {
+        long modified;
+        if (connection instanceof JarURLConnection)
+        {
+            // The following hack is required to work-around a JDK bug.
+            // getLastModified() on a JAR entry URL delegates to the actual JAR file
+            // rather than the JAR entry.
+            // This opens internally, and does not close, an input stream to the JAR
+            // file.
+            // In turn, you cannot close it by yourself, because it's internal.
+            // The work-around is to get the modification date of the JAR file
+            // manually,
+            // and then close that connection again.
+
+            URL jarFileUrl = ((JarURLConnection) connection).getJarFileURL();
+            URLConnection jarFileConnection = jarFileUrl.openConnection();
+
+            try
+            {
+                modified = jarFileConnection.getLastModified();
+            }
+            finally
+            {
+                try
+                {
+                    jarFileConnection.getInputStream().close();
+                }
+                catch (Exception exception)
+                {
+                    // Ignored
+                }
+            }
+        }
+        else
+        {
+            modified = connection.getLastModified();
+        }
+
+        return modified;
+    }
+}



Mime
View raw message