4

I am facing a very strange issue. I am using Jackson databind version 2.7.4 in my application for serialization. Application runs fine for many days but sometimes it starts throwing data serialization errors (Explanation is similar to JsonMappingException: (was java.lang.ArrayIndexOutOfBoundsException) )

This happens randomly and starts throwing ArrayOutOfBoundException when serializing a date field using custom serializer.

Exception trace

R] 2017-02-11 23:39:16 ERROR GlobalExceptionHandlerController:171 - Trace for 500:org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: (was java.lang.ArrayIndexOutOfBoundsException) (through reference chain: com.mycompany.model.DTO.ApiResponse["data"]->com.mycompany.model.Entity["date"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.ArrayIndexOutOfBoundsException) (through reference chain: com.mycompany.model.DTO.ApiResponse["data"]->com.mycompany.model.Entity["date"])
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:276)
at org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:100)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:222)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:153)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:165)
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:81)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:126)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:832)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:743)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:961)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:528)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1099)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:670)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1520)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1476)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.ArrayIndexOutOfBoundsException) (through reference chain: com.mycompany.model.DTO.ApiResponse["data"]->com.mycompany.model.Entity["date"])
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:378)
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:338)
    at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:342)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:686)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:672)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:678)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:130)
    at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1428)
    at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:930)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:269)
    ... 36 more
Caused by: java.lang.ArrayIndexOutOfBoundsException

It seems like a shared variable is getting negative value and causing this issue but I am not able to identify the code .

Entity class

public class Entity {
    @JsonSerialize(using=CustomDateSerializer.class)
    @JsonDeserialize(using=CustomDateDeserializer.class)
    private Date date;

    public Entity(){}

    public Date getDate() {
        return date;
    }
    public void setDate(Date date) {
        this.date = date;
    }       
}

De-Serializer

public class CustomDateDeserializer extends JsonDeserializer<Date>{

    private SimpleDateFormat formatter = 
              new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
    @Override
    public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        Date date=null;
        String str=p.getText();
        try{
            date=formatter.parse(str);
        }catch(ParseException e){
            throw new RuntimeException(e);
        }
        return date;
    }    
}

Serializer

 public class CustomDateSerializer extends JsonSerializer<Date>{

        private SimpleDateFormat formatter = 
          new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

        @Override
        public void serialize (Date value, JsonGenerator gen, SerializerProvider arg)
          throws IOException, JsonProcessingException {
            gen.writeString(formatter.format(value));
        }

}

Any help will be appreciated .

8
  • Most likely a problem in your custom serializer. But you cut the stacktrace just when it became interesting, and you posted the code of the deserializer instead :) Can you provide both of them ? Commented Feb 11, 2017 at 21:49
  • @FabienBenoit-Koch : I have added the serializer code. I have pasted the full trace which is present in logs. you can scroll it to the right to get the details . Commented Feb 11, 2017 at 21:59
  • I can't see anything after the root cause: Caused by: java.lang.ArrayIndexOutOfBoundsException Commented Feb 11, 2017 at 22:01
  • I'm pretty sure that problem somewhere else on upper level. You also have the class com.mycompany.model.DTO.ApiResponse - problem can be with it. Commented Feb 11, 2017 at 22:01
  • 1
    SimpleDateFormat is not thread-safe Commented Feb 11, 2017 at 22:02

2 Answers 2

8

Quickfix: If you're using Java 8 or above, consider using the thread-safe DateTimeFormatter class instead.

Original answer:

SimpleDateFormat is not thread-safe, as mentioned in its javadoc:

Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.

This blog post demonstrates the problem and lists a selection of the errors that may be encountered, among them ArrayIndexOutOfBoundsException, if the above recommendation is not followed.

I suggest synchronizing on the date formatter instances. The simplest approach looks something like this:

public void serialize (Date value, JsonGenerator gen, SerializerProvider arg)
    throws IOException, JsonProcessingException {
    synchronized(formatter) {
        gen.writeString(formatter.format(value));
    }
}
Sign up to request clarification or add additional context in comments.

Comments

0

My solution with java8:

timestampFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SS'Z'");
//java.sql.Timestamp x
String s = timestampFormatter.format(x.toInstant());

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.