spring-soy-view
spring-soy-view copied to clipboard
Infinite Loop in DefaultToSoyDataConverter when model contains BindingResult
Spring can add a BindingResult object into the model for objects in the model that are not primitive or collections. Unfortunately if this happens, DefaultToSoyDataConverter blows the stack. When DefaultToSoyDataConverter creates a pojo map from the BindingResult objects, it adds an entry with the key 'model', and a value of the entire model which essentially starts the whole recursive stack over again. It eventually works its way through the model until it gets to the BindingResult again, infinite loop...
Here is a simple test case that reproduces this error. (I put it into pl.matisoft.soy.data.DefaultToSoyDataConverterTest)
@Test
public void testNestedObjectInfiniteLoopBug() throws Exception {
ModelAndView mav = new ModelAndView("test-view");
PojoTest nestedReference = new PojoTest();
mav.addObject("top-level", nestedReference);
mav.addObject("binding", new DirectFieldBindingResult(nestedReference, "nested"));
Object results = objectToSoyDataConverter.toSoyCompatibleMap(mav);
}
Running the above test produced the following results :
Tests run: 15, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.141 sec (ReflectiveOperationException.java:89)
at java.lang.reflect.InvocationTargetException.(InvocationTargetException.java:72)
at sun.reflect.GeneratedMethodAccessor11.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at pl.matisoft.soy.data.DefaultToSoyDataConverter.pojoToMap(DefaultToSoyDataConverter.java:145)
at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:126)
at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:102)
at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:129)
at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:102)
at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:129)
at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:102)
at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:129)
at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:102)
at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:129)
at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:102)
...
at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:129)
at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:102)
at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:129)
at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:102)
Results :
Tests in error:
» StackOverflow
Tests run: 131, Failures: 0, Errors: 1, Skipped: 0
I am not sure about the best way to deal with this. We could add explicit code to deal with BindingResults, but I am not sure that is the best solution...
I am aware of this, this is why I have this https://github.com/matiwinnetou/spring-soy-view/blob/master/spring-soy-view/src/main/java/pl/matisoft/soy/data/adjust/ModelAdjuster.java, this basically allows to filter out the model passed to DefaultToSoyDataConverter class.
How do I fix that? It worked with POJOs, but since I'm using Spring Data JPA, it stackoverflows:
2016-08-09 04:39:23.388 [qtp1327127443-25] [DEBUG] o.s.w.s.DispatcherServlet - Could not complete request org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.StackOverflowError at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:986) ~[spring-webmvc-4.3.1.RELEASE.jar:4.3.1.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) ~[spring-webmvc-4.3.1.RELEASE.jar:4.3.1.RELEASE] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) [spring-webmvc-4.3.1.RELEASE.jar:4.3.1.RELEASE] at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) [spring-webmvc-4.3.1.RELEASE.jar:4.3.1.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:687) [javax.servlet-api-3.1.0.jar:3.1.0] at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) [spring-webmvc-4.3.1.RELEASE.jar:4.3.1.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) [javax.servlet-api-3.1.0.jar:3.1.0] at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:751) [jetty-servlet-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:566) [jetty-servlet-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) [jetty-server-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:578) [jetty-security-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:221) [jetty-server-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1111) [jetty-server-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:498) [jetty-servlet-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:183) [jetty-server-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1045) [jetty-server-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) [jetty-server-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:199) [jetty-server-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:109) [jetty-server-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:98) [jetty-server-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.server.Server.handle(Server.java:461) [jetty-server-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:284) [jetty-server-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:244) [jetty-server-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:534) [jetty-io-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:607) [jetty-util-9.2.1.v20140609.jar:9.2.1.v20140609] at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:536) [jetty-util-9.2.1.v20140609.jar:9.2.1.v20140609] at java.lang.Thread.run(Thread.java:745) [na:1.8.0_25] Caused by: java.lang.StackOverflowError: null at java.beans.WeakIdentityMap.get(WeakIdentityMap.java:56) ~[na:1.8.0_25] at java.beans.ThreadGroupContext.getContext(ThreadGroupContext.java:57) ~[na:1.8.0_25] at java.beans.Introspector.getBeanInfo(Introspector.java:167) ~[na:1.8.0_25] at pl.matisoft.soy.data.DefaultToSoyDataConverter.pojoToMap(DefaultToSoyDataConverter.java:135) ~[spring-soy-view-1.25.4.jar:na] at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:122) ~[spring-soy-view-1.25.4.jar:na] at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:106) ~[spring-soy-view-1.25.4.jar:na] at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:125) ~[spring-soy-view-1.25.4.jar:na] at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:125) ~[spring-soy-view-1.25.4.jar:na] at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:106) ~[spring-soy-view-1.25.4.jar:na] at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:125) ~[spring-soy-view-1.25.4.jar:na] at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:125) ~[spring-soy-view-1.25.4.jar:na] at pl.matisoft.soy.data.DefaultToSoyDataConverter.toSoyCompatibleObjects(DefaultToSoyDataConverter.java:106) ~[spring-soy-view-1.25.4.jar:na]