@@ -103,6 +112,29 @@ public interface CloudFilesConnection {
@PathParam("key") @PathParamParser(CFObjectKey.class) @EntityParam(CFObjectBinder.class)
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 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 userMetadata);
+
@DELETE
@ResponseParser(ReturnTrueOn204FalseOtherwise.class)
@ExceptionParser(ReturnTrueOn404FalseOtherwise.class)
diff --git a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/binders/CFObjectBinder.java b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/binders/CFObjectBinder.java
index a15a86b677..10cc2f9e7c 100644
--- a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/binders/CFObjectBinder.java
+++ b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/binders/CFObjectBinder.java
@@ -72,7 +72,7 @@ public class CFObjectBinder implements EntityBinder {
String hexETag = HttpUtils.toHexString(object.getMetadata().getETag());
request.getHeaders().put(HttpHeaders.ETAG, hexETag);
} 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);
}
}
diff --git a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/binders/UserMetadataBinder.java b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/binders/UserMetadataBinder.java
new file mode 100644
index 0000000000..9a375b89df
--- /dev/null
+++ b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/binders/UserMetadataBinder.java
@@ -0,0 +1,39 @@
+/**
+ *
+ * Copyright (C) 2009 Global Cloud Specialists, Inc.
+ *
+ * ====================================================================
+ * 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 userMetadata = (Multimap) entity;
+ request.getHeaders().putAll(userMetadata);
+ }
+
+}
diff --git a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseObjectFromHeadersAndHttpContent.java b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseObjectFromHeadersAndHttpContent.java
new file mode 100644
index 0000000000..107fa58b37
--- /dev/null
+++ b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseObjectFromHeadersAndHttpContent.java
@@ -0,0 +1,83 @@
+/**
+ *
+ * Copyright (C) 2009 Global Cloud Specialists, Inc.
+ *
+ * ====================================================================
+ * 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 {
+ 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)));
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseObjectMetadataFromHeaders.java b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseObjectMetadataFromHeaders.java
new file mode 100644
index 0000000000..3b47c46f86
--- /dev/null
+++ b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseObjectMetadataFromHeaders.java
@@ -0,0 +1,128 @@
+/**
+ *
+ * Copyright (C) 2009 Global Cloud Specialists, Inc.
+ *
+ * ====================================================================
+ * 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 {
+ 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: ////