Uploaded image for project: 'Nuxeo Platform'
  1. Nuxeo Platform
  2. NXP-17714

Must rollback transaction on WebEngine/JAX-RS error

    Details

    • Type: Bug
    • Status: Resolved
    • Priority: Critical
    • Resolution: Fixed
    • Affects Version/s: 5.8
    • Fix Version/s: 5.8.0-HF36, 6.0-HF19, 7.4
    • Component/s: WebEngine

      Description

      If an exception is thrown during a webengine calls, let's say at service level, the Transaction is not Rolled back but committed.

      This is because jersey catches and processes the exception without re-throwing it.

      Typical use case to reproduce is the travel-expenses sample.

      Let's create a task with nature = 'lunch', there is a directory validator on the dc:nature field. Because the entry 'lunch does not exist', a DocumentValidationException is thrown in org.nuxeo.ecm.platform.routing.core.impl.GraphNodeImpl.setAllVariables(Map<String, Object>, boolean).

      In the stack below:

      Daemon Thread [http-bio-0.0.0.0-8080-exec-6] (Suspended (breakpoint at line 376 in org.nuxeo.ecm.platform.routing.core.impl.GraphNodeImpl)) (out of synch)	
      	owns: org.apache.tomcat.util.net.SocketWrapper<E>  (id=17551)	
      	org.nuxeo.ecm.platform.routing.core.impl.GraphNodeImpl.setAllVariables(java.util.Map<java.lang.String,java.lang.Object>, boolean) line: 376	
      	org.nuxeo.ecm.platform.routing.core.impl.GraphRunner.resume(org.nuxeo.ecm.core.api.CoreSession, org.nuxeo.ecm.platform.routing.api.DocumentRouteElement, java.lang.String, java.lang.String, java.util.Map<java.lang.String,java.lang.Object>, java.lang.String) line: 123	
      	org.nuxeo.ecm.platform.routing.core.impl.GraphRouteImpl(org.nuxeo.ecm.platform.routing.core.impl.DocumentRouteElementImpl).resume(org.nuxeo.ecm.core.api.CoreSession, java.lang.String, java.lang.String, java.util.Map<java.lang.String,java.lang.Object>, java.lang.String) line: 79	
      	org.nuxeo.ecm.platform.routing.core.impl.DocumentRoutingEngineServiceImpl.resume(org.nuxeo.ecm.platform.routing.api.DocumentRoute, java.lang.String, java.lang.String, java.util.Map<java.lang.String,java.lang.Object>, java.lang.String, org.nuxeo.ecm.core.api.CoreSession) line: 46	
      	org.nuxeo.ecm.platform.routing.core.impl.DocumentRoutingServiceImpl$CompleteTaskRunner.run() line: 347	
      	org.nuxeo.ecm.platform.routing.core.impl.DocumentRoutingServiceImpl$CompleteTaskRunner(org.nuxeo.ecm.core.api.UnrestrictedSessionRunner).runUnrestricted() line: 139	
      	org.nuxeo.ecm.platform.routing.core.impl.DocumentRoutingServiceImpl.completeTask(java.lang.String, java.lang.String, org.nuxeo.ecm.platform.task.Task, java.util.Map<java.lang.String,java.lang.Object>, java.lang.String, org.nuxeo.ecm.core.api.CoreSession) line: 313	
      	org.nuxeo.ecm.platform.routing.core.impl.DocumentRoutingServiceImpl.endTask(org.nuxeo.ecm.core.api.CoreSession, org.nuxeo.ecm.platform.task.Task, java.util.Map<java.lang.String,java.lang.Object>, java.lang.String) line: 841	
      	org.nuxeo.ecm.restapi.server.jaxrs.routing.TaskObject.completeTask(java.lang.String, java.lang.String, org.nuxeo.ecm.restapi.server.jaxrs.routing.model.TaskCompletionRequest) line: 85	
      	sun.reflect.NativeMethodAccessorImpl.invoke0(java.lang.reflect.Method, java.lang.Object, java.lang.Object[]) line: not available [native method]	
      	sun.reflect.NativeMethodAccessorImpl.invoke(java.lang.Object, java.lang.Object[]) line: 62	
      	sun.reflect.DelegatingMethodAccessorImpl.invoke(java.lang.Object, java.lang.Object[]) line: 43	
      	java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object...) line: 497	
      	com.sun.jersey.spi.container.JavaMethodInvokerFactory$1.invoke(java.lang.reflect.Method, java.lang.Object, java.lang.Object...) line: 60	
      	com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$ResponseOutInvoker._dispatch(java.lang.Object, com.sun.jersey.api.core.HttpContext) line: 205	
      	com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$ResponseOutInvoker(com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher).dispatch(java.lang.Object, com.sun.jersey.api.core.HttpContext) line: 75	
      	com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(java.lang.CharSequence, java.lang.Object, com.sun.jersey.spi.uri.rules.UriRuleContext) line: 302	
      	com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(java.lang.CharSequence, java.lang.Object, com.sun.jersey.spi.uri.rules.UriRuleContext) line: 147	
      	com.sun.jersey.server.impl.uri.rules.SubLocatorRule.accept(java.lang.CharSequence, java.lang.Object, com.sun.jersey.spi.uri.rules.UriRuleContext) line: 137	
      	com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(java.lang.CharSequence, java.lang.Object, com.sun.jersey.spi.uri.rules.UriRuleContext) line: 147	
      	com.sun.jersey.server.impl.uri.rules.SubLocatorRule.accept(java.lang.CharSequence, java.lang.Object, com.sun.jersey.spi.uri.rules.UriRuleContext) line: 137	
      	com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(java.lang.CharSequence, java.lang.Object, com.sun.jersey.spi.uri.rules.UriRuleContext) line: 147	
      	com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(java.lang.CharSequence, java.lang.Object, com.sun.jersey.spi.uri.rules.UriRuleContext) line: 108	
      	com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(java.lang.CharSequence, java.lang.Object, com.sun.jersey.spi.uri.rules.UriRuleContext) line: 147	
      	com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(java.lang.CharSequence, java.lang.Object, com.sun.jersey.spi.uri.rules.UriRuleContext) line: 84	
      	com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(com.sun.jersey.server.impl.application.WebApplicationContext, com.sun.jersey.spi.container.ContainerRequest) line: 1542	
      	com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(com.sun.jersey.server.impl.application.WebApplicationContext, com.sun.jersey.spi.container.ContainerRequest, com.sun.jersey.spi.container.ContainerResponse) line: 1473	
      	com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(com.sun.jersey.spi.container.ContainerRequest, com.sun.jersey.spi.container.ContainerResponse) line: 1419	
      	com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(com.sun.jersey.spi.container.ContainerRequest, com.sun.jersey.spi.container.ContainerResponseWriter) line: 1409	
      	com.sun.jersey.spi.container.servlet.ServletContainer$InternalWebComponent(com.sun.jersey.spi.container.servlet.WebComponent).service(java.net.URI, java.net.URI, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 409	
      	com.sun.jersey.spi.container.servlet.ServletContainer.service(java.net.URI, java.net.URI, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 558	
      	com.sun.jersey.spi.container.servlet.ServletContainer.service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 733	
      	org.nuxeo.ecm.webengine.app.jersey.WebEngineServlet.containerService(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 171 (out of synch)	
      	org.nuxeo.ecm.webengine.app.jersey.WebEngineServlet.service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 148 (out of synch)	
      	org.nuxeo.ecm.webengine.app.jersey.WebEngineServlet(javax.servlet.http.HttpServlet).service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 727	
      	org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 303	
      	org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 208	
      	org.apache.tomcat.websocket.server.WsFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 52	
      	org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 241	
      	org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 208	
      	org.nuxeo.ecm.webengine.app.WebEngineFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 92 (out of synch)	
      	org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 241	
      	org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 208	
      	org.nuxeo.ecm.webengine.jaxrs.session.SessionCleanupFilter.run(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain) line: 43	
      	org.nuxeo.ecm.webengine.jaxrs.session.SessionCleanupFilter(org.nuxeo.ecm.webengine.jaxrs.HttpFilter).doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 42	
      	org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 241	
      	org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 208	
      	org.nuxeo.ecm.webengine.jaxrs.context.RequestContextFilter.run(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain) line: 41	
      	org.nuxeo.ecm.webengine.jaxrs.context.RequestContextFilter(org.nuxeo.ecm.webengine.jaxrs.HttpFilter).doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 42	
      	org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 241	
      	org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 208	
      	org.nuxeo.ecm.platform.web.common.requestcontroller.filter.NuxeoThreadTrackerFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 28	
      	org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 241	
      	org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 208	
      	org.nuxeo.ecm.core.management.jtajca.internal.Log4jWebFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 67	
      	org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 241	
      	org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 208	
      	org.nuxeo.ecm.platform.ui.web.rest.FancyURLFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 118	
      	org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 241	
      	org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 208	
      	org.nuxeo.ecm.platform.web.common.requestcontroller.filter.NuxeoRequestControllerFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 140	
      	org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 241	
      	org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 208	
      	org.nuxeo.ecm.webdav.service.WIRequestFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 59	
      	org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 241	
      	org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 208	
      	org.nuxeo.ecm.platform.ui.web.auth.NuxeoAuthenticationFilter.doFilterInternal(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 580	
      	org.nuxeo.ecm.platform.ui.web.auth.service.NuxeoAuthFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 36	
      	org.nuxeo.ecm.platform.ui.web.auth.oauth.NuxeoOAuthFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 120	
      	org.nuxeo.ecm.platform.ui.web.auth.service.NuxeoAuthFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 34	
      	org.nuxeo.ecm.platform.ui.web.auth.oauth2.NuxeoOAuth2Filter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 68	
      	org.nuxeo.ecm.platform.ui.web.auth.service.NuxeoAuthFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 34	
      	org.nuxeo.ecm.platform.ui.web.auth.NuxeoAuthenticationFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 393	
      	org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 241	
      	org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 208	
      	org.nuxeo.ecm.platform.web.common.requestcontroller.filter.NuxeoCorsFilter(com.thetransactioncompany.cors.CORSFilter).doFilter(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain) line: 208	
      	org.nuxeo.ecm.platform.web.common.requestcontroller.filter.NuxeoCorsFilter(com.thetransactioncompany.cors.CORSFilter).doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 274	
      	org.nuxeo.ecm.platform.web.common.requestcontroller.filter.NuxeoCorsFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 47	
      	org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 241	
      	org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 208	
      	org.nuxeo.ecm.platform.web.common.exceptionhandling.NuxeoExceptionFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 75	
      	org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 241	
      	org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 208	
      	org.nuxeo.ecm.platform.web.common.encoding.NuxeoEncodingFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 73	
      	org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 241	
      	org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 208	
      	org.apache.catalina.core.StandardWrapperValve.invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) line: 220	
      	org.apache.catalina.core.StandardContextValve.invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) line: 122	
      	org.apache.catalina.authenticator.NonLoginAuthenticator(org.apache.catalina.authenticator.AuthenticatorBase).invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) line: 503	
      	org.apache.catalina.core.StandardHostValve.invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) line: 170	
      	org.apache.catalina.valves.ErrorReportValve.invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) line: 103	
      	org.apache.catalina.core.StandardEngineValve.invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) line: 116	
      	org.apache.catalina.connector.CoyoteAdapter.service(org.apache.coyote.Request, org.apache.coyote.Response) line: 421	
      	org.apache.coyote.http11.Http11Processor(org.apache.coyote.http11.AbstractHttp11Processor<S>).process(org.apache.tomcat.util.net.SocketWrapper<S>) line: 1070	
      	org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler(org.apache.coyote.AbstractProtocol$AbstractConnectionHandler<S,P>).process(org.apache.tomcat.util.net.SocketWrapper<S>, org.apache.tomcat.util.net.SocketStatus) line: 611	
      	org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run() line: 316	
      	org.apache.tomcat.util.threads.ThreadPoolExecutor(java.util.concurrent.ThreadPoolExecutor).runWorker(java.util.concurrent.ThreadPoolExecutor$Worker) line: 1142	
      	java.util.concurrent.ThreadPoolExecutor$Worker.run() line: 617	
      	org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run() line: 61	
      	org.apache.tomcat.util.threads.TaskThread(java.lang.Thread).run() line: 745	
      

      we can check that com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationContext, ContainerRequest, ContainerResponse) will catch the exception wihtout re-throwing it.

      Then, later, the org.nuxeo.ecm.webengine.app.WebEngineFilter.doFilter(ServletRequest, ServletResponse, FilterChain) will call org.nuxeo.ecm.webengine.app.WebEngineFilter.cleanup(Config, AbstractWebContext, HttpServletRequest, HttpServletResponse) which will commit the Transaction despite there was an exception thrown.

      As a result, regarding the travel-expenses sample, the task is marked as ended but the GraphRunner did not resume the workflow and we end up in a inconsistent workflow state.

      I am not really sure about the fix, it seems that jersey has a bad design to handle this use case.

      A simple (and dirty) fix could be:

      --- i/nuxeo-webengine/nuxeo-webengine-core/src/main/java/org/nuxeo/ecm/webengine/app/WebEngineExceptionMapper.java
      +++ w/nuxeo-webengine/nuxeo-webengine-core/src/main/java/org/nuxeo/ecm/webengine/app/WebEngineExceptionMapper.java
      @@ -30,6 +30,7 @@ import org.apache.commons.logging.LogFactory;
       import org.nuxeo.ecm.core.api.validation.DocumentValidationException;
       import org.nuxeo.ecm.webengine.WebException;
       import org.nuxeo.ecm.webengine.model.exceptions.WebResourceNotFoundException;
      +import org.nuxeo.runtime.transaction.TransactionHelper;
       
       import com.sun.jersey.api.NotFoundException;
       
      @@ -46,6 +47,7 @@ public class WebEngineExceptionMapper implements ExceptionMapper<Throwable> {
       
           @Override
           public Response toResponse(Throwable cause) {
      +        TransactionHelper.setTransactionRollbackOnly();
               if (headers.getAcceptableMediaTypes().contains(APPLICATION_JSON_TYPE)) {
                   if (cause instanceof DocumentValidationException) {
                       DocumentValidationException dve = (DocumentValidationException) cause;
      

      But ideally we should have a dedicated filter for Transaction Management correctly inserted in the filter execution chain. This filter would be able to detect exception as soon as possible and roll back transactions before jersey catches it.

        Attachments

          Activity

            People

            • Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                PagerDuty

                Error rendering 'com.pagerduty.jira-server-plugin:PagerDuty'. Please contact your Jira administrators.