Handle with exception on auto proxy / proxy factory object - exception

I start learning Spring.NET framework and I am very confusing with behavior of proxy, auto-proxy and exception handling.
for example I defined simple business object and from this object I will throw custom exception.
namespace Aspect.Managers
{
public interface IDbCustomerManager
{
Customer GetCustomerById(long id);
}
public class DbCustomerManager:IDbCustomerManager
{
public Customer GetCustomerById(long id)
{
throw new DbException(string.Format("Problem load customer with Id: {0}",id));
}
}
}
Second I defined Advice for handling with exception.
public class LogExThrowsAdvice:IThrowsAdvice
{
public void AfterThrowing(MethodInfo method, Object[] args,
Object target, DbException exception)
{
Console.WriteLine(exception.Message);
}
}
And last I join togheter business object and advice with proxy.
In app.confing
Advice:
<object id="theLogExThrowsAdvice"
type="Aspect.LogExThrowsAdvice, Log4NetInSpringNet"/>
Auto-Proxy
<object id="theProxyCreator"
type="Spring.Aop.Framework.AutoProxy.TypeNameAutoProxyCreator, Spring.Aop">
<property name="TypeNames" value="Aspect.Managers.DbCustomerManager*"/>
<property name="InterceptorNames">
<list>
<value>theLogExThrowsAdvice</value>
</list>
</property>
</object>
And test it:
var springContext = ContextRegistry.GetContext();
var dbMgr = (IDbCustomerManager)springContext["theDbCustomerManager"];
dbMgr.GetCustomerById(1);
Exception is throwed, method AfterThrowing from LogExThrowsAdvice is not calling.
I try changed type of advice for type BeforeAdvice.
public class DbAccessAdvice:IMethodBeforeAdvice
{
#region Implementation of IMethodBeforeAdvice
public void Before(MethodInfo method, object[] args, object target)
{
Console.WriteLine("You try access to DB");
}
#endregion
}
and in app.config:
<object id="theDbAccessAdvice"
type="Aspect.DbAccessAdvice, Log4NetInSpringNet"/>
<object id="theProxyCreator"
type="Spring.Aop.Framework.AutoProxy.TypeNameAutoProxyCreator, Spring.Aop">
<property name="TypeNames" value="Aspect.Managers.DbCustomerManager*"/>
<property name="InterceptorNames">
<list>
<value>theDbAccessAdvice</value>
<value>theLogExThrowsAdvice</value>
</list>
</property>
</object>
BeforeAdvice is fire but ThrowsAdvice no. Why?
I tried change auto proxy for proxy object factory and tried proxying interfaces IDbCustomerManager.
<object id="theProxy"
type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
<property name="ProxyInterfaces" value="Aspect.Managers.IDbCustomerManager"/>
<property name="Target">
<object type="Aspect.Managers.DbCustomerManager">
</object>
</property>
<property name="InterceptorNames">
<list>
<value>theDbAccessAdvice</value>
<value>theLogAdvice</value>
</list>
</property>
</object>
var springContext = ContextRegistry.GetContext();
var dbMgr = (IDbCustomerManager)springContext["theProxy"];
dbMgr.GetCustomerById(1);
Before advice is fired but throws advice are not? why? Only exception is throwed.
For me is magic how it really work.
I tried used Advisors instead advices:
<!--Advisor-->
<object id="theDbAccessAdvisor"
type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop">
<property name="Pattern" value="Aspect*"/>
<property name="Advice" ref="theDbAccessAdvice"/>
</object>
<object id="theLogAdvisor"
type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop">
<property name="Pattern" value="Aspect*"/>
<property name="Advice" ref="theLogAdvice"/>
</object>
But same result before advice is fired but throws advice not.
I tried use also ExceptionHandleAdvice aspect from Spring.NET same exception is throwed but advice not.
<object id="exAdvice"
type="Spring.Aspects.Exceptions.ExceptionHandlerAdvice, Spring.Aop">
<property name="ExceptionHandlers">
<list>
<value>on exception name DbException swallow</value>
</list>
</property>
</object>
This project is for me magic I upload all VS project here:
http://hotfile.com/dl/135485464/93558e0/Log4Net.7z.html
Here is stackTrace of exception:
at Aspect.Managers.DbCustomerManager.GetCustomerById(Int64 id) in
E:\C#
PROJECTS\STUDY\SPRING.NET\Study.Spring.Net\Aspects\Logging\Log4Net\Managers\DbCustomerManager.cs:line
20 at
_dynamic_Aspect.Managers.DbCustomerManager.GetCustomerById(Object , Object[] ) at Spring.Reflection.Dynamic.SafeMethod.Invoke(Object
target, Object[] arguments) at
Spring.Aop.Framework.DynamicMethodInvocation.InvokeJoinpoint() at
Spring.Aop.Framework.AbstractMethodInvocation.Proceed() at
Spring.Aspects.Exceptions.ExceptionHandlerAdvice.Invoke(IMethodInvocation
invocation)
Also if I try catch exception something like this:
try
{
var springContext = ContextRegistry.GetContext();
var dbMgr = (IDbCustomerManager)springContext["theDbCustomerManager"];
dbMgr.GetCustomerById(1);
}
catch (Exception ex)
{
Console.WriteLine("{0}\n{1}", ex.GetType(), ex.Message);
}
It is not possible..system show message is unhandled exception....

