Handling REST calls xml response using JAXB - java

I have sent a GET REST call to "http://services.enterprisecloud.terremark.com/cloudapi/ecloud/organizations/" and the response is:
HTTP/1.1 200 OK Content-Length: 1373 Content-Type: application/vnd.tmrk.cloud.organization; type=collection x-tmrk-currentuser: /cloudapi/ecloud/admin/users/101 x-tmrk-token: cloud-F2A27F74-C04B-4566-AB53-CCC06DA2F798 Date: Thu, 12 May 2011 19:09:13 GMT
<Organizations href="/cloudapi/ecloud/organizations" type="application/vnd.tmrk.cloud.organization; type=collection" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
.....
.....
There is an XSD given by the vendor which didn't generate any class of type Organization or Organizations. The Organization related class generated by JAXB is ArrayOfOrganization which looks like:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "ArrayOfOrganizationType", propOrder = {
"organization"
})
public class ArrayOfOrganizationType {
#XmlElement(name = "Organization", nillable = true)
protected List<OrganizationType> organization;
....
....
When I try to use the following code:
ResponseEntity exchange = template.exchange(URL,
HttpMethod.GET,
new HttpEntity(operation.getInput(), operation.getHeader()),
ArrayOfOrganizationType.class,
urlVariables);
The error I get is:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [com.trmk.dto.ArrayOfOrganizationType] and content type [application/vnd.tmrk.cloud.organization;type=collection]
In spring-dispatcher.xml, I have following:
<!-- Rest client -->
<bean id="httpClient" class="org.apache.http.impl.client.DefaultHttpClient">
<constructor-arg>
<bean class="org.apache.http.impl.conn.PoolingClientConnectionManager" />
</constructor-arg>
</bean>
<bean id="restClient" class="com.transport.ext.RestClient">
</bean>
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<bean id="marshallingHttpMessageConverter"
class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"
p:marshaller-ref="jaxb2Marshaller" p:unmarshaller-ref="jaxb2Marshaller"
p:supportedMediaTypes="application/vnd.tmrk.cloud.organization" />
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
</list>
</property>
</bean>
<bean id="jaxb2Marshaller" class="com.util.DefaultJaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>java.lang.String</value>
<value>com.trmk.dto.ArrayOfOrganizationType</value>
</list>
</property>
</bean>
<!-- End of Rest client -->
Any recommendation on how to proceed will be highly helpful as I have run out of ideas to solve this problem

You can try adding #XmlRootElement(name="Organizations") to ArrayOfOrganizationType although obviously that's not ideal since you'd need to re-add it each time you regenerate the JAXB classes.

Related

How to set FAIL_ON_UNKNOWN_PROPERTIES to false using spring xml

I am trying to deserialize a json response I am receiving in to an object. I am getting the following error:
org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Unrecognized field "initialized"
I know where this initialized field is coming from but I can't edit the object class at the moment. Rather I'd want to turn off this exception being thrown when it encounters a field not present in the class by editing the object mapper: DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
I am using RestTemplate to call the url. The restTemplate instance I am using is a bean hence a singleton and this is being created in an xml file like this:
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate"
p:interceptors-ref="rest-template-client-interceptors"/>
The problem is that I'm not sure how to set DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES to false by the construction of the RestTemplate via xml. I am new to spring so not sure where to start.
Any suggestions?
You must configure RestTemplate message converter (MappingJacksonHttpMessageConverter) to use a custom object mapper.
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper" ref="customObjectMapper"/>
</bean>
</list>
</property>
</bean>
<bean id="customObjectMapper" class="org.codehaus.jackson.map.ObjectMapper"/>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="customObjectMapper" />
<property name="targetMethod" value="configure" />
<property name="arguments">
<list>
<value type="org.codehaus.jackson.map.DeserializationConfig.Feature">FAIL_ON_UNKNOWN_PROPERTIES</value>
<value>false</value>
</list>
</property>
</bean>

Spring MVC - set JAXB marshaller property when using #ResponseBody

I am trying to return a object from my controller which should be parsed to xml by spring.
But I used the #XmlNamedObjectGraph (from moxy eclipselink) annotation in my class to customize the returned object. So I have to set the property MarshallerProperties.OBJECT_GRAPH from the marshaller.
How can I access the marshaller, which is used by spring to parse my object, in my controller?
ie:
#RequestMapping(value = "/xml/", method = RequestMethod.GET, produces = "application/xml")
#ResponseBody
public ResponseEntity<Customer> getXml() {
Customer customer = _customerService.getById(12);
...
marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, "default");
...
return new ResponseEntity<>(customer, HttpStatus.OK);
}
Thanks for your help in advance.
It is like Sotirios Delimanolis said. You have to implement your own AbstractJaxb2HttpMessageConverter. But additional to that you have implementet an WebBindingInitializer and register it with:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="webBindingInitializer">
<bean class="com.example.CommonWebBindingInitializer" />
</property>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter" />
<bean class="com.example.Jaxb2RootElementHttpMessageConverter" />
</list>
</property>
</bean>
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
You'll need to implement your own AbstractJaxb2HttpMessageConverter class and override its createMarshaller method to provide a Marshaller with your own properties. Look at Jaxb2RootElementHttpMessageConverter for implementation hints.
Once you've implemented such a class, you'll need to register it as a HttpMessageConverter with your MVC stack. If you're doing your configuration through Java, look into WebMvcConfigurationSupport#configureMessageConverters(..). If you are doing it through XML, look into
<mvc:annotation-driven>
<mvc:message-converters>
<!-- bean goes here -->
</mvc:message-converters>
</mvc:annotation-driven>
If you need to customize the marshaller, create a marshalling view and configure the marshaller with the properties you need, this is an example of configuring a JAXB marshaller (see this answer):
<!-- XML view using a JAXB marshaller -->
<bean id="jaxbView" class="org.springframework.web.servlet.view.xml.MarshallingView">
<constructor-arg>
<bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.company.AClass</value>
</list>
</property>
</bean>
</constructor-arg>
</bean>
<!-- Resolve views based on string names -->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>

