Updated JAXB parser configuration

Now JAXB response parser is activated by default if the Accept header is
set to application/xml or the method is annotated with the @JAXBResponseParser
annotation (this way we can still use it to parse documents with vendor-specific
mime-types).

This will not break current XML parsers, since they must be explicitly
defined with the @XMLResponseParser annotation, and will provide a
default XML parsing mechanism. The only requirement will be to have
domain objects annotated with standard Java JAXB annotations.

It would be great to refactor the @XMLResponseParser annotation to be
more flexible and to allow using other parsers than SAX, this way the
@JAXBResponseParser annotation could be removed. However, this change
may affect existing providers and must be studied with care.
This commit is contained in:
Ignasi Barrera 2011-10-26 10:12:45 +02:00 committed by Adrian Cole
parent b90f74352f
commit 0387b1bb92
2 changed files with 44 additions and 10 deletions

View File

@ -777,9 +777,7 @@ public class RestAnnotationProcessor<T> {
public static Key<? extends Function<HttpResponse, ?>> getParserOrThrowException(Method method) { public static Key<? extends Function<HttpResponse, ?>> getParserOrThrowException(Method method) {
ResponseParser annotation = method.getAnnotation(ResponseParser.class); ResponseParser annotation = method.getAnnotation(ResponseParser.class);
if (annotation == null) { if (annotation == null) {
if (method.isAnnotationPresent(JAXBResponseParser.class)) { if (method.getReturnType().equals(void.class)
return getJAXBParserKeyForMethod(method);
} else if (method.getReturnType().equals(void.class)
|| TypeLiteral.get(method.getGenericReturnType()).equals(futureVoidLiteral)) { || TypeLiteral.get(method.getGenericReturnType()).equals(futureVoidLiteral)) {
return Key.get(ReleasePayloadAndReturn.class); return Key.get(ReleasePayloadAndReturn.class);
} else if (method.getReturnType().equals(boolean.class) || method.getReturnType().equals(Boolean.class) } else if (method.getReturnType().equals(boolean.class) || method.getReturnType().equals(Boolean.class)
@ -793,6 +791,9 @@ public class RestAnnotationProcessor<T> {
return Key.get((Class) IdentityFunction.class); return Key.get((Class) IdentityFunction.class);
} else if (getAcceptHeadersOrNull(method).contains(MediaType.APPLICATION_JSON)) { } else if (getAcceptHeadersOrNull(method).contains(MediaType.APPLICATION_JSON)) {
return getJsonParserKeyForMethod(method); return getJsonParserKeyForMethod(method);
} else if (getAcceptHeadersOrNull(method).contains(MediaType.APPLICATION_XML)
|| method.isAnnotationPresent(JAXBResponseParser.class)) {
return getJAXBParserKeyForMethod(method);
} else if (method.getReturnType().equals(String.class) } else if (method.getReturnType().equals(String.class)
|| TypeLiteral.get(method.getGenericReturnType()).equals(futureStringLiteral)) { || TypeLiteral.get(method.getGenericReturnType()).equals(futureStringLiteral)) {
return Key.get(ReturnStringIf2xx.class); return Key.get(ReturnStringIf2xx.class);

View File

@ -32,7 +32,6 @@ import static org.jclouds.io.Payloads.newStringPayload;
import static org.jclouds.rest.RestContextFactory.contextSpec; import static org.jclouds.rest.RestContextFactory.contextSpec;
import static org.jclouds.rest.RestContextFactory.createContextBuilder; import static org.jclouds.rest.RestContextFactory.createContextBuilder;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -2456,9 +2455,14 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
public interface TestJAXBResponseParser { public interface TestJAXBResponseParser {
@GET @GET
@Path("/jaxb") @Path("/jaxb/annotation")
@JAXBResponseParser @JAXBResponseParser
public ListenableFuture<TestJAXBDomain> jaxbGet(); public ListenableFuture<TestJAXBDomain> jaxbGetWithAnnotation();
@GET
@Path("/jaxb/header")
@Consumes(MediaType.APPLICATION_XML)
public ListenableFuture<TestJAXBDomain> jaxbGetWithAcceptHeader();
} }
@XmlRootElement(name = "test") @XmlRootElement(name = "test")
@ -2474,9 +2478,20 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
} }
@Test @Test
public void testCreateJAXBResponseParser() throws SecurityException, NoSuchMethodException { public void testCreateJAXBResponseParserWithAnnotation() throws SecurityException, NoSuchMethodException {
RestAnnotationProcessor<TestJAXBResponseParser> processor = factory(TestJAXBResponseParser.class); RestAnnotationProcessor<TestJAXBResponseParser> processor = factory(TestJAXBResponseParser.class);
Method method = TestJAXBResponseParser.class.getMethod("jaxbGet"); Method method = TestJAXBResponseParser.class.getMethod("jaxbGetWithAnnotation");
GeneratedHttpRequest<TestJAXBResponseParser> request = GeneratedHttpRequest.<TestJAXBResponseParser> builder().method("GET")
.endpoint(URI.create("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<TestJAXBResponseParser> processor = factory(TestJAXBResponseParser.class);
Method method = TestJAXBResponseParser.class.getMethod("jaxbGetWithAcceptHeader");
GeneratedHttpRequest<TestJAXBResponseParser> request = GeneratedHttpRequest.<TestJAXBResponseParser> builder().method("GET") GeneratedHttpRequest<TestJAXBResponseParser> request = GeneratedHttpRequest.<TestJAXBResponseParser> builder().method("GET")
.endpoint(URI.create("http://localhost")).declaring(TestJAXBResponseParser.class).javaMethod(method).args( .endpoint(URI.create("http://localhost")).declaring(TestJAXBResponseParser.class).javaMethod(method).args(
new Object[] {}).build(); new Object[] {}).build();
@ -2486,8 +2501,26 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Test @Test
public void testJAXBResponseParser() throws SecurityException, NoSuchMethodException, IOException { public void testJAXBResponseParserWithAnnotation() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestJAXBResponseParser.class.getMethod("jaxbGet"); 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);
StringBuffer payload = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
payload.append("<test><elem>Hello World</elem></test>");
TestJAXBDomain domain = parser.apply(new HttpResponse(200, "ok", newStringPayload(payload.toString())));
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); HttpRequest request = factory(TestJAXBResponseParser.class).createRequest(method);
assertResponseParserClassEquals(method, request, ParseXMLWithJAXB.class); assertResponseParserClassEquals(method, request, ParseXMLWithJAXB.class);