/*
* Copyright 2014 Bazaarvoice, Inc.
*
* 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.
*/
package com.bazaarvoice.dropwizard.caching;
import com.google.common.base.Optional;
import com.sun.jersey.api.core.HttpResponseContext;
import com.sun.jersey.spi.container.ContainerResponse;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.core.CacheControl;
import java.util.List;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.net.HttpHeaders.AGE;
import static com.google.common.net.HttpHeaders.CACHE_CONTROL;
import static com.google.common.net.HttpHeaders.DATE;
import static com.google.common.net.HttpHeaders.EXPIRES;
/**
* Cache related context information for a response.
*/
public class CacheResponseContext {
private static final Logger LOG = LoggerFactory.getLogger(CacheResponseContext.class);
private final HttpResponseContext _httpContext;
private transient CacheControl _cacheControl;
private transient Optional<DateTime> _date;
public CacheResponseContext(HttpResponseContext httpContext) {
_httpContext = checkNotNull(httpContext);
}
public HttpResponseContext getHttpContext() {
return _httpContext;
}
/**
* Get the HTTP response status code (e.g. 200, 404, 503, etc).
*
* @return status code
*/
public int getStatusCode() {
return _httpContext.getStatus();
}
/**
* Get the cache control options specified in the response.
* <p/>
* Although {@link CacheControl} is mutable, the returned value should be treated as immutable.
*
* @return response cache control options
*/
public CacheControl getCacheControl() {
if (_cacheControl == null) {
List<Object> headerValues = _httpContext.getHttpHeaders().get(CACHE_CONTROL);
if (headerValues != null) {
try {
_cacheControl = CacheControl.valueOf(HttpHeaderUtils.transformAndJoin(headerValues));
} catch (Exception ex) {
LOG.debug("Failed to parse cache-control header: value='{}'", headerValues, ex);
}
}
if (_cacheControl == null) {
_cacheControl = new CacheControl();
}
}
return _cacheControl;
}
/**
* Get the max time, in seconds, the response should be served from a shared cache or -1 if no max age has been set.
*
* @return response shared cache max age, in seconds, or -1
*/
public int getSharedCacheMaxAge() {
return CacheControlUtils.getSharedCacheMaxAge(getCacheControl());
}
/**
* Get the {@link com.google.common.net.HttpHeaders#DATE} HTTP header.
*
* @return date header value or absent if the header is not set or the value is invalid
*/
public Optional<DateTime> getDate() {
if (_date == null) {
Object headerValue = _httpContext.getHttpHeaders().getFirst(DATE);
_date = Optional.absent();
if (headerValue != null) {
try {
_date = Optional.of(HttpHeaderUtils.parseDate(ContainerResponse.getHeaderValue(headerValue)));
} catch (Exception ex) {
LOG.debug("Error parsing date header: value={}", headerValue, ex);
}
}
}
return _date;
}
/**
* Set the {@link com.google.common.net.HttpHeaders#DATE} HTTP header.
*
* @param date instant to set the date header to
*/
public void setDate(DateTime date) {
_date = Optional.of(date);
_httpContext.getHttpHeaders().putSingle(DATE, HttpHeaderUtils.dateToString(date));
}
/**
* Set the {@link com.google.common.net.HttpHeaders#EXPIRES} HTTP header.
*
* @param date instant to set the expires header to
*/
public void setExpires(DateTime date) {
checkNotNull(date);
_httpContext.getHttpHeaders().putSingle(EXPIRES, HttpHeaderUtils.dateToString(date));
}
/**
* Set the {@link com.google.common.net.HttpHeaders#AGE} HTTP header as the difference between two instants
* (end - start).
* <p/>
* Sets the age to 0 if end < start.
*
* @param start starting instant
* @param end ending instant
*/
public void setAge(DateTime start, DateTime end) {
_httpContext.getHttpHeaders().putSingle(AGE, HttpHeaderUtils.toAge(start, end));
}
/**
* Set the {@link com.google.common.net.HttpHeaders#AGE} HTTP header.
*
* @param seconds number of seconds to set the header to (>= 0)
*/
public void setAge(int seconds) {
checkArgument(seconds >= 0, "seconds must be >= 0");
_httpContext.getHttpHeaders().putSingle(AGE, Integer.toString(seconds));
}
}