View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.http.client.cache;
28  
29  import java.io.Serializable;
30  import java.util.Collections;
31  import java.util.Date;
32  import java.util.HashMap;
33  import java.util.Map;
34  
35  import org.apache.http.Header;
36  import org.apache.http.HeaderIterator;
37  import org.apache.http.ProtocolVersion;
38  import org.apache.http.StatusLine;
39  import org.apache.http.annotation.Contract;
40  import org.apache.http.annotation.ThreadingBehavior;
41  import org.apache.http.client.utils.DateUtils;
42  import org.apache.http.message.HeaderGroup;
43  import org.apache.http.protocol.HTTP;
44  import org.apache.http.util.Args;
45  
46  /**
47   * Structure used to store an {@link org.apache.http.HttpResponse} in a cache.
48   * Some entries can optionally depend on system resources that may require
49   * explicit deallocation. In such a case {@link #getResource()} should return
50   * a non null instance of {@link Resource} that must be deallocated by calling
51   * {@link Resource#dispose()} method when no longer used.
52   *
53   * @since 4.1
54   */
55  @Contract(threading = ThreadingBehavior.IMMUTABLE)
56  public class HttpCacheEntry implements Serializable {
57  
58      private static final long serialVersionUID = -6300496422359477413L;
59      private static final String REQUEST_METHOD_HEADER_NAME = "Hc-Request-Method";
60  
61      private final Date requestDate;
62      private final Date responseDate;
63      private final StatusLine statusLine;
64      private final HeaderGroup responseHeaders;
65      private final Resource resource;
66      private final Map<String,String> variantMap;
67      private final Date date;
68  
69      /**
70       * Create a new {@link HttpCacheEntry} with variants.
71       * @param requestDate
72       *          Date/time when the request was made (Used for age
73       *            calculations)
74       * @param responseDate
75       *          Date/time that the response came back (Used for age
76       *            calculations)
77       * @param statusLine
78       *          HTTP status line from origin response
79       * @param responseHeaders
80       *          Header[] from original HTTP Response
81       * @param resource representing origin response body
82       * @param variantMap describing cache entries that are variants
83       *   of this parent entry; this maps a "variant key" (derived
84       *   from the varying request headers) to a "cache key" (where
85       *   in the cache storage the particular variant is located)
86       * @param requestMethod HTTP method used when the request was made
87       */
88      public HttpCacheEntry(
89              final Date requestDate,
90              final Date responseDate,
91              final StatusLine statusLine,
92              final Header[] responseHeaders,
93              final Resource resource,
94              final Map<String,String> variantMap,
95              final String requestMethod) {
96          super();
97          Args.notNull(requestDate, "Request date");
98          Args.notNull(responseDate, "Response date");
99          Args.notNull(statusLine, "Status line");
100         Args.notNull(responseHeaders, "Response headers");
101         this.requestDate = requestDate;
102         this.responseDate = responseDate;
103         this.statusLine = statusLine;
104         this.responseHeaders = new HeaderGroup();
105         this.responseHeaders.setHeaders(responseHeaders);
106         this.resource = resource;
107         this.variantMap = variantMap != null
108             ? new HashMap<String,String>(variantMap)
109             : null;
110         this.date = parseDate();
111     }
112 
113     /**
114      * Create a new {@link HttpCacheEntry} with variants.
115      * @param requestDate
116      *          Date/time when the request was made (Used for age
117      *            calculations)
118      * @param responseDate
119      *          Date/time that the response came back (Used for age
120      *            calculations)
121      * @param statusLine
122      *          HTTP status line from origin response
123      * @param responseHeaders
124      *          Header[] from original HTTP Response
125      * @param resource representing origin response body
126      * @param variantMap describing cache entries that are variants
127      *   of this parent entry; this maps a "variant key" (derived
128      *   from the varying request headers) to a "cache key" (where
129      *   in the cache storage the particular variant is located)
130      */
131     public HttpCacheEntry(
132             final Date requestDate,
133             final Date responseDate,
134             final StatusLine statusLine,
135             final Header[] responseHeaders,
136             final Resource resource,
137             final Map<String,String> variantMap) {
138         this(requestDate, responseDate, statusLine, responseHeaders, resource,
139                 variantMap, null);
140     }
141 
142     /**
143      * Create a new {@link HttpCacheEntry}.
144      *
145      * @param requestDate
146      *          Date/time when the request was made (Used for age
147      *            calculations)
148      * @param responseDate
149      *          Date/time that the response came back (Used for age
150      *            calculations)
151      * @param statusLine
152      *          HTTP status line from origin response
153      * @param responseHeaders
154      *          Header[] from original HTTP Response
155      * @param resource representing origin response body
156      */
157     public HttpCacheEntry(final Date requestDate, final Date responseDate, final StatusLine statusLine,
158             final Header[] responseHeaders, final Resource resource) {
159         this(requestDate, responseDate, statusLine, responseHeaders, resource,
160                 new HashMap<String,String>());
161     }
162 
163     /**
164      * Create a new {@link HttpCacheEntry}.
165      *
166      * @param requestDate
167      *          Date/time when the request was made (Used for age
168      *            calculations)
169      * @param responseDate
170      *          Date/time that the response came back (Used for age
171      *            calculations)
172      * @param statusLine
173      *          HTTP status line from origin response
174      * @param responseHeaders
175      *          Header[] from original HTTP Response
176      * @param resource representing origin response body
177      * @param requestMethod HTTP method used when the request was made
178      */
179     public HttpCacheEntry(final Date requestDate, final Date responseDate, final StatusLine statusLine,
180             final Header[] responseHeaders, final Resource resource, final String requestMethod) {
181         this(requestDate, responseDate, statusLine, responseHeaders, resource,
182                 new HashMap<String,String>(),requestMethod);
183     }
184 
185     /**
186      * Find the "Date" response header and parse it into a java.util.Date
187      * @return the Date value of the header or null if the header is not present
188      */
189     private Date parseDate() {
190         final Header dateHdr = getFirstHeader(HTTP.DATE_HEADER);
191         if (dateHdr == null) {
192             return null;
193         }
194         return DateUtils.parseDate(dateHdr.getValue());
195     }
196 
197     /**
198      * Returns the {@link StatusLine} from the origin
199      * {@link org.apache.http.HttpResponse}.
200      */
201     public StatusLine getStatusLine() {
202         return this.statusLine;
203     }
204 
205     /**
206      * Returns the {@link ProtocolVersion} from the origin
207      * {@link org.apache.http.HttpResponse}.
208      */
209     public ProtocolVersion getProtocolVersion() {
210         return this.statusLine.getProtocolVersion();
211     }
212 
213     /**
214      * Gets the reason phrase from the origin
215      * {@link org.apache.http.HttpResponse}, for example, "Not Modified".
216      */
217     public String getReasonPhrase() {
218         return this.statusLine.getReasonPhrase();
219     }
220 
221     /**
222      * Returns the HTTP response code from the origin
223      * {@link org.apache.http.HttpResponse}.
224      */
225     public int getStatusCode() {
226         return this.statusLine.getStatusCode();
227     }
228 
229     /**
230      * Returns the time the associated origin request was initiated by the
231      * caching module.
232      * @return {@link Date}
233      */
234     public Date getRequestDate() {
235         return requestDate;
236     }
237 
238     /**
239      * Returns the time the origin response was received by the caching module.
240      * @return {@link Date}
241      */
242     public Date getResponseDate() {
243         return responseDate;
244     }
245 
246     /**
247      * Returns all the headers that were on the origin response.
248      */
249     public Header[] getAllHeaders() {
250         final HeaderGroup filteredHeaders = new HeaderGroup();
251         for (final HeaderIterator iterator = responseHeaders.iterator(); iterator
252                 .hasNext();) {
253             final Header header = (Header) iterator.next();
254             if (!REQUEST_METHOD_HEADER_NAME.equals(header.getName())) {
255                 filteredHeaders.addHeader(header);
256             }
257         }
258         return filteredHeaders.getAllHeaders();
259     }
260 
261     /**
262      * Returns the first header from the origin response with the given
263      * name.
264      */
265     public Header getFirstHeader(final String name) {
266         if (REQUEST_METHOD_HEADER_NAME.equalsIgnoreCase(name)) {
267             return null;
268         }
269         return responseHeaders.getFirstHeader(name);
270     }
271 
272     /**
273      * Gets all the headers with the given name that were on the origin
274      * response.
275      */
276     public Header[] getHeaders(final String name) {
277         if (REQUEST_METHOD_HEADER_NAME.equalsIgnoreCase(name)) {
278             return new Header[0];
279         }
280         return responseHeaders.getHeaders(name);
281     }
282 
283     /**
284      * Gets the Date value of the "Date" header or null if the header is missing or cannot be
285      * parsed.
286      *
287      * @since 4.3
288      */
289     public Date getDate() {
290         return date;
291     }
292 
293     /**
294      * Returns the {@link Resource} containing the origin response body.
295      */
296     public Resource getResource() {
297         return this.resource;
298     }
299 
300     /**
301      * Indicates whether the origin response indicated the associated
302      * resource had variants (i.e. that the Vary header was set on the
303      * origin response).
304      * @return {@code true} if this cached response was a variant
305      */
306     public boolean hasVariants() {
307         return getFirstHeader(HeaderConstants.VARY) != null;
308     }
309 
310     /**
311      * Returns an index about where in the cache different variants for
312      * a given resource are stored. This maps "variant keys" to "cache keys",
313      * where the variant key is derived from the varying request headers,
314      * and the cache key is the location in the
315      * {@link org.apache.http.client.cache.HttpCacheStorage} where that
316      * particular variant is stored. The first variant returned is used as
317      * the "parent" entry to hold this index of the other variants.
318      */
319     public Map<String, String> getVariantMap() {
320         return Collections.unmodifiableMap(variantMap);
321     }
322 
323     /**
324      * Returns the HTTP request method that was used to create the cached
325      * response entry.
326      *
327      * @since 4.4
328      */
329     public String getRequestMethod() {
330         final Header requestMethodHeader = responseHeaders
331                 .getFirstHeader(REQUEST_METHOD_HEADER_NAME);
332         if (requestMethodHeader != null) {
333             return requestMethodHeader.getValue();
334         }
335         return HeaderConstants.GET_METHOD;
336     }
337 
338     /**
339      * Provides a string representation of this instance suitable for
340      * human consumption.
341      */
342     @Override
343     public String toString() {
344         return "[request date=" + this.requestDate + "; response date=" + this.responseDate
345                 + "; statusLine=" + this.statusLine + "]";
346     }
347 
348 }