Spring Data Rest #EmbeddedId converters registration - spring

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.

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 Framework 4.1 ignores custom Object Mapper in ContentNegotiation

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.

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.

Using Jaxb2Marshaller to unmarshall an xml and validate against a schema

I am implementing a REST service with xml as the payload and have the following configuration to use Jaxb2Marshaller to unmarshall my xml. Here is the configuration from my spring context file
<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.my.examples.Product</value>
</list>
</property>
<property name="schema" value="classpath:schemadefinitions/product.xsd" />
</bean>
On my bean Product I have just this annotation as
#XmlRootElement(name="product")
public class ProductInfo {
The issue is when I make the REST request it unmarshalls xml to bean properly but doesn't perform any validation against the schema configured with the marshaller. Am I missing anything ?
I had to attach a validationeventhandler to the marshaller as jaxb2Mashaller.setValidationEventHandler(...) Once this is set the unmarshaller started to validate input xml.
With Java 8 and JaxB 2.2.4, I don't see any problems with the original setup! Defining the schema property in the applicationcontext.xml is ALL you need to do in order to get schema validation going.
If one creates Jaxb2Marshaller "manually", one needs to make sure to call the afterPropertiesSet method after setting the schema resource, since it loads the schema resource into memory.

Resources