bug when using MarshallingHttpMessageConverter with packagesToScan of Jaxb2Marshaller in Spring RestTemplate

We could achieve auto xsd validation by Using MarshallingHttpMessageConverter in our RestTemplate. To let the Jaxb2Marshaller know what class we want in the context, we could specify one of the 3 properties:

  1. classes to be bounded
  2. context path
  3. packages to scan

Using the 1 and 2 are easy but require you add class/path every time new classes are introduced which is not idea.

iI you want to use packagesToScan property to auto detect newly added class, you simply cannot(as of Spring oxm 4.1.6).

Typically we have some config like this for using

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

   <bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
      <property name="packagesToScan">
         <list>
            <value>org.finra.rc.report.model.cboe</value>
         </list>
      </property>
<!--      <property name="classesToBeBound">
         <list>
            <value>org.finra.rc.report.model.cboe.coats.CoatsReport</value>
         </list>
      </property>-->
      <property name="schemas">
         <list>
            <value>classpath:org/finra/rc/report/cboe/coats/coats-summary.xsd</value>
            <value>classpath:org/finra/rc/report/cboe/ips/ips-summary.xsd</value>
         </list>
      </property>
   </bean>

   <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
      <property name="messageConverters">
         <list>
            <bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
               <property name="marshaller" ref="jaxbMarshaller"/>
               <property name="unmarshaller" ref="jaxbMarshaller"/>
            </bean>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
         </list>
      </property>
   </bean>
</beans>


The reason is although all the classes are scanned and added into the context, when we do the real request and get the response back, Spring checks whether the httpmessageconverter could handle message type(in this case is application/xml) and whether the class is supported which is determined by the below code checking inside ‘org.springframework.oxm.jaxb.Jaxb2Marshaller‘.

	public boolean supports(Class<?> clazz) {
		if (supportJaxbElementClass &amp;&amp; JAXBElement.class.isAssignableFrom(clazz)) {
			return true;
		}
		return supportsInternal(clazz, true);
	}

	private boolean supportsInternal(Class<?> clazz, boolean checkForXmlRootElement) {
		if (checkForXmlRootElement &amp;&amp; AnnotationUtils.findAnnotation(clazz, XmlRootElement.class) == null) {
			return false;
		}
		if (StringUtils.hasLength(getContextPath())) {
			String packageName = ClassUtils.getPackageName(clazz);
			String[] contextPaths = StringUtils.tokenizeToStringArray(getContextPath(), ":");
			for (String contextPath : contextPaths) {
				if (contextPath.equals(packageName)) {
					return true;
				}
			}
			return false;
		}
		else if (!ObjectUtils.isEmpty(getClassesToBeBound())) {
			return Arrays.asList(getClassesToBeBound()).contains(clazz);
		}
		return false;
	}

 

As you can see above, if we only specify packagesToScan but not classesToBeBound nor contextPaths, it returns false which will result in some error message like ‘no suitable httpmessageconverter found for response type’ which is frustrating.

So the conclusion is either use classesToBeBound or contextPaths for now until Spring fixes this. 

 


I was wrong about this. actually the package scan will add classese to the ‘ClassesToBeBound’ which makes it return true in the Spring 4.1.2

 

The core problem for the setSchemas is that:

If you have multiple schema files that are in the same namespace (targetNamespace?) then It could be this bug that is causing trouble:

https://issues.apache.org/jira/browse/XERCESJ-1130

This Apache bug has not been fixed for 3 year(till now). I am not sure why the setSchemas exist then if it does not support multiple schema validation!

 

Advertisements

2 comments

  1. mkwong · March 11, 2016

    We encountered the same issue, but we work around by creating an “envelop” xsd file that include the xsd files.

    For example,

    MyBase.xsd

    ….

    then set this “envelop” xsd to the “schema” property of Jaxb2Marshaller

    • LEON · March 11, 2016

      I see. Interesting.
      For us, our XSD is not used in the marshaller for validation purpose, but also in generating jaxb pojos for which we need a separate/specific path.

      I clicked the apache issue link above and someone posted a new solution on Mar 2016, which i have not get a chance to try:
      SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
      sf.setFeature(Constants.XERCES_FEATURE_PREFIX + Constants.NAMESPACE_GROWTH_FEATURE, Boolean.TRUE);

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s