Html head flushing with freemarker - html

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>

Related

Wrap a primitive returned from a Spring controller into json

For now, I am using something like this:
#RequestBody
#RequestMapping("whatever")
public ObjectWrapper<Integer> foo() {
return new ObjectWrapper<>(42);
}
What I would like to do is to rewrite the method in the following way
#RequestBody
#RequestMapping("whatever")
public int foo() {
return 42;
}
and get 42 (or any other primitive) wrapped into ObjectWrapper before it gets serialized (by Jackson) and gets written into response. I wonder if it is actually possible and, if so, how to do that.
As I have misunderstood your question, I updated my answer:
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
super();
super.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
}
}
Add to default message converter:
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
</mvc:message-converters>
<bean id="jacksonObjectMapper" class="com.mysite.CustomObjectMapper" />
However this might not produce the output you desired.
Best thing is to write your own serializer and use it with your custom object mapper and wrap primitives in your serializer.
Here is something related: https://github.com/FasterXML/jackson-databind/issues/34

Binary File To SQL Database Apache Camel

I need some guidance around which approach to use to load binary files from a folder into a MySQL Database using Camel. Basically I want to store voice logs from our PBX system into a database. The directory with the voice logs will be a remote directory
I have designed a prototype but I am not sure if this is really efficient, it works but I am not happy with the design. Let me explain what I am doing. Camel route as follows:
<camelContext xmlns="http://camel.apache.org/schema/spring">
<package>com.hia.camelone</package>
<route>
<from uri="file://c:/CTest/Inbox?noop=true&recursive=true&delay=3000"/>
<to uri="bean://fileToSQL"/>
<to uri="jdbc://timlogdb"/>
</route>
</camelContext>
<bean id="timlogdb" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value=" com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/TimLog" />
<property name="username" value="root" />
<property name="password" value="blahblah" />
</bean>
<bean id="fileToSQL" class="com.hia.camelone.fileToSQL"/>
And the code to fileToSQL bean is:
public class fileToSQL {
public String toString(#Headers Map<String,Object> header, #Body Object body){
StringBuilder sb = new StringBuilder();
String filename =(String)header.get("CamelFileNameOnly");
String escapedFileName = StringEscapeUtils.escapeJava(filename).replace("\'", "");
String filePath = StringEscapeUtils.escapeJava((String)header.get("CamelFilePath"));
sb.append("insert into FileLog ");
sb.append("(FileName,FileData) values (");
sb.append("'").append(escapedFileName).append("',").append("LOAD_FILE(\"").append(filePath).append("\")");
sb.append(")");
System.out.println(sb.toString());
System.out.println(body);
System.out.println(header.toString());
return sb.toString();
}
}
Ok short explanation I get the file component to consume the files then I build a SQL string using the MySQL LOAD_FILE() function to load the file.
My thoughts around this:
The LOAD_FILE function only works on the local machine and thus this route will only with the files being on the local machine. I could use a file producer to copy the files from some remote directory to a local directory and then use the route. My route would be something like this then:
<route>
<from uri="file://c:/CTest/Inbox?noop=true&recursive=true&delay=3000"/>
<to uri="file://c:/outbox"/>
<to uri="bean://fileToSQL"/>
<to uri="jdbc://timlogdb"/>
</route>
However since I have access to the files content in the message from the files consumer I should be able to theoretically be able to access the body/content of the string and build a SQL command that does NOT use the LOAD_FILE() function.
The only way I know how to build such a string is by using the prepared statement of JDBC. This would be first prize if I could somehow build a insert statement with the content from the file consumer.
Can I create a prepared statement in my fileToSQL bean and pass it to my jdbc component?
Or how do I build a INSERT statement without the LOAD_FILE() function?
Since I have to use the LOAD_FILE() function I would now have to cater for both unix and windows filepaths. While this should not be difficult I just dont like the idea of putting OS specific code into my applications(feels like a work around).
Anybody here ever uploaded binary files to a MySQL database using Camel who can give me some guidance on the points above. While I could work around the problems I just want to make sure I dont miss a obvious way of doing things.
I had a look around here and only found people working with mostly text files. Guys please don't even go down the route of me storing the file on the files system and linking it to the database. We have some very specific disaster recovery requirements and legal requirements that enforce the need for me to store it in a database.
Right so I managed to find a way and it was not that difficult. What I essentially did was get rid of the JDBC Camel Component in the route. I then injected the data source bean into my fileToSQL bean. I then used a simple prepared statement to insert the file and its name into MySQL.
As always code is much more explicit than my english.
<camelContext xmlns="http://camel.apache.org/schema/spring">
<package>com.hia.camelone</package>
<route>
<from uri="file://c:/CTest/Inbox?noop=true&recursive=true&delay=3000"/>
<to uri="bean://fileToSQL"/>
<!--<to uri="jdbc://timlogdb"/>-->
</route>
</camelContext>
<bean id="timlogdb" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value=" com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/TimLog" />
<property name="username" value="root" />
<property name="password" value="lalala" />
</bean>
<bean id="fileToSQL" class="com.hia.camelone.fileToSQL">
<property name="dataSource" ref="timlogdb"/>
</bean>
As you can see I inject my timlogdb bean into my fileToSQL bean. Spring ROCKS!
So here is my fileToSQL bean.
public class fileToSQL {
private DriverManagerDataSource dataSource;
private static final String SQL_INSERT="insert into FileLog(FileName,FileData)values(?,?)";
#Handler
public void toString(#Headers Map<String,Object> header,Exchange exchange){
Connection conn = null;
PreparedStatement stmt=null;
String filename =StringEscapeUtils.escapeJava(((String)header.get("CamelFileNameOnly")).replace("\'", ""));
try {
conn= dataSource.getConnection();
stmt =conn.prepareStatement(SQL_INSERT);
stmt.setString(1, filename);
byte[] filedata = exchange.getIn().getBody(byte[].class);
stmt.setBytes(2,filedata );
int s = stmt.executeUpdate();
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
finally{
try
{
if (stmt!=null)
{
stmt.close();
}
if (conn!=null)
{
conn.close();
}
}
catch(SQLException e)
{
System.out.println(e.getMessage());
}
}
}
/**
* #param dataSource the dataSource to set
*/
public void setDataSource(DriverManagerDataSource dataSource) {
this.dataSource = dataSource;
}
}
The guys from Camel did a great job. Camel is truly flexible especially when you combine it with Spring.
What a ride!

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"
}
]
]

