mirror of https://github.com/apache/jclouds.git
Issue 75: Implemented HEAD, POST and (simple) GET for objects
git-svn-id: http://jclouds.googlecode.com/svn/trunk@1642 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
parent
14053f0341
commit
1e56bb6eea
|
@ -30,18 +30,25 @@ import java.util.concurrent.Future;
|
||||||
import javax.ws.rs.DELETE;
|
import javax.ws.rs.DELETE;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.HEAD;
|
import javax.ws.rs.HEAD;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.PUT;
|
import javax.ws.rs.PUT;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
|
|
||||||
import org.jclouds.http.functions.ParseETagHeader;
|
import org.jclouds.http.functions.ParseETagHeader;
|
||||||
import org.jclouds.rackspace.cloudfiles.binders.CFObjectBinder;
|
import org.jclouds.rackspace.cloudfiles.binders.CFObjectBinder;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.binders.UserMetadataBinder;
|
||||||
import org.jclouds.rackspace.cloudfiles.domain.AccountMetadata;
|
import org.jclouds.rackspace.cloudfiles.domain.AccountMetadata;
|
||||||
import org.jclouds.rackspace.cloudfiles.domain.CFObject;
|
import org.jclouds.rackspace.cloudfiles.domain.CFObject;
|
||||||
import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata;
|
import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata;
|
||||||
import org.jclouds.rackspace.cloudfiles.functions.CFObjectKey;
|
import org.jclouds.rackspace.cloudfiles.functions.CFObjectKey;
|
||||||
import org.jclouds.rackspace.cloudfiles.functions.ParseAccountMetadataResponseFromHeaders;
|
import org.jclouds.rackspace.cloudfiles.functions.ParseAccountMetadataResponseFromHeaders;
|
||||||
import org.jclouds.rackspace.cloudfiles.functions.ParseContainerListFromGsonResponse;
|
import org.jclouds.rackspace.cloudfiles.functions.ParseContainerListFromGsonResponse;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.functions.ParseObjectFromHeadersAndHttpContent;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.functions.ParseObjectMetadataFromHeaders;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.functions.ReturnCFObjectMetadataNotFoundOn404;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.functions.ReturnS3ObjectNotFoundOn404;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.functions.ReturnTrueOn202FalseOtherwise;
|
||||||
import org.jclouds.rackspace.cloudfiles.functions.ReturnTrueOn204FalseOtherwise;
|
import org.jclouds.rackspace.cloudfiles.functions.ReturnTrueOn204FalseOtherwise;
|
||||||
import org.jclouds.rackspace.cloudfiles.functions.ReturnTrueOn404FalseOtherwise;
|
import org.jclouds.rackspace.cloudfiles.functions.ReturnTrueOn404FalseOtherwise;
|
||||||
import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions;
|
import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions;
|
||||||
|
@ -54,6 +61,8 @@ import org.jclouds.rest.RequestFilters;
|
||||||
import org.jclouds.rest.ResponseParser;
|
import org.jclouds.rest.ResponseParser;
|
||||||
import org.jclouds.rest.SkipEncoding;
|
import org.jclouds.rest.SkipEncoding;
|
||||||
|
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides access to Cloud Files via their REST API.
|
* Provides access to Cloud Files via their REST API.
|
||||||
* <p/>
|
* <p/>
|
||||||
|
@ -103,6 +112,29 @@ public interface CloudFilesConnection {
|
||||||
@PathParam("key") @PathParamParser(CFObjectKey.class) @EntityParam(CFObjectBinder.class)
|
@PathParam("key") @PathParamParser(CFObjectKey.class) @EntityParam(CFObjectBinder.class)
|
||||||
CFObject object);
|
CFObject object);
|
||||||
|
|
||||||
|
@HEAD
|
||||||
|
@ResponseParser(ParseObjectMetadataFromHeaders.class)
|
||||||
|
@ExceptionParser(ReturnCFObjectMetadataNotFoundOn404.class)
|
||||||
|
@Path("{container}/{key}")
|
||||||
|
CFObject.Metadata headObject(@PathParam("container") String container,
|
||||||
|
@PathParam("key") String key);
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@ResponseParser(ParseObjectFromHeadersAndHttpContent.class)
|
||||||
|
@ExceptionParser(ReturnS3ObjectNotFoundOn404.class)
|
||||||
|
@Path("{container}/{key}")
|
||||||
|
Future<CFObject> getObject(@PathParam("container") String container,
|
||||||
|
@PathParam("key") String key);
|
||||||
|
|
||||||
|
// TODO: GET object with options
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@ResponseParser(ReturnTrueOn202FalseOtherwise.class)
|
||||||
|
@Path("{container}/{key}")
|
||||||
|
boolean setObjectMetadata(@PathParam("container") String container,
|
||||||
|
@PathParam("key") String key,
|
||||||
|
@EntityParam(UserMetadataBinder.class) Multimap<String, String> userMetadata);
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
@ResponseParser(ReturnTrueOn204FalseOtherwise.class)
|
@ResponseParser(ReturnTrueOn204FalseOtherwise.class)
|
||||||
@ExceptionParser(ReturnTrueOn404FalseOtherwise.class)
|
@ExceptionParser(ReturnTrueOn404FalseOtherwise.class)
|
||||||
|
|
|
@ -72,7 +72,7 @@ public class CFObjectBinder implements EntityBinder {
|
||||||
String hexETag = HttpUtils.toHexString(object.getMetadata().getETag());
|
String hexETag = HttpUtils.toHexString(object.getMetadata().getETag());
|
||||||
request.getHeaders().put(HttpHeaders.ETAG, hexETag);
|
request.getHeaders().put(HttpHeaders.ETAG, hexETag);
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
// TODO: Any sane way to recover? Should EntityBinder#addEntityToRequest throw errors?
|
throw new RuntimeException("Failed to encode ETag for object: " + object, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.rackspace.cloudfiles.binders;
|
||||||
|
|
||||||
|
import org.jclouds.http.HttpRequest;
|
||||||
|
import org.jclouds.rest.EntityBinder;
|
||||||
|
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
|
public class UserMetadataBinder implements EntityBinder {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void addEntityToRequest(Object entity, HttpRequest request) {
|
||||||
|
Multimap<String, String> userMetadata = (Multimap<String, String>) entity;
|
||||||
|
request.getHeaders().putAll(userMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.rackspace.cloudfiles.functions;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
|
||||||
|
import org.jclouds.http.HttpException;
|
||||||
|
import org.jclouds.http.HttpResponse;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.CFObject;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses response headers and creates a new CFObject from them and the HTTP content.
|
||||||
|
*
|
||||||
|
* @see ParseMetadataFromHeaders
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
public class ParseObjectFromHeadersAndHttpContent implements Function<HttpResponse, CFObject> {
|
||||||
|
private final ParseObjectMetadataFromHeaders metadataParser;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ParseObjectFromHeadersAndHttpContent(ParseObjectMetadataFromHeaders metadataParser) {
|
||||||
|
this.metadataParser = metadataParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First, calls {@link ParseMetadataFromHeaders}.
|
||||||
|
*
|
||||||
|
* Then, sets the object size based on the Content-Length header and adds the content to the
|
||||||
|
* {@link S3Object} result.
|
||||||
|
*
|
||||||
|
* @throws org.jclouds.http.HttpException
|
||||||
|
*/
|
||||||
|
public CFObject apply(HttpResponse from) {
|
||||||
|
CFObject.Metadata metadata = metadataParser.apply(from);
|
||||||
|
CFObject object = new CFObject(metadata, from.getContent());
|
||||||
|
parseContentLengthOrThrowException(from, object);
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void parseContentLengthOrThrowException(HttpResponse from, CFObject object) throws HttpException {
|
||||||
|
String contentLength = from.getFirstHeaderOrNull(HttpHeaders.CONTENT_LENGTH);
|
||||||
|
String contentRange = from.getFirstHeaderOrNull("Content-Range");
|
||||||
|
if (contentLength == null)
|
||||||
|
throw new HttpException(HttpHeaders.CONTENT_LENGTH + " header not present in headers: "
|
||||||
|
+ from.getHeaders());
|
||||||
|
object.setContentLength(Long.parseLong(contentLength));
|
||||||
|
|
||||||
|
if (contentRange == null) {
|
||||||
|
object.getMetadata().setSize(object.getContentLength());
|
||||||
|
} else {
|
||||||
|
object.setContentRange(contentRange);
|
||||||
|
object.getMetadata().setSize(
|
||||||
|
Long.parseLong(contentRange.substring(contentRange.lastIndexOf('/') + 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.rackspace.cloudfiles.functions;
|
||||||
|
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
|
||||||
|
import org.jclouds.http.HttpException;
|
||||||
|
import org.jclouds.http.HttpResponse;
|
||||||
|
import org.jclouds.http.HttpUtils;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.CFObject;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.CFObject.Metadata;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.reference.CloudFilesHeaders;
|
||||||
|
import org.jclouds.util.DateService;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This parses @{link {@link CFObject.Metadata} from HTTP headers.
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
public class ParseObjectMetadataFromHeaders implements Function<HttpResponse, CFObject.Metadata> {
|
||||||
|
private final DateService dateParser;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ParseObjectMetadataFromHeaders(DateService dateParser) {
|
||||||
|
this.dateParser = dateParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* parses the http response headers to create a new
|
||||||
|
* {@link CFObject.Metadata} object.
|
||||||
|
*/
|
||||||
|
public Metadata apply(HttpResponse from) {
|
||||||
|
// URL Path components: /<api version>/<account>/<container>/<object key>
|
||||||
|
String[] pathElements = from.getRequestURL().getPath().split("/");
|
||||||
|
String objectKey = from.getRequestURL().getPath().substring(
|
||||||
|
(pathElements[1] + pathElements[2] + pathElements[3]).length() + 4);
|
||||||
|
|
||||||
|
CFObject.Metadata to = new CFObject.Metadata(objectKey);
|
||||||
|
addAllHeadersTo(from, to);
|
||||||
|
|
||||||
|
addUserMetadataTo(from, to);
|
||||||
|
addETagTo(from, to);
|
||||||
|
|
||||||
|
parseLastModifiedOrThrowException(from, to);
|
||||||
|
setContentTypeOrThrowException(from, to);
|
||||||
|
setContentLengthOrThrowException(from, to);
|
||||||
|
|
||||||
|
to.setCacheControl(from.getFirstHeaderOrNull(HttpHeaders.CACHE_CONTROL));
|
||||||
|
to.setContentDisposition(from.getFirstHeaderOrNull("Content-Disposition"));
|
||||||
|
to.setContentEncoding(from.getFirstHeaderOrNull(HttpHeaders.CONTENT_ENCODING));
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAllHeadersTo(HttpResponse from, Metadata metadata) {
|
||||||
|
metadata.getAllHeaders().putAll(from.getHeaders());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setContentTypeOrThrowException(HttpResponse from, Metadata metadata)
|
||||||
|
throws HttpException {
|
||||||
|
String contentType = from.getFirstHeaderOrNull(HttpHeaders.CONTENT_TYPE);
|
||||||
|
if (contentType == null)
|
||||||
|
throw new HttpException(HttpHeaders.CONTENT_TYPE + " not found in headers");
|
||||||
|
else
|
||||||
|
metadata.setContentType(contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setContentLengthOrThrowException(HttpResponse from, Metadata metadata)
|
||||||
|
throws HttpException {
|
||||||
|
String contentLength = from.getFirstHeaderOrNull(HttpHeaders.CONTENT_LENGTH);
|
||||||
|
if (contentLength == null)
|
||||||
|
throw new HttpException(HttpHeaders.CONTENT_LENGTH + " not found in headers");
|
||||||
|
else
|
||||||
|
metadata.setSize(Long.parseLong(contentLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseLastModifiedOrThrowException(HttpResponse from, Metadata metadata)
|
||||||
|
throws HttpException {
|
||||||
|
String lastModified = from.getFirstHeaderOrNull(HttpHeaders.LAST_MODIFIED);
|
||||||
|
metadata.setLastModified(dateParser.rfc822DateParse(lastModified));
|
||||||
|
if (metadata.getLastModified() == null)
|
||||||
|
throw new HttpException("could not parse: " + HttpHeaders.LAST_MODIFIED + ": "
|
||||||
|
+ lastModified);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addETagTo(HttpResponse from, Metadata metadata) {
|
||||||
|
String eTag = from.getFirstHeaderOrNull("Etag"); // TODO: Should be HttpHeaders.ETAG
|
||||||
|
if (metadata.getETag() == null && eTag != null) {
|
||||||
|
metadata.setETag(HttpUtils.fromHexString(eTag.replaceAll("\"", "")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addUserMetadataTo(HttpResponse from, Metadata metadata) {
|
||||||
|
for (Entry<String, String> header : from.getHeaders().entries()) {
|
||||||
|
if (header.getKey() != null
|
||||||
|
&& header.getKey().startsWith(CloudFilesHeaders.USER_METADATA_PREFIX))
|
||||||
|
{
|
||||||
|
metadata.getUserMetadata().put(header.getKey(), header.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.rackspace.cloudfiles.functions;
|
||||||
|
|
||||||
|
import org.jclouds.http.HttpResponseException;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.CFObject.Metadata;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
public class ReturnCFObjectMetadataNotFoundOn404 implements Function<Exception, Metadata> {
|
||||||
|
|
||||||
|
public Metadata apply(Exception from) {
|
||||||
|
if (from instanceof HttpResponseException) {
|
||||||
|
HttpResponseException responseException = (HttpResponseException) from;
|
||||||
|
if (responseException.getResponse().getStatusCode() == 404) {
|
||||||
|
return Metadata.NOT_FOUND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.rackspace.cloudfiles.functions;
|
||||||
|
|
||||||
|
import org.jclouds.http.HttpResponseException;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.CFObject;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
public class ReturnS3ObjectNotFoundOn404 implements Function<Exception, CFObject> {
|
||||||
|
|
||||||
|
public CFObject apply(Exception from) {
|
||||||
|
if (from instanceof HttpResponseException) {
|
||||||
|
HttpResponseException responseException = (HttpResponseException) from;
|
||||||
|
if (responseException.getResponse().getStatusCode() == 404) {
|
||||||
|
return CFObject.NOT_FOUND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.rackspace.cloudfiles.functions;
|
||||||
|
|
||||||
|
import org.jclouds.http.HttpResponse;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
public class ReturnTrueOn202FalseOtherwise implements Function<HttpResponse, Boolean> {
|
||||||
|
|
||||||
|
public Boolean apply(HttpResponse from) {
|
||||||
|
return (from.getStatusCode() == 202);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -31,9 +31,11 @@ import static org.testng.Assert.assertTrue;
|
||||||
import static org.testng.Assert.fail;
|
import static org.testng.Assert.fail;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.jclouds.http.HttpResponseException;
|
import org.jclouds.http.HttpResponseException;
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
|
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
|
||||||
|
@ -45,6 +47,10 @@ import org.jclouds.rackspace.cloudfiles.reference.CloudFilesHeaders;
|
||||||
import org.testng.annotations.BeforeGroups;
|
import org.testng.annotations.BeforeGroups;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import com.google.common.collect.HashMultimap;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests behavior of {@code JaxrsAnnotationProcessor}
|
* Tests behavior of {@code JaxrsAnnotationProcessor}
|
||||||
*
|
*
|
||||||
|
@ -142,26 +148,65 @@ public class CloudFilesConnectionLiveTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPutAndDeleteObjects() throws Exception {
|
public void testObjectOperations() throws Exception {
|
||||||
String containerName = bucketPrefix + ".testPutAndDeleteObjects";
|
String containerName = bucketPrefix + ".testObjectOperations";
|
||||||
String data = "Here is my data";
|
String data = "Here is my data";
|
||||||
|
|
||||||
assertTrue(connection.putContainer(containerName));
|
assertTrue(connection.putContainer(containerName));
|
||||||
|
|
||||||
// Test with string data, ETag hash, and a piece of metadata
|
// Test PUT with string data, ETag hash, and a piece of metadata
|
||||||
CFObject object = new CFObject("object", data);
|
CFObject object = new CFObject("object", data);
|
||||||
object.setContentLength(data.length());
|
object.setContentLength(data.length());
|
||||||
object.generateETag();
|
object.generateETag();
|
||||||
object.getMetadata().setContentType("text/plain");
|
object.getMetadata().setContentType("text/plain");
|
||||||
// TODO: Metadata values aren't being stored by CF, but the names are. Odd...
|
|
||||||
object.getMetadata().getUserMetadata().put(
|
object.getMetadata().getUserMetadata().put(
|
||||||
CloudFilesHeaders.USER_METADATA_PREFIX + "metadata", "metadata-value");
|
CloudFilesHeaders.USER_METADATA_PREFIX + "metadata", "metadata-value");
|
||||||
byte[] md5 = connection.putObject(containerName, object).get(10, TimeUnit.SECONDS);
|
byte[] md5 = connection.putObject(containerName, object).get(10, TimeUnit.SECONDS);
|
||||||
assertEquals(HttpUtils.toHexString(md5), HttpUtils
|
assertEquals(HttpUtils.toHexString(md5),
|
||||||
.toHexString(object.getMetadata().getETag()));
|
HttpUtils.toHexString(object.getMetadata().getETag()));
|
||||||
// TODO: Get and confirm data
|
|
||||||
|
// Test HEAD of missing object
|
||||||
|
CFObject.Metadata metadata = connection.headObject(containerName, "non-existent-object");
|
||||||
|
assertEquals(metadata, CFObject.Metadata.NOT_FOUND);
|
||||||
|
|
||||||
|
// Test HEAD of object
|
||||||
|
metadata = connection.headObject(containerName, object.getKey());
|
||||||
|
assertEquals(metadata.getKey(), object.getKey());
|
||||||
|
assertEquals(metadata.getSize(), data.length());
|
||||||
|
assertEquals(metadata.getContentType(), "text/plain");
|
||||||
|
assertEquals(metadata.getETag(), object.getMetadata().getETag());
|
||||||
|
assertEquals(metadata.getUserMetadata().entries().size(), 1);
|
||||||
|
// Notice the quirk where CF changes the case of returned metadata names
|
||||||
|
assertEquals(Iterables.getLast(metadata.getUserMetadata().get(
|
||||||
|
CloudFilesHeaders.USER_METADATA_PREFIX + "Metadata")),
|
||||||
|
"metadata-value");
|
||||||
|
|
||||||
// Test with invalid ETag (as if object's data was corrupted in transit)
|
// Test POST to update object's metadata
|
||||||
|
Multimap<String, String> userMetadata = HashMultimap.create();
|
||||||
|
userMetadata.put(CloudFilesHeaders.USER_METADATA_PREFIX + "new-metadata-1", "value-1");
|
||||||
|
userMetadata.put(CloudFilesHeaders.USER_METADATA_PREFIX + "new-metadata-2", "value-2");
|
||||||
|
assertTrue(connection.setObjectMetadata(containerName, object.getKey(), userMetadata));
|
||||||
|
|
||||||
|
// Test GET of missing object
|
||||||
|
CFObject getObject = connection.getObject(containerName, "non-existent-object")
|
||||||
|
.get(10, TimeUnit.SECONDS);
|
||||||
|
assertEquals(getObject, CFObject.NOT_FOUND);
|
||||||
|
|
||||||
|
// Test GET of object (including updated metadata)
|
||||||
|
getObject = connection.getObject(containerName, object.getKey()).get(10, TimeUnit.SECONDS);
|
||||||
|
assertEquals(IOUtils.toString((InputStream)getObject.getData()), data);
|
||||||
|
assertEquals(getObject.getKey(), object.getKey());
|
||||||
|
assertEquals(getObject.getContentLength(), data.length());
|
||||||
|
assertEquals(getObject.getMetadata().getContentType(), "text/plain");
|
||||||
|
assertEquals(getObject.getMetadata().getETag(), object.getMetadata().getETag());
|
||||||
|
assertEquals(getObject.getMetadata().getUserMetadata().entries().size(), 2);
|
||||||
|
// Notice the quirk where CF changes the case of sreturned metadata names
|
||||||
|
assertEquals(Iterables.getLast(getObject.getMetadata().getUserMetadata().get(
|
||||||
|
CloudFilesHeaders.USER_METADATA_PREFIX + "New-Metadata-1")), "value-1");
|
||||||
|
assertEquals(Iterables.getLast(getObject.getMetadata().getUserMetadata().get(
|
||||||
|
CloudFilesHeaders.USER_METADATA_PREFIX + "New-Metadata-2")), "value-2");
|
||||||
|
|
||||||
|
// Test PUT with invalid ETag (as if object's data was corrupted in transit)
|
||||||
String correctEtag = HttpUtils.toHexString(object.getMetadata().getETag());
|
String correctEtag = HttpUtils.toHexString(object.getMetadata().getETag());
|
||||||
String incorrectEtag = "0" + correctEtag.substring(1);
|
String incorrectEtag = "0" + correctEtag.substring(1);
|
||||||
object.getMetadata().setETag(HttpUtils.fromHexString(incorrectEtag));
|
object.getMetadata().setETag(HttpUtils.fromHexString(incorrectEtag));
|
||||||
|
@ -172,7 +217,7 @@ public class CloudFilesConnectionLiveTest {
|
||||||
assertEquals(((HttpResponseException) e.getCause()).getResponse().getStatusCode(), 422);
|
assertEquals(((HttpResponseException) e.getCause()).getResponse().getStatusCode(), 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test chunked/streamed upload with data of "unknown" length
|
// Test PUT chunked/streamed upload with data of "unknown" length
|
||||||
ByteArrayInputStream bais = new ByteArrayInputStream(data.getBytes("UTF-8"));
|
ByteArrayInputStream bais = new ByteArrayInputStream(data.getBytes("UTF-8"));
|
||||||
object = new CFObject("chunked-object", bais);
|
object = new CFObject("chunked-object", bais);
|
||||||
md5 = connection.putObject(containerName, object).get(10, TimeUnit.SECONDS);
|
md5 = connection.putObject(containerName, object).get(10, TimeUnit.SECONDS);
|
||||||
|
|
Loading…
Reference in New Issue