diff --git a/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3Connection.java b/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3Connection.java index 1992d78b5f..ad174747ce 100644 --- a/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3Connection.java +++ b/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3Connection.java @@ -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 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 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); /** diff --git a/core/src/main/java/org/jclouds/http/binders/JsonBinder.java b/core/src/main/java/org/jclouds/http/binders/JsonBinder.java new file mode 100644 index 0000000000..a4abdcc4f6 --- /dev/null +++ b/core/src/main/java/org/jclouds/http/binders/JsonBinder.java @@ -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 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)); + } + +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/rest/EntityBinder.java b/core/src/main/java/org/jclouds/rest/EntityBinder.java index 59e7dc4243..d190a55151 100644 --- a/core/src/main/java/org/jclouds/rest/EntityBinder.java +++ b/core/src/main/java/org/jclouds/rest/EntityBinder.java @@ -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); } diff --git a/core/src/main/java/org/jclouds/rest/JaxrsAnnotationProcessor.java b/core/src/main/java/org/jclouds/rest/JaxrsAnnotationProcessor.java index 3df278e686..8956937caa 100644 --- a/core/src/main/java/org/jclouds/rest/JaxrsAnnotationProcessor.java +++ b/core/src/main/java/org/jclouds/rest/JaxrsAnnotationProcessor.java @@ -88,7 +88,8 @@ public class JaxrsAnnotationProcessor { private final Map>> methodToIndexOfParamToHeaderParamAnnotations = createMethodToIndexOfParamToAnnotation(HeaderParam.class); private final Map>> methodToIndexOfParamToHostPrefixParamAnnotations = createMethodToIndexOfParamToAnnotation(HostPrefixParam.class); private final Map>> methodToindexOfParamToPathParamAnnotations = createMethodToIndexOfParamToAnnotation(PathParam.class); - private final Map>> methodToindexOfParamToPathParamParserAnnotations = createMethodToIndexOfParamToAnnotation(PathParamParser.class); + private final Map>> methodToindexOfParamToPostParamAnnotations = createMethodToIndexOfParamToAnnotation(PostParam.class); + private final Map>> methodToindexOfParamToParamParserAnnotations = createMethodToIndexOfParamToAnnotation(ParamParser.class); static Map>> createMethodToIndexOfParamToAnnotation( final Class 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 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 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> indexToPathParam = methodToindexOfParamToPathParamAnnotations .get(method); - Map> indexToPathParamExtractor = methodToindexOfParamToPathParamParserAnnotations + Map> indexToParamExtractor = methodToindexOfParamToParamParserAnnotations .get(method); for (Entry> entry : indexToPathParam.entrySet()) { for (Annotation key : entry.getValue()) { - Set extractors = indexToPathParamExtractor.get(entry.getKey()); + Set 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 buildPostParams(Method method, Object[] args) { + Map postParams = Maps.newHashMap(); + Map> indexToPathParam = methodToindexOfParamToPostParamAnnotations + .get(method); + Map> indexToParamExtractor = methodToindexOfParamToParamParserAnnotations + .get(method); + for (Entry> entry : indexToPathParam.entrySet()) { + for (Annotation key : entry.getValue()) { + Set 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; + } } diff --git a/core/src/main/java/org/jclouds/rest/PathParamParser.java b/core/src/main/java/org/jclouds/rest/ParamParser.java old mode 100644 new mode 100755 similarity index 94% rename from core/src/main/java/org/jclouds/rest/PathParamParser.java rename to core/src/main/java/org/jclouds/rest/ParamParser.java index 568c79b73d..1cbaac86be --- a/core/src/main/java/org/jclouds/rest/PathParamParser.java +++ b/core/src/main/java/org/jclouds/rest/ParamParser.java @@ -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> value(); } diff --git a/core/src/main/java/org/jclouds/rest/PostBinder.java b/core/src/main/java/org/jclouds/rest/PostBinder.java new file mode 100644 index 0000000000..64ef89cede --- /dev/null +++ b/core/src/main/java/org/jclouds/rest/PostBinder.java @@ -0,0 +1,46 @@ +/** + * + * 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.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 value(); +} diff --git a/core/src/main/java/org/jclouds/rest/PostEntityBinder.java b/core/src/main/java/org/jclouds/rest/PostEntityBinder.java new file mode 100644 index 0000000000..01ed6a599e --- /dev/null +++ b/core/src/main/java/org/jclouds/rest/PostEntityBinder.java @@ -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 postParams, HttpRequest request); + +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/rest/PostParam.java b/core/src/main/java/org/jclouds/rest/PostParam.java new file mode 100644 index 0000000000..0209b4543e --- /dev/null +++ b/core/src/main/java/org/jclouds/rest/PostParam.java @@ -0,0 +1,45 @@ +/** + * + * 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.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(); +} diff --git a/core/src/test/java/org/jclouds/http/BaseHttpCommandExecutorServiceTest.java b/core/src/test/java/org/jclouds/http/BaseHttpCommandExecutorServiceTest.java index 598a0fef95..4014124f9c 100644 --- a/core/src/test/java/org/jclouds/http/BaseHttpCommandExecutorServiceTest.java +++ b/core/src/test/java/org/jclouds/http/BaseHttpCommandExecutorServiceTest.java @@ -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 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 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 put = client.upload("", "foo"); - assertEquals(put.get(10, TimeUnit.SECONDS), new Boolean(true)); + Future 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 put = client.upload("redirect", "foo"); - assertEquals(put.get(10, TimeUnit.SECONDS), new Boolean(true)); + Future put = client.upload("redirect", "foo"); + assertEquals(put.get(10, TimeUnit.SECONDS).trim(), "fooPUTREDIRECT"); } @Test(invocationCount = 50, timeOut = 5000) diff --git a/core/src/test/java/org/jclouds/http/BaseJettyTest.java b/core/src/test/java/org/jclouds/http/BaseJettyTest.java index a7b21f7874..d76f76058b 100644 --- a/core/src/test/java/org/jclouds/http/BaseJettyTest.java +++ b/core/src/test/java/org/jclouds/http/BaseJettyTest.java @@ -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"); } diff --git a/core/src/test/java/org/jclouds/http/IntegrationTestClient.java b/core/src/test/java/org/jclouds/http/IntegrationTestClient.java index 30490a7642..1cb4e733f6 100644 --- a/core/src/test/java/org/jclouds/http/IntegrationTestClient.java +++ b/core/src/test/java/org/jclouds/http/IntegrationTestClient.java @@ -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 upload(@PathParam("id") String id, @EntityParam String toPut); + Future upload(@PathParam("id") String id, @EntityParam String toPut); + + @POST + @Path("objects/{id}") + Future post(@PathParam("id") String id, @EntityParam String toPut); + + @POST + @Path("objects/{id}") + @PostBinder(JsonBinder.class) + Future 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 download(@PathParam("id") String id, @HeaderParam("test") String header); diff --git a/core/src/test/java/org/jclouds/rest/JaxrsAnnotationProcessorTest.java b/core/src/test/java/org/jclouds/rest/JaxrsAnnotationProcessorTest.java index 30ecd968b0..dd577c3e82 100644 --- a/core/src/test/java/org/jclouds/rest/JaxrsAnnotationProcessorTest.java +++ b/core/src/test/java/org/jclouds/rest/JaxrsAnnotationProcessorTest.java @@ -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 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 put(@PathParam("id") @PathParamParser(FirstCharacter.class) String id, + public Future put(@PathParam("id") @ParamParser(FirstCharacter.class) String id, @EntityParam String payload) { return null; } diff --git a/extensions/gae/src/test/java/org/jclouds/gae/GaeHttpCommandExecutorServiceIntegrationTest.java b/extensions/gae/src/test/java/org/jclouds/gae/GaeHttpCommandExecutorServiceIntegrationTest.java index c2209e07b9..fa7091b974 100644 --- a/extensions/gae/src/test/java/org/jclouds/gae/GaeHttpCommandExecutorServiceIntegrationTest.java +++ b/extensions/gae/src/test/java/org/jclouds/gae/GaeHttpCommandExecutorServiceIntegrationTest.java @@ -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) { } + } \ No newline at end of file diff --git a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesConnection.java b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesConnection.java index c604302fdf..39a8690e10 100644 --- a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesConnection.java +++ b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesConnection.java @@ -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 putObject( @PathParam("container") String container, - @PathParam("key") @PathParamParser(CFObjectKey.class) @EntityParam(CFObjectBinder.class) + @PathParam("key") @ParamParser(CFObjectKey.class) @EntityParam(CFObjectBinder.class) CFObject object); @HEAD