I tested your package. Everything works fine (I'm using v1.3.2).
The exception is caught by the AfterThrows advice (use a breakpoint) but not ignored.
Here is my config:
public void AfterThrowing(MethodInfo method, Object[] args,
Object target, DbException exception)
{
Console.WriteLine(exception.Message);
}
<!--DAO-->
<object id="theCustomerDao"
type="Aspect.Dao.CustomerDao"/>
<!--Business object-->
<object id="theDbCustomerManager"
type="Aspect.Managers.DbCustomerManager">
<!--<property name="CustomerDao" ref="theCustomerDao"/>-->
</object>
<!--Advices-->
<object id="theLogAdvice"
type="Aspect.LogExThrowsAdvice, Log4NetInSpringNet"/>
<!--Proxy creator-->
<object type="Spring.Aop.Framework.AutoProxy.TypeNameAutoProxyCreator, Spring.Aop">
<property name="TypeNames" value="Aspect.Managers.DbCustomerManager*"/>
<property name="InterceptorNames">
<list>
<value>theLogAdvice</value>
</list>
</property>
</object>

If you take your code and combine it with bbaia's config, then you're halfway there I think.
From your question and comments, I understand that you want to swallow the DbExceptions.
Note that generally speaking, you'd never want your logger swallow exceptions, but for the question's sake assume you do - but promise me you'll never do this in a production environment :-)
I suggest you slightly adjust your LogExThrowsAdvice to:
public class LogExThrowsAdvice : Spring.Aspects.Exceptions.ExceptionHandlerAdvice, IThrowsAdvice
{
public void AfterThrowing(MethodInfo method, Object[] args,
Object target, Exception exception)
{
Console.WriteLine(exception.Message);
}
}
Inheriting from ExceptionHandlerAdvice allows you to specify how to handle thrown exceptions, for instance swallow it.
Then take bbaia's config and add an event handler to it:
<object id="theLogAdvice"
type="Aspect.LogExThrowsAdvice, Log4NetInSpringNet">
<property name="exceptionHandlers">
<list>
<value>on exception name DbException swallow</value>
</list>
</property>
</object>
Now all DbExceptions are swallowed.
With respect to your question as to how this still throws in Visual Studio: could it be that your VS is set to "break on a thrown exception"? Go to Debug -> Exceptions and _un_check the Thrown checkbox for Common Language Runtime Exceptions.
Note that if you keep this checked, you can still continue when the exception is raised: the program will not crash, because the exception is handled.

Related

Html head flushing with freemarker

I have a Spring MVC web application that uses freemarker as the template language. I am currently working on the changes to flush the html head section rather than buffering the whole html and flushing at the end. I tried setting the auto_flush freemarker setting to false and used freemarker's builtin <#flush> directive as below, but that doesn't seem to work.
common-header.ftl
<head>
.......
</head>
<#flush>
page.ftl
<#include "common-header.ftl" />
<body>
.......
</body>
I would appreciate your help with this. Also, per the API documentation, autoFlush() seems to only work for pages which aren't composed with #include statements and require multiple Template.process() methods. If that's correct, should i write a custom template processor to handle the head and body sections in my page ? Any pointers would be helpful.
Update:
Tried using FreeMarkerView.java as the view class as it uses the default writer (PrinterWriter) of HttpServletResponse to process the writer. This doesn't work either though PrinterWriter does support flush() and the <#flush> freemarker directive in my template doesn't seem to be invoking this.
Tried extending the FreeMarkerView class to wrap the PrinterWriter inside a BufferedWriter, and that doesn't work as well.
<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="suffix"><value>.ftl</value></property>
<property name="contentType"><value>text/html; charset=utf-8</value></property>
<property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView"/>
<property name="exposeSpringMacroHelpers"><value>true</value></property>
</bean>
I would appreciate any help with this.
<#flush> simply calls Writer.flush() on the Writer that was given to Template.process. If, for example, that Writer is a StringWriter, the flush() call will not do nothing of course. The thing that passes the Writer to FreeMarker will have to ensure that that Writer does the right thing when its flush() method is called.
auto_flush is unrelated to your problem. (But you misunderstand the API docs. auto_flush is always supported. The docs describe the case when you want to set it to false.)
Not an answer to the original question but to #soontobeared comment. I was never notified about your comment so sorry for the late response.
I'm using a buffered view during development so I can catch various exception and output debug etc.. This will give access to the Writer. I'm using Spring 3.2 and FreeMarker 2.3.
BufferedFreeMarkerView.java
package com.example.web;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Set;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.view.freemarker.FreeMarkerView;
import freemarker.core.InvalidReferenceException;
import freemarker.template.SimpleHash;
import freemarker.template.Template;
import freemarker.template.TemplateException;
public class BufferedFreeMarkerView extends FreeMarkerView {
#Override
protected void processTemplate(Template template, SimpleHash model,
HttpServletResponse response) throws IOException, TemplateException {
StringWriter buffer = new StringWriter(50000);
try {
template.process(model, buffer);
} catch (TemplateException e) {
logger.warn(e.getMessage() + "\n" + e.getFTLInstructionStack(), e);
throw new RuntimeException(e.getMessage() + "\n"
+ e.getFTLInstructionStack(), e);
} // ommited more catches
try (Writer out = response.getWriter()) {
out.write(buffer.toString());
}
}
}
BufferedFreeMarkerViewResolver.java
package com.example.web;
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;
public class BufferedFreeMarkerViewResolver extends FreeMarkerViewResolver {
/**
*
*/
public BufferedFreeMarkerViewResolver() {
setViewClass(requiredViewClass());
}
#SuppressWarnings("rawtypes")
#Override
protected Class requiredViewClass() {
return BufferedFreeMarkerView.class;
}
}
From spring dispatcher context
<bean id="viewResolver" class="com.example.web.BufferedFreeMarkerViewResolver">
<property name="cache" value="true"/>
<property name="prefix" value=""/>
<property name="suffix" value=".ftl"/>
<property name="allowSessionOverride" value="true"/>
<property name="exposeSpringMacroHelpers" value="true"/>
<property name="exposeRequestAttributes" value="true"/>
<property name="exposeSessionAttributes" value="true"/>
<property name="requestContextAttribute" value="rc"/>
</bean>

connection.createStatement in Spring-managed datasource never return

This is very strange in my RESTful WS development. I am using Tomcat 7 with Jersey 1.8, Spring 2.5 and MySQL database.
I defined two data sources like this:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean" scope="singleton">
<property name="jndiName" value="jdbc/MAINDB" />
<property name="resourceRef" value="true" />
</bean>
<bean id="orderDataSource" class="org.springframework.jndi.JndiObjectFactoryBean" scope="singleton">
<property name="jndiName" value="jdbc/ORDERS" />
<property name="resourceRef" value="true" />
</bean>
and inject one data source into the servlet context like this:
<bean class="org.springframework.web.context.support.ServletContextAttributeExporter">
<property name="attributes">
<map>
<entry key="orderData">
<ref bean="orderDataSource" />
</entry>
</map>
</property>
</bean>
and this code:
orderDataSource = (DataSource) sc.getServletContext().getAttribute("orderData");
Order ord = new Order();
Statement stmt = null;
try {
stmt = orderDataSource.getConnection().createStatement();
} catch (SQLException ex) {
Logger.getLogger(OrdersResource.class.getName()).log(Level.SEVERE, null, ex);
}
gets executed 6 or 7 times flawlessly and from the 8th request, it gets stall at createStatement() line and never return or throw any exception!
However, all other resources API from MAINDB still works very well.
Anyone knows what's happening?
Why are you bothering with such code yourself? If you are using Spring already, use the JdbcTemplate instead. It can be initialized with your DataSource and will manage all that for you.
You are requiring a connection from the pool but you never closes it (same for the statement actually). Using JdbcTemplate prevents you from having those issues in the first place.
Something like
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.execute(...)
See the javadoc for more details.

Spring 3.0 exception converting String to java.util.Date on POST

I'm hoping someone can help me since I have been banging my head against a wall for a couple of days on a issue which seems straightforward and which has been documented in other threads on the web.
I am using Smart GWT client (3.0) in conjunction with Spring 3.1 server and using JSON to communicate (with Jackson API 1.9).
The issue is that when I attempt to save a date from my SmartGWT client and it is sent to the server I get the following exception:
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'comment' on field 'dateAdded': rejected value [2012-06-27T10:57:47+0100]; codes [typeMismatch.comment.dateAdded,typeMismatch.dateAdded,typeMismatch.java.util.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [comment.dateAdded,dateAdded]; arguments []; default message [dateAdded]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'dateAdded'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type java.util.Date for value '2012-06-27T10:57:47+0100'; nested exception is java.lang.IllegalArgumentException]
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:110)
I have seen this issue in a few other posts, but most relate to not having formatted the Date in the correct format, but I have tried various formats:
- yyyy-MM-dd
- yyyy-MM-dd'T'HH:mm:ssZ
- yyyyMMddHHmmssZ (as per suggestion here: http://code.google.com/p/usersapi/issues/detail?id=8)
So in my code I have done the following:
Configured a CustomObjectMapper:
`
public class CustomObjectMapper extends ObjectMapper {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
public CustomObjectMapper() {
super();
configure(Feature.WRITE_DATES_AS_TIMESTAMPS, false);
setDateFormat(formatter);
getDeserializationConfig().setDateFormat(formatter);
}
}
`
Spring app context thusly:
`
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<constructor-arg ref="jaxbMarshaller" />
<property name="supportedMediaTypes" value="application/xml"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
<property name="supportedMediaTypes" value="application/json" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<context:component-scan base-package="com.jpmorgan.creditriskreporting.server" />
<bean id="marshallingConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<constructor-arg ref="jaxbMarshaller" />
<property name="supportedMediaTypes" value="application/xml"/>
</bean>
<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
<bean id="jacksonObjectMapper" class="com.jpmorgan.creditriskreporting.server.util.CustomObjectMapper" />
<!-- Client -->
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<ref bean="marshallingConverter" />
<ref bean="jsonConverter" />
</list>
</property>
</bean>
`
Bean object:
`
import java.util.Date;
#JsonAutoDetect
public class Comment {
private int id;
private String comment;
private Date dateAdded;
public Comment() {}
public Comment(int id) {
this.id = id;
}
...
//#JsonSerialize(using=JsonDateSerializer.class) -- I had previously tried to use these custom Date serializer class
public Date getDateAdded() {
return dateAdded;
}
//#JsonDeserialize(using=JsonDateDeserializer.class)
public void setDateAdded(Date dateAdded) {
this.dateAdded = dateAdded;
}
`
EDIT:
Controller Class
This may be where the issue lies, since when I use #RequestBody it works from my Integration tests, however, my Abstract RestDataSource in SmartGWT only works with #ModelAttribute, so I'm not sure how to proceed.
#RequestMapping(value="/", method=RequestMethod.POST)
public #ResponseBody Comment createNewComment2(#ModelAttribute Comment comment) {
log.info("calling createComment with comment: {}", comment);
comment.setDateAdded(new Date());
Comment added = commentDao.create(comment);
log.info("created comment: {}", added);
return commentDao.get(comment);
}
So I can fetch data from the server and the date is displayed in SmartGWT fine. It's only when I do the add data that I get the issue. From Smart GWT Developer Console:
{
"dataSource":"CommentDS",
"operationType":"add",
"componentId":"isc_DynamicForm_1",
"data":{
"userAdded":"sharper",
"dateAdded":"2012-06-27T10:57:47+0100",
"comment":"sample"
},
"callback":{
"target":[DynamicForm ID:isc_DynamicForm_1],
"methodName":"saveEditorReply"
},
"showPrompt":true,
"prompt":"Saving form...",
"oldValues":{
},
"clientContext":{
},
"requestId":"CommentDS$6272"
}
Any help with this is hugely appreciated.
Cheers,
Steve
I found out the issue thanks to http://vkubushyn.wordpress.com/2011/05/31/smart-gwt-restful-spring-mvc
Had to use Spring's InitBinder
#InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}
You should add DateFormat into your model.
#DateTimeFormat(pattern = "dd.MM.yyyy")
private Date beginDate;
#DateTimeFormat(pattern = "dd.MM.yyyy")
private Date endDate;
as a function parameter
void functionName** (#RequestParam("beginDate") #DateTimeFormat(pattern = "dd.MM.yyyy")Date beginDate, #RequestParam("endDate") #DateTimeFormat(pattern = "dd.MM.yyyy")Date endDate)
I might be wrong, but as far as I remember the Z stands for timezone in ISOwhoknowswhatformat. And that's 4 chars wide, so I would try this:
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZ");
By the way: if this is the issue you should've catched it in your unit tests. You do have unit test for CustomObjectMapper don't you? :P

Can #JsonTypeInfo be used with Collections?

Using Spring 3 and Jackson 1.7.6, I can serialize implementations of an abstract class and output the fully-qualified name of the class as a property called #class. This works fine when my Spring controllers return a single instance from a controller annotated with #ResponseBody.
When returning a Collection of the above types the resulting JSON changes according to which type is being serialized (fields from each subclass are present), but it does not include the #class property, which our client code needs.
How can I get this type hint into the serialized JSON when returning a collection?
//Returns complete with #class=com.package.blah
#RequestMapping("/json/getProduct.json")
public #ResponseBody Product getProduct(Integer id)
{
return service.getProduct(id);
}
//Does not include #class
#RequestMapping("/json/getProducts.json")
public #ResponseBody List<Product> getProducts()
{
return service.getProducts();
}
In order to do this you will need to configure ObjectMapper. This is not straightforward via Spring, as rather than settable properties, ObjectMapper has invokable methods that set its state (and then it stores this as a bitmask).
If you are using <mvc:annotation-driven /> you will need to replace it with the equivalent markup, which can be found in the Spring JavaDocs.
Extend ObjectMapper:
public class ConfigurableObjectMapper extends ObjectMapper
{
public ConfigurableObjectMapper()
{
this.enableDefaultTypingAsProperty(DefaultTyping.JAVA_LANG_OBJECT, JsonTypeInfo.Id.CLASS.getDefaultPropertyName());
}
}
Then tell Spring to use an instance of this class instead of the default implementation.
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="order" value="0" />
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="validator" ref="validator" />
</bean>
</property>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper">
<bean class="com.blitzgamesstudios.web.common.json.ConfigurableObjectMapper" />
</property>
</bean>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.FormHttpMessageConverter" />
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
</list>
</property>
</bean>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
You can use #JsonTypeInfo with POJOs, Collections and Maps, but note that the declared value type of Collections and Maps must be one that has (or inherits) #JsonTypeInfo annotation (when using per-class #JsonTypeInfo annotation). This would not work, for example, if you have type like "Collection" -- in this case, Deejay's answer is correct, as you can force inclusion with "default typing" option.
But things should also work if you have a Collection property to serialize/deserialize, i.e.:
public class Bean {
#JsonTypeInfo(....)
public Collection<Object> listOfObjects; // does work because it's per-property annotation!
// ... also, applies to value type and not Collection type itself
}
since that will override any #JsonTypeInfo annotations value type might otherwise have
I had the problem withjava.util.Map, so I did something like:
public interface MyMap extends Map<Long, Product> {}
and
public class MyHashMap extends HashMap<Long, Product> implements MyMap {}
Found on: http://jackson-users.ning.com/forum/topics/mapper-not-include-type-information-when-serializing-object-why
Object mapper bean can enable default typing:
ObjectMapper mapper = new ObjectMapper()
mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);
This will give the json output as following:
[
"java.util.ArrayList",
[
{
"#class": "com.xyz.Product",
"name": "myName"
}
]
]

