Issue 76: POST support; Added PostParam PostBinder and renamed PathParamParser to ParamParser so that it can be reused for post.

git-svn-id: http://jclouds.googlecode.com/svn/trunk@1644 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-07-18 14:52:19 +00:00
parent 205a929526
commit 5a548f5a7e
14 changed files with 385 additions and 28 deletions

View File

@ -66,7 +66,7 @@ import org.jclouds.rest.ExceptionParser;
import org.jclouds.rest.Header;
import org.jclouds.rest.HostPrefixParam;
import org.jclouds.rest.HttpRequestOptionsBinder;
import org.jclouds.rest.PathParamParser;
import org.jclouds.rest.ParamParser;
import org.jclouds.rest.Query;
import org.jclouds.rest.RequestFilters;
import org.jclouds.rest.ResponseParser;
@ -212,7 +212,7 @@ public interface S3Connection {
@ResponseParser(ParseETagHeader.class)
Future<byte[]> putObject(
@HostPrefixParam String bucketName,
@PathParam("key") @PathParamParser(S3ObjectKey.class) @EntityParam(S3ObjectBinder.class) S3Object object);
@PathParam("key") @ParamParser(S3ObjectKey.class) @EntityParam(S3ObjectBinder.class) S3Object object);
/**
* Like {@link #putObject(String, S3Object)} except you can use {@link PutObjectOptions} to
@ -234,7 +234,7 @@ public interface S3Connection {
@ResponseParser(ParseETagHeader.class)
Future<byte[]> putObject(
@HostPrefixParam String bucketName,
@PathParam("key") @PathParamParser(S3ObjectKey.class) @EntityParam(S3ObjectBinder.class) S3Object object,
@PathParam("key") @ParamParser(S3ObjectKey.class) @EntityParam(S3ObjectBinder.class) S3Object object,
PutObjectOptions options);
/**

View File

@ -0,0 +1,42 @@
package org.jclouds.http.binders;
import java.util.Collections;
import java.util.Map;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.PostEntityBinder;
import com.google.gson.Gson;
import com.google.inject.Inject;
/**
* Binds the object to the request as a json object.
*
* @author adriancole
* @since 4.0
*/
public class JsonBinder implements PostEntityBinder {
protected final Gson gson;
@Inject
public JsonBinder(Gson gson) {
this.gson = gson;
}
public void addEntityToRequest(Map<String, String> postParams, HttpRequest request) {
addEntityToRequest((Object) postParams, request);
}
public void addEntityToRequest(Object toBind, HttpRequest request) {
String json = gson.toJson(toBind);
request.setEntity(json);
request.getHeaders().replaceValues(HttpHeaders.CONTENT_LENGTH,
Collections.singletonList(json.getBytes().length + ""));
request.getHeaders().replaceValues(HttpHeaders.CONTENT_TYPE,
Collections.singletonList(MediaType.APPLICATION_JSON));
}
}

View File

@ -31,5 +31,5 @@ import org.jclouds.http.HttpRequest;
* @author Adrian Cole
*/
public interface EntityBinder {
public void addEntityToRequest(Object args, HttpRequest request);
public void addEntityToRequest(Object toBind, HttpRequest request);
}

View File

