Spring Framework 4.1 ignores custom Object Mapper in ContentNegotiation - spring

I'm trying to upgrade to Spring 4.1.5.
I have a custom Object mapper defined like so
<bean id="apiObjectMapper" class="my.company.ApiObjectMapper" />
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="apiObjectMapper"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
The object mapper itself looks like this:
public class ApiObjectMapper extends ObjectMapper {
private static final long serialVersionUID = 1L;
public ApiObjectMapper() {
JaxbAnnotationModule module = new JaxbAnnotationModule();
module.setPriority(Priority.SECONDARY);
registerModule(module);
setSerializationInclusion(JsonInclude.Include.NON_NULL);
configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
}
}
The issue happens during content negotiation it seems
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorParameter" value="true" />
<property name="defaultContentType" value="text/xml" />
<property name="mediaTypes" >
<value>
json=application/json
xml=text/xml
</value>
</property>
</bean>
After upgrading, the object mapper is simply ignored. No Jaxb annotations are taken into account, NULLs appear.
Spring 4.0.9 works fine.
I tried Java Configuration with the same result.
Also tried configuring the new Jackson2ObjectMapperFactoryBean directly, but couldn't get the original behavior to happen either.
E.g.: Original output for an endpoint like "list.xml"
<result>
<typeB>
<itemA>...</itemA>
<itemB>...</itemB>
</typeB>
</result>
Now outputs (typeA is empty/null):
<result>
<typeA />
<typeB>
<itemA>...</itemA>
<itemB>...</itemB>
</typeB>
</result>
Any ideas?

