You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by pe...@apache.org on 2011/07/01 01:49:50 UTC
svn commit: r1141755 - in /wicket/trunk:
wicket-core/src/main/java/org/apache/wicket/mock/
wicket-core/src/main/java/org/apache/wicket/protocol/http/
wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/
wicket-core/src/main/java/org/apach...
Author: pete
Date: Thu Jun 30 23:49:49 2011
New Revision: 1141755
URL: http://svn.apache.org/viewvc?rev=1141755&view=rev
Log:
WICKET-3845 There is not a good way to use custom response headers with AbstractResource:
- added abstract method WebResponse#addHeader(name) and the implementations in concrete subclasses of WebResponse
- as a side-effect, use HttpHeadersCollection in MockWebResponse to support multi-value headers
- moved check for forbidden header values in AbstractResource to better place
- added tests for HttpHeaderCollection
- added proper support for dates in HttpHeaderCollection
Added:
wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/HttpHeaderCollection.java
- copied, changed from r1141729, wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/HeaderCollection.java
Removed:
wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/HeaderCollection.java
Modified:
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/mock/MockWebResponse.java
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/protocol/http/BufferedWebResponse.java
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/protocol/http/HeaderBufferingWebResponse.java
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/ServletWebResponse.java
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/AbstractResource.java
wicket/trunk/wicket-examples/src/main/java/org/apache/wicket/examples/resourcedecoration/MergedResourcesResource.java
wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java
wicket/trunk/wicket-request/src/test/java/org/apache/wicket/request/HeadersCollectionTest.java
Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/mock/MockWebResponse.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/mock/MockWebResponse.java?rev=1141755&r1=1141754&r2=1141755&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/mock/MockWebResponse.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/mock/MockWebResponse.java Thu Jun 30 23:49:49 2011
@@ -20,14 +20,13 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import javax.servlet.http.Cookie;
import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.request.HttpHeaderCollection;
import org.apache.wicket.request.http.WebResponse;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.time.Time;
@@ -127,13 +126,13 @@ public class MockWebResponse extends Web
return contentType;
}
- private final Map<String, Object> headers = new HashMap<String, Object>();
+ private final HttpHeaderCollection headers = new HttpHeaderCollection();
@Override
public void setDateHeader(String name, Time date)
{
Args.notNull(date, "date");
- headers.put(name, date);
+ headers.setDateHeader(name, date);
}
/**
@@ -143,31 +142,32 @@ public class MockWebResponse extends Web
*/
public Time getDateHeader(String name)
{
- Object value = headers.get(name);
- if (value == null)
+ final Time time = headers.getDateHeader(name);
+
+ if (time == null)
{
throw new WicketRuntimeException("Date header '" + name + "' is not set.");
}
- else if (value instanceof Time == false)
- {
- throw new WicketRuntimeException("Header '" + name + "' is not date type.");
- }
- else
- {
- return (Time)value;
- }
+ return time;
}
@Override
public void setHeader(String name, String value)
{
- headers.put(name, value);
+ headers.setHeader(name, value);
+
if (name.equals("Content-Type"))
{
setContentType(value);
}
}
+ @Override
+ public void addHeader(String name, String value)
+ {
+ headers.addHeader(name, value);
+ }
+
/**
* @param name
*
@@ -175,8 +175,7 @@ public class MockWebResponse extends Web
*/
public String getHeader(String name)
{
- Object value = headers.get(name);
- return value != null ? value.toString() : null;
+ return headers.getHeader(name);
}
/**
@@ -186,7 +185,7 @@ public class MockWebResponse extends Web
*/
public boolean hasHeader(String name)
{
- return headers.containsKey(name);
+ return headers.containsHeader(name);
}
/**
@@ -194,7 +193,7 @@ public class MockWebResponse extends Web
*/
public Set<String> getHeaderNames()
{
- return Collections.unmodifiableSet(headers.keySet());
+ return headers.getHeaderNames();
}
Integer status;
Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/protocol/http/BufferedWebResponse.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/protocol/http/BufferedWebResponse.java?rev=1141755&r1=1141754&r2=1141755&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/protocol/http/BufferedWebResponse.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/protocol/http/BufferedWebResponse.java Thu Jun 30 23:49:49 2011
@@ -251,6 +251,24 @@ public class BufferedWebResponse extends
}
}
+ private static class AddHeaderAction extends MetaDataAction
+ {
+ private final String name;
+ private final String value;
+
+ public AddHeaderAction(String name, String value)
+ {
+ this.name = name;
+ this.value = value;
+ }
+
+ @Override
+ protected void invoke(WebResponse response)
+ {
+ response.addHeader(name, value);
+ }
+ }
+
private static class SetDateHeaderAction extends MetaDataAction
{
private final String name;
@@ -410,6 +428,12 @@ public class BufferedWebResponse extends
}
@Override
+ public void addHeader(String name, String value)
+ {
+ actions.add(new AddHeaderAction(name, value));
+ }
+
+ @Override
public void write(CharSequence sequence)
{
if (dataAction != null)
Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/protocol/http/HeaderBufferingWebResponse.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/protocol/http/HeaderBufferingWebResponse.java?rev=1141755&r1=1141754&r2=1141755&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/protocol/http/HeaderBufferingWebResponse.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/protocol/http/HeaderBufferingWebResponse.java Thu Jun 30 23:49:49 2011
@@ -143,6 +143,13 @@ class HeaderBufferingWebResponse extends
}
@Override
+ public void addHeader(String name, String value)
+ {
+ checkHeader();
+ bufferedResponse.addHeader(name, value);
+ }
+
+ @Override
public void setStatus(int sc)
{
bufferedResponse.setStatus(sc);
Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/ServletWebResponse.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/ServletWebResponse.java?rev=1141755&r1=1141754&r2=1141755&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/ServletWebResponse.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/ServletWebResponse.java Thu Jun 30 23:49:49 2011
@@ -96,6 +96,12 @@ public class ServletWebResponse extends
}
@Override
+ public void addHeader(String name, String value)
+ {
+ httpServletResponse.addHeader(name, value);
+ }
+
+ @Override
public void write(CharSequence sequence)
{
try
Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/AbstractResource.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/AbstractResource.java?rev=1141755&r1=1141754&r2=1141755&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/AbstractResource.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/AbstractResource.java Thu Jun 30 23:49:49 2011
@@ -26,7 +26,7 @@ import javax.servlet.http.HttpServletRes
import org.apache.wicket.Application;
import org.apache.wicket.WicketRuntimeException;
-import org.apache.wicket.request.HeaderCollection;
+import org.apache.wicket.request.HttpHeaderCollection;
import org.apache.wicket.request.Response;
import org.apache.wicket.request.http.WebRequest;
import org.apache.wicket.request.http.WebResponse;
@@ -46,6 +46,24 @@ public abstract class AbstractResource i
{
private static final long serialVersionUID = 1L;
+ /** header values that are managed internally and must not be set directly */
+ public static final Set<String> INTERNAL_HEADERS;
+
+ static
+ {
+ INTERNAL_HEADERS = new HashSet<String>();
+ INTERNAL_HEADERS.add("server");
+ INTERNAL_HEADERS.add("date");
+ INTERNAL_HEADERS.add("expires");
+ INTERNAL_HEADERS.add("last-modified");
+ INTERNAL_HEADERS.add("content-type");
+ INTERNAL_HEADERS.add("content-length");
+ INTERNAL_HEADERS.add("content-disposition");
+ INTERNAL_HEADERS.add("transfer-encoding");
+ INTERNAL_HEADERS.add("connection");
+ INTERNAL_HEADERS.add("content-disposition");
+ }
+
/**
* Construct.
*/
@@ -69,9 +87,6 @@ public abstract class AbstractResource i
*/
public static class ResourceResponse
{
- /** header values that are managed internally and must not be set directly */
- public static final Set<String> INTERNAL_HEADERS;
-
private Integer errorCode;
private String errorMessage;
private String fileName = null;
@@ -83,23 +98,8 @@ public abstract class AbstractResource i
private WriteCallback writeCallback;
private Duration cacheDuration;
private WebResponse.CacheScope cacheScope;
- private final HeaderCollection headers;
+ private final HttpHeaderCollection headers;
- static
- {
- INTERNAL_HEADERS = new HashSet<String>();
- INTERNAL_HEADERS.add("server");
- INTERNAL_HEADERS.add("date");
- INTERNAL_HEADERS.add("expires");
- INTERNAL_HEADERS.add("last-modified");
- INTERNAL_HEADERS.add("content-type");
- INTERNAL_HEADERS.add("content-length");
- INTERNAL_HEADERS.add("content-disposition");
- INTERNAL_HEADERS.add("transfer-encoding");
- INTERNAL_HEADERS.add("connection");
- INTERNAL_HEADERS.add("content-disposition");
- }
-
/**
* Construct.
*/
@@ -111,7 +111,7 @@ public abstract class AbstractResource i
cacheScope = WebResponse.CacheScope.PRIVATE;
// collection of directly set response headers
- headers = new HeaderCollection();
+ headers = new HttpHeaderCollection();
}
/**
@@ -423,84 +423,11 @@ public abstract class AbstractResource i
}
/**
- * add a response header value
- *
- * you can only set header values that or not already modified by
- * the other methods of this class like 'Content-Length', 'Last-Modified', etc.
- *
- * @param name
- * header name
- * @param value
- * header value
- */
- public void addHeader(String name, String value)
- {
- // check if header can be directly access
- checkHeaderAccess(name);
-
- //set header value
- headers.addHeader(name, value);
- }
-
- /**
- * get header value
- * <p/>
- * you can only get header values that or not already handled by
- * the other methods of this class like 'Content-Length', 'Last-Modified', etc.
- *
- *
- * @param name
- * header name
- *
- * @return array of header values
+ * get custom headers
*/
- public String[] getHeaderValues(final String name)
+ public HttpHeaderCollection getHeaders()
{
- // check if header can be directly access
- checkHeaderAccess(name);
-
- // get header value
- return headers.getValues(name);
- }
-
- /**
- * remove header value
- *
- * you can only access header values that or not already modified by
- * the other methods of this class like 'Content-Length', 'Last-Modified', etc.
- *
- * @param name
- * header name
- */
- public void removeHeader(String name)
- {
- // check if header can be directly access
- checkHeaderAccess(name);
-
- // remove header value
- headers.removeHeaderValues(name);
- }
-
- /**
- * check if header is directly modifyable
- *
- * @param name
- * header name
- *
- * @throws IllegalArgumentException
- * if access is forbidden
- */
- private void checkHeaderAccess(String name)
- {
- name = Args.notEmpty(name.trim().toLowerCase(), "name");
-
- if (INTERNAL_HEADERS.contains(name))
- {
- throw new IllegalArgumentException(
- "you are not allowed to directly access header [" + name + "], " +
- "use one of the other specialized methods of " + getClass().getSimpleName() +
- " to get or modify its value");
- }
+ return headers;
}
}
@@ -555,6 +482,28 @@ public abstract class AbstractResource i
}
/**
+ * check if header is directly modifyable
+ *
+ * @param name
+ * header name
+ *
+ * @throws IllegalArgumentException
+ * if access is forbidden
+ */
+ private void checkHeaderAccess(String name)
+ {
+ name = Args.notEmpty(name.trim().toLowerCase(), "name");
+
+ if (INTERNAL_HEADERS.contains(name))
+ {
+ throw new IllegalArgumentException(
+ "you are not allowed to directly access header [" + name + "], " +
+ "use one of the other specialized methods of " + getClass().getSimpleName() +
+ " to get or modify its value");
+ }
+ }
+
+ /**
* @param data
* @param attributes
*/
@@ -628,15 +577,19 @@ public abstract class AbstractResource i
webResponse.setContentLength(contentLength);
}
- // set additional response headers
- for (HeaderCollection.Entry entry : data.headers)
+ // add custom headers and values
+ final HttpHeaderCollection headers = data.getHeaders();
+
+ for (String name : headers.getHeaderNames())
{
- for (String value : entry.getValues())
+ checkHeaderAccess(name);
+
+ for (String value : headers.getHeaderValues(name))
{
- webResponse.setHeader(entry.getName(), value);
+ webResponse.addHeader(name, value);
}
}
-
+
// 6. Flush the response
// This is necessary for firefox if this resource is an image, otherwise it messes up
// other images on page
Modified: wicket/trunk/wicket-examples/src/main/java/org/apache/wicket/examples/resourcedecoration/MergedResourcesResource.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-examples/src/main/java/org/apache/wicket/examples/resourcedecoration/MergedResourcesResource.java?rev=1141755&r1=1141754&r2=1141755&view=diff
==============================================================================
--- wicket/trunk/wicket-examples/src/main/java/org/apache/wicket/examples/resourcedecoration/MergedResourcesResource.java (original)
+++ wicket/trunk/wicket-examples/src/main/java/org/apache/wicket/examples/resourcedecoration/MergedResourcesResource.java Thu Jun 30 23:49:49 2011
@@ -19,6 +19,7 @@ package org.apache.wicket.examples.resou
import java.io.BufferedReader;
import java.io.InputStreamReader;
+import org.apache.wicket.request.HttpHeaderCollection;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.request.resource.AbstractResource;
import org.apache.wicket.util.lang.WicketObjects;
@@ -45,6 +46,14 @@ public class MergedResourcesResource ext
ResourceResponse resourceResponse = new ResourceResponse();
+ HttpHeaderCollection headers = resourceResponse.getHeaders();
+
+ headers.addHeader(" blA ", "xx ");
+ headers.addHeader("BLA", " 12xx ");
+ headers.addHeader("xx", "xX12x ");
+ String[] val = headers.getHeaderValues("bla");
+// resourceResponse.removeHeader("bla");
+
if (resourceResponse.dataNeedsToBeWritten(attributes))
{
if (isCss)
Copied: wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/HttpHeaderCollection.java (from r1141729, wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/HeaderCollection.java)
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/HttpHeaderCollection.java?p2=wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/HttpHeaderCollection.java&p1=wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/HeaderCollection.java&r1=1141729&r2=1141755&rev=1141755&view=diff
==============================================================================
--- wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/HeaderCollection.java (original)
+++ wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/HttpHeaderCollection.java Thu Jun 30 23:49:49 2011
@@ -16,31 +16,95 @@
*/
package org.apache.wicket.request;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import java.util.Set;
+import java.util.TimeZone;
import org.apache.wicket.util.lang.Args;
+import org.apache.wicket.util.time.Time;
/**
* a multivalue map of headers names and header values suitable for
- * processing request and response headers.
+ * processing http request and response headers.
*
* @author Peter Ertl
*
* @since 1.5
*/
-public class HeaderCollection implements Iterable<HeaderCollection.Entry>
+public class HttpHeaderCollection
{
+ private final Map<HeaderKey, List<Object>> headers;
+
+ /** Greenwich Mean Time (GMT) timezone */
+ private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
+
+ /** rfc 1123 compliant time stamp for headers */
+ private static final String RFC_1123_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
+
+ /** date format for date headers */
+ public static final DateFormat DATE_FORMAT;
+
+ /** returned in case no header values were found */
private static final String[] NO_VALUES = new String[0];
- private final Map<String, List<String>> headers;
+ static
+ {
+ DATE_FORMAT = new SimpleDateFormat(RFC_1123_DATE_FORMAT, Locale.US);
+ DATE_FORMAT.setTimeZone(GMT);
+ }
- public HeaderCollection()
+ public HttpHeaderCollection()
{
- headers = new HashMap<String, List<String>>();
+ headers = new HashMap<HeaderKey, List<Object>>();
+ }
+
+ /**
+ * internally add new object to header values
+ *
+ * @param name
+ * header name
+ * @param object
+ * header value (can be a string or a {@link Time} object
+ */
+ private void internalAdd(String name, Object object)
+ {
+ final HeaderKey key = new HeaderKey(name);
+
+ List<Object> values = headers.get(key);
+
+ if (values == null)
+ {
+ values = new ArrayList<Object>();
+ headers.put(key, values);
+ }
+ values.add(object);
+ }
+
+ /**
+ * set header value (and remove previous values)
+ *
+ * @param name
+ * header name
+ * @param value
+ * header value
+ */
+ public void setHeader(String name, String value)
+ {
+ // remove previous values
+ removeHeader(name);
+
+ // add new values
+ addHeader(name, value);
}
/**
@@ -54,98 +118,178 @@ public class HeaderCollection implements
public void addHeader(String name, String value)
{
// be lenient and strip leading / trailing blanks
- name = Args.notEmpty(name, "name").trim();
value = Args.notEmpty(value, "value").trim();
- List<String> values = headers.get(name);
+ internalAdd(name, value);
+ }
- if (values == null)
- {
- values = new ArrayList<String>();
- headers.put(name, values);
- }
- values.add(value);
+ /**
+ * add date header value
+ *
+ * @param name
+ * header name
+ * @param time
+ * timestamp
+ */
+ public void addDateHeader(String name, Time time)
+ {
+ internalAdd(name, time);
}
/**
- * remove header values for header name
+ * add date header value
*
* @param name
* header name
+ * @param time
+ * timestamp
*/
- public void removeHeaderValues(String name)
+ public void setDateHeader(String name, Time time)
{
- name = Args.notEmpty(name, "name").trim();
+ // remove previous values
+ removeHeader(name);
- final Iterator<Map.Entry<String, List<String>>> it = headers.entrySet().iterator();
+ // add time object to values
+ addDateHeader(name, time);
+ }
+
+ /**
+ * remove header values for header name
+ *
+ * @param name
+ * header name
+ */
+ public void removeHeader(String name)
+ {
+ final HeaderKey key = new HeaderKey(name);
+ final Iterator<Map.Entry<HeaderKey, List<Object>>> it = headers.entrySet().iterator();
while (it.hasNext())
{
- Map.Entry<String, List<String>> header = it.next();
+ final Map.Entry<HeaderKey, List<Object>> header = it.next();
- if (header.getKey().equalsIgnoreCase(name))
+ if (header.getKey().equals(key))
{
it.remove();
}
}
}
+ private String valueToString(Object value)
+ {
+ if (value instanceof Time)
+ {
+ synchronized(DATE_FORMAT)
+ {
+ return DATE_FORMAT.format(new Date(((Time)value).getMilliseconds()));
+ }
+ }
+ else
+ {
+ return value.toString();
+ }
+ }
+
/**
- * get header values
- *
- *
+ * check if header is defined
*
* @param name
- * header name
- *
- * @return header value or <code>null</code> if not found
+ * header name
+ * @return <code>true</code> if header has one or more values
*/
- public String[] getValues(String name)
+ public boolean containsHeader(String name)
{
- Args.notEmpty(name, "name");
+ final HeaderKey searchKey = new HeaderKey(name);
// get the header value (case might differ)
- for (Map.Entry<String, List<String>> header : headers.entrySet())
+ for (HeaderKey key : headers.keySet())
{
- if (header.getKey().equalsIgnoreCase(name))
+ if (key.equals(searchKey))
{
- return header.getValue().toArray(new String[header.getValue().size()]);
+ return true;
}
}
- return NO_VALUES;
+ return false;
}
/**
- * get iterator over header values
+ * returns names of headers
*
- * @return iterator
+ * @return set of header names
*/
- public Iterator<Entry> iterator()
+ public Set<String> getHeaderNames()
{
- final Iterator<Map.Entry<String,List<String>>> iterator = headers.entrySet().iterator();
+ if (headers.isEmpty())
+ {
+ return Collections.emptySet();
+ }
- return new Iterator<Entry>()
+ final Set<String> names = new HashSet<String>(headers.size());
+
+ for (HeaderKey key : headers.keySet())
{
- public boolean hasNext()
- {
- return iterator.hasNext();
- }
+ names.add(key.getName());
+ }
+ return names;
+ }
- public Entry next()
- {
- return new Entry(iterator.next());
- }
+ /**
+ * get header values (dates will be converted into strings)
+ *
+ * @param name
+ * header name
+ *
+ * @return array of header values or empty array if not found
+ */
+ public String[] getHeaderValues(String name)
+ {
+ final List<Object> objects = headers.get(new HeaderKey(name));
- public void remove()
- {
- throw new UnsupportedOperationException();
- }
- };
+ if (objects == null)
+ {
+ return NO_VALUES;
+ }
+
+ final String[] values = new String[objects.size()];
+
+ for (int i = 0; i < values.length; i++)
+ {
+ values[i] = valueToString(objects.get(i));
+ }
+ return values;
+ }
+
+ public String getHeader(String name)
+ {
+ final List<Object> objects = headers.get(new HeaderKey(name));
+
+ if (objects.isEmpty())
+ {
+ return null;
+ }
+ return valueToString(objects.get(0));
+ }
+
+ public Time getDateHeader(String name)
+ {
+ final List<Object> objects = headers.get(new HeaderKey(name));
+
+ if (objects.isEmpty())
+ {
+ return null;
+ }
+ Object object = objects.get(0);
+
+ if ((object instanceof Time) == false)
+ {
+ throw new IllegalStateException("header value is not of type date");
+ }
+ return (Time)object;
}
/**
* check if collection is empty
- *
+ *
* @return <code>true</code> if collection is empty, <code>false</code> otherwise
*/
public boolean isEmpty()
@@ -155,7 +299,7 @@ public class HeaderCollection implements
/**
* get number of headers
- *
+ *
* @return count
*/
public int getCount()
@@ -164,25 +308,53 @@ public class HeaderCollection implements
}
/**
- * read-only header entry
+ * clear all headers
+ */
+ public void clear()
+ {
+ headers.clear();
+ }
+
+ /**
+ * key for header collection
*/
- public static class Entry
+ private static class HeaderKey
{
- private final Map.Entry<String, List<String>> header;
+ private final String key;
+ private final String name;
- public Entry(Map.Entry<String, List<String>> header)
+ private HeaderKey(String name)
{
- this.header = header;
+ this.name = Args.notEmpty(name, "name").trim();
+ this.key = this.name.toLowerCase(Locale.US);
}
public String getName()
{
- return header.getKey();
+ return name;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ return true;
+
+ if (!(o instanceof HeaderKey))
+ return false;
+
+ HeaderKey that = (HeaderKey)o;
+
+ if (!key.equals(that.key))
+ return false;
+
+ return true;
}
- public String[] getValues()
+ @Override
+ public int hashCode()
{
- return header.getValue().toArray(new String[header.getValue().size()]);
+ return key.hashCode();
}
}
}
Modified: wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java?rev=1141755&r1=1141754&r2=1141755&view=diff
==============================================================================
--- wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java (original)
+++ wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java Thu Jun 30 23:49:49 2011
@@ -62,6 +62,14 @@ public abstract class WebResponse extend
public abstract void setHeader(String name, String value);
/**
+ * Add a value to the servlet response stream.
+ *
+ * @param name
+ * @param value
+ */
+ public abstract void addHeader(String name, String value);
+
+ /**
* Set a header to the date value in the servlet response stream.
*
* @param name
@@ -109,7 +117,7 @@ public abstract class WebResponse extend
public void setAttachmentHeader(final String filename)
{
setHeader("Content-Disposition", "attachment" +
- ((!Strings.isEmpty(filename)) ? ("; filename=\"" + filename + "\"") : ""));
+ ((!Strings.isEmpty(filename))? ("; filename=\"" + filename + "\"") : ""));
}
/**
@@ -206,7 +214,8 @@ public abstract class WebResponse extend
setDateHeader("Expires", now.add(duration));
// Enable caching and set max age
- setHeader("Cache-Control", scope.cacheControl + ", max-age=" + duration.getMilliseconds());
+ setHeader("Cache-Control", scope.cacheControl);
+ addHeader("Cache-Control", "max-age=" + duration.getMilliseconds());
}
/**
Modified: wicket/trunk/wicket-request/src/test/java/org/apache/wicket/request/HeadersCollectionTest.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-request/src/test/java/org/apache/wicket/request/HeadersCollectionTest.java?rev=1141755&r1=1141754&r2=1141755&view=diff
==============================================================================
--- wicket/trunk/wicket-request/src/test/java/org/apache/wicket/request/HeadersCollectionTest.java (original)
+++ wicket/trunk/wicket-request/src/test/java/org/apache/wicket/request/HeadersCollectionTest.java Thu Jun 30 23:49:49 2011
@@ -16,6 +16,10 @@
*/
package org.apache.wicket.request;
+import java.util.Locale;
+import java.util.Set;
+
+import org.apache.wicket.util.time.Time;
import org.junit.Test;
import static org.junit.Assert.*;
@@ -25,25 +29,83 @@ public class HeadersCollectionTest
@Test
public void testHeaderCollection()
{
- HeaderCollection headers = new HeaderCollection();
+ HttpHeaderCollection headers = new HttpHeaderCollection();
assertTrue(headers.isEmpty());
headers.addHeader("X-Test", "foo");
headers.addHeader("X-Test", "bar");
- assertArrayEquals(new String[]{"foo", "bar"}, headers.getValues("X-Test"));
+ assertArrayEquals(new String[]{"foo", "bar"}, headers.getHeaderValues("X-Test"));
- headers.removeHeaderValues("x-test");
+ headers.removeHeader("x-test");
assertTrue(headers.isEmpty());
headers.addHeader(" X-Image ", " jpeg ");
headers.addHeader("X-Image ", " gif ");
- assertArrayEquals(new String[]{"jpeg", "gif"}, headers.getValues("X-IMAGE"));
+ assertArrayEquals(new String[]{"jpeg", "gif"}, headers.getHeaderValues("X-IMAGE"));
assertEquals(1, headers.getCount());
headers.addHeader("X-Test", "123");
assertEquals(2, headers.getCount());
- headers.removeHeaderValues(" x-tesT ");
+ headers.removeHeader(" x-tesT ");
assertEquals(1, headers.getCount());
}
+
+ @Test
+ public void getHeaderNames()
+ {
+ final HttpHeaderCollection headers = new HttpHeaderCollection();
+
+ headers.addHeader("key1", "a");
+ headers.addHeader("Key1", "b");
+ headers.addHeader("key2", "c");
+
+ Set<String> names = headers.getHeaderNames();
+ assertTrue(names.contains("key1"));
+ assertFalse(names.contains("Key1"));
+ assertTrue(names.contains("key2"));
+ }
+
+ @Test
+ public void dateValues()
+ {
+ final HttpHeaderCollection headers = new HttpHeaderCollection();
+
+ final Time time1 = Time.millis(1000000);
+ final Time time2 = Time.millis(2000000);
+
+ headers.setDateHeader("date", time1);
+ headers.addDateHeader("date", time2);
+ headers.addHeader("date", "not-a-date");
+
+ assertEquals(time1, headers.getDateHeader("date"));
+ assertEquals("Thu, 01 Jan 1970 00:16:40 GMT", headers.getHeader("date"));
+
+ // a change of the locale must not affect the date format
+ final Locale defaultLocale = Locale.getDefault();
+
+ try
+ {
+ Locale.setDefault(Locale.CHINESE);
+ assertEquals("Thu, 01 Jan 1970 00:16:40 GMT", headers.getHeader("date"));
+ }
+ finally
+ {
+ Locale.setDefault(defaultLocale);
+ }
+
+ assertArrayEquals(new String[]{"Thu, 01 Jan 1970 00:16:40 GMT", "Thu, 01 Jan 1970 00:33:20 GMT", "not-a-date"},
+ headers.getHeaderValues("date"));
+
+ headers.setHeader("date", "foobar");
+ try
+ {
+ Time date = headers.getDateHeader("date");
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // ok
+ }
+ }
}