mirror of https://github.com/apache/jclouds.git
Added parser to RestAnnotationProcessor to parse XML using JAXB
This commit is contained in:
parent
f4a53fa0da
commit
45a1bcac85
|
@ -0,0 +1,105 @@
|
||||||
|
/**
|
||||||
|
* 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.http.functions;
|
||||||
|
|
||||||
|
import static org.jclouds.http.HttpUtils.releasePayload;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import org.jclouds.http.HttpResponse;
|
||||||
|
import org.jclouds.http.HttpResponseException;
|
||||||
|
import org.jclouds.logging.Logger;
|
||||||
|
import org.jclouds.util.Strings2;
|
||||||
|
import org.jclouds.xml.XMLParser;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object will parse the body of an HttpResponse and return the result of type <T> back to the
|
||||||
|
* caller.
|
||||||
|
*
|
||||||
|
* @author Ignasi Barrera
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class ParseXMLWithJAXB<T> implements Function<HttpResponse, T>
|
||||||
|
{
|
||||||
|
@Resource
|
||||||
|
protected Logger logger = Logger.NULL;
|
||||||
|
|
||||||
|
protected XMLParser xml;
|
||||||
|
|
||||||
|
protected final TypeLiteral<T> type;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ParseXMLWithJAXB(final XMLParser xml, final TypeLiteral<T> type)
|
||||||
|
{
|
||||||
|
this.xml = xml;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T apply(final HttpResponse from)
|
||||||
|
{
|
||||||
|
InputStream xml = from.getPayload().getInput();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return apply(xml);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
StringBuffer message = new StringBuffer();
|
||||||
|
message.append("Error parsing input");
|
||||||
|
logger.error(e, message.toString());
|
||||||
|
throw new HttpResponseException(message.toString() + "\n" + from, null, from, e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
releasePayload(from);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public T apply(final InputStream stream) throws IOException
|
||||||
|
{
|
||||||
|
return (T) apply(stream, type.getRawType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <V> V apply(final InputStream stream, final Class<V> type) throws IOException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return xml.fromXML(Strings2.toStringAndClose(stream), type);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (stream != null)
|
||||||
|
{
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
* 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.annotations;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.METHOD;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import org.jclouds.http.functions.ParseXMLWithJAXB;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the transformer type used to parse XML with the {@link ParseXMLWithJAXB} parser in a
|
||||||
|
* HttpResponse.
|
||||||
|
*
|
||||||
|
* @author Ignasi Barrera
|
||||||
|
*/
|
||||||
|
@Target(METHOD)
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
public @interface JAXBResponseParser
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
|
@ -78,6 +78,7 @@ import org.jclouds.http.functions.ParseFirstJsonValueNamed;
|
||||||
import org.jclouds.http.functions.ParseJson;
|
import org.jclouds.http.functions.ParseJson;
|
||||||
import org.jclouds.http.functions.ParseSax;
|
import org.jclouds.http.functions.ParseSax;
|
||||||
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
|
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
|
||||||
|
import org.jclouds.http.functions.ParseXMLWithJAXB;
|
||||||
import org.jclouds.http.functions.ReleasePayloadAndReturn;
|
import org.jclouds.http.functions.ReleasePayloadAndReturn;
|
||||||
import org.jclouds.http.functions.ReturnInputStream;
|
import org.jclouds.http.functions.ReturnInputStream;
|
||||||
import org.jclouds.http.functions.ReturnStringIf2xx;
|
import org.jclouds.http.functions.ReturnStringIf2xx;
|
||||||
|
@ -109,6 +110,7 @@ import org.jclouds.rest.annotations.EndpointParam;
|
||||||
import org.jclouds.rest.annotations.ExceptionParser;
|
import org.jclouds.rest.annotations.ExceptionParser;
|
||||||
import org.jclouds.rest.annotations.FormParams;
|
import org.jclouds.rest.annotations.FormParams;
|
||||||
import org.jclouds.rest.annotations.Headers;
|
import org.jclouds.rest.annotations.Headers;
|
||||||
|
import org.jclouds.rest.annotations.JAXBResponseParser;
|
||||||
import org.jclouds.rest.annotations.MapBinder;
|
import org.jclouds.rest.annotations.MapBinder;
|
||||||
import org.jclouds.rest.annotations.MatrixParams;
|
import org.jclouds.rest.annotations.MatrixParams;
|
||||||
import org.jclouds.rest.annotations.OnlyElement;
|
import org.jclouds.rest.annotations.OnlyElement;
|
||||||
|
@ -775,7 +777,9 @@ 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.getReturnType().equals(void.class)
|
if (method.isAnnotationPresent(JAXBResponseParser.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)
|
||||||
|
@ -807,6 +811,13 @@ public class RestAnnotationProcessor<T> {
|
||||||
return getJsonParserKeyForMethodAnType(method, returnVal);
|
return getJsonParserKeyForMethodAnType(method, returnVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
public static Type getReturnTypeForMethod(Method method) {
|
public static Type getReturnTypeForMethod(Method method) {
|
||||||
Type returnVal;
|
Type returnVal;
|
||||||
if (method.getReturnType().getTypeParameters().length == 0) {
|
if (method.getReturnType().getTypeParameters().length == 0) {
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/**
|
||||||
|
* 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.xml;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.jclouds.xml.internal.JAXBParser;
|
||||||
|
|
||||||
|
import com.google.inject.ImplementedBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses XML documents.
|
||||||
|
*
|
||||||
|
* @author Ignasi Barrera
|
||||||
|
*/
|
||||||
|
@ImplementedBy(JAXBParser.class)
|
||||||
|
public interface XMLParser
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Serialize the object into xml. If the object is a generic type, use
|
||||||
|
* {@link #toXML(Object, Type)}
|
||||||
|
*/
|
||||||
|
public String toXML(Object src) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize the generic object into xml. If the object is not a generic, use
|
||||||
|
* {@link #toXML(Object, Type)}
|
||||||
|
*/
|
||||||
|
public <T> String toXML(Object src, Class<T> type) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize the generic object from xml. If the object is not a generic type, use
|
||||||
|
* {@link #fromXML(Object, Class)}
|
||||||
|
*/
|
||||||
|
public <T> T fromXML(String xml, Class<T> type) throws IOException;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/**
|
||||||
|
* 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.xml.internal;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import javax.xml.bind.JAXBContext;
|
||||||
|
import javax.xml.bind.JAXBException;
|
||||||
|
import javax.xml.bind.Marshaller;
|
||||||
|
import javax.xml.bind.Unmarshaller;
|
||||||
|
|
||||||
|
import org.jclouds.xml.XMLParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses XML documents using JAXB.
|
||||||
|
*
|
||||||
|
* @author Ignasi Barrera
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class JAXBParser implements XMLParser
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public String toXML(final Object src) throws IOException
|
||||||
|
{
|
||||||
|
return toXML(src, src.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> String toXML(final Object src, final Class<T> type) throws IOException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JAXBContext context = JAXBContext.newInstance(type);
|
||||||
|
Marshaller marshaller = context.createMarshaller();
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
marshaller.marshal(src, writer);
|
||||||
|
return writer.toString();
|
||||||
|
}
|
||||||
|
catch (JAXBException ex)
|
||||||
|
{
|
||||||
|
throw new IOException("Could not marshall object", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <T> T fromXML(final String xml, final Class<T> type) throws IOException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StringReader reader = new StringReader(xml);
|
||||||
|
JAXBContext context = JAXBContext.newInstance(type);
|
||||||
|
Unmarshaller unmarshaller = context.createUnmarshaller();
|
||||||
|
return (T) unmarshaller.unmarshal(reader);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new IOException("Could not unmarshall document", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ 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;
|
||||||
|
@ -72,6 +73,7 @@ import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.UriBuilder;
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
|
||||||
import org.easymock.IArgumentMatcher;
|
import org.easymock.IArgumentMatcher;
|
||||||
import org.eclipse.jetty.http.HttpHeaders;
|
import org.eclipse.jetty.http.HttpHeaders;
|
||||||
|
@ -91,6 +93,7 @@ import org.jclouds.http.functions.ParseFirstJsonValueNamed;
|
||||||
import org.jclouds.http.functions.ParseJson;
|
import org.jclouds.http.functions.ParseJson;
|
||||||
import org.jclouds.http.functions.ParseSax;
|
import org.jclouds.http.functions.ParseSax;
|
||||||
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
|
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
|
||||||
|
import org.jclouds.http.functions.ParseXMLWithJAXB;
|
||||||
import org.jclouds.http.functions.ReleasePayloadAndReturn;
|
import org.jclouds.http.functions.ReleasePayloadAndReturn;
|
||||||
import org.jclouds.http.functions.ReturnInputStream;
|
import org.jclouds.http.functions.ReturnInputStream;
|
||||||
import org.jclouds.http.functions.ReturnStringIf2xx;
|
import org.jclouds.http.functions.ReturnStringIf2xx;
|
||||||
|
@ -120,6 +123,7 @@ import org.jclouds.rest.annotations.Endpoint;
|
||||||
import org.jclouds.rest.annotations.EndpointParam;
|
import org.jclouds.rest.annotations.EndpointParam;
|
||||||
import org.jclouds.rest.annotations.FormParams;
|
import org.jclouds.rest.annotations.FormParams;
|
||||||
import org.jclouds.rest.annotations.Headers;
|
import org.jclouds.rest.annotations.Headers;
|
||||||
|
import org.jclouds.rest.annotations.JAXBResponseParser;
|
||||||
import org.jclouds.rest.annotations.MapBinder;
|
import org.jclouds.rest.annotations.MapBinder;
|
||||||
import org.jclouds.rest.annotations.MatrixParams;
|
import org.jclouds.rest.annotations.MatrixParams;
|
||||||
import org.jclouds.rest.annotations.OnlyElement;
|
import org.jclouds.rest.annotations.OnlyElement;
|
||||||
|
@ -2450,6 +2454,54 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
|
||||||
assertEquals(form, "x-amz-copy-source=/eggs/robot");
|
assertEquals(form, "x-amz-copy-source=/eggs/robot");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface TestJAXBResponseParser {
|
||||||
|
@GET
|
||||||
|
@Path("/jaxb")
|
||||||
|
@JAXBResponseParser
|
||||||
|
public ListenableFuture<TestJAXBDomain> jaxbGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 testCreateJAXBResponseParser() throws SecurityException, NoSuchMethodException {
|
||||||
|
RestAnnotationProcessor<TestJAXBResponseParser> processor = factory(TestJAXBResponseParser.class);
|
||||||
|
Method method = TestJAXBResponseParser.class.getMethod("jaxbGet");
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Test
|
||||||
|
public void testJAXBResponseParser() throws SecurityException, NoSuchMethodException, IOException {
|
||||||
|
Method method = TestJAXBResponseParser.class.getMethod("jaxbGet");
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
DateService dateService = new SimpleDateFormatDateService();
|
DateService dateService = new SimpleDateFormatDateService();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue