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.QueryParam;
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.UriBuilder;
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
import javax.ws.rs.core.UriBuilderException;
|
||||||
|
|
||||||
import org.jboss.resteasy.util.IsHttpMethod;
|
import org.jboss.resteasy.util.IsHttpMethod;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
|
@ -161,7 +162,8 @@ public class JaxrsAnnotationProcessor {
|
||||||
private final ParseSax.Factory parserFactory;
|
private final ParseSax.Factory parserFactory;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public Function<HttpResponse, ?> createResponseParser(Method method) {
|
public Function<HttpResponse, ?> createResponseParser(Method method, HttpRequest request,
|
||||||
|
Object[] args) {
|
||||||
Function<HttpResponse, ?> transformer;
|
Function<HttpResponse, ?> transformer;
|
||||||
Class<? extends HandlerWithResult<?>> handler = getXMLTransformerOrNull(method);
|
Class<? extends HandlerWithResult<?>> handler = getXMLTransformerOrNull(method);
|
||||||
if (handler != null) {
|
if (handler != null) {
|
||||||
|
@ -169,6 +171,9 @@ public class JaxrsAnnotationProcessor {
|
||||||
} else {
|
} else {
|
||||||
transformer = injector.getInstance(getParserOrThrowException(method));
|
transformer = injector.getInstance(getParserOrThrowException(method));
|
||||||
}
|
}
|
||||||
|
if (transformer instanceof RestContext) {
|
||||||
|
((RestContext) transformer).setContext(request, args);
|
||||||
|
}
|
||||||
return transformer;
|
return transformer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,8 +321,12 @@ public class JaxrsAnnotationProcessor {
|
||||||
} else {
|
} else {
|
||||||
endPoint = builder.buildFromEncodedMap(getEncodedPathParamKeyValues(method, args));
|
endPoint = builder.buildFromEncodedMap(getEncodedPathParamKeyValues(method, args));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new IllegalStateException("problem encoding parameters", 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);
|
HttpRequest request = new HttpRequest(httpMethod, endPoint, headers);
|
||||||
addHostHeaderIfAnnotatedWithVirtualHost(headers, request.getEndpoint().getHost(), method);
|
addHostHeaderIfAnnotatedWithVirtualHost(headers, request.getEndpoint().getHost(), method);
|
||||||
|
@ -609,22 +618,22 @@ public class JaxrsAnnotationProcessor {
|
||||||
Headers header) throws UnsupportedEncodingException {
|
Headers header) throws UnsupportedEncodingException {
|
||||||
for (int i = 0; i < header.keys().length; i++) {
|
for (int i = 0; i < header.keys().length; i++) {
|
||||||
String value = header.values()[i];
|
String value = header.values()[i];
|
||||||
for (Entry<String, Object> tokenValue : getEncodedPathParamKeyValues(method, args)
|
for (Entry<String, String> tokenValue : getEncodedPathParamKeyValues(method, args)
|
||||||
.entrySet()) {
|
.entrySet()) {
|
||||||
value = value.replaceAll("\\{" + tokenValue.getKey() + "\\}", tokenValue.getValue()
|
value = value.replaceAll("\\{" + tokenValue.getKey() + "\\}", tokenValue.getValue());
|
||||||
.toString());
|
|
||||||
}
|
}
|
||||||
headers.put(header.keys()[i], value);
|
headers.put(header.keys()[i], value);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> getEncodedPathParamKeyValues(Method method, Object[] args,
|
private Map<String, String> getEncodedPathParamKeyValues(Method method, Object[] args,
|
||||||
char... skipEncode) throws UnsupportedEncodingException {
|
final char... skipEncode) throws UnsupportedEncodingException {
|
||||||
Map<String, Object> pathParamValues = Maps.newHashMap();
|
Map<String, String> pathParamValues = Maps.newHashMap();
|
||||||
pathParamValues.putAll(constants);
|
pathParamValues.putAll(constants);
|
||||||
Map<Integer, Set<Annotation>> indexToPathParam = methodToindexOfParamToPathParamAnnotations
|
Map<Integer, Set<Annotation>> indexToPathParam = methodToindexOfParamToPathParamAnnotations
|
||||||
.get(method);
|
.get(method);
|
||||||
|
|
||||||
Map<Integer, Set<Annotation>> indexToParamExtractor = methodToindexOfParamToParamParserAnnotations
|
Map<Integer, Set<Annotation>> indexToParamExtractor = methodToindexOfParamToParamParserAnnotations
|
||||||
.get(method);
|
.get(method);
|
||||||
for (Entry<Integer, Set<Annotation>> entry : indexToPathParam.entrySet()) {
|
for (Entry<Integer, Set<Annotation>> entry : indexToPathParam.entrySet()) {
|
||||||
|
@ -638,19 +647,36 @@ public class JaxrsAnnotationProcessor {
|
||||||
} else {
|
} else {
|
||||||
paramValue = args[entry.getKey()].toString();
|
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);
|
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) {
|
private Map<String, String> getQueryParamKeyValues(Method method, Object[] args) {
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.rest;
|
package org.jclouds.rest;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.METHOD;
|
||||||
import static java.lang.annotation.ElementType.PARAMETER;
|
import static java.lang.annotation.ElementType.PARAMETER;
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
@ -34,12 +35,13 @@ import javax.ws.rs.PathParam;
|
||||||
import com.google.common.base.Function;
|
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
|
* @see PathParam
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
@Target(PARAMETER)
|
@Target( { METHOD, PARAMETER })
|
||||||
@Retention(RUNTIME)
|
@Retention(RUNTIME)
|
||||||
public @interface ParamParser {
|
public @interface ParamParser {
|
||||||
Class<? extends Function<Object, String>> value();
|
Class<? extends Function<Object, String>> value();
|
||||||
|
|
|
@ -30,8 +30,10 @@ package org.jclouds.rest;
|
||||||
*/
|
*/
|
||||||
import java.lang.reflect.InvocationHandler;
|
import java.lang.reflect.InvocationHandler;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
@ -83,12 +85,51 @@ public class RestClientProxy implements InvocationHandler {
|
||||||
} else if (util.getDelegateOrNull(method) != null) {
|
} else if (util.getDelegateOrNull(method) != null) {
|
||||||
method = util.getDelegateOrNull(method);
|
method = util.getDelegateOrNull(method);
|
||||||
logger.trace("%s - converting method to request", 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
|
Function<Exception, ?> exceptionParser = util
|
||||||
.createExceptionParserOrNullIfNotFound(method);
|
.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",
|
logger.trace("%s - creating command for request %s, transformer %s, exceptionParser %s",
|
||||||
method, request, transformer, exceptionParser);
|
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();
|
||||||
|
}
|
|
@ -147,13 +147,14 @@ public class JaxrsAnnotationProcessorTest {
|
||||||
public void testQuery3() throws SecurityException, NoSuchMethodException {
|
public void testQuery3() throws SecurityException, NoSuchMethodException {
|
||||||
Method method = TestQuery.class.getMethod("foo3", String.class);
|
Method method = TestQuery.class.getMethod("foo3", String.class);
|
||||||
HttpRequest httpMethod = factory.create(TestQuery.class).createRequest(method,
|
HttpRequest httpMethod = factory.create(TestQuery.class).createRequest(method,
|
||||||
new Object[] {"wonder"});
|
new Object[] { "wonder" });
|
||||||
assertEquals(httpMethod.getEndpoint().getHost(), "localhost");
|
assertEquals(httpMethod.getEndpoint().getHost(), "localhost");
|
||||||
assertEquals(httpMethod.getEndpoint().getPath(), "");
|
assertEquals(httpMethod.getEndpoint().getPath(), "");
|
||||||
assertEquals(httpMethod.getEndpoint().getQuery(),
|
assertEquals(httpMethod.getEndpoint().getQuery(),
|
||||||
"x-ms-version=2009-07-17&foo=bar&fooble=baz&robbie=wonder");
|
"x-ms-version=2009-07-17&foo=bar&fooble=baz&robbie=wonder");
|
||||||
assertEquals(httpMethod.getMethod(), "FOO");
|
assertEquals(httpMethod.getMethod(), "FOO");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Endpoint(Localhost.class)
|
@Endpoint(Localhost.class)
|
||||||
public class TestCustomMethod {
|
public class TestCustomMethod {
|
||||||
@FOO
|
@FOO
|
||||||
|
@ -428,6 +429,33 @@ public class JaxrsAnnotationProcessorTest {
|
||||||
public void onePathParamExtractor(
|
public void onePathParamExtractor(
|
||||||
@PathParam("path") @ParamParser(FirstCharacter.class) String path) {
|
@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> {
|
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)
|
@Endpoint(Localhost.class)
|
||||||
public class TestHeader {
|
public class TestHeader {
|
||||||
@GET
|
@GET
|
||||||
|
@ -535,6 +569,31 @@ public class JaxrsAnnotationProcessorTest {
|
||||||
@ResponseParser(ReturnStringIf200.class)
|
@ResponseParser(ReturnStringIf200.class)
|
||||||
public void oneTransformer() {
|
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")
|
@SuppressWarnings("static-access")
|
||||||
|
@ -546,6 +605,17 @@ public class JaxrsAnnotationProcessorTest {
|
||||||
assertEquals(transformer, ReturnStringIf200.class);
|
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")
|
@SuppressWarnings("static-access")
|
||||||
public void testOneTransformer() throws SecurityException, NoSuchMethodException {
|
public void testOneTransformer() throws SecurityException, NoSuchMethodException {
|
||||||
Method method = TestTransformers.class.getMethod("oneTransformer");
|
Method method = TestTransformers.class.getMethod("oneTransformer");
|
||||||
|
|
Loading…
Reference in New Issue