| /* |
| * 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; |
| } |
| } |