Spring 4 (no boot) with custom jackson ObjectMapper - spring

Secton 65.3 of the spring boot manual indicates that I can replace the default ObjectMapper by providing my own. I am not using boot, just a spring WebMVC application that builds to a .war and runs in tomcat.
It instantiates my ObjectMapper but doesn't use it. I used the debugger to trace through why timestamps still come out as numeric and found that it was using a different instance of ObjectMapper. It's not clear to me where it came from, or why this doesn't cause it to only use mine:
#Primary
#Bean
public ObjectMapper localObjectMapper() {
JodaMapper mapper = new JodaMapper();
mapper.setWriteDatesAsTimestamps(false);
mapper.getSerializationConfig().with(SerializationFeature.INDENT_OUTPUT)
.without(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS)
.without(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS)
.without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return mapper;
}
The above is in a #Configure bean that's definitely getting loaded.
The approach I took above worked fine in Spring 3, just not when I ugpraded to 4.2.2. I have read Jackson Integration Improvements as well, and tried approaches listed there, to the same effect.
--Chris

The way I always did it was:
#Configuration
#EnableWebMvc
public class MyWebMvcConfigurer extends WebMvcConfigurerAdapter {
#Bean
public ObjectMapper localObjectMapper() {
JodaMapper mapper = new JodaMapper();
// ...
return mapper;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter (localObjectMapper())); // use your own ObjectMapper
}
}
One warning, to quote the JavaDoc of WebMvcConfigurer.html#configureMessageConverters:
Note that adding converters to the list, turns off default converter registration. To simply add a converter without impacting default registration, consider using the method extendMessageConverters(java.util.List) instead.

