I have a web application where the server returns any results in JSON format. For creating the JSON, I use the codehaus Jackson ObjectWriter, version 1.9.8.
The problem I'm having that sometimes there is an error, in the mapping, and from then on all server calls result in an error. I haven't been able to determine what causes the error, I did discover the origin.
When the exception occurs, the server returns "(was java.lang.ArrayIndexOutOfBoundsException) (through reference chain: com.onior.modm.restlet.helpers.ServiceResult["success"])", which means that the exception thrown in 'toJSON' was catched and mapped to JSON by 'toRepresentation', otherwise it would have returned empty.
I just don't know why it is sometimes failing. I will be able to use the application all morning without problems, and then suddenly I will get this error. It happens on different calls, but these calls will succeed at other times. From my point of view it seems quite random, but maybe someone can help me see the light? :)
The server result that is being mapped:
public class ServiceResult {
private boolean success;
private T results = null;
private ModmServiceStatus message = null;
public ServiceResult() {
}
public ServiceResult(T results) {
this.success = true;
this.results = results;
}
public ServiceResult(boolean success, ModmServiceStatus message) {
this.success = success;
this.message = message;
}
public ServiceResult(ServiceError error) {
this(false, new ModmServiceStatus(error));
}
public static ServiceResult serviceException(
ServiceError error) {
return new ServiceResult(false,
new ModmServiceStatus(error.getCode(), error.getDescription()));
}
public static ServiceResult dbError() {
return ServiceResult
.serviceException(ServiceError.GENERIC_DATABASE_ERROR);
}
public static ServiceResult invalidJson() {
return ServiceResult
.serviceException(ServiceError.GENERIC_INVALID_JSON);
}
public static ServiceResult missingEntity() {
return ServiceResult .serviceException(ServiceError.GENERIC_MISSING_OR_INCOMPLETE_ENTITY);
}
public static ServiceResult entityNotFound() {
return ServiceResult
.serviceException(ServiceError.GENERIC_ENTITY_NOT_FOUND);
}
public static ServiceResult entityDeleted(String entity) {
return new ServiceResult(true,
new ModmServiceStatus(0, entity + " deleted."));
}
}
The mapping:
public class RestUtils {
private static final boolean PRETTY_PRINT = true;
public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
public static final ObjectWriter OBJECT_WRITER = (PRETTY_PRINT ? OBJECT_MAPPER
.writerWithDefaultPrettyPrinter() : OBJECT_MAPPER.writer());
@SuppressWarnings("unchecked")
public static JacksonRepresentation toJSON(T t) throws IOException {
JsonRepresentation jsonRepresentation = null;
JacksonRepresentation jacksonRepresentation = null;
jsonRepresentation = new JsonRepresentation(
OBJECT_WRITER.writeValueAsString(t)); // Origin of incidental
// server error
jacksonRepresentation = new JacksonRepresentation(
jsonRepresentation, (Class) t.getClass());
return jacksonRepresentation;
}
public static Representation toRepresentation(ServiceResult ss) {
Representation representation = null;
try {
representation = RestUtils.toJSON(ss);
} catch (IOException jsonException) {
jsonException.printStackTrace();
try {
jsonException.printStackTrace();
representation = RestUtils.toJSON(jsonException.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
return representation;
}
}
The call:
RestUtils.toRepresentation(new ServiceResult>(groups));
The exception with stacktrace:
org.codehaus.jackson.map.JsonMappingException: (was java.lang.ArrayIndexOutOfBoundsException) (through reference chain: com.onior.modm.restlet.helpers.ServiceResult["success"])
at org.codehaus.jackson.map.JsonMappingException.wrapWithPath(JsonMappingException.java:218)
at org.codehaus.jackson.map.JsonMappingException.wrapWithPath(JsonMappingException.java:183)
at org.codehaus.jackson.map.ser.std.SerializerBase.wrapAndThrow(SerializerBase.java:140)
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:158)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:610)
at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:256)
at org.codehaus.jackson.map.ObjectWriter._configAndWriteValue(ObjectWriter.java:456)
at org.codehaus.jackson.map.ObjectWriter.writeValueAsString(ObjectWriter.java:393)
at com.onior.modm.restlet.helpers.RestUtils.toJSON(RestUtils.java:52)
at com.onior.modm.restlet.helpers.RestUtils.toRepresentation(RestUtils.java:71)
at com.onior.modm.restlet.resources.GroupCollectionResource.toJsonRead(GroupCollectionResource.java:191)
at sun.reflect.GeneratedMethodAccessor143.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.restlet.resource.ServerResource.doHandle(ServerResource.java:506)
at org.restlet.resource.ServerResource.get(ServerResource.java:707)
at org.restlet.resource.ServerResource.doHandle(ServerResource.java:589)
at org.restlet.resource.ServerResource.doNegotiatedHandle(ServerResource.java:649)
at org.restlet.resource.ServerResource.doConditionalHandle(ServerResource.java:348)
at org.restlet.resource.ServerResource.handle(ServerResource.java:952)
at org.restlet.resource.Finder.handle(Finder.java:246)
at org.restlet.routing.Filter.doHandle(Filter.java:159)
at org.restlet.routing.Filter.handle(Filter.java:206)
at org.restlet.routing.Router.doHandle(Router.java:431)
at org.restlet.routing.Router.handle(Router.java:648)
at org.restlet.routing.Filter.doHandle(Filter.java:159)
at org.restlet.routing.Filter.handle(Filter.java:206)
at org.restlet.routing.Filter.doHandle(Filter.java:159)
at org.restlet.routing.Filter.handle(Filter.java:206)
at org.restlet.routing.Router.doHandle(Router.java:431)
at org.restlet.routing.Router.handle(Router.java:648)
at org.restlet.routing.Filter.doHandle(Filter.java:159)
at org.restlet.routing.Filter.handle(Filter.java:206)
at org.restlet.routing.Filter.doHandle(Filter.java:159)
at org.restlet.routing.Filter.handle(Filter.java:206)
at org.restlet.routing.Filter.doHandle(Filter.java:159)
at org.restlet.engine.application.StatusFilter.doHandle(StatusFilter.java:155)
at org.restlet.routing.Filter.handle(Filter.java:206)
at org.restlet.routing.Filter.doHandle(Filter.java:159)
at org.restlet.routing.Filter.handle(Filter.java:206)
at org.restlet.engine.CompositeHelper.handle(CompositeHelper.java:211)
at org.restlet.engine.application.ApplicationHelper.handle(ApplicationHelper.java:84)
at org.restlet.Application.handle(Application.java:381)
at org.restlet.ext.servlet.ServletAdapter.service(ServletAdapter.java:206)
at org.restlet.ext.spring.RestletFrameworkServlet.doService(RestletFrameworkServlet.java:124)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:861)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:606)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.ArrayIndexOutOfBoundsException
at org.codehaus.jackson.impl.WriterBasedGenerator.writeRaw(WriterBasedGenerator.java:577)
at org.codehaus.jackson.util.DefaultPrettyPrinter$Lf2SpacesIndenter.writeIndentation(DefaultPrettyPrinter.java:279)
at org.codehaus.jackson.util.DefaultPrettyPrinter.beforeObjectEntries(DefaultPrettyPrinter.java:98)
at org.codehaus.jackson.impl.WriterBasedGenerator._writePPFieldName(WriterBasedGenerator.java:410)
at org.codehaus.jackson.impl.WriterBasedGenerator._writeFieldName(WriterBasedGenerator.java:340)
at org.codehaus.jackson.impl.WriterBasedGenerator.writeFieldName(WriterBasedGenerator.java:217)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:444)
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:150)
... 58 more
解决方案
Got the exact same ArrayIndexOutOfBoundsException with Jackson 1.9.9
After investigating, our root cause was a shared PrettyPrinter used by multiple threads.
If you look in the Jackson code for the DefaultPrettyPrinter, you will see:
/**
* Number of open levels of nesting. Used to determine amount of
* indentation to use.
*/
protected int _nesting = 0;
_nesting ends up being used to access arrays. This variable is incremented and decremented when processing object. If multiple threads decrements it, it may end up negative causing the ArrayIndexOutOfBoundsException. Once negative, it will not be ever increased again because the exception will be generated before reaching a piece of code that would increment it.
In your code, I see you have a static object writer. You probably end up with a single instance of the DefaultPrettyPrinter. If your application can concurrently produce json object, given enough time, you will get the exception.
Stéphan