blob: 9844aed80e8d20de9859abc281160cddd40b6ac5 [file] [log] [blame]
/*
* 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.jclouds.http.options;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.net.HttpHeaders.IF_MATCH;
import static com.google.common.net.HttpHeaders.IF_MODIFIED_SINCE;
import static com.google.common.net.HttpHeaders.IF_NONE_MATCH;
import static com.google.common.net.HttpHeaders.IF_UNMODIFIED_SINCE;
import static com.google.common.net.HttpHeaders.RANGE;
import java.util.Date;
import java.util.List;
import org.jclouds.date.DateService;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
/**
* Contains options supported for HTTP GET operations. <h2>
* Usage</h2> The recommended way to instantiate a GetObjectOptions object is to statically import
* GetObjectOptions.Builder.* and invoke a static creation method followed by an instance mutator
* (if needed):
* <p/>
* <code>
* import static org.jclouds.http.options.GetOptions.Builder.*
*
*
* // this will get the first megabyte of an object.
* blob = client.get("objectName",range(0,1024));
* <code>
*/
public class GetOptions extends BaseHttpRequestOptions {
private static final DateService dateService = new SimpleDateFormatDateService();
public static final GetOptions NONE = new GetOptions();
private final List<String> ranges = Lists.newArrayList();
@Override
public Multimap<String, String> buildRequestHeaders() {
Multimap<String, String> headers = super.buildRequestHeaders();
String range = getRange();
if (range != null)
headers.put(RANGE, this.getRange());
return headers;
}
/**
* download the specified range of the object.
*/
public GetOptions range(long start, long end) {
checkArgument(start >= 0, "start must be >= 0");
checkArgument(end >= 0, "end must be >= 0");
ranges.add(String.format("%d-%d", start, end));
return this;
}
/**
* download the object offset at <code>start</code>
*/
public GetOptions startAt(long start) {
checkArgument(start >= 0, "start must be >= 0");
ranges.add(String.format("%d-", start));
return this;
}
/**
* download the last <code>count</code> bytes of the object
*/
public GetOptions tail(long count) {
checkArgument(count > 0, "count must be > 0");
ranges.add(String.format("-%d", count));
return this;
}
/**
* For use in the header Range
* <p />
*
* @see GetOptions#range(long, long)
*/
public String getRange() {
return (!ranges.isEmpty()) ? String.format("bytes=%s", Joiner.on(",").join(ranges)) : null;
}
/**
* Only return the object if it has changed since this time.
* <p />
* Not compatible with {@link #ifETagMatches(String)} or {@link #ifUnmodifiedSince(Date)}
*/
public GetOptions ifModifiedSince(Date ifModifiedSince) {
checkArgument(getIfMatch() == null, "ifETagMatches() is not compatible with ifModifiedSince()");
checkArgument(getIfUnmodifiedSince() == null, "ifUnmodifiedSince() is not compatible with ifModifiedSince()");
this.headers.put(IF_MODIFIED_SINCE,
dateService.rfc822DateFormat(checkNotNull(ifModifiedSince, "ifModifiedSince")));
return this;
}
/**
* For use in the header If-Modified-Since
* <p />
* Return the object only if it has been modified since the specified time, otherwise return a
* 304 (not modified).
*
* @see #ifModifiedSince(Date)
*/
public String getIfModifiedSince() {
return this.getFirstHeaderOrNull(IF_MODIFIED_SINCE);
}
/**
* Only return the object if it hasn't changed since this time.
* <p />
* Not compatible with {@link #ifETagDoesntMatch(String)} or {@link #ifModifiedSince(Date)}
*/
public GetOptions ifUnmodifiedSince(Date ifUnmodifiedSince) {
checkArgument(getIfNoneMatch() == null, "ifETagDoesntMatch() is not compatible with ifUnmodifiedSince()");
checkArgument(getIfModifiedSince() == null, "ifModifiedSince() is not compatible with ifUnmodifiedSince()");
this.headers.put(IF_UNMODIFIED_SINCE,
dateService.rfc822DateFormat(checkNotNull(ifUnmodifiedSince, "ifUnmodifiedSince")));
return this;
}
/**
* For use in the header If-Unmodified-Since
* <p />
* Return the object only if it has not been modified since the specified time, otherwise return
* a 412 (precondition failed).
*
* @see #ifUnmodifiedSince(Date)
*/
public String getIfUnmodifiedSince() {
return this.getFirstHeaderOrNull(IF_UNMODIFIED_SINCE);
}
/**
* The object's eTag hash should match the parameter <code>eTag</code>.
*
* <p />
* Not compatible with {@link #ifETagDoesntMatch(byte[])} or {@link #ifModifiedSince(Date)}
*
* @param eTag
* hash representing the payload
*/
public GetOptions ifETagMatches(String eTag) {
checkArgument(getIfNoneMatch() == null, "ifETagDoesntMatch() is not compatible with ifETagMatches()");
checkArgument(getIfModifiedSince() == null, "ifModifiedSince() is not compatible with ifETagMatches()");
this.headers.put(IF_MATCH, maybeQuoteETag(checkNotNull(eTag, "eTag")));
return this;
}
/**
* For use in the request header: If-Match
* <p />
* Return the object only if its payload tag (ETag) is the same as the eTag specified, otherwise
* return a 412 (precondition failed).
*
* @see #ifETagMatches(String)
*/
public String getIfMatch() {
return this.getFirstHeaderOrNull(IF_MATCH);
}
/**
* The object should not have a eTag hash corresponding with the parameter <code>eTag</code>.
* <p />
* Not compatible with {@link #ifETagMatches(String)} or {@link #ifUnmodifiedSince(Date)}
*
* @param eTag
* hash representing the payload
*/
public GetOptions ifETagDoesntMatch(String eTag) {
checkArgument(getIfMatch() == null, "ifETagMatches() is not compatible with ifETagDoesntMatch()");
checkArgument(getIfUnmodifiedSince() == null, "ifUnmodifiedSince() is not compatible with ifETagDoesntMatch()");
this.headers.put(IF_NONE_MATCH, maybeQuoteETag(checkNotNull(eTag, "ifETagDoesntMatch")));
return this;
}
/**
* For use in the request header: If-None-Match
* <p />
* Return the object only if its payload tag (ETag) is different from the one specified,
* otherwise return a 304 (not modified).
*
* @see #ifETagDoesntMatch(String)
*/
public String getIfNoneMatch() {
return this.getFirstHeaderOrNull(IF_NONE_MATCH);
}
public List<String> getRanges() {
return ranges;
}
public static class Builder {
/**
* @see GetOptions#range(long, long)
*/
public static GetOptions range(long start, long end) {
GetOptions options = new GetOptions();
return options.range(start, end);
}
/**
* @see GetOptions#startAt(long)
*/
public static GetOptions startAt(long start) {
GetOptions options = new GetOptions();
return options.startAt(start);
}
/**
* @see GetOptions#tail(long)
*/
public static GetOptions tail(long count) {
GetOptions options = new GetOptions();
return options.tail(count);
}
/**
* @see GetOptions#getIfModifiedSince()
*/
public static GetOptions ifModifiedSince(Date ifModifiedSince) {
GetOptions options = new GetOptions();
return options.ifModifiedSince(ifModifiedSince);
}
/**
* @see GetOptions#ifUnmodifiedSince(Date)
*/
public static GetOptions ifUnmodifiedSince(Date ifUnmodifiedSince) {
GetOptions options = new GetOptions();
return options.ifUnmodifiedSince(ifUnmodifiedSince);
}
/**
* @see GetOptions#ifETagMatches(String)
*/
public static GetOptions ifETagMatches(String eTag) {
GetOptions options = new GetOptions();
return options.ifETagMatches(eTag);
}
/**
* @see GetOptions#ifETagDoesntMatch(String)
*/
public static GetOptions ifETagDoesntMatch(String eTag) {
GetOptions options = new GetOptions();
return options.ifETagDoesntMatch(eTag);
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((ranges == null) ? 0 : ranges.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
GetOptions other = (GetOptions) obj;
if (ranges == null) {
if (other.ranges != null)
return false;
} else if (!ranges.equals(other.ranges))
return false;
return true;
}
@Override
public String toString() {
return "[formParameters=" + formParameters + ", queryParameters=" + queryParameters + ", headers=" + headers
+ ", payload=" + payload + ", pathSuffix=" + pathSuffix + ", ranges=" + ranges + "]";
}
private static String maybeQuoteETag(String eTag) {
if (!eTag.startsWith("\"") && !eTag.endsWith("\"")) {
eTag = "\"" + eTag + "\"";
}
return eTag;
}
}