Merge pull request #1139 from nacx/jaxb-custom-class

Make JAXBResponseParser parameterizable
This commit is contained in:
Adrian Cole 2013-01-03 14:55:40 -08:00
commit 67b385f8e1
5 changed files with 200 additions and 90 deletions

View File

@ -24,6 +24,10 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.lang.model.type.NullType;
import org.jclouds.http.functions.ParseXMLWithJAXB;
/**
* Shows the transformer type used to parse XML with the
* {@link ParseXMLWithJAXB} parser in a HttpResponse.
@ -34,4 +38,10 @@ import java.lang.annotation.Target;
@Retention(RUNTIME)
public @interface JAXBResponseParser {
/**
* If present, this is the class that will be used to unmarshal the XML
* document. If omitted, the return type of the annotated method will be
* used.
*/
Class<?> value() default NullType.class;
}

View File

@ -67,6 +67,7 @@ import java.util.Set;
import java.util.SortedSet;
import javax.annotation.Resource;
import javax.lang.model.type.NullType;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.HeaderParam;
@ -815,10 +816,16 @@ public abstract class RestAnnotationProcessor {
@SuppressWarnings("unchecked")
public static Key<? extends Function<HttpResponse, ?>> getJAXBParserKeyForMethod(Method method) {
Type returnVal = getReturnTypeForMethod(method);
Type parserType = Types.newParameterizedType(ParseXMLWithJAXB.class, returnVal);
return (Key<? extends Function<HttpResponse, ?>>) Key.get(parserType);
}
Optional<Type> configuredReturnVal = Optional.absent();
if (method.isAnnotationPresent(JAXBResponseParser.class)) {
Type configuredClass = method.getAnnotation(JAXBResponseParser.class).value();
configuredReturnVal = configuredClass.equals(NullType.class) ? Optional.<Type> absent() : Optional
.<Type> of(configuredClass);
}
Type returnVal = configuredReturnVal.or(getReturnTypeForMethod(method));
Type parserType = Types.newParameterizedType(ParseXMLWithJAXB.class, returnVal);
return (Key<? extends Function<HttpResponse, ?>>) Key.get(parserType);
}
public static Type getReturnTypeForMethod(Method method) {
Type returnVal;

View File

@ -0,0 +1,158 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.annotationparsing;
import static org.jclouds.providers.AnonymousProviderMetadata.forClientMappedToAsyncClientOnEndpoint;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.xml.bind.annotation.XmlRootElement;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.providers.ProviderMetadata;
import org.jclouds.rest.annotations.JAXBResponseParser;
import org.jclouds.rest.annotations.Transform;
import org.jclouds.rest.internal.BaseRestClientExpectTest;
import org.testng.annotations.Test;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.util.concurrent.ListenableFuture;
/**
* Tests the use of the {@link JAXBResponseParser} annotation.
*
* @author Ignasi Barrera
*/
@Test(groups = "unit", testName = "JAXBResponseParserAnnotationExpectTest")
public class JAXBResponseParserAnnotationExpectTest extends
BaseRestClientExpectTest<JAXBResponseParserAnnotationExpectTest.TestJAXBApi> {
@XmlRootElement(name = "test")
public static class TestJAXBDomain {
private String elem;
public String getElem() {
return elem;
}
public void setElem(String elem) {
this.elem = elem;
}
@Override
public String toString() {
return "TestJAXBDomain [elem=" + elem + "]";
}
}
public interface TestJAXBApi {
public TestJAXBDomain jaxbGetWithAnnotation();
public Object jaxbGetWithAnnotationAndCustomClass();
public TestJAXBDomain jaxbGetWithAcceptHeader();
public String jaxbGetWithTransformer();
}
public interface TestJAXBAsyncApi {
@GET
@Path("/jaxb/annotation")
@JAXBResponseParser
public ListenableFuture<TestJAXBDomain> jaxbGetWithAnnotation();
@GET
@Path("/jaxb/custom")
@JAXBResponseParser(TestJAXBDomain.class)
public ListenableFuture<Object> jaxbGetWithAnnotationAndCustomClass();
@GET
@Path("/jaxb/header")
@Consumes(MediaType.APPLICATION_XML)
public ListenableFuture<TestJAXBDomain> jaxbGetWithAcceptHeader();
@GET
@Path("/jaxb/transformer")
@JAXBResponseParser(TestJAXBDomain.class)
@Transform(ToString.class)
public ListenableFuture<String> jaxbGetWithTransformer();
}
private static class ToString implements Function<Object, String> {
@Override
public String apply(Object input) {
return Functions.toStringFunction().apply(input);
}
}
@Test
public void testJAXBResponseParserAnnotationWithoutValue() throws SecurityException, NoSuchMethodException {
TestJAXBApi api = requestSendsResponse( //
HttpRequest.builder().method("GET").endpoint("http://mock/jaxb/annotation").build(), //
HttpResponse.builder().statusCode(200).payload("<test><elem>Hello World</elem></test>").build());
TestJAXBDomain result = api.jaxbGetWithAnnotation();
assertEquals(result.getElem(), "Hello World");
}
@Test
public void testJAXBResponseParserAnnotationWithCustomValue() throws SecurityException, NoSuchMethodException {
TestJAXBApi api = requestSendsResponse( //
HttpRequest.builder().method("GET").endpoint("http://mock/jaxb/custom").build(), //
HttpResponse.builder().statusCode(200).payload("<test><elem>Hello World</elem></test>").build());
Object result = api.jaxbGetWithAnnotationAndCustomClass();
assertTrue(result instanceof TestJAXBDomain);
assertEquals(TestJAXBDomain.class.cast(result).getElem(), "Hello World");
}
@Test
public void testJAXBResponseParserAnnotationWithAcceptHeader() throws SecurityException, NoSuchMethodException {
TestJAXBApi api = requestSendsResponse( //
HttpRequest.builder().method("GET").endpoint("http://mock/jaxb/header")
.addHeader("Accept", MediaType.APPLICATION_XML).build(), //
HttpResponse.builder().statusCode(200).payload("<test><elem>Hello World</elem></test>").build());
TestJAXBDomain result = api.jaxbGetWithAcceptHeader();
assertEquals(result.getElem(), "Hello World");
}
@Test
public void testJAXBResponseParserAnnotationWithTransformer() throws SecurityException, NoSuchMethodException {
TestJAXBApi api = requestSendsResponse( //
HttpRequest.builder().method("GET").endpoint("http://mock/jaxb/transformer").build(), //
HttpResponse.builder().statusCode(200).payload("<test><elem>Hello World</elem></test>").build());
String result = api.jaxbGetWithTransformer();
assertEquals(result, "TestJAXBDomain [elem=Hello World]");
}
@Override
public ProviderMetadata createProviderMetadata() {
return forClientMappedToAsyncClientOnEndpoint(TestJAXBApi.class, TestJAXBAsyncApi.class, "http://mock");
}
}

View File

@ -20,8 +20,9 @@ package org.jclouds.rest.binders;
import static org.testng.Assert.assertEquals;
import javax.xml.bind.annotation.XmlRootElement;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.internal.RestAnnotationProcessorTest.TestJAXBDomain;
import org.jclouds.xml.XMLParser;
import org.jclouds.xml.internal.JAXBParser;
import org.testng.annotations.Test;
@ -48,7 +49,8 @@ public class BindToXMLPayloadTest {
HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://momma").build();
request = binder.bindToRequest(request, obj);
assertEquals(request.getPayload().getRawContent(), XMLParser.DEFAULT_XML_HEADER + "\n<test>\n <elem>Hello World</elem>\n</test>\n");
assertEquals(request.getPayload().getRawContent(), XMLParser.DEFAULT_XML_HEADER
+ "\n<test>\n <elem>Hello World</elem>\n</test>\n");
assertEquals(request.getPayload().getContentMetadata().getContentType(), "application/xml");
}
@ -63,11 +65,11 @@ public class BindToXMLPayloadTest {
// Add the unknown content-type header to verify it is changed by the
// binder
Multimap<String, String> headers = ImmutableMultimap.<String, String> of("Content-type", "application/unknown");
HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://momma").headers(headers)
.build();
HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://momma").headers(headers).build();
request = binder.bindToRequest(request, obj);
assertEquals(request.getPayload().getRawContent(), XMLParser.DEFAULT_XML_HEADER + "\n<test>\n <elem>Hello World</elem>\n</test>\n");
assertEquals(request.getPayload().getRawContent(), XMLParser.DEFAULT_XML_HEADER
+ "\n<test>\n <elem>Hello World</elem>\n</test>\n");
assertEquals(request.getPayload().getContentMetadata().getContentType(), "application/xml");
}
@ -83,4 +85,18 @@ public class BindToXMLPayloadTest {
HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://momma").build();
request = binder.bindToRequest(request, new Object());
}
@XmlRootElement(name = "test")
public static class TestJAXBDomain {
private String elem;
public String getElem() {
return elem;
}
public void setElem(String elem) {
this.elem = elem;
}
}
}