HTTP basic authentication through CXF interceptor not working

I'm having some trouble setting the HTTP Authorization header for a web service request using Apache CXF. I have my client setup through spring:
<bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor" />
<bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
<bean id="myHTTPAuthInterceptor" class="my.app.MyHTTPAuthInterceptor" autowire="constructor" />
<bean id="webServiceFactory" class="my.app.WebServiceFactory">
<property name="wsdlLocation" value="classpath:/my/app/webservice.wsdl" />
<property name="serviceURL">
<jee:jndi-lookup jndi-name="webservice/url" />
</property>
<property name="inInterceptors">
<list>
<ref bean="loggingInInterceptor" />
</list>
</property>
<property name="outInterceptors">
<list>
<ref bean="loggingOutInterceptor" />
<ref bean="myHTTPAuthInterceptor" />
</list>
</property>
</bean>
<bean id="myWebService" factory-bean="webServiceFactory" factory-method="getInstance" />
Headers are set through MyHTTPAuthInterceptor like this:
public MyHTTPAuthInterceptor(ConfigDao configDao)
{
super(Phase.POST_PROTOCOL);
this.configDao = configDao;
}
#Override
public void handleMessage(Message message) throws Fault
{
Map<String, List<?>> headers = (Map<String, List<?>>) message.get(Message.PROTOCOL_HEADERS);
String authString = configDao.getUsername() + ":" + config.getPassword();
headers.put("Authorization", Collections.singletonList("Basic " + new String(Base64.encodeBase64(authString.getBytes()))));
}
With username and both set to 'test', everything seems to be OK in the logs:
Headers: {SOAPAction=[""], Accept=[*/*], Authorization=[Basic dGVzdDp0ZXN0]}
However, the server returns a HTTP 401: Unauthorized.
Not knowing what's going wrong, I took a whole other approach by changing my web service client factory code. I added a basic authorization policy to the client's conduit like this:
HTTPConduit httpConduit = (HTTPConduit) client.getConduit();
AuthorizationPolicy authorizationPolicy = new AuthorizationPolicy();
authorizationPolicy.setUserName("test");
authorizationPolicy.setPassword("test");
authorizationPolicy.setAuthorizationType("Basic");
httpConduit.setAuthorization(authorizationPolicy);
Tested my setup again, same log (different order though):
Headers: {SOAPAction=[""], Authorization=[Basic dGVzdDp0ZXN0], Accept=[*/*]}
Now the server's response is 200 OK!
Problem solved you might think, but the second approach doesn't really work for me. My application is a multi-tenant environment, all with different username and password. With the second approach I cannot reuse my client.
How can I get my interceptor to work correctly? Am I plugging into the wrong phase? Does the order of the headers matter? If so, how do I change it?
I have almost exactly the same setup as yours but I am putting my interceptor in the PRE_PROTOCOL phase. So far, I have not experienced any problem. You might try that.
I think POST_PROTOCOL is just too late because too much has already been written to the stream.
If you are looking to externalize the client and authentication best approach is to setup httpConduit in spring context..
**in your spring context file...**
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
...
<bean id="properties" class="org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer">
<property name="locations">
<util:list>
<value>file:${config.dir}/application.properties</value>
</util:list>
</property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
</bean>
...
<jaxws:client id="serviceClient" serviceClass="com.your.ServiceClass" address="${webservice.soap.address}" >
<jaxws:inInterceptors>
<bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor" >
<property name="prettyLogging" value="true" />
</bean>
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor" >
<property name="prettyLogging" value="true" />
</bean>
</jaxws:outInterceptors>
</jaxws:client>
...
applicaiton.properties
---------------------
webservices.http.auth.username=userName
webservices.http.auth.password=Password
webservice.soap.address=https://your.service.url/services/service
a) by mentioning the SOAP Address in the name attribute. which your can find in your WSDL
Ex: if in your WSDL..
<wsdl-definitions ... targetNamespace="http://your.target.namespace.com/" ...>
...
<wsdl:port binding="tns:YourServiceSoapBinding"
name="YourServiceImplPort">
<soap:address location="https://your.service.url/services/service" />
Then
...
xmlns:http-conf="http://cxf.apache.org/transports/http/configuration"
xmlns:sec="http://cxf.apache.org/configuration/security"
...
<http-conf:conduit name="https://your.service.url/services/service">
<http-conf:authorization>
<sec:UserName>${webservices.http.auth.username}</sec:UserName>
<sec:Password>${webservices.http.auth.password}</sec:Password>
<sec:AuthorizationType>Basic</sec:AuthorizationType>
</http-conf:authorization>
</http-conf:conduit>
Or
b) name attribute should be {targetNamespace}portName.http_conduit
<http-conf:conduit name="{http://your.target.namespace.com/}YourServiceImplPort.http_conduit">
<http-conf:authorization>
<sec:UserName>${webservices.http.auth.username}</sec:UserName>
<sec:Password>${webservices.http.auth.password}</sec:Password>
<sec:AuthorizationType>Basic</sec:AuthorizationType>
</http-conf:authorization>
</http-conf:conduit>