When using Spring MVC for REST, how do you enable Jackson to pretty-print rendered JSON?

While developing REST services using Spring MVC, I would like render JSON 'pretty printed' in development but normal (reduced whitespace) in production.
If you are using Spring Boot 1.2 or later the simple solution is to add
spring.jackson.serialization.INDENT_OUTPUT=true
to the application.properties file. This assumes that you are using Jackson for serialization.
If you are using an earlier version of Spring Boot then you can add
http.mappers.json-pretty-print=true
This solution still works with Spring Boot 1.2 but it is deprecated and will eventually be removed entirely. You will get a deprecation warning in the log at startup time.
(tested using spring-boot-starter-web)
I had an answer when I posted this question, but I thought I'd post it anyway in case there are better alternative solutions. Here was my experience:
First thing's first. The MappingJacksonHttpMessageConverter expects you to inject a Jackson ObjectMapper instance and perform Jackson configuration on that instance (and not through a Spring class).
I thought it would be as easy as doing this:
Create an ObjectMapperFactoryBean implementation that allows me to customize the ObjectMapper instance that can be injected into the MappingJacksonHttpMessageConverter. For example:
<bean id="jacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper">
<bean class="com.foo.my.ObjectMapperFactoryBean">
<property name="prettyPrint" value="${json.prettyPrint}"/>
</bean>
</property>
</bean>
And then, in my ObjectMapperFactoryBean implementation, I could do this (as has been documented as a solution elsewhere on SO):
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, isPrettyPrint());
return mapper;
But it didn't work. And trying to figure out why is a nightmare. It is a major test of patience to figure Jackson out. Looking at its source code only confuses you further as it uses outdated and obtuse forms of configuration (integer bitmasks for turning on/off features? Are you kidding me?)
I essentially had to re-write Spring's MappingJacksonHttpMessageConverter from scratch, and override its writeInternal implementation to be the following:
#Override
protected void writeInternal(Object o, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
JsonEncoding encoding = getEncoding(outputMessage.getHeaders().getContentType());
JsonGenerator jsonGenerator =
getObjectMapper().getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding);
try {
if (this.prefixJson) {
jsonGenerator.writeRaw("{} && ");
}
if (isPrettyPrint()) {
jsonGenerator.useDefaultPrettyPrinter();
}
getObjectMapper().writeValue(jsonGenerator, o);
}
catch (JsonGenerationException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
}
The only thing I added to the existing implementation is the following block:
if (isPrettyPrint()) {
jsonGenerator.useDefaultPrettyPrinter();
}
isPrettyPrint() is just a JavaBeans compatible getter w/ matching setter that I added to my MappingJacksonHttpMessageConverter subclass.
Only after jumping through these hoops was I able to turn on or off pretty printing based on my ${json.prettyPrint} value (that is set as a property depending on how the app is deployed).
I hope this helps someone out in the future!
When you are using Jackson 2.0.0, you can do it in a way Les wanted to.
I currently use RC3 and the configuration seems to be working as expected.
ObjectMapper jacksonMapper = new ObjectMapper();
jacksonMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
translates
{"foo":"foo","bar":{"field1":"field1","field2":"field2"}}
into
{
"foo" : "foo",
"bar" : {
"field1" : "field1",
"field2" : "field2"
}
}
Might I suggest this approach, it is valid with Spring 4.0.x and possibly older versions.
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#Configuration
#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper());
return mappingJackson2HttpMessageConverter;
}
#Bean
public ObjectMapper objectMapper() {
ObjectMapper objMapper = new ObjectMapper();
objMapper.enable(SerializationFeature.INDENT_OUTPUT);
return objMapper;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
converters.add(mappingJackson2HttpMessageConverter());
}
}
Thanks to Willie Wheeler for the solution: Willie Wheeler's Spring blog
How do I make Jackson pretty-print the JSON content it generates?
Here's a simple example:
Original JSON Input:
{"one":"AAA","two":["BBB","CCC"],"three":{"four":"DDD","five":["EEE","FFF"]}}
Foo.java:
import java.io.FileReader;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ObjectWriter;
public class Foo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
MyClass myObject = mapper.readValue(new FileReader("input.json"), MyClass.class);
// this is Jackson 1.x API only:
ObjectWriter writer = mapper.defaultPrettyPrintingWriter();
// ***IMPORTANT!!!*** for Jackson 2.x use the line below instead of the one above:
// ObjectWriter writer = mapper.writer().withDefaultPrettyPrinter();
System.out.println(writer.writeValueAsString(myObject));
}
}
class MyClass
{
String one;
String[] two;
MyOtherClass three;
public String getOne() {return one;}
void setOne(String one) {this.one = one;}
public String[] getTwo() {return two;}
void setTwo(String[] two) {this.two = two;}
public MyOtherClass getThree() {return three;}
void setThree(MyOtherClass three) {this.three = three;}
}
class MyOtherClass
{
String four;
String[] five;
public String getFour() {return four;}
void setFour(String four) {this.four = four;}
public String[] getFive() {return five;}
void setFive(String[] five) {this.five = five;}
}
Output:
{
"one" : "AAA",
"two" : [ "BBB", "CCC" ],
"three" : {
"four" : "DDD",
"five" : [ "EEE", "FFF" ]
}
}
If this approach doesn't exactly fit your needs, if you search the API docs v1.8.1 for "pretty", it'll turn up the relevant components available. If you use API version 2.x then look instead at the newer API 2.1.0 docs.
Pretty print will be enable by adding and configure the MappingJackson2HttpMessageConverter converter. Disable prettyprint within production environment.
Message converter configuration
<mvc:annotation-driven>
<mvc:message-converters>
<bean id="jacksonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="prettyPrint" value="${json.prettyPrint}" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
Based on baeldung this could be a nice idea using java 8:
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
Optional<HttpMessageConverter<?>> converterFound;
converterFound = converters.stream().filter(c -> c instanceof AbstractJackson2HttpMessageConverter).findFirst();
if (converterFound.isPresent()) {
final AbstractJackson2HttpMessageConverter converter;
converter = (AbstractJackson2HttpMessageConverter) converterFound.get();
converter.getObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
converter.getObjectMapper().enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
}
}
I had trouble getting the custom MappingJacksonHttpMessageConverter to work as suggested above but I was finally able to get it to work after struggling w/ the configuration. From the code stand point I did exactly what was mentioned above but I had to add the following configuration to my springapp-servlet.xml to get it to work.
I hope this helps others who are looking to implement the same.
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonConverter" />
</list>
</property>
</bean>
<bean id="jsonConverter" class="com.xxx.xxx.xxx.common.PrettyPrintMappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
<property name="prettyPrint" value="true" />
</bean>
Jackson 2 has a nicer API, agreed, but it won't resolve this problem in a Spring MVC environment given Spring MVC uses ObjectMapper#writeValue(JsonGenerator, Object) to write objects out as JSON. This writeValue variant does not apply ObjectMapper serialization features such as INDENT_OUTPUT in either Jackson 1.x or 2.0.
I do think this is somewhat confusing. Since we use the ObjectMapper to construct JsonGenerators, I'd expect returned generators to be initialized based on configured ObjectMapper settings. I reported this as a issue against Jackson 2.0 here: https://github.com/FasterXML/jackson-databind/issues/12.
Les's suggestion of calling JsonGenerator#useDefaultPrettyPrinter based on the value of a prettyPrint flag is about the best we can do at the moment. I've gone ahead and created a Jackson2 HttpMessageConverter that does this based on the enabled status of the INDENT_OUTPUT SerializationFeature: https://gist.github.com/2423129.
I would make that a rendering issue, not the concern of the REST service.
Who's doing the rendering? Let that component format the JSON. Maybe it can be two URLs - one for production and another for development.

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