I believe I found the culprit and fix for this issue.
When the content negotiation determines that the response should be serialized in JSON then org.springframework.http.converter.json.MappingJackson2HttpMessageConverter is used. However, if the response should be serialized in XML then org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter is used instead. Different message converter that will use a default object mapper if none is provided.
What I had to change was
public class ApiObjectMapper extends XmlMapper { // XmlMapper extends ObjectMapper
// same logic
}
and
<bean id="apiObjectMapper" class="my.company.ApiObjectMapper" />
<mvc:annotation-driven content-negotiation- manager="contentNegotiationManager">
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="apiObjectMapper"/>
</bean>
<!-- Added this bean configuration for XML serialization -->
<bean class="org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter">
<property name="objectMapper" ref="apiObjectMapper"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
I have yet to find what changed in Spring Framework or Jackson to make this happen although my money is n the separation of Jackson XML handling into a different library (I'll update this answer once I do).
Hope it helps.

Leaving some more information for future reference.
Based on this Spring blog post, starting with Spring 4.1, Jackson XML is used to generate XML instead of Jaxb if the former is found on the classpath. That's the main reason for the output to change, because the library generating it changed.
Now after some trial and error, getting Jackson XML to generate the same output as Jaxb is quite the endeavour. Instead you could just remove all references to Jackson XML (not Jackson itself) and everything should work the same way it worked before.

Related

Default value for spring #Value annotation

I have a property annotated with #Value, normally populated from context.xml (jndi/tomcat)
#Value("${some.property}")
private String property
This works fine, but we have installations of our software, where that property shouldn't be configured.
However, if the property is missing, I get a javax.naming.NameNotFoundException: Name [some.property] is not bound in this Context. Unable to find [some.property]., which is logical.
I tried fixing this, by adding a default value this way:
#Value("${some.property:some_property_not_configured}")
private String property
However, I still get the same error.
Any ideas how to prevent/fix this?
I would like to use this in a Spring 3.2.x and a Spring 4+ environment.
The annotation #Value is available from Spring 3+
UPDATE:
The problem was not with the #Value annotation, but in app-config.xml
<entry key="some.property">
<jee:jndi-lookup jndi-name="java:comp/env/some.property" />
</entry>
This caused the error at startup time!
However, if I add default-value="something" here, it still fails with the same error
I solved this by defining a default value in the property-placeholder AND in the #value annotation:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties">
<bean class="java.util.Properties">
<constructor-arg>
<map>
<entry key="some.property">
<jee:jndi-lookup jndi-name="java:comp/env/some.property" default-value="not_configured" />
</entry>
</map>
</constructor-arg>
</bean>
</property>
</bean>
and:
#Value(value = "${some.property:not_configured}")
private String property;

What is the purpose of conversion-service and content-negotiation-manager in Spring?

What is the purpose of conversion-service and content-negotiation-manager in Spring? We have this in our Spring appContext but I am not sure about its purpose.
For content-negotiation-manager:
I have read here:
http://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-config-content-negotiation that the content-negotiation-manager acts like a 'resolver' for #RequestMapping - e.g. if my mapping URL is "/person/create" - it will be called when the client accesses /person/create.json and /person/create.html (given the configuration below).
I am also able to access /person/list.xml and it returns an xml result even if xml is not defined in the content-negotiation-manager since I have the Jackson in my classpath:
For file extensions in the request URI, the MVC Java config and the
MVC namespace, automatically register extensions such as .json, .xml,
.rss, and .atom if the corresponding dependencies such as Jackson,
JAXB2, or Rome are present on the classpath.
So, we defined the content-negotiation-manager since we support html, and it is not mapped by default. Is my understanding correct?
For conversion-service:
In our classes, we have an ObjectMapper.readValue(json, Obj.class) and #RestController returning an object in xml/json format depending on the request (it returns an xml format if you access /list.xml and returns json format when you access /list.json). But I've read that #RestController can work without the conversion-service. So I am not sure why it is supplied in the <mvc:annotation-driven>
<mvc:annotation-driven conversion-service="conversionService" content-negotiation-manager="contentNegotiationManager"/>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" />
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="html" value="text/html"/>
</map>
</property>
<property name="defaultContentType" value="application/json"/>
</bean>
Both the conversion service and the content negotation manager are always used by Spring MVC, regardless whether you define them or not. You can configure them according to your needs. That's the case here.
E.g. name="defaultContentType" value="application/json" means that if the client doesn't prefer a specific media type the server should send back JSON.

Spring Data Rest #EmbeddedId converters registration

I have spring data rest project with EmbeddedId that I have created a converter for. My configuration is in XML and I have registered the converter like this:
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="example.MyCustomConverter"/>
</set>
</property>
</bean>
For some reason I am still get the no converter found error, not sure what I am missing here.
Found a solution for this problem:
It seems like I don't even need a converter here. Just having a toString() and constructor with a single string argument (that can parse the toString() output) in the ID class is sufficient here.

Register a custom conversion service while retaining the defaults?

In a spring-mvc 3.2.RELEASE project I'd like to use org.springframework.data.repository.support.DomainClassConverter to easily get me entities injected.
It works fine when using this config:
<beans:bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean" />
<beans:bean id="conversionService"
class="org.springframework.core.convert.support.DefaultConversionService" />
<beans:bean
class="org.springframework.data.repository.support.DomainClassConverter">
<beans:constructor-arg ref="conversionService" />
</beans:bean>
<annotation-driven conversion-service="conversionService" />
But then Spring isn't loading the formatter for dealing with Joda time types and i get "Failed to convert property value of type java.lang.String to required type org.joda.time.LocalDate for property"
Using only
<annotation-driven/>
The Joda conversion works but not the entity injection.
How do you wire it upp so both work?
Not sure if this answers the question, but I came across a similar problem and this is how I resolved it.
I had implemented a custom converter and conversion service using the documentation
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="example.MyCustomConverter"/>
</list>
</property>
</bean>
The result was what #NA described - this loses the default joda time support and the following definition in an unrelated controller no longer works
#PathVariable(value="day") #DateTimeFormat(pattern=DATE_FORMAT) LocalDate date
The solutions was instead of using org.springframework.context.support.ConversionServiceFactoryBean, I began using org.springframework.format.support.FormattingConversionServiceFactoryBean.

Configuring Datasource in Spring 3.0

Hello guys I have configured a connection pool and JNDI resource in glassfish 2.1. I can get the Datasource via lookup method in my projects and everything works good. However I decided to try Spring framework and to use my existing connection pool.
In the Spring context file I have the following:
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/name" />
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">
<constructor-arg ref="dataSource"/>
</bean>
<bean id="dao" class="com.mycompany.mavenproject3.Dao">
<property name="simpleJdbcTemplate" ref="jdbcTemplate"/>
</bean>
When I deploy the project I get:
java.lang.IllegalArgumentException: 'dataSource' or 'jdbcTemplate' is required]
Is there anything else I have to configure in that file or in any other file in order to get the Datasource?
Presumably, com.mycompany.mavenproject3.Dao extends JdbcDaoSupport, but you're setting a property named simpleJdbcTemplate on it, leading me to believe that you've defined your own property to hold the template since that doesn't exist on Spring's implementation. It's therefore complaining at you because you're required to set either the dataSource property or the jdbcTemplate property of the JdbcDaoSupport object before using it, exactly like it's telling you. Change <property name="simpleJdbcTemplate"... to <property name="jdbcTemplate"....
If your DAO doesn't extend JdbcDaoSupport, then find what does and remove it or set its properties appropriately.
You can also call your datasource directly in your dao bean, don't need to do an another bean for jdbcTemplate. So your context file become something like this:
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/name" />
<bean id="dao" class="com.mycompany.mavenproject3.Dao">
<property name="dataSource" ref="dataSource"/>
</bean>
After you just have to extends JdbcDaoSupport spring class (in which contain the getter and setter of datasource) on your Dao class.

Resources