[forge/core] 1a7ed2: [maven-release-plugin] prepare release 1.0.0.Beta1
by noreply@github.com
Branch: refs/heads/master
Home: https://github.com/forge/core
Commit: 1a7ed2021e7246ab3479f25286213a43e47decfc
https://github.com/forge/core/commit/1a7ed2021e7246ab3479f25286213a43e47d...
Author: Lincoln Baxter, III <lincolnbaxter(a)gmail.com>
Date: 2011-08-11 (Thu, 11 Aug 2011)
Changed paths:
M dev-plugins/pom.xml
M event-bus-api/pom.xml
M git-tools/pom.xml
M javaee-api/pom.xml
M javaee-impl/pom.xml
M maven-api/pom.xml
M modules-build/pom.xml
M parser-java-api/pom.xml
M parser-java/pom.xml
M parser-xml/pom.xml
M plugin-loader/pom.xml
M pom.xml
M project-model-maven-tests/pom.xml
M project-model-maven/pom.xml
M scaffold-api/pom.xml
M scaffold-metawidget/pom.xml
M scaffold-plugins/pom.xml
M shell-api/pom.xml
M shell/pom.xml
M test-harness/pom.xml
Log Message:
-----------
[maven-release-plugin] prepare release 1.0.0.Beta1
13 years, 3 months
[forge/core] f7d5a7: [maven-release-plugin] prepare for next developmen...
by noreply@github.com
Branch: refs/heads/master
Home: https://github.com/forge/core
Commit: f7d5a77874d05557fc3814772ba4fb7b1c00c6b7
https://github.com/forge/core/commit/f7d5a77874d05557fc3814772ba4fb7b1c00...
Author: Lincoln Baxter, III <lincolnbaxter(a)gmail.com>
Date: 2011-08-11 (Thu, 11 Aug 2011)
Changed paths:
M dev-plugins/pom.xml
M event-bus-api/pom.xml
M git-tools/pom.xml
M javaee-api/pom.xml
M javaee-impl/pom.xml
M maven-api/pom.xml
M modules-build/pom.xml
M parser-java-api/pom.xml
M parser-java/pom.xml
M parser-xml/pom.xml
M plugin-loader/pom.xml
M pom.xml
M project-model-maven-tests/pom.xml
M project-model-maven/pom.xml
M scaffold-api/pom.xml
M scaffold-metawidget/pom.xml
M scaffold-plugins/pom.xml
M shell-api/pom.xml
M shell/pom.xml
M test-harness/pom.xml
Log Message:
-----------
[maven-release-plugin] prepare for next development iteration
13 years, 3 months
[forge/core] 146c3d: [maven-release-plugin] prepare release 1.0.0.Beta1
by noreply@github.com
Branch: refs/heads/master
Home: https://github.com/forge/core
Commit: 146c3dca8848a3c81c3f18d4a6acaa784868d660
https://github.com/forge/core/commit/146c3dca8848a3c81c3f18d4a6acaa784868...
Author: Lincoln Baxter, III <lincolnbaxter(a)gmail.com>
Date: 2011-08-11 (Thu, 11 Aug 2011)
Changed paths:
M dev-plugins/pom.xml
M event-bus-api/pom.xml
M git-tools/pom.xml
M javaee-api/pom.xml
M javaee-impl/pom.xml
M maven-api/pom.xml
M modules-build/pom.xml
M parser-java-api/pom.xml
M parser-java/pom.xml
M parser-xml/pom.xml
M plugin-loader/pom.xml
M pom.xml
M project-model-maven-tests/pom.xml
M project-model-maven/pom.xml
M scaffold-api/pom.xml
M scaffold-metawidget/pom.xml
M scaffold-plugins/pom.xml
M shell-api/pom.xml
M shell/pom.xml
M test-harness/pom.xml
Log Message:
-----------
[maven-release-plugin] prepare release 1.0.0.Beta1
13 years, 3 months
[forge/core] 69fc0e: [maven-release-plugin] prepare for next developmen...
by noreply@github.com
Branch: refs/heads/master
Home: https://github.com/forge/core
Commit: 69fc0ebc5b145da4cfcefd3df1e9fa9c861dea9b
https://github.com/forge/core/commit/69fc0ebc5b145da4cfcefd3df1e9fa9c861d...
Author: Lincoln Baxter, III <lincolnbaxter(a)gmail.com>
Date: 2011-08-11 (Thu, 11 Aug 2011)
Changed paths:
M dev-plugins/pom.xml
M event-bus-api/pom.xml
M git-tools/pom.xml
M javaee-api/pom.xml
M javaee-impl/pom.xml
M maven-api/pom.xml
M modules-build/pom.xml
M parser-java-api/pom.xml
M parser-java/pom.xml
M parser-xml/pom.xml
M plugin-loader/pom.xml
M pom.xml
M project-model-maven-tests/pom.xml
M project-model-maven/pom.xml
M scaffold-api/pom.xml
M scaffold-metawidget/pom.xml
M scaffold-plugins/pom.xml
M shell-api/pom.xml
M shell/pom.xml
M test-harness/pom.xml
Log Message:
-----------
[maven-release-plugin] prepare for next development iteration
13 years, 3 months
[forge/core] d16a0e: [maven-release-plugin] prepare release 1.0.0.Beta1
by noreply@github.com
Branch: refs/heads/master
Home: https://github.com/forge/core
Commit: d16a0e1744e674e0fcfc3fce2267cbcebe6fea29
https://github.com/forge/core/commit/d16a0e1744e674e0fcfc3fce2267cbcebe6f...
Author: Lincoln Baxter, III <lincolnbaxter(a)gmail.com>
Date: 2011-08-11 (Thu, 11 Aug 2011)
Changed paths:
M dev-plugins/pom.xml
M event-bus-api/pom.xml
M git-tools/pom.xml
M javaee-api/pom.xml
M javaee-impl/pom.xml
M maven-api/pom.xml
M modules-build/pom.xml
M parser-java-api/pom.xml
M parser-java/pom.xml
M parser-xml/pom.xml
M plugin-loader/pom.xml
M pom.xml
M project-model-maven-tests/pom.xml
M project-model-maven/pom.xml
M scaffold-api/pom.xml
M scaffold-metawidget/pom.xml
M scaffold-plugins/pom.xml
M shell-api/pom.xml
M shell/pom.xml
M test-harness/pom.xml
Log Message:
-----------
[maven-release-plugin] prepare release 1.0.0.Beta1
13 years, 3 months
Seam SVN: r14147 - in branches/enterprise/JBPAPP_5_0: src/main/org/jboss/seam and 3 other directories.
by seam-commits@lists.jboss.org
Author: manaRH
Date: 2011-08-11 10:57:17 -0400 (Thu, 11 Aug 2011)
New Revision: 14147
Added:
branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/web/CacheControlFilter.java
branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/web/ConditionalAbstractResource.java
branches/enterprise/JBPAPP_5_0/src/test/unit/org/jboss/seam/test/unit/web/ConditionalRequestTest.java
Modified:
branches/enterprise/JBPAPP_5_0/doc/Seam_Reference_Guide/en-US/Configuration.xml
branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/web-2.2.xsd
branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/web/AbstractResource.java
branches/enterprise/JBPAPP_5_0/src/test/unit/org/jboss/seam/test/unit/testng.xml
Log:
JBPAPP-6498
Modified: branches/enterprise/JBPAPP_5_0/doc/Seam_Reference_Guide/en-US/Configuration.xml
===================================================================
--- branches/enterprise/JBPAPP_5_0/doc/Seam_Reference_Guide/en-US/Configuration.xml 2011-08-11 14:14:38 UTC (rev 14146)
+++ branches/enterprise/JBPAPP_5_0/doc/Seam_Reference_Guide/en-US/Configuration.xml 2011-08-11 14:57:17 UTC (rev 14147)
@@ -317,8 +317,51 @@
</para>
</section>
+ <section>
+ <title>Enabling HTTP cache-control headers</title>
+
+ <para>
+ Seam does
+ <emphasis>not</emphasis>
+ automatically add
+ <literal>cache-control</literal>
+ HTTP headers to
+ any resources served by the Seam resource servlet, or directly from your
+ view directory by the servlet
+ container. This means that your images, Javascript and CSS files, and resource
+ representations from
+ Seam resource servlet such as Seam Remoting Javascript interfaces are
+ usually not cached by the browser.
+ This is convenient in development but should be changed in production when
+ optimizing the application.
+ </para>
+
+ <para>
+ You can configure a Seam filter to enable automatic addition of
+ <literal>cache-control</literal>
+ headers
+ depending on the requested URI in
+ <literal>components.xml</literal>
+ :
+ </para>
+
+ <programlisting role="XML"><![CDATA[<web:cache-control-filter name="commonTypesCacheControlFilter"
+ regex-url-pattern=".*(\.gif|\.png|\.jpg|\.jpeg|\.css|\.js)"
+ value="max-age=86400"/> <!-- 1 day -->
+ <web:cache-control-filter name="anotherCacheControlFilter"
+ url-pattern="/my/cachable/resources/*"
+ value="max-age=432000"/> <!-- 5 days -->]]></programlisting>
+
+ <para>
+
+ You do not have to name the filters unless you have more than one filter
+ enabled.
+
+ </para>
+ </section>
+
<section>
- <title>Adding custom filters</title>
+ <title>Adding custom filters</title>
<para>
Seam can install your filters for you. This allows you to specify your filter's placement in the chain — the Servlet specification does not provide a well-defined order when you specify your filters in <filename>web.xml</filename>. Add a <literal>@Filter</literal> annotation to your Seam component. (Your Seam component must implement <literal>javax.servlet.Filter</literal>.)
</para>
Modified: branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/web/AbstractResource.java
===================================================================
--- branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/web/AbstractResource.java 2011-08-11 14:14:38 UTC (rev 14146)
+++ branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/web/AbstractResource.java 2011-08-11 14:57:17 UTC (rev 14147)
@@ -1,42 +1,177 @@
package org.jboss.seam.web;
-import java.io.IOException;
-
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.zip.GZIPOutputStream;
/**
- * Superclass of Seam components that serve up
- * "resources" to the client via the Seam
+ * Superclass of Seam components that serve up
+ * "resources" to the client via the Seam
* resource servlet. Note that since a filter is
* potentially called outside of a set of Seam
- * contexts, it is not a true Seam component.
+ * contexts, it is not a true Seam component.
* However, we are able to reuse the functionality
- * for component scanning, installation and
+ * for component scanning, installation and
* configuration for filters. All resources
* must extend this class.
- *
+ *
* @author Shane Bryzak
*
*/
public abstract class AbstractResource
{
private ServletContext context;
-
+
protected ServletContext getServletContext()
{
return context;
}
-
+
public void setServletContext(ServletContext context)
{
this.context = context;
}
-
+
public abstract void getResource(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException;
-
+
public abstract String getResourcePath();
+
+ protected OutputStream selectOutputStream(HttpServletRequest request, HttpServletResponse response)
+ throws IOException
+ {
+
+ String acceptEncoding = request.getHeader("Accept-Encoding");
+ String mimeType = response.getContentType();
+
+ if (isGzipEnabled()
+ && acceptEncoding != null
+ && acceptEncoding.length() > 0
+ && acceptEncoding.indexOf("gzip") > -1
+ && isCompressedMimeType(mimeType))
+ {
+ return new GZIPResponseStream(response);
+ }
+ else
+ {
+ return response.getOutputStream();
+ }
+ }
+
+ protected boolean isCompressedMimeType(String mimeType)
+ {
+ return mimeType.matches("text/.+");
+ }
+
+ protected boolean isGzipEnabled()
+ {
+ return true;
+ }
+
+ /*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * Licensed 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.
+ *
+ * @See org/springframework/js/resource/ResourceServlet.java
+ */
+ private class GZIPResponseStream extends ServletOutputStream
+ {
+
+ private ByteArrayOutputStream byteStream = null;
+
+ private GZIPOutputStream gzipStream = null;
+
+ private boolean closed = false;
+
+ private HttpServletResponse response = null;
+
+ private ServletOutputStream servletStream = null;
+
+ public GZIPResponseStream(HttpServletResponse response) throws IOException
+ {
+ super();
+ closed = false;
+ this.response = response;
+ this.servletStream = response.getOutputStream();
+ byteStream = new ByteArrayOutputStream();
+ gzipStream = new GZIPOutputStream(byteStream);
+ }
+
+ public void close() throws IOException
+ {
+ if (closed)
+ {
+ throw new IOException("This output stream has already been closed");
+ }
+ gzipStream.finish();
+
+ byte[] bytes = byteStream.toByteArray();
+
+ response.setContentLength(bytes.length);
+ response.addHeader("Content-Encoding", "gzip");
+ servletStream.write(bytes);
+ servletStream.flush();
+ servletStream.close();
+ closed = true;
+ }
+
+ public void flush() throws IOException
+ {
+ if (closed)
+ {
+ throw new IOException("Cannot flush a closed output stream");
+ }
+ gzipStream.flush();
+ }
+
+ public void write(int b) throws IOException
+ {
+ if (closed)
+ {
+ throw new IOException("Cannot write to a closed output stream");
+ }
+ gzipStream.write((byte) b);
+ }
+
+ public void write(byte b[]) throws IOException
+ {
+ write(b, 0, b.length);
+ }
+
+ public void write(byte b[], int off, int len) throws IOException
+ {
+ if (closed)
+ {
+ throw new IOException("Cannot write to a closed output stream");
+ }
+ gzipStream.write(b, off, len);
+ }
+
+ public boolean closed()
+ {
+ return (this.closed);
+ }
+
+ public void reset()
+ {
+ // noop
+ }
+ }
}
Added: branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/web/CacheControlFilter.java
===================================================================
--- branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/web/CacheControlFilter.java (rev 0)
+++ branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/web/CacheControlFilter.java 2011-08-11 14:57:17 UTC (rev 14147)
@@ -0,0 +1,64 @@
+package org.jboss.seam.web;
+
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.log.Logging;
+import org.jboss.seam.log.LogProvider;
+import org.jboss.seam.annotations.Install;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+import org.jboss.seam.annotations.web.Filter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Provides automatic addition of cache-control HTTP headers to matching resource responses.
+ *
+ * @author Christian Bauer
+ */
+(a)Scope(ScopeType.APPLICATION)
+@Name("org.jboss.seam.web.cacheControlFilter")
+@Install(value = false, precedence = Install.BUILT_IN)
+@BypassInterceptors
+@Filter(within = "org.jboss.seam.web.exceptionFilter")
+public class CacheControlFilter extends AbstractFilter
+{
+
+ private static final LogProvider log = Logging.getLogProvider(CacheControlFilter.class);
+
+ private String value;
+
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException
+ {
+
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
+
+ if (isMappedToCurrentRequestPath(request))
+ {
+ log.debug("Applying Cache-Control HTTP header for resource '"
+ + httpRequest.getRequestURI() + "': " + getValue());
+
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
+ httpResponse.setHeader("Cache-Control", getValue());
+ }
+
+ chain.doFilter(request, response);
+ }
+
+ public String getValue()
+ {
+ return value;
+ }
+
+ public void setValue(String value)
+ {
+ this.value = value;
+ }
+}
Added: branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/web/ConditionalAbstractResource.java
===================================================================
--- branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/web/ConditionalAbstractResource.java (rev 0)
+++ branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/web/ConditionalAbstractResource.java 2011-08-11 14:57:17 UTC (rev 14147)
@@ -0,0 +1,281 @@
+package org.jboss.seam.web;
+
+import org.jboss.seam.log.LogProvider;
+import org.jboss.seam.log.Logging;
+import org.jboss.seam.util.Resources;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.net.URLConnection;
+import java.net.URL;
+import java.lang.management.ManagementFactory;
+
+/**
+ * Subclass this resource if you want to be able to send the right response automatically to
+ * any conditional <tt>GET</tt> or <tt>HEAD</tt> request. The typically usecase is as follows:
+ * <p/>
+ * <pre>
+ * public class MyResource extends ConditionalAbstractResource {
+ *
+ * public void getResource(final HttpServletRequest request, final HttpServletResponse response) {
+ * String resourceVersion = ... // Calculate current state as string
+ * or
+ * byte[] resourceVersion = ... // Calculate current state as bytes
+ *
+ * String resourcePath = ... // Get the relative (to servlet) path of the requested resource
+ *
+ * if ( !sendConditional(request,
+ * response,
+ * createdEntityTag(resourceVersion, false),
+ * getLastModifiedTimestamp(resourcePath) ) {
+ *
+ * // Send the regular resource representation with 200 OK etc.
+ * }
+ * }
+ * }
+ * </pre>
+ * <p/>
+ * Note that the <tt>getLastModifiedTimestamp()</tt> method is only supplied for convenience; it may not
+ * return what you expect as the "last modification timestamp" of the given resource. In many cases you'd
+ * rather calculate that timestamp yourself.
+ * <p/>
+ *
+ * @author Christian Bauer
+ */
+public abstract class ConditionalAbstractResource extends AbstractResource
+{
+
+ public static final String HEADER_LAST_MODIFIED = "Last-Modified";
+ public static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";
+
+ public static final String HEADER_ETAG = "ETag";
+ public static final String HEADER_IF_NONE_MATCH = "If-None-Match";
+
+ private static final LogProvider log = Logging.getLogProvider(ConditionalAbstractResource.class);
+
+ /**
+ * Validates the request headers <tt>If-Modified-Since</tt> and <tt>If-None-Match</tt> to determine
+ * if a <tt>304 NOT MODIFIED</tt> response can be send. If that is the case, this method will automatically
+ * send the response and return <tt>true</tt>. If condition validation fails, it will not change the
+ * response and return <tt>false</tt>.
+ * <p/>
+ * Note that both <tt>entityTag</tt> and <tt>lastModified</tt> arguments can be <tt>null</tt>. The validation
+ * procedure and the outcome depends on what the client requested. If the client requires that both entity tags and
+ * modification timestamps be validated, both arguments must be supplied to the method and they must match, for
+ * a 304 response to be send.
+ * <p/>
+ * In addition to responding with <tt>304 NOT MODIFIED</tt> when conditions match, this method will also, if
+ * arguments are not <tt>null</tt>, send the right entity tag and last modification timestamps with the response,
+ * so that future requests from the client can be made conditional.
+ * <p/>
+ *
+ * @param request The usual HttpServletRequest for header retrieval.
+ * @param response The usual HttpServletResponse for header manipulation.
+ * @param entityTag An entity tag (weak or strong, in doublequotes), typically produced by hashing the content
+ * of the resource representation. If <tt>null</tt>, no entity tag will be send and if
+ * validation is requested by the client, no match for a NOT MODIFIED response will be possible.
+ * @param lastModified The timestamp in number of milliseconds since unix epoch when the resource was
+ * last modified. If <tt>null</tt>, no last modification timestamp will be send and if
+ * validation is requested by the client, no match for a NOT MODIFIED response will be possible.
+ * @return <tt>true</tt> if a <tt>304 NOT MODIFIED</tt> response status has been set, <tt>false</tt> if requested
+ * conditions were invalid given the current state of the resource.
+ * @throws IOException If setting the response status failed.
+ */
+ public boolean sendConditional(HttpServletRequest request,
+ HttpServletResponse response,
+ String entityTag, Long lastModified) throws IOException
+ {
+
+ String noneMatchHeader = request.getHeader(HEADER_IF_NONE_MATCH);
+ Long modifiedSinceHeader = request.getDateHeader(HEADER_IF_MODIFIED_SINCE); // Careful, returns -1 instead of null!
+
+ boolean noneMatchValid = false;
+ if (entityTag != null)
+ {
+
+ if (! (entityTag.startsWith("\"") || entityTag.startsWith("W/\"")) && !entityTag.endsWith("\""))
+ {
+ throw new IllegalArgumentException("Entity tag is not properly formatted (or quoted): " + entityTag);
+ }
+
+ // Always send an entity tag with the response
+ response.setHeader(HEADER_ETAG, entityTag);
+
+ if (noneMatchHeader != null)
+ {
+ noneMatchValid = isNoneMatchConditionValid(noneMatchHeader, entityTag);
+ }
+ }
+
+ boolean modifiedSinceValid = false;
+ if (lastModified != null)
+ {
+
+ // Always send the last modified timestamp with the response
+ response.setDateHeader(HEADER_LAST_MODIFIED, lastModified);
+
+ if (modifiedSinceHeader != -1)
+ {
+ modifiedSinceValid = isModifiedSinceConditionValid(modifiedSinceHeader, lastModified);
+ }
+
+ }
+
+ if (noneMatchHeader != null && modifiedSinceHeader != -1)
+ {
+ log.debug(HEADER_IF_NONE_MATCH + " and " + HEADER_IF_MODIFIED_SINCE + " must match");
+
+ // If both are received, we must not return 304 unless doing so is consistent with both header fields in the request!
+ if (noneMatchValid && modifiedSinceValid)
+ {
+ log.debug(HEADER_IF_NONE_MATCH + " and " + HEADER_IF_MODIFIED_SINCE + " conditions match, sending 304");
+ response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
+ return true;
+ }
+ else
+ {
+ log.debug(HEADER_IF_NONE_MATCH + " and " + HEADER_IF_MODIFIED_SINCE + " conditions do not match, not sending 304");
+ return false;
+ }
+ }
+
+ if (noneMatchHeader != null && noneMatchValid)
+ {
+ log.debug(HEADER_IF_NONE_MATCH + " condition matches, sending 304");
+ response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
+ return true;
+ }
+
+ if (modifiedSinceHeader != -1 && modifiedSinceValid)
+ {
+ log.debug(HEADER_IF_MODIFIED_SINCE + " condition matches, sending 304");
+ response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
+ return true;
+ }
+
+ log.debug("None of the cache conditions match, not sending 304");
+ return false;
+ }
+
+ protected boolean isNoneMatchConditionValid(String noneMatchHeader, String entityTag)
+ {
+ if (noneMatchHeader.trim().equals("*"))
+ {
+ log.debug("Found * conditional request, hence current entity tag matches");
+ return true;
+ }
+ String[] entityTagsArray = noneMatchHeader.trim().split(",");
+ for (String requestTag : entityTagsArray)
+ {
+ if (requestTag.trim().equals(entityTag))
+ {
+ log.debug("Found matching entity tag in request");
+ return true;
+ }
+ }
+ log.debug("Resource has different entity tag than requested");
+ return false;
+ }
+
+ protected boolean isModifiedSinceConditionValid(Long modifiedSinceHeader, Long lastModified)
+ {
+ if (lastModified <= modifiedSinceHeader)
+ {
+ log.debug("Resource has not been modified since requested timestamp");
+ return true;
+ }
+ log.debug("Resource has been modified since requested timestamp");
+ return false;
+ }
+
+ /**
+ * Tries to get last modification timestamp of the resource by obtaining
+ * a <tt>URLConnection</tt> to the file in the filesystem or JAR.
+ *
+ * @param resourcePath The relative (to the servlet) resource path.
+ * @return Either the last modified filestamp or if an error occurs, the JVM system startup timestamp.
+ */
+ protected Long getLastModifiedTimestamp(String resourcePath)
+ {
+ try
+ {
+ // Try to load it from filesystem or JAR through URLConnection
+ URL resourceURL = Resources.getResource(resourcePath, getServletContext());
+ if (resourceURL == null)
+ {
+ // Fall back to startup time of the JVM
+ return ManagementFactory.getRuntimeMXBean().getStartTime();
+ }
+ URLConnection resourceConn = resourceURL.openConnection();
+ return resourceConn.getLastModified();
+ }
+ catch (Exception ex)
+ {
+ // Fall back to startup time of the JVM
+ return ManagementFactory.getRuntimeMXBean().getStartTime();
+ }
+ }
+
+ /**
+ * Generates a (globally) unique identifier of the current state of the resource. The string will be
+ * hashed with MD5 and the hash result is then formatted before it is returned. If <tt>null</tt>,
+ * a <tt>null</tt> will be returned.
+ *
+ * @param hashSource The string source for hashing or the already hashed (strong or weak) entity tag.
+ * @param weak Set to <tt>true</tt> if you want a weak entity tag.
+ * @return The hashed and formatted entity tag result.
+ */
+ protected String createEntityTag(String hashSource, boolean weak)
+ {
+ if (hashSource == null) return null;
+ return (weak ? "W/\"" : "\"") + hash(hashSource, "UTF-8", "MD5") + "\"";
+ }
+
+ /**
+ * Generates a (globally) unique identifier of the current state of the resource. The bytes will be
+ * hashed with MD5 and the hash result is then formatted before it is returned. If <tt>null</tt>,
+ * a <tt>null</tt> will be returned.
+ *
+ * @param hashSource The string source for hashing.
+ * @param weak Set to <tt>true</tt> if you want a weak entity tag.
+ * @return The hashed and formatted entity tag result.
+ */
+ protected String createEntityTag(byte[] hashSource, boolean weak)
+ {
+ if (hashSource == null) return null;
+ return (weak ? "W/\"" : "\"") + hash(hashSource, "MD5") + "\"";
+ }
+
+ protected String hash(String text, String charset, String algorithm)
+ {
+ try
+ {
+ return hash(text.getBytes(charset), algorithm);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected String hash(byte[] bytes, String algorithm)
+ {
+ try
+ {
+ MessageDigest md = MessageDigest.getInstance(algorithm);
+ md.update(bytes);
+ BigInteger number = new BigInteger(1, md.digest());
+ StringBuffer sb = new StringBuffer("0");
+ sb.append(number.toString(16));
+ return sb.toString();
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
Modified: branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/web-2.2.xsd
===================================================================
--- branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/web-2.2.xsd 2011-08-11 14:14:38 UTC (rev 14146)
+++ branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/web-2.2.xsd 2011-08-11 14:57:17 UTC (rev 14147)
@@ -185,4 +185,15 @@
</xs:attribute>
</xs:attributeGroup>
+ <xs:element name="cache-control-filter">
+ <xs:annotation>
+ <xs:documentation>Sets the HTTP Cache-Control header</xs:documentation>
+ </xs:annotation>
+ <xs:complexType mixed="true">
+ <xs:attributeGroup ref="components:attlist.component"/>
+ <xs:attributeGroup ref="web:attlist.filter"/>
+ <xs:attribute name="value" type="components:string"/>
+ </xs:complexType>
+ </xs:element>
+
</xs:schema>
Modified: branches/enterprise/JBPAPP_5_0/src/test/unit/org/jboss/seam/test/unit/testng.xml
===================================================================
--- branches/enterprise/JBPAPP_5_0/src/test/unit/org/jboss/seam/test/unit/testng.xml 2011-08-11 14:14:38 UTC (rev 14146)
+++ branches/enterprise/JBPAPP_5_0/src/test/unit/org/jboss/seam/test/unit/testng.xml 2011-08-11 14:57:17 UTC (rev 14147)
@@ -72,6 +72,7 @@
<test name="Seam Unit Tests - Resources and i8ln">
<classes>
<class name="org.jboss.seam.test.unit.InterpolatorTest"/>
+ <class name="org.jboss.seam.test.unit.web.ConditionalRequestTest" />
</classes>
</test>
Added: branches/enterprise/JBPAPP_5_0/src/test/unit/org/jboss/seam/test/unit/web/ConditionalRequestTest.java
===================================================================
--- branches/enterprise/JBPAPP_5_0/src/test/unit/org/jboss/seam/test/unit/web/ConditionalRequestTest.java (rev 0)
+++ branches/enterprise/JBPAPP_5_0/src/test/unit/org/jboss/seam/test/unit/web/ConditionalRequestTest.java 2011-08-11 14:57:17 UTC (rev 14147)
@@ -0,0 +1,228 @@
+package org.jboss.seam.test.unit.web;
+
+import org.testng.annotations.Test;
+import org.testng.Assert;
+import static org.testng.Assert.assertEquals;
+import org.jboss.seam.mock.MockHttpSession;
+import org.jboss.seam.mock.EnhancedMockHttpServletRequest;
+import org.jboss.seam.mock.EnhancedMockHttpServletResponse;
+import org.jboss.seam.web.ConditionalAbstractResource;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletException;
+import java.io.IOException;
+import java.util.Date;
+
+/**
+ * @author Christian Bauer
+ *
+ */
+public class ConditionalRequestTest
+{
+ @Test
+ public void testNotModifiedOnlyETag() throws Exception
+ {
+
+ HttpSession session = new MockHttpSession();
+ EnhancedMockHttpServletRequest request = new EnhancedMockHttpServletRequest(session);
+ EnhancedMockHttpServletResponse response = new EnhancedMockHttpServletResponse();
+
+ request.addHeader(ConditionalAbstractResource.HEADER_IF_NONE_MATCH, "\"1234\", \"5678\"");
+
+ ConditionalAbstractResource resource = new ConditionalAbstractResource()
+ {
+ public void getResource(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ if (!sendConditional(request, response, "\"5678\"", null))
+ {
+ response.sendError(HttpServletResponse.SC_OK);
+ }
+ }
+
+ public String getResourcePath()
+ {
+ return null;
+ }
+ };
+
+ resource.getResource(request, response);
+
+ assertEquals(response.getStatus(), HttpServletResponse.SC_NOT_MODIFIED);
+ assertEquals(response.getHeader(ConditionalAbstractResource.HEADER_ETAG), "\"5678\"");
+
+ }
+
+ @Test
+ public void testModifiedOnlyETag() throws Exception
+ {
+
+ HttpSession session = new MockHttpSession();
+ EnhancedMockHttpServletRequest request = new EnhancedMockHttpServletRequest(session);
+ EnhancedMockHttpServletResponse response = new EnhancedMockHttpServletResponse();
+
+ request.addHeader(ConditionalAbstractResource.HEADER_IF_NONE_MATCH, "\"123\", \"456\"");
+
+ ConditionalAbstractResource resource = new ConditionalAbstractResource()
+ {
+ public void getResource(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ if (!sendConditional(request, response, "\"5678\"", null))
+ {
+ response.sendError(HttpServletResponse.SC_OK);
+ }
+ }
+
+ public String getResourcePath()
+ {
+ return null;
+ }
+ };
+
+ resource.getResource(request, response);
+
+ assertEquals(response.getStatus(), HttpServletResponse.SC_OK);
+ assertEquals(response.getHeader(ConditionalAbstractResource.HEADER_ETAG), "\"5678\"");
+ }
+
+ @Test
+ public void testNotModifiedOnlyLastModified() throws Exception
+ {
+
+ HttpSession session = new MockHttpSession();
+ EnhancedMockHttpServletRequest request = new EnhancedMockHttpServletRequest(session);
+ EnhancedMockHttpServletResponse response = new EnhancedMockHttpServletResponse();
+
+ final Long currentTime = new Date().getTime();
+ request.addHeader(ConditionalAbstractResource.HEADER_IF_MODIFIED_SINCE, currentTime);
+
+ ConditionalAbstractResource resource = new ConditionalAbstractResource()
+ {
+ public void getResource(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ if (!sendConditional(request, response, null, currentTime))
+ {
+ response.sendError(HttpServletResponse.SC_OK);
+ }
+ }
+
+ public String getResourcePath()
+ {
+ return null;
+ }
+ };
+
+ resource.getResource(request, response);
+
+ assertEquals(response.getStatus(), HttpServletResponse.SC_NOT_MODIFIED);
+ assertEquals(response.getHeader(ConditionalAbstractResource.HEADER_LAST_MODIFIED), currentTime);
+
+ }
+
+ @Test
+ public void testModifiedOnlyLastModified() throws Exception
+ {
+
+ HttpSession session = new MockHttpSession();
+ EnhancedMockHttpServletRequest request = new EnhancedMockHttpServletRequest(session);
+ EnhancedMockHttpServletResponse response = new EnhancedMockHttpServletResponse();
+
+ final Long currentTime = new Date().getTime();
+ request.addHeader(ConditionalAbstractResource.HEADER_IF_MODIFIED_SINCE, currentTime);
+
+ ConditionalAbstractResource resource = new ConditionalAbstractResource()
+ {
+ public void getResource(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ if (!sendConditional(request, response, null, currentTime + 5000))
+ {
+ response.sendError(HttpServletResponse.SC_OK);
+ }
+ }
+
+ public String getResourcePath()
+ {
+ return null;
+ }
+ };
+
+ resource.getResource(request, response);
+
+ assertEquals(response.getStatus(), HttpServletResponse.SC_OK);
+ assertEquals(response.getHeader(ConditionalAbstractResource.HEADER_LAST_MODIFIED), currentTime + 5000);
+
+ }
+
+ @Test
+ public void testNotModifiedETagLastModified() throws Exception
+ {
+
+ HttpSession session = new MockHttpSession();
+ EnhancedMockHttpServletRequest request = new EnhancedMockHttpServletRequest(session);
+ EnhancedMockHttpServletResponse response = new EnhancedMockHttpServletResponse();
+
+ final Long currentTime = new Date().getTime();
+ request.addHeader(ConditionalAbstractResource.HEADER_IF_MODIFIED_SINCE, currentTime);
+ request.addHeader(ConditionalAbstractResource.HEADER_IF_NONE_MATCH, "\"1234\", \"5678\"");
+
+ ConditionalAbstractResource resource = new ConditionalAbstractResource()
+ {
+ public void getResource(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ if (!sendConditional(request, response, "\"5678\"", currentTime))
+ {
+ response.sendError(HttpServletResponse.SC_OK);
+ }
+ }
+
+ public String getResourcePath()
+ {
+ return null;
+ }
+ };
+
+ resource.getResource(request, response);
+
+ assertEquals(response.getStatus(), HttpServletResponse.SC_NOT_MODIFIED);
+ assertEquals(response.getHeader(ConditionalAbstractResource.HEADER_LAST_MODIFIED), currentTime);
+ assertEquals(response.getHeader(ConditionalAbstractResource.HEADER_ETAG), "\"5678\"");
+
+ }
+
+ @Test
+ public void testModifiedETagLastModified() throws Exception
+ {
+
+ HttpSession session = new MockHttpSession();
+ EnhancedMockHttpServletRequest request = new EnhancedMockHttpServletRequest(session);
+ EnhancedMockHttpServletResponse response = new EnhancedMockHttpServletResponse();
+
+ final Long currentTime = new Date().getTime();
+ request.addHeader(ConditionalAbstractResource.HEADER_IF_MODIFIED_SINCE, currentTime);
+ request.addHeader(ConditionalAbstractResource.HEADER_IF_NONE_MATCH, "\"1234\", \"5678\"");
+
+ ConditionalAbstractResource resource = new ConditionalAbstractResource()
+ {
+ public void getResource(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ if (!sendConditional(request, response, "\"5678\"", currentTime + 5000))
+ {
+ response.sendError(HttpServletResponse.SC_OK);
+ }
+ }
+
+ public String getResourcePath()
+ {
+ return null;
+ }
+ };
+
+ resource.getResource(request, response);
+
+ assertEquals(response.getStatus(), HttpServletResponse.SC_OK);
+ assertEquals(response.getHeader(ConditionalAbstractResource.HEADER_LAST_MODIFIED), currentTime + 5000);
+ assertEquals(response.getHeader(ConditionalAbstractResource.HEADER_ETAG), "\"5678\"");
+
+ }
+}
13 years, 3 months