Jackson serializationConfig

I am using Jackson JSON in a Spring 3 MVC app. To not serialize each and every single Date field, I created a custom objectmapper that uses a specific DateFormat:
#Component("jacksonObjectMapper")
public class CustomObjectMapper extends ObjectMapper
{
Logger log = Logger.getLogger(CustomObjectMapper.class);
#PostConstruct
public void afterProps()
{
log.info("PostConstruct... RUNNING");
//ISO 8601
getSerializationConfig().setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SZ"));
}
//constructors...
}
This custom ObjectMapper is injected into the JsonConverter:
<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
<property name="objectMapper" ref="jacksonObjectMapper" /> <!-- defined in CustomObjectMapper -->
</bean>
There is no exception in the logs and serialization works, but it is not picking up the dateformat, it simple serializes to a timestamp. The #PostConstruct annotation works, the log statement in the method is in the logs.
Does anyone know why this fails?
You may also need to specify that you want textual Date serialization, by doing:
configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
(although I was assuming setting non-null date format might also trigger it, but maybe not)
Also, you can do configuration of mapper directly from constructor (which is safe). Not that it should change behavior, but would remove need for separate configuration method.
I've done the below which works to get around compatability with Java / PHP timestamps. Java uses milliseconds since EPOCH and PHP uses seconds so was simpler to use ISO dates.
I declare the below message adapters:
<bean id="messageAdapter"
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<bean id="jacksonJsonMessageConvertor"
class="my.app.MyMappingJacksonHttpMessageConverter"/>
</list>
</property>
</bean>
And MyMappingJacksonHttpMessageConverter looks like the below:
public class MyMappingJacksonHttpMessageConverter extends MappingJacksonHttpMessageConverter {
public MyMappingJacksonHttpMessageConverter(){
super();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(Feature.WRITE_DATES_AS_TIMESTAMPS, false);
setObjectMapper(objectMapper);
}
}
With the above all dates are written out in ISO format.
For Spring config application.properties
spring.jackson.serialization.fail-on-empty-beans=false