View File

@ -2437,87 +2437,6 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
assertEquals(form, "x-amz-copy-source=/eggs/robot");
}
public interface TestJAXBResponseParser {
@GET
@Path("/jaxb/annotation")
@JAXBResponseParser
public ListenableFuture<TestJAXBDomain> jaxbGetWithAnnotation();
@GET
@Path("/jaxb/header")
@Consumes("application/xml")
public ListenableFuture<TestJAXBDomain> jaxbGetWithAcceptHeader();
}
@XmlRootElement(name = "test")
public static class TestJAXBDomain {
private String elem;
public String getElem() {
return elem;
}
public void setElem(String elem) {
this.elem = elem;
}
}
@Test
public void testCreateJAXBResponseParserWithAnnotation() throws SecurityException, NoSuchMethodException {
RestAnnotationProcessor processor = factory(TestJAXBResponseParser.class);
Method method = TestJAXBResponseParser.class.getMethod("jaxbGetWithAnnotation");
GeneratedHttpRequest request = GeneratedHttpRequest.builder().method("GET").endpoint("http://localhost")
.declaring(TestJAXBResponseParser.class).javaMethod(method).args(new Object[] {}).build();
Function<HttpResponse, ?> transformer = processor.createResponseParser(method, request);
assertEquals(transformer.getClass(), ParseXMLWithJAXB.class);
}
@Test
public void testCreateJAXBResponseParserWithAcceptHeader() throws SecurityException, NoSuchMethodException {
RestAnnotationProcessor processor = factory(TestJAXBResponseParser.class);
Method method = TestJAXBResponseParser.class.getMethod("jaxbGetWithAcceptHeader");
GeneratedHttpRequest request = GeneratedHttpRequest.builder().method("GET").endpoint("http://localhost")
.declaring(TestJAXBResponseParser.class).javaMethod(method).args(new Object[] {}).build();
Function<HttpResponse, ?> transformer = processor.createResponseParser(method, request);
assertEquals(transformer.getClass(), ParseXMLWithJAXB.class);
}
@SuppressWarnings("unchecked")
@Test
public void testJAXBResponseParserWithAnnotation() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestJAXBResponseParser.class.getMethod("jaxbGetWithAnnotation");
HttpRequest request = factory(TestJAXBResponseParser.class).createRequest(method);
assertResponseParserClassEquals(method, request, ParseXMLWithJAXB.class);
// now test that it works!
Function<HttpResponse, TestJAXBDomain> parser = (Function<HttpResponse, TestJAXBDomain>) RestAnnotationProcessor
.createResponseParser(parserFactory, injector, method, request);
StringBuilder payload = new StringBuilder(XMLParser.DEFAULT_XML_HEADER);
payload.append("<test><elem>Hello World</elem></test>");
TestJAXBDomain domain = parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload(payload.toString()).build());
assertEquals(domain.getElem(), "Hello World");
}
@SuppressWarnings("unchecked")
@Test
public void testJAXBResponseParserWithAcceptHeader() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestJAXBResponseParser.class.getMethod("jaxbGetWithAcceptHeader");
HttpRequest request = factory(TestJAXBResponseParser.class).createRequest(method);
assertResponseParserClassEquals(method, request, ParseXMLWithJAXB.class);
// now test that it works!
Function<HttpResponse, TestJAXBDomain> parser = (Function<HttpResponse, TestJAXBDomain>) RestAnnotationProcessor
.createResponseParser(parserFactory, injector, method, request);
StringBuilder payload = new StringBuilder(XMLParser.DEFAULT_XML_HEADER);
payload.append("<test><elem>Hello World</elem></test>");
TestJAXBDomain domain = parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload(payload.toString()).build());
assertEquals(domain.getElem(), "Hello World");
}
@Test(expectedExceptions = NullPointerException.class)
public void testAddHostNullWithHost() throws Exception {
assertNull(RestAnnotationProcessor.addHostIfMissing(null, null));