PUT method Spring MVC

I'm trying to use the following format for making a put request through the RESTtemplate.
#Autowired
RestTemplate template;
#RequestMapping(value = "/change", method = RequestMethod.PUT)
public ModelAndView change(Data data){
List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
acceptableMediaTypes.add(MediaType.APPLICATION_XML);
HttpHeaders headers = new HttpHeaders();
headers.setAccept(acceptableMediaTypes);
HttpEntity<Data> entity = new HttpEntity<Data>(data, headers);
String url="http://www...com";
try {
template.put(url, entity);
} catch (Exception e) {
System.out.println(e);
}
return new ModelAndView("redirect:/home");
}
I checked on the database and I realized that there is no change. Even the request is not written on the log file. When I'm debugging, I am not getting any error. Probably I'm not using correctly the put method. Can anyone suggest me how should I use the put method or what else should I try to perform a put request with the RestTemplate?
Also i try to use the exchange method instead of the put:
try {
ResponseEntity<Data> result = template.exchange(Url, HttpMethod.PUT, entity, Data.class);
} catch (Exception e) {
System.out.println(e);
}
But in this case i'm taking the following exception:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [website.Data] and content type [text/html;charset=utf-8].
As you can see from the headers i set the content type as application/xml and not text/html. I look at the headers and i can see that contained:
Accept: application/xml
I really can't understand. What else should i change? Any comments on the exception?
Configuration:
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver"
p:basename="config/views" p:order="1" />
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location">
<value>/WEB-INF/classes/config/xml-views.xml</value>
</property>
<property name="order" value="0" />
</bean>
<!--It is used for redirect-->
<bean id="urlBasedViewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value=""/>
<property name="suffix" value=""/>
<property name="order" value="2" />
</bean>
<context:annotation-config />
<!--<context:annotation-config />-->
<context:component-scan base-package="data.controller" />
<context:component-scan base-package="data.service" />
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
</list>
</property>
</bean>
I guess the client of your application is a web-page (then HTML). This article explain what to do for your webapp to be ready for the future browsers compatibility (if they decide later to support e.g PUT, DELETE operations).
In summary for our project we just declared these line in the web.xml:
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/app/*</url-pattern>
</filter-mapping>
The article talk about javascript to add (probably for version prior to Spring 3.0 M1), but we found this solution better as it is just a configuration.

Is it possible to return both xml or json in Spring and Jersey?

Just doing a little comparison with spring and jersey.
Curious if it is possible for a method to return both xml or json.
Maybe default to xml, but have an optional flag that will return json.
Possible?
You mention Jersey, so I assume you are referring to a REST implementation. Jersey will automatically serialize your content as XML and JSON if your resource is properly setup. You just have to indicate a wider range of media types for your #Produces annotations:
#GET
#Path("/somepath")
#Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_XML})
public MyBean getData() ;
Where MyBean is an appropriately JAXB annotated POJO. With this resource endpoint definition a caller can specify what content type they want via the Accept: header:
GET http://www.example.com/somepath
Accept: text/xml
Spring offers a ContentNegotiatingViewResolver, which aims to provide this functionality. The example from the docs:
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="atom" value="application/atom+xml"/>
<entry key="html" value="text/html"/>
<entry key="json" value="application/json"/>
</map>
</property>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
</bean>
describes switching between HTML and JSON, but this can easily be augmented to support XML as well, using JAXB or some other XML serializer. For further reading:
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-multiple-representations
I'd post an example using XML and Jersey, but I'm not familiar with the beans for Jersey.

Categories