In Spring 4 I've solved with the following xml-configuration
<bean name="jacksonObjectMapper"
class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="featuresToDisable">
<array>
<util:constant
static-field="com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS" />
</array>
</property>
</bean>
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
putting it in the Servlet configuration file, usually under
/WEB-INF/spring/appServlet/*.xml

Related

How to use java Java configuration class in spring integration xml file related to apache kafka?

we are using xml file to implement apache kafka using spring-integration. for now producer and consumer factories are called using #bean in xml file. but I want to read configurations from java class and use that class in the xml file. How do I do that for producer and consumer?
current version for producer:
<int:chain input-channel="kafka-output-channel">
<int:object-to-json-transformer/>
<int-kafka:outbound-channel-adapter id="kafkaOutboundChannelAdapter"
kafka-template="template"
topic="${topic.name}" />
</int:chain>
<bean id="pf" class="org.springframework.kafka.core.DefaultKafkaProducerFactory">
<constructor-arg>
<map>
<entry key="bootstrap.servers" value="${broker.list}" />
<entry key="key.serializer" value="org.apache.kafka.common.serialization.StringSerializer"/>
<entry key="value.serializer" value="org.apache.kafka.common.serialization.StringSerializer"/>
</map>
</constructor-arg>
</bean>
<!-- Kafka Template -->
<bean id="template" class="org.springframework.kafka.core.KafkaTemplate">
<constructor-arg ref="pf"/>
</bean>
But I want to create class like below and use beans below in the xml file,
#Configuration
public class KafkaProducerConfig {
#Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
#Bean
public ProducerFactory<String, String> producerFactory() {
Map<String, Object> props = new HashMap<>();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, appConfig.getBrokersList());
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
return new DefaultKafkaProducerFactory<>(props);
}
}
same thing I want to do for consumer part!
could someone please help?
Nothing is changed here. The XML and Java & Annotation configurations contributes to the same ApplicationContext and bean declared in one place can be references in the other, and wise versa. So, to use that kafkaTemplate bean in the XML, you just need to use exactly this name instead of that template and remove those bean definitions in the XML.
Only the problem that annotation configuration has to be primary: use an AnnotationConfigApplicationContext. Although I see that you use Spring Boot, so everything is OK. Your XML config has to be used on some #Configuration class as a #ImportResource. On the other hand if you have already enough experience with annotations configuration, there might not be a reason to stick with XML config even for Spring Integration. Consider to investigate Java DSL which smoothly replaces whatever you can have in XML: https://docs.spring.io/spring-integration/docs/current/reference/html/dsl.html#java-dsl
Please, also pay attention that there is an auto-configuration for Apache Kafka in Spring Boot. So you might not need to configure your template and produce/consumer factories: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#messaging.kafka

How to use MappingJackson2HttpMessageConverter to convert Maps and List correctly?

In my Spring MVC project MappingJackson2HttpMessageConverter configured as follows:
<bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonMessageConverter" />
</list>
</property>
</bean>
For plain simple Java POJO Beans, serialization works well. All good. But, i also have Beans that include Maps that needed to be serialized as well, and it's fails (JsonMappingException).
From Jackson instructions, I know that to solve this what needed is to indicate the actual type for the object mapper. It looks like that:
Map<String, ResultValue> results = mapper.readValue(jsonSource,
new TypeReference<Map<String, ResultValue>>() { } );
How can be the same configuration be done to MappingJackson2HttpMessageConverter (with based on Jackson2 object mapper)?
It's not clear from your question what doesn't work, but I'm guessing that you have a bean that contains java.util.Map or java.util.List property and you're getting com.fasterxml.jackson.databind.JsonMappingException when deserializing that bean.
In that case you can give hints on fields with #JsonDeserialize annotation. So for instance if you have a java.util.Map<String, ResultValue> field, you can annotate it like:
#JsonDeserialize(keyAs = String.class, contentAs = ResultValue.class)
public Map<String, ResultValue> map;

Is it possible to use SpringData-JPA with a hibernate4.LocalSessionFactoryBean?

I am already using Hibernate 4 directly with a LocalSessionFactoryBean and a SessionFactory in my code.
I would now like to include Spring-Data-JPA in my code.
But Spring-Data needs an EntityManagerFactory to work, which can be configured through a LocalContainerEntityManagerFactoryBean. Can these Beans LocalSessionFactoryBean and LocalContainerEntityManagerFactoryBean coexist in one Spring project?
(Or can one be adapted by the other?)
What is the best practice?
Although they can coexists it will be problematic especially if you want to have them participate in the same transaction. However if you switch your logic around and configure a LocalContainerEntityManagerFactoryBean instead of a LocalSessionFactoryBean you can use the HibernateJpaSessionFactoryBean to get access to the underlying SessionFactory.
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!-- Your properties here -->
</bean>
<bean id="sessionFactory" class="org.springframework.orm.jpa.vendor.HibernateJpaSessionFactoryBean">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
Now you have both and can participate in the same transaction.
This solution is also documented in the Spring Data JPA reference guide in the FAQ section.
#Autowired
private EntityManager entitymanager;
public List<SpBooking> list() {
// #SuppressWarnings("unchecked")
System.out.println("******************************");
// #SuppressWarnings("unchecked")
List<SpBooking> listUser = (List<SpBooking>)((Session)entitymanager.getDelegate())
.createCriteria(SpBooking.class)
.list();
for (SpBooking i:listUser)
System.out.println("------------------"+i.getBookingId());
return listUser;
}
And as of JPA 2.1, EntityManagerFactory.unwrap(java.lang.Class) provides a nice approach, documented here: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/orm/jpa/vendor/HibernateJpaSessionFactoryBean.html
#Bean
public SessionFactory sessionFactory(#Qualifier("entityManagerFactory") EntityManagerFactory emf) {
return emf.unwrap(SessionFactory.class);
}

Does Spring framework makes possible to inject a collection in annotation-driven fashion?

Is it possible to do the same using annotation-driven injection:
<beans>
...
<bean id="interceptorsList" class="com.mytest.AnyAction">
<property name="interceptors">
<list>
<ref bean="validatorInteceptor"/>
<ref bean="profilingInterceptor"/>
</list>
</property>
</bean>
</beans>
Is it possible to do the same using annotation-driven injection?
Good question - I don't think so (assuming that by "annotation-driven injection" you're referring to annotations on AnyAction).
It's possible that the following might work, but I don't think Spring recognises the #Resources annotation:
#Resources({
#Resource(name="validatorInteceptor"),
#Resource(name="profilingInterceptor")
})
private List interceptors;
Give it a try anyway, you never know.
Other than, you can use #Configuration-style configuration instead of XML:
#Configuration
public class MyConfig {
private #Resource Interceptor profilingInterceptor;
private #Resource Interceptor validatorInteceptor;
#Bean
public AnyAction anyAction() {
AnyAction anyAction = new AnyAction();
anyAction.setInterceptors(Arrays.asList(
profilingInterceptor, validatorInteceptor
));
return anyAction;
}
}
Yes, Spring will happily inject all configured interceptors if you use this pattern:
#Autowired
public void setInterceptors(List<Interceptor> interceptors){
this.interceptors = interceptors;
}
private List<Interceptor> interceptors;
Note that you will probably have to configure default-autowire=byType on your context.xml. I don't know if there's an alternative to this in plain annotation configuration.

Another question on Spring 3, servlet, #autowired

I think I've read every question and answer on Spring and autowiring a servlet, both here and at springsource.org, and I still can't get it working.
All I want to do is have the datasource automatically set in my servlets. I understand that the container creates the servlet and not Spring.
Here is code from my test servlet:
package mypackage.servlets;
imports go here...
#Service
public class TestServlet extends HttpServlet
{
private JdbcTemplate _jt;
#Autowired
public void setDataSource(DataSource dataSource)
{
_jt = new JdbcTemplate(dataSource);
}
etc etc
In my applicationContext.xml I have:
<context:annotation-config />
<context:component-scan base-package="mypackage.servlets />
<import resource="datasource.xml" />
and in my datasource.xml:
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/db" />
If I can't get this working I'll just use WebApplicationContextUtils in the servlet's init method but I'd really like to make this work after all the reading I've been doing.
I'm using Spring 3, Java 1.6.
Thanks,
Paul
You need to replace your Servlets by Spring MVC contollers. Because Spring will not inject anything the classes (servlets) created by someone else then Spring itselfe (except #Configurable).
(To get an very simple example, take a look at the STS Spring Template Project: MVC).
What I wanted to do was get a DataSource reference in my Servlet for free, i.e. not calling a static getDatasource method on some class.
Here's what I learned and how I got it working:
Servlets cannot be configured or autowired by Spring. Servlets are created before Spring's app context is loaded. See issue SPR-7801: https://jira.springsource.org/browse/SPR-7801
What I did was create a DataSource in my applicationContext.xml and export that as a property:
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/db" />
<bean class="org.springframework.web.context.support.ServletContextAttributeExporter">
<property name="attributes">
<map>
<entry key="myDatasource">
<ref bean="dataSource"/>
</entry>
</map>
</property>
</bean>
In my servlet's init method I read the property:
public void init(ServletConfig config)
{
Object obj = config.getServletContext().getAttribute("myDatasource");
setDataSource((DataSource)obj);
}
public void setDataSource(DataSource datasource)
{
// do something here with datasource, like
// store it or make a JdbcTemplate out of it
}
If I'd been using DAOs instead of hitting the database from the servlets it would have been easy to wire them up for #Autowired by marking them #Configurable, and also be able to use #Transactional and other Spring goodies.

Resources