@ -88,7 +88,8 @@ public class JaxrsAnnotationProcessor {
private final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToHeaderParamAnnotations = createMethodToIndexOfParamToAnnotation(HeaderParam.class);
private final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToHostPrefixParamAnnotations = createMethodToIndexOfParamToAnnotation(HostPrefixParam.class);
private final Map<Method, Map<Integer, Set<Annotation>>> methodToindexOfParamToPathParamAnnotations = createMethodToIndexOfParamToAnnotation(PathParam.class);
private final Map<Method, Map<Integer, Set<Annotation>>> methodToindexOfParamToPathParamParserAnnotations = createMethodToIndexOfParamToAnnotation(PathParamParser.class);
private final Map<Method, Map<Integer, Set<Annotation>>> methodToindexOfParamToPostParamAnnotations = createMethodToIndexOfParamToAnnotation(PostParam.class);
private final Map<Method, Map<Integer, Set<Annotation>>> methodToindexOfParamToParamParserAnnotations = createMethodToIndexOfParamToAnnotation(ParamParser.class);
static Map<Method, Map<Integer, Set<Annotation>>> createMethodToIndexOfParamToAnnotation(
final Class<? extends Annotation> annotation) {
@ -184,7 +185,8 @@ public class JaxrsAnnotationProcessor {
methodToIndexOfParamToHeaderParamAnnotations.get(method).get(index);
methodToIndexOfParamToHostPrefixParamAnnotations.get(method).get(index);
methodToindexOfParamToPathParamAnnotations.get(method).get(index);
methodToindexOfParamToPathParamParserAnnotations.get(method).get(index);
methodToindexOfParamToPostParamAnnotations.get(method).get(index);
methodToindexOfParamToParamParserAnnotations.get(method).get(index);
methodToIndexesOfOptions.get(method);
}
} else if (isConstantDeclaration(method)) {
@ -310,6 +312,18 @@ public class JaxrsAnnotationProcessor {
return null;
}
public PostEntityBinder getPostEntityBinderOrNull(Method method, Object[] args) {
for (Object arg : args) {
if (arg instanceof PostEntityBinder)
return (PostEntityBinder) arg;
}
PostBinder annotation = method.getAnnotation(PostBinder.class);
if (annotation != null) {
return injector.getInstance(annotation.value());
}
return null;
}
private Map<String, String> constants = Maps.newHashMap();
public boolean isHttpMethod(Method method) {
@ -347,9 +361,18 @@ public class JaxrsAnnotationProcessor {
public HttpRequest buildEntityIfPostOrPutRequest(Method method, Object[] args,
HttpRequest request) {
switch (request.getMethod()) {
case PUT:
case POST:
PostEntityBinder postBinder = null;
Map<String, String> postParams = buildPostParams(method, args);
// post binder is only useful if there are parameters. We guard here in case the
// PostEntityBinder is also an EntityBinder. If so, it can be used with or without
// parameters.
if (postParams.size() > 0
&& (postBinder = this.getPostEntityBinderOrNull(method, args)) != null) {
postBinder.addEntityToRequest(postParams, request);
break;
}
case PUT:
HttpRequestOptions options = findOptionsIn(method, args);
if (options != null) {
optionsBinder.addEntityToRequest(options, request);
@ -455,14 +478,14 @@ public class JaxrsAnnotationProcessor {
pathParamValues.putAll(constants);
Map<Integer, Set<Annotation>> indexToPathParam = methodToindexOfParamToPathParamAnnotations
.get(method);
Map<Integer, Set<Annotation>> indexToPathParamExtractor = methodToindexOfParamToPathParamParserAnnotations
Map<Integer, Set<Annotation>> indexToParamExtractor = methodToindexOfParamToParamParserAnnotations
.get(method);
for (Entry<Integer, Set<Annotation>> entry : indexToPathParam.entrySet()) {
for (Annotation key : entry.getValue()) {
Set<Annotation> extractors = indexToPathParamExtractor.get(entry.getKey());
Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
if (extractors != null && extractors.size() > 0) {
PathParamParser extractor = (PathParamParser) extractors.iterator().next();
ParamParser extractor = (ParamParser) extractors.iterator().next();
pathParamValues.put(((PathParam) key).value(), injector.getInstance(
extractor.value()).apply(args[entry.getKey()]));
} else {
@ -479,4 +502,28 @@ public class JaxrsAnnotationProcessor {
}
return pathParamValues;
}
private Map<String, String> buildPostParams(Method method, Object[] args) {
Map<String, String> postParams = Maps.newHashMap();
Map<Integer, Set<Annotation>> indexToPathParam = methodToindexOfParamToPostParamAnnotations
.get(method);
Map<Integer, Set<Annotation>> indexToParamExtractor = methodToindexOfParamToParamParserAnnotations
.get(method);
for (Entry<Integer, Set<Annotation>> entry : indexToPathParam.entrySet()) {
for (Annotation key : entry.getValue()) {
Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
if (extractors != null && extractors.size() > 0) {
ParamParser extractor = (ParamParser) extractors.iterator().next();
postParams.put(((PathParam) key).value(), injector.getInstance(extractor.value())
.apply(args[entry.getKey()]));
} else {
String paramKey = ((PostParam) key).value();
String paramValue = args[entry.getKey()].toString();
postParams.put(paramKey, paramValue);
}
}
}
return postParams;
}
}

View File

@ -34,13 +34,13 @@ import javax.ws.rs.PathParam;
import com.google.common.base.Function;
/**
* Extracts the value of a path parameter from an object.
* Extracts the value of a parameter from an object.
*
* @see PathParam
* @author Adrian Cole
*/
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface PathParamParser {
public @interface ParamParser {
Class<? extends Function<Object, String>> value();
}

View File

@ -0,0 +1,46 @@
/**
*
* 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.rest;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* Designates that this parameter will hold the entity for a PUT or POST command.
*
* @author Adrian Cole
*/
@Target(METHOD)
@Retention(RUNTIME)
public @interface PostBinder {
/**
* How to bind {@link PostParam} values, if there is no {@link PostEntityBinder} in the method
* definition
*/
Class<? extends PostEntityBinder> value();
}

View File

@ -0,0 +1,22 @@
package org.jclouds.rest;
import java.util.Map;
import org.jclouds.http.HttpRequest;
/**
* Builds the entity of a Post request.
*
* @author Adrian Cole
*
*/
public interface PostEntityBinder extends EntityBinder {
/**
* creates and binds the POST entity to the request using parameters specified.
*
* @see PostParam
*/
public void addEntityToRequest(Map<String,String> postParams, HttpRequest request);
}

View File

@ -0,0 +1,45 @@
/**
*
* 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.rest;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* Designates that this parameter will hold the entity for a PUT or POST command.
*
* @author Adrian Cole
*/
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface PostParam {
/**
* The key used in a map passed to the {@link PostEntityBinder} associated with the request.
*/
String value();
}

View File

@ -101,18 +101,32 @@ public abstract class BaseHttpCommandExecutorServiceTest extends BaseJettyTest {
// TODO assert misses are only one, as permanent redirects paths should be remembered.
}
@Test(invocationCount = 50, timeOut = 5000)
public void testPost() throws MalformedURLException, ExecutionException, InterruptedException,
TimeoutException {
Future<String> put = client.post("", "foo");
assertEquals(put.get(10, TimeUnit.SECONDS).trim(), "fooPOST");
}
@Test(invocationCount = 50, timeOut = 5000)
public void testPostBinder() throws MalformedURLException, ExecutionException,
InterruptedException, TimeoutException {
Future<String> put = client.postJson("", "foo");
assertEquals(put.get(10, TimeUnit.SECONDS).trim(), "{\"key\":\"foo\"}POST");
}
@Test(invocationCount = 50, timeOut = 5000)
public void testPut() throws MalformedURLException, ExecutionException, InterruptedException,
TimeoutException {
Future<Boolean> put = client.upload("", "foo");
assertEquals(put.get(10, TimeUnit.SECONDS), new Boolean(true));
Future<String> put = client.upload("", "foo");
assertEquals(put.get(10, TimeUnit.SECONDS).trim(), "fooPUT");
}
@Test(invocationCount = 50, timeOut = 5000)
public void testPutRedirect() throws MalformedURLException, ExecutionException,
InterruptedException, TimeoutException {
Future<Boolean> put = client.upload("redirect", "foo");
assertEquals(put.get(10, TimeUnit.SECONDS), new Boolean(true));
Future<String> put = client.upload("redirect", "foo");
assertEquals(put.get(10, TimeUnit.SECONDS).trim(), "fooPUTREDIRECT");
}
@Test(invocationCount = 50, timeOut = 5000)

View File

@ -73,11 +73,23 @@ public abstract class BaseJettyTest {
Handler server1Handler = new AbstractHandler() {
public void handle(String target, HttpServletRequest request,
HttpServletResponse response, int dispatch) throws IOException, ServletException {
if (failIfNoContentLength(request, response))
if (failIfNoContentLength(request, response)) {
return;
else if (request.getMethod().equals("PUT")) {
} else if (target.indexOf("redirect") > 0) {
response.sendRedirect("http://localhost:" + (testPort + 1));
} else if (request.getMethod().equals("PUT")) {
if (request.getContentLength() > 0) {
Utils.toStringAndClose(request.getInputStream());
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(
Utils.toStringAndClose(request.getInputStream()) + "PUT");
} else {
response.sendError(500, "no content");
}
} else if (request.getMethod().equals("POST")) {
if (request.getContentLength() > 0) {
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(
Utils.toStringAndClose(request.getInputStream()) + "POST");
} else {
response.sendError(500, "no content");
}
@ -87,8 +99,6 @@ public abstract class BaseJettyTest {
response.setContentType("text/plain");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("test");
} else if (target.indexOf("redirect") > 0) {
response.sendRedirect("http://localhost:" + (testPort + 1));
} else {
if (failOnRequest(request, response))
return;
@ -109,7 +119,9 @@ public abstract class BaseJettyTest {
HttpServletResponse response, int dispatch) throws IOException, ServletException {
if (request.getMethod().equals("PUT")) {
if (request.getContentLength() > 0) {
Utils.toStringAndClose(request.getInputStream());
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(
Utils.toStringAndClose(request.getInputStream()) + "PUTREDIRECT");
} else {
response.sendError(500, "no content");
}

View File

@ -28,14 +28,18 @@ import java.util.concurrent.Future;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import org.jclouds.http.binders.JsonBinder;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.http.options.HttpRequestOptions;
import org.jclouds.rest.EntityParam;
import org.jclouds.rest.ExceptionParser;
import org.jclouds.rest.PostBinder;
import org.jclouds.rest.PostParam;
import org.jclouds.rest.RequestFilters;
import org.jclouds.rest.XMLResponseParser;
@ -80,7 +84,16 @@ public interface IntegrationTestClient {
@PUT
@Path("objects/{id}")
Future<Boolean> upload(@PathParam("id") String id, @EntityParam String toPut);
Future<String> upload(@PathParam("id") String id, @EntityParam String toPut);
@POST
@Path("objects/{id}")
Future<String> post(@PathParam("id") String id, @EntityParam String toPut);
@POST
@Path("objects/{id}")
@PostBinder(JsonBinder.class)
Future<String> postJson(@PathParam("id") String id, @PostParam("key") String toPut);
@GET
@Path("objects/{id}")
@ -94,6 +107,7 @@ public interface IntegrationTestClient {
}
}
}
@GET
@Path("objects/{id}")
Future<String> download(@PathParam("id") String id, @HeaderParam("test") String header);

View File

@ -31,10 +31,12 @@ import java.lang.reflect.Method;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.Future;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
@ -46,6 +48,7 @@ import org.jclouds.http.HttpMethod;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.binders.JsonBinder;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.http.functions.ReturnStringIf200;
import org.jclouds.http.functions.ReturnTrueIf2xx;
@ -75,6 +78,101 @@ import com.google.inject.name.Names;
@Test(groups = "unit", testName = "jaxrs.JaxrsUtilTest")
public class JaxrsAnnotationProcessorTest {
public class TestPost {
@POST
public void post(@EntityParam String content) {
}
@POST
public void postAsJson(@EntityParam(JsonBinder.class) String content) {
}
@POST
@Path("{foo}")
public void postWithPath(@PathParam("foo") @PostParam("fooble") String path,
PostEntityBinder content) {
}
@POST
@Path("{foo}")
@PostBinder(JsonBinder.class)
public void postWithMethodBinder(@PathParam("foo") @PostParam("fooble") String path) {
}
}
public void testCreatePostRequest() throws SecurityException, NoSuchMethodException {
Method method = TestPost.class.getMethod("post", String.class);
URI endpoint = URI.create("http://localhost");
HttpRequest httpMethod = factory.create(TestPost.class).createRequest(endpoint, method,
new Object[] { "data" });
assertEquals(httpMethod.getEndpoint().getHost(), "localhost");
assertEquals(httpMethod.getEndpoint().getPath(), "");
assertEquals(httpMethod.getMethod(), HttpMethod.POST);
assertEquals(httpMethod.getHeaders().size(), 2);
assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_TYPE), Collections
.singletonList("application/unknown"));
assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_LENGTH), Collections
.singletonList("data".getBytes().length + ""));
assertEquals(httpMethod.getEntity(), "data");
}
public void testCreatePostJsonRequest() throws SecurityException, NoSuchMethodException {
Method method = TestPost.class.getMethod("postAsJson", String.class);
URI endpoint = URI.create("http://localhost");
HttpRequest httpMethod = factory.create(TestPost.class).createRequest(endpoint, method,
new Object[] { "data" });
assertEquals(httpMethod.getEndpoint().getHost(), "localhost");
assertEquals(httpMethod.getEndpoint().getPath(), "");
assertEquals(httpMethod.getMethod(), HttpMethod.POST);
assertEquals(httpMethod.getHeaders().size(), 2);
assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_TYPE), Collections
.singletonList("application/json"));
assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_LENGTH), Collections
.singletonList("\"data\"".getBytes().length + ""));
assertEquals(httpMethod.getEntity(), "\"data\"");
}
public void testCreatePostWithPathRequest() throws SecurityException, NoSuchMethodException {
Method method = TestPost.class
.getMethod("postWithPath", String.class, PostEntityBinder.class);
URI endpoint = URI.create("http://localhost");
HttpRequest httpMethod = factory.create(TestPost.class).createRequest(endpoint, method,
new Object[] { "data", new PostEntityBinder() {
public void addEntityToRequest(Map<String, String> postParams, HttpRequest request) {
request.setEntity(postParams.get("fooble"));
}
public void addEntityToRequest(Object toBind, HttpRequest request) {
throw new RuntimeException("this shouldn't be used in POST");
}
} });
assertEquals(httpMethod.getEndpoint().getHost(), "localhost");
assertEquals(httpMethod.getEndpoint().getPath(), "/data");
assertEquals(httpMethod.getMethod(), HttpMethod.POST);
assertEquals(httpMethod.getHeaders().size(), 0);
assertEquals(httpMethod.getEntity(), "data");
}
public void testCreatePostWithMethodBinder() throws SecurityException, NoSuchMethodException {
Method method = TestPost.class.getMethod("postWithMethodBinder", String.class);
URI endpoint = URI.create("http://localhost");
HttpRequest httpMethod = factory.create(TestPost.class).createRequest(endpoint, method,
new Object[] { "data", });
assertEquals(httpMethod.getEndpoint().getHost(), "localhost");
assertEquals(httpMethod.getEndpoint().getPath(), "/data");
assertEquals(httpMethod.getMethod(), HttpMethod.POST);
assertEquals(httpMethod.getHeaders().size(), 2);
assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_TYPE), Collections
.singletonList("application/json"));
String expected = "{\"fooble\":\"data\"}";
assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_LENGTH), Collections
.singletonList(expected.getBytes().length + ""));
assertEquals(httpMethod.getEntity(), expected);
}
static class TestRequestFilter1 implements HttpRequestFilter {
public void filter(HttpRequest request) throws HttpException {
@ -188,7 +286,7 @@ public class JaxrsAnnotationProcessorTest {
@GET
@Path("{path}")
public void onePathParamExtractor(
@PathParam("path") @PathParamParser(FirstCharacter.class) String path) {
@PathParam("path") @ParamParser(FirstCharacter.class) String path) {
}
}
@ -316,7 +414,7 @@ public class JaxrsAnnotationProcessorTest {
@PUT
@Path("/{id}")
public Future<String> put(@PathParam("id") @PathParamParser(FirstCharacter.class) String id,
public Future<String> put(@PathParam("id") @ParamParser(FirstCharacter.class) String id,
@EntityParam String payload) {
return null;
}

View File

@ -52,6 +52,14 @@ import com.google.inject.Module;
public class GaeHttpCommandExecutorServiceIntegrationTest extends
BaseHttpCommandExecutorServiceTest {
@Override
@Test(invocationCount = 50, timeOut = 3000)
public void testPostBinder() throws MalformedURLException, ExecutionException,
InterruptedException, TimeoutException {
setupApiProxy();
super.testPostBinder();
}
@BeforeTest
void validateExecutor() {
ExecutorService executorService = injector.getInstance(ExecutorService.class);
@ -124,6 +132,14 @@ public class GaeHttpCommandExecutorServiceIntegrationTest extends
super.testGetSynchException();
}
@Override
@Test(invocationCount = 50, timeOut = 3000)
public void testPost() throws MalformedURLException, ExecutionException, InterruptedException,
TimeoutException {
setupApiProxy();
super.testPost();
}
@Override
@Test(invocationCount = 50, timeOut = 3000)
public void testPut() throws MalformedURLException, ExecutionException, InterruptedException,
@ -207,4 +223,5 @@ public class GaeHttpCommandExecutorServiceIntegrationTest extends
@Override
protected void addConnectionProperties(Properties props) {
}
}

View File

@ -55,7 +55,7 @@ import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions;
import org.jclouds.rackspace.filters.AuthenticateRequest;
import org.jclouds.rest.EntityParam;
import org.jclouds.rest.ExceptionParser;
import org.jclouds.rest.PathParamParser;
import org.jclouds.rest.ParamParser;
import org.jclouds.rest.Query;
import org.jclouds.rest.RequestFilters;
import org.jclouds.rest.ResponseParser;
@ -109,7 +109,7 @@ public interface CloudFilesConnection {
@ResponseParser(ParseETagHeader.class)
Future<byte[]> putObject(
@PathParam("container") String container,
@PathParam("key") @PathParamParser(CFObjectKey.class) @EntityParam(CFObjectBinder.class)
@PathParam("key") @ParamParser(CFObjectKey.class) @EntityParam(CFObjectBinder.class)
CFObject object);
@HEAD