mirror of https://github.com/apache/jclouds.git
Issue 76: added ParamParser on Methods and added ability for ResponseTransformers to implement RestContext which will give them access to the originating request and args
git-svn-id: http://jclouds.googlecode.com/svn/trunk@1931 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
parent
09b2c80cd2
commit
ac8c681eb1
|
@ -48,6 +48,7 @@ import javax.ws.rs.PathParam;
|
|||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriBuilderException;
|
||||
|
||||
import org.jboss.resteasy.util.IsHttpMethod;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
|
@ -161,7 +162,8 @@ public class JaxrsAnnotationProcessor {
|
|||
private final ParseSax.Factory parserFactory;
|
||||
|
||||
@VisibleForTesting
|
||||
public Function<HttpResponse, ?> createResponseParser(Method method) {
|
||||
public Function<HttpResponse, ?> createResponseParser(Method method, HttpRequest request,
|
||||
Object[] args) {
|
||||
Function<HttpResponse, ?> transformer;
|
||||
Class<? extends HandlerWithResult<?>> handler = getXMLTransformerOrNull(method);
|
||||
if (handler != null) {
|
||||
|
@ -169,6 +171,9 @@ public class JaxrsAnnotationProcessor {
|
|||
} else {
|
||||
transformer = injector.getInstance(getParserOrThrowException(method));
|
||||
}
|
||||
if (transformer instanceof RestContext) {
|
||||
((RestContext) transformer).setContext(request, args);
|
||||
}
|
||||
return transformer;
|
||||
}
|
||||
|
||||
|
@ -316,8 +321,12 @@ public class JaxrsAnnotationProcessor {
|
|||
} else {
|
||||
endPoint = builder.buildFromEncodedMap(getEncodedPathParamKeyValues(method, args));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("problem encoding parameters", e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalStateException(e);
|
||||
} catch (UriBuilderException e) {
|
||||
throw new IllegalStateException(e);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
HttpRequest request = new HttpRequest(httpMethod, endPoint, headers);
|
||||
addHostHeaderIfAnnotatedWithVirtualHost(headers, request.getEndpoint().getHost(), method);
|
||||
|
@ -609,22 +618,22 @@ public class JaxrsAnnotationProcessor {
|
|||
Headers header) throws UnsupportedEncodingException {
|
||||
for (int i = 0; i < header.keys().length; i++) {
|
||||
String value = header.values()[i];
|
||||
for (Entry<String, Object> tokenValue : getEncodedPathParamKeyValues(method, args)
|
||||
for (Entry<String, String> tokenValue : getEncodedPathParamKeyValues(method, args)
|
||||
.entrySet()) {
|
||||
value = value.replaceAll("\\{" + tokenValue.getKey() + "\\}", tokenValue.getValue()
|
||||
.toString());
|
||||
value = value.replaceAll("\\{" + tokenValue.getKey() + "\\}", tokenValue.getValue());
|
||||
}
|
||||
headers.put(header.keys()[i], value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Map<String, Object> getEncodedPathParamKeyValues(Method method, Object[] args,
|
||||
char... skipEncode) throws UnsupportedEncodingException {
|
||||
Map<String, Object> pathParamValues = Maps.newHashMap();
|
||||
private Map<String, String> getEncodedPathParamKeyValues(Method method, Object[] args,
|
||||
final char... skipEncode) throws UnsupportedEncodingException {
|
||||
Map<String, String> pathParamValues = Maps.newHashMap();
|
||||
pathParamValues.putAll(constants);
|
||||
Map<Integer, Set<Annotation>> indexToPathParam = methodToindexOfParamToPathParamAnnotations
|
||||
.get(method);
|
||||
|
||||
Map<Integer, Set<Annotation>> indexToParamExtractor = methodToindexOfParamToParamParserAnnotations
|
||||
.get(method);
|
||||
for (Entry<Integer, Set<Annotation>> entry : indexToPathParam.entrySet()) {
|
||||
|
@ -638,19 +647,36 @@ public class JaxrsAnnotationProcessor {
|
|||
} else {
|
||||
paramValue = args[entry.getKey()].toString();
|
||||
}
|
||||
paramValue = URLEncoder.encode(paramValue, "UTF-8");
|
||||
// Web browsers do not always handle '+' characters well, use the well-supported
|
||||
// '%20' instead.
|
||||
paramValue = paramValue.replaceAll("\\+", "%20");
|
||||
for (char c : skipEncode) {
|
||||
String value = Character.toString(c);
|
||||
String encoded = URLEncoder.encode(value, "UTF-8");
|
||||
paramValue = paramValue.replaceAll(encoded, value);
|
||||
}
|
||||
pathParamValues.put(paramKey, paramValue);
|
||||
}
|
||||
}
|
||||
return pathParamValues;
|
||||
|
||||
if (method.isAnnotationPresent(PathParam.class)
|
||||
&& method.isAnnotationPresent(ParamParser.class)) {
|
||||
String paramKey = method.getAnnotation(PathParam.class).value();
|
||||
String paramValue = injector.getInstance(method.getAnnotation(ParamParser.class).value())
|
||||
.apply(args);
|
||||
pathParamValues.put(paramKey, paramValue);
|
||||
}
|
||||
|
||||
return Maps.transformValues(pathParamValues, new Function<String, String>() {
|
||||
public String apply(String paramValue) {
|
||||
try {
|
||||
paramValue = URLEncoder.encode(paramValue, "UTF-8");
|
||||
// Web browsers do not always handle '+' characters well, use the well-supported
|
||||
// '%20' instead.
|
||||
paramValue = paramValue.replaceAll("\\+", "%20");
|
||||
for (char c : skipEncode) {
|
||||
String value = Character.toString(c);
|
||||
String encoded = URLEncoder.encode(value, "UTF-8");
|
||||
paramValue = paramValue.replaceAll(encoded, value);
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("jclouds only supports UTF-8", e);
|
||||
}
|
||||
return paramValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Map<String, String> getQueryParamKeyValues(Method method, Object[] args) {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
package org.jclouds.rest;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.ElementType.PARAMETER;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
|
@ -34,12 +35,13 @@ import javax.ws.rs.PathParam;
|
|||
import com.google.common.base.Function;
|
||||
|
||||
/**
|
||||
* Extracts the value of a parameter from an object.
|
||||
* Extracts the value of a parameter from an object. If placed on a method, the function will be
|
||||
* presented with a Object [] from the method args used to derive the value;
|
||||
*
|
||||
* @see PathParam
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Target(PARAMETER)
|
||||
@Target( { METHOD, PARAMETER })
|
||||
@Retention(RUNTIME)
|
||||
public @interface ParamParser {
|
||||
Class<? extends Function<Object, String>> value();
|
||||
|
|
|
@ -30,8 +30,10 @@ package org.jclouds.rest;
|
|||
*/
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Inject;
|
||||
|
@ -83,12 +85,51 @@ public class RestClientProxy implements InvocationHandler {
|
|||
} else if (util.getDelegateOrNull(method) != null) {
|
||||
method = util.getDelegateOrNull(method);
|
||||
logger.trace("%s - converting method to request", method);
|
||||
HttpRequest request = util.createRequest(method, args);
|
||||
logger.trace("%s - converted method to request %s", method, request);
|
||||
|
||||
Function<HttpResponse, ?> transformer = util.createResponseParser(method);
|
||||
Function<Exception, ?> exceptionParser = util
|
||||
.createExceptionParserOrNullIfNotFound(method);
|
||||
HttpRequest request;
|
||||
try {
|
||||
request = util.createRequest(method, args);
|
||||
} catch (RuntimeException e) {
|
||||
if (exceptionParser != null) {
|
||||
final Object toReturn = exceptionParser.apply(e);
|
||||
if (toReturn == null)
|
||||
throw e;
|
||||
if (method.getReturnType().isAssignableFrom(Future.class)) {
|
||||
return new Future<Object>() {
|
||||
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object get() throws InterruptedException, ExecutionException {
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
public Object get(long timeout, TimeUnit unit) throws InterruptedException,
|
||||
ExecutionException, TimeoutException {
|
||||
return get();
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isDone() {
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
} else {
|
||||
return toReturn;
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
logger.trace("%s - converted method to request %s", method, request);
|
||||
|
||||
Function<HttpResponse, ?> transformer = util.createResponseParser(method, request, args);
|
||||
|
||||
logger.trace("%s - creating command for request %s, transformer %s, exceptionParser %s",
|
||||
method, request, transformer, exceptionParser);
|
||||
|
|
|
@ -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.rest;
|
||||
|
||||
import javax.ws.rs.PathParam;
|
||||
|
||||
import org.jclouds.http.HttpRequest;
|
||||
|
||||
/**
|
||||
* Passes parsed Http request and Object [] from the method args used to derive the request into
|
||||
* this object;
|
||||
*
|
||||
* @see PathParam
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public interface RestContext {
|
||||
void setContext(HttpRequest request, Object[] args);
|
||||
|
||||
Object[] getArgs();
|
||||
|
||||
HttpRequest getRequest();
|
||||
}
|
|
@ -116,7 +116,7 @@ public class JaxrsAnnotationProcessorTest {
|
|||
@QueryParams(keys = { "foo", "fooble" }, values = { "bar", "baz" })
|
||||
public void foo2() {
|
||||
}
|
||||
|
||||
|
||||
@FOO
|
||||
@QueryParams(keys = { "foo", "fooble" }, values = { "bar", "baz" })
|
||||
public void foo3(@QueryParam("robbie") String robbie) {
|
||||
|
@ -147,13 +147,14 @@ public class JaxrsAnnotationProcessorTest {
|
|||
public void testQuery3() throws SecurityException, NoSuchMethodException {
|
||||
Method method = TestQuery.class.getMethod("foo3", String.class);
|
||||
HttpRequest httpMethod = factory.create(TestQuery.class).createRequest(method,
|
||||
new Object[] {"wonder"});
|
||||
new Object[] { "wonder" });
|
||||
assertEquals(httpMethod.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(httpMethod.getEndpoint().getPath(), "");
|
||||
assertEquals(httpMethod.getEndpoint().getQuery(),
|
||||
"x-ms-version=2009-07-17&foo=bar&fooble=baz&robbie=wonder");
|
||||
assertEquals(httpMethod.getMethod(), "FOO");
|
||||
}
|
||||
|
||||
@Endpoint(Localhost.class)
|
||||
public class TestCustomMethod {
|
||||
@FOO
|
||||
|
@ -428,6 +429,33 @@ public class JaxrsAnnotationProcessorTest {
|
|||
public void onePathParamExtractor(
|
||||
@PathParam("path") @ParamParser(FirstCharacter.class) String path) {
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{path}")
|
||||
@PathParam("path")
|
||||
@ParamParser(FirstCharacterFirstElement.class)
|
||||
public void onePathParamExtractorMethod(String path) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParamExtractor() throws SecurityException, NoSuchMethodException {
|
||||
Method method = TestPath.class.getMethod("onePathParamExtractor", String.class);
|
||||
HttpRequest httpMethod = factory.create(TestPath.class).createRequest(method,
|
||||
new Object[] { "localhost" });
|
||||
assertEquals(httpMethod.getEndpoint().getPath(), "/l");
|
||||
assertEquals(httpMethod.getMethod(), HttpMethod.GET);
|
||||
assertEquals(httpMethod.getHeaders().size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParamExtractorMethod() throws SecurityException, NoSuchMethodException {
|
||||
Method method = TestPath.class.getMethod("onePathParamExtractorMethod", String.class);
|
||||
HttpRequest httpMethod = factory.create(TestPath.class).createRequest(method,
|
||||
new Object[] { "localhost" });
|
||||
assertEquals(httpMethod.getEndpoint().getPath(), "/l");
|
||||
assertEquals(httpMethod.getMethod(), HttpMethod.GET);
|
||||
assertEquals(httpMethod.getHeaders().size(), 0);
|
||||
}
|
||||
|
||||
static class FirstCharacter implements Function<Object, String> {
|
||||
|
@ -436,6 +464,12 @@ public class JaxrsAnnotationProcessorTest {
|
|||
}
|
||||
}
|
||||
|
||||
static class FirstCharacterFirstElement implements Function<Object, String> {
|
||||
public String apply(Object from) {
|
||||
return ((String) ((Object[]) from)[0]).substring(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Endpoint(Localhost.class)
|
||||
public class TestHeader {
|
||||
@GET
|
||||
|
@ -535,6 +569,31 @@ public class JaxrsAnnotationProcessorTest {
|
|||
@ResponseParser(ReturnStringIf200.class)
|
||||
public void oneTransformer() {
|
||||
}
|
||||
|
||||
@GET
|
||||
@ResponseParser(ReturnStringIf200Context.class)
|
||||
public void oneTransformerWithContext() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ReturnStringIf200Context extends ReturnStringIf200 implements RestContext {
|
||||
private Object[] args;
|
||||
private HttpRequest request;
|
||||
|
||||
public Object[] getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public HttpRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public void setContext(HttpRequest request, Object[] args) {
|
||||
this.request = request;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("static-access")
|
||||
|
@ -546,6 +605,17 @@ public class JaxrsAnnotationProcessorTest {
|
|||
assertEquals(transformer, ReturnStringIf200.class);
|
||||
}
|
||||
|
||||
public void oneTransformerWithContext() throws SecurityException, NoSuchMethodException {
|
||||
Method method = TestTransformers.class.getMethod("oneTransformerWithContext");
|
||||
HttpRequest request = new HttpRequest("GET", URI.create("http://localhost"));
|
||||
Object[] args = new Object[] {};
|
||||
Function<HttpResponse, ?> transformer = factory.create(TestTransformers.class)
|
||||
.createResponseParser(method, request, args);
|
||||
assertEquals(transformer.getClass(), ReturnStringIf200Context.class);
|
||||
assertEquals(((ReturnStringIf200Context) transformer).getArgs(), args);
|
||||
assertEquals(((ReturnStringIf200Context) transformer).getRequest(), request);
|
||||
}
|
||||
|
||||
@SuppressWarnings("static-access")
|
||||
public void testOneTransformer() throws SecurityException, NoSuchMethodException {
|
||||
Method method = TestTransformers.class.getMethod("oneTransformer");
|
||||
|
|
Loading…
Reference in New Issue