You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2017/11/28 13:47:43 UTC

[isis] 01/02: ISIS-1768: fixes so that returns 404 for non-existent object. Also improves matters by returning an appropriate XML or JSON representation.

This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 0abcf2b2f65b9c75edd172bdefc741483ee5ace2
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Mon Nov 20 10:19:47 2017 +0000

    ISIS-1768: fixes so that returns 404 for non-existent object.  Also improves matters by returning an appropriate XML or JSON representation.
---
 .../server/ExceptionMapperAbstract.java            |  65 ++++++++++++
 ...cationExceptionPojo.java => ExceptionPojo.java} |  52 ++--------
 ...ptionPojo.java => ExceptionPojoWithDetail.java} |  40 ++------
 .../server/ObjectNotFoundExceptionMapper.java      |  71 +++++++++++++
 .../server/RestfulObjectsApplication.java          |   2 +
 .../RestfulObjectsApplicationExceptionMapper.java  | 113 +++++++++------------
 .../server/RuntimeExceptionMapper.java             |   7 +-
 .../IsisTransactionFilterForRestfulObjects.java    |   6 +-
 8 files changed, 214 insertions(+), 142 deletions(-)

diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ExceptionMapperAbstract.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ExceptionMapperAbstract.java
new file mode 100644
index 0000000..ad38101
--- /dev/null
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ExceptionMapperAbstract.java
@@ -0,0 +1,65 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.restfulobjects.server;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.ext.ExceptionMapper;
+
+import org.apache.isis.viewer.restfulobjects.applib.RestfulMediaType;
+
+public abstract class ExceptionMapperAbstract<T extends Throwable> implements ExceptionMapper<T> {
+
+    @Context
+    protected HttpHeaders httpHeaders;
+
+    protected void setContentTypeOn(final ResponseBuilder builder) {
+        final boolean xml = isXmlButNotHtml();
+        if(!xml) {
+            builder.type(RestfulMediaType.APPLICATION_JSON_ERROR);
+        } else {
+            builder.type(RestfulMediaType.APPLICATION_XML_ERROR);
+        }
+    }
+
+    protected boolean isHtml() {
+        for (final MediaType acceptableMediaType : httpHeaders.getAcceptableMediaTypes()) {
+            if(acceptableMediaType.getType().equals("text") && acceptableMediaType.getSubtype().equals("html")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    protected boolean isXmlButNotHtml() {
+        if (isHtml()) {
+            return false;
+        }
+        for (final MediaType acceptableMediaType : httpHeaders.getAcceptableMediaTypes()) {
+            if(acceptableMediaType.getSubtype().equals("xml")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+}
diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionPojo.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ExceptionPojo.java
similarity index 51%
copy from core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionPojo.java
copy to core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ExceptionPojo.java
index 194344e..2e745f0 100644
--- a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionPojo.java
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ExceptionPojo.java
@@ -18,51 +18,25 @@
  */
 package org.apache.isis.viewer.restfulobjects.server;
 
-import java.util.List;
-import com.google.common.collect.Lists;
 import org.apache.isis.viewer.restfulobjects.rendering.HasHttpStatusCode;
 
-class RestfulObjectsApplicationExceptionPojo {
+class ExceptionPojo {
 
-    public static RestfulObjectsApplicationExceptionPojo create(final Throwable ex) {
-        return new RestfulObjectsApplicationExceptionPojo(ex);
+    static int getHttpStatusCodeIfAny(final Throwable ex) {
+        if (!(ex instanceof HasHttpStatusCode)) {
+            return 0;
+        }
+        final HasHttpStatusCode hasHttpStatusCode = (HasHttpStatusCode) ex;
+        return hasHttpStatusCode.getHttpStatusCode().getStatusCode();
     }
 
-    private static String format(final StackTraceElement stackTraceElement) {
-        return stackTraceElement.toString();
-    }
 
-    private final String className;
     private final int httpStatusCode;
     private final String message;
-    private final List<String> stackTrace = Lists.newArrayList();
-    private RestfulObjectsApplicationExceptionPojo causedBy;
 
-    public RestfulObjectsApplicationExceptionPojo(final Throwable ex) {
-        this.className = ex.getClass().getName();
+    public ExceptionPojo(final Throwable ex) {
         this.httpStatusCode = getHttpStatusCodeIfAny(ex);
         this.message = ex.getMessage();
-        final StackTraceElement[] stackTraceElements = ex.getStackTrace();
-        for (final StackTraceElement stackTraceElement : stackTraceElements) {
-            this.stackTrace.add(format(stackTraceElement));
-        }
-        final Throwable cause = ex.getCause();
-        if (cause != null && cause != ex) {
-            this.causedBy = new RestfulObjectsApplicationExceptionPojo(cause);
-        }
-    }
-
-    private int getHttpStatusCodeIfAny(final Throwable ex) {
-        if (!(ex instanceof HasHttpStatusCode)) {
-            return 0;
-        }
-        final HasHttpStatusCode hasHttpStatusCode = (HasHttpStatusCode) ex;
-        return hasHttpStatusCode.getHttpStatusCode().getStatusCode();
-    }
-
-    @SuppressWarnings("unused")
-    public String getClassName() {
-        return className;
     }
 
     @SuppressWarnings("unused")
@@ -75,14 +49,4 @@ class RestfulObjectsApplicationExceptionPojo {
         return message;
     }
 
-    @SuppressWarnings("unused")
-    public List<String> getStackTrace() {
-        return stackTrace;
-    }
-
-    @SuppressWarnings("unused")
-    public RestfulObjectsApplicationExceptionPojo getCausedBy() {
-        return causedBy;
-    }
-
 }
diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionPojo.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ExceptionPojoWithDetail.java
similarity index 60%
rename from core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionPojo.java
rename to core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ExceptionPojoWithDetail.java
index 194344e..18af126 100644
--- a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionPojo.java
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ExceptionPojoWithDetail.java
@@ -19,45 +19,31 @@
 package org.apache.isis.viewer.restfulobjects.server;
 
 import java.util.List;
-import com.google.common.collect.Lists;
-import org.apache.isis.viewer.restfulobjects.rendering.HasHttpStatusCode;
 
-class RestfulObjectsApplicationExceptionPojo {
+import com.google.common.collect.Lists;
 
-    public static RestfulObjectsApplicationExceptionPojo create(final Throwable ex) {
-        return new RestfulObjectsApplicationExceptionPojo(ex);
-    }
+class ExceptionPojoWithDetail extends ExceptionPojo {
 
     private static String format(final StackTraceElement stackTraceElement) {
         return stackTraceElement.toString();
     }
 
     private final String className;
-    private final int httpStatusCode;
-    private final String message;
+
     private final List<String> stackTrace = Lists.newArrayList();
-    private RestfulObjectsApplicationExceptionPojo causedBy;
+    private ExceptionPojoWithDetail causedBy;
 
-    public RestfulObjectsApplicationExceptionPojo(final Throwable ex) {
+    public ExceptionPojoWithDetail(final Throwable ex) {
+        super(ex);
         this.className = ex.getClass().getName();
-        this.httpStatusCode = getHttpStatusCodeIfAny(ex);
-        this.message = ex.getMessage();
         final StackTraceElement[] stackTraceElements = ex.getStackTrace();
         for (final StackTraceElement stackTraceElement : stackTraceElements) {
             this.stackTrace.add(format(stackTraceElement));
         }
         final Throwable cause = ex.getCause();
         if (cause != null && cause != ex) {
-            this.causedBy = new RestfulObjectsApplicationExceptionPojo(cause);
-        }
-    }
-
-    private int getHttpStatusCodeIfAny(final Throwable ex) {
-        if (!(ex instanceof HasHttpStatusCode)) {
-            return 0;
+            this.causedBy = new ExceptionPojoWithDetail(cause);
         }
-        final HasHttpStatusCode hasHttpStatusCode = (HasHttpStatusCode) ex;
-        return hasHttpStatusCode.getHttpStatusCode().getStatusCode();
     }
 
     @SuppressWarnings("unused")
@@ -66,22 +52,12 @@ class RestfulObjectsApplicationExceptionPojo {
     }
 
     @SuppressWarnings("unused")
-    public int getHttpStatusCode() {
-        return httpStatusCode;
-    }
-
-    @SuppressWarnings("unused")
-    public String getMessage() {
-        return message;
-    }
-
-    @SuppressWarnings("unused")
     public List<String> getStackTrace() {
         return stackTrace;
     }
 
     @SuppressWarnings("unused")
-    public RestfulObjectsApplicationExceptionPojo getCausedBy() {
+    public ExceptionPojoWithDetail getCausedBy() {
         return causedBy;
     }
 
diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ObjectNotFoundExceptionMapper.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ObjectNotFoundExceptionMapper.java
new file mode 100644
index 0000000..4b1e154
--- /dev/null
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ObjectNotFoundExceptionMapper.java
@@ -0,0 +1,71 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.restfulobjects.server;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.isis.core.runtime.persistence.ObjectNotFoundException;
+import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse;
+import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse.HttpStatusCode;
+import org.apache.isis.viewer.restfulobjects.applib.util.JsonMapper;
+
+@Provider
+public class ObjectNotFoundExceptionMapper extends ExceptionMapperAbstract<ObjectNotFoundException> {
+
+    @Override
+    public Response toResponse(final ObjectNotFoundException ex) {
+
+
+        final HttpStatusCode statusCode = HttpStatusCode.NOT_FOUND;
+        final ResponseBuilder builder =
+                Response.status(statusCode.getJaxrsStatusType());
+
+        final String message = ex.getMessage();
+        if (message != null) {
+            builder.header(RestfulResponse.Header.WARNING.getName(), RestfulResponse.Header.WARNING.render(message));
+        }
+
+        setContentTypeOn(builder);
+
+        String body = toBody(ex);
+
+        if(body != null) {
+            builder.entity(body);
+        }
+
+        return builder.build();
+    }
+
+    protected String toBody(final ObjectNotFoundException ex) {
+        final boolean xml = isXmlButNotHtml();
+        if (!xml) {
+            String body;
+            try {
+                body = JsonMapper.instance().write(new ExceptionPojo(ex));
+            } catch (final Exception e) {
+                // fallback
+                body = "{ \"message\": \"" + ex.getMessage() + "\" }";
+            }
+            return body;
+        }
+        return null;
+    }
+}
diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplication.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplication.java
index bd6d3af..fac1c27 100644
--- a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplication.java
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplication.java
@@ -46,8 +46,10 @@ public class RestfulObjectsApplication extends AbstractJaxRsApplication {
 
         final RestfulObjectsJaxbWriterForXml roWriter = new RestfulObjectsJaxbWriterForXml();
         addSingleton(roWriter);
+
         addSingleton(new RestfulObjectsApplicationExceptionMapper());
         addSingleton(new RuntimeExceptionMapper());
+        addSingleton(new ObjectNotFoundExceptionMapper());
 
         addSingleton(new AcceptHeaderServiceForRest.RequestFilter());
         addSingleton(new AcceptHeaderServiceForRest.ResponseFilter());
diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionMapper.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionMapper.java
index 9ad8be3..210f8aa 100644
--- a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionMapper.java
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionMapper.java
@@ -18,29 +18,19 @@
  */
 package org.apache.isis.viewer.restfulobjects.server;
 
-import java.util.List;
-
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.ResponseBuilder;
-import javax.ws.rs.ext.ExceptionMapper;
 import javax.ws.rs.ext.Provider;
 
 import org.apache.isis.core.commons.exceptions.ExceptionUtils;
 import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
-import org.apache.isis.viewer.restfulobjects.applib.RestfulMediaType;
 import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse;
 import org.apache.isis.viewer.restfulobjects.applib.util.JsonMapper;
 import org.apache.isis.viewer.restfulobjects.rendering.RestfulObjectsApplicationException;
 
 //@Path("/") // FIXME: workaround for TomEE ... but breaks the RestEasy TCK tests so commented out:-(
 @Provider
-public class RestfulObjectsApplicationExceptionMapper implements ExceptionMapper<RestfulObjectsApplicationException> {
-
-    @Context
-    HttpHeaders httpHeaders;
+public class RestfulObjectsApplicationExceptionMapper extends ExceptionMapperAbstract<RestfulObjectsApplicationException> {
 
     @Override
     public Response toResponse(final RestfulObjectsApplicationException ex) {
@@ -51,66 +41,61 @@ public class RestfulObjectsApplicationExceptionMapper implements ExceptionMapper
             builder.header(RestfulResponse.Header.WARNING.getName(), RestfulResponse.Header.WARNING.render(message));
         }
 
-        // xml handling (only if also not text/html, ie what browsers would send).
-        boolean html = false;
-        final List<MediaType> acceptableMediaTypes = httpHeaders.getAcceptableMediaTypes();
-        for (MediaType acceptableMediaType : acceptableMediaTypes) {
-            html = html || (acceptableMediaType.getType().equals("text") && acceptableMediaType.getSubtype().equals("html"));
-        }
-        boolean xml = false;
-        if(!html) {
-            for (MediaType acceptableMediaType : acceptableMediaTypes) {
-                xml = xml || acceptableMediaType.getSubtype().equals("xml");
+        setContentTypeOn(builder);
+
+        final boolean xml = isXmlButNotHtml();
+        String body = null;
+        if(!xml) {
+            final JsonRepresentation bodyRepr = ex.getBody();
+            if (bodyRepr != null) {
+                body = bodyRepr.toString();
             }
         }
+        if (body == null) {
+            body = toBody(ex);
+        }
 
-        // body and content-type
-        final JsonRepresentation bodyRepr = ex.getBody();
-        final Throwable cause = ex.getCause();
-        if (bodyRepr != null) {
-            if(!xml) {
-                final String body = bodyRepr.toString();
-                builder.entity(body);
-                builder.type(MediaType.APPLICATION_JSON); // generic; the spec doesn't define what the media type should be
-            } else {
-                builder.type(MediaType.APPLICATION_XML);
-            }
-        } else if(cause == null) {
-            if(!xml) {
-                builder.type(MediaType.APPLICATION_JSON); // generic; the spec doesn't define what the media type should be
-            } else {
-                builder.type(MediaType.APPLICATION_XML);
-            }
-        } else {
-            if(!xml) {
-                String body;
-                try {
-                    body = JsonMapper.instance().write(RestfulObjectsApplicationExceptionPojo.create(cause));
-                } catch (final Exception e) {
-                    // fallback
-                    body = "{ \"exception\": \"" + ExceptionUtils.getFullStackTrace(cause) + "\" }";
-                }
-                builder.entity(body);
-                builder.type(RestfulMediaType.APPLICATION_JSON_ERROR);
-            } else {
-                final RestfulObjectsApplicationExceptionPojo exceptionPojo = RestfulObjectsApplicationExceptionPojo.create(cause);
-                final StringBuilder buf = new StringBuilder();
-                buf.append("<exception>\n");
-                buf.append("  <httpStatusCode>").append(exceptionPojo.getHttpStatusCode()).append("</httpStatusCode>/n");
-                buf.append("  <message>").append(exceptionPojo.getMessage()).append("</message>/n");
-                buf.append("  <stackTrace>/n");
-                for (String line : exceptionPojo.getStackTrace()) {
-                    buf.append("    <stackTraceElement>").append(line).append("    </stackTraceElement>/n");
-                }
-                buf.append("  </stackTrace>/n");
-                buf.append("</exception>");
-                builder.entity(buf.toString());
-                builder.type(RestfulMediaType.APPLICATION_XML_ERROR);
-            }
+        if(body != null) {
+            builder.entity(body);
         }
 
         return builder.build();
     }
 
+    protected String toBody(final RestfulObjectsApplicationException ex) {
+        if(ex.getHttpStatusCode() == RestfulResponse.HttpStatusCode.NOT_FOUND) {
+            final ExceptionPojo exceptionPojo = new ExceptionPojo(ex);
+            try {
+                return JsonMapper.instance().write(exceptionPojo);
+            } catch (final Exception e) {
+                // fallback
+                return null;
+            }
+        }
+
+        final boolean xml = isXmlButNotHtml();
+        final ExceptionPojoWithDetail exceptionPojo = new ExceptionPojoWithDetail(ex);
+        if (!xml) {
+            try {
+                return JsonMapper.instance().write(exceptionPojo);
+            } catch (final Exception e) {
+                // fallback
+                return "{ \"exception\": \"" + ExceptionUtils.getFullStackTrace(ex) + "\" }";
+            }
+        } else {
+            final StringBuilder buf = new StringBuilder();
+            buf.append("<exception>\n");
+            buf.append("  <httpStatusCode>").append(exceptionPojo.getHttpStatusCode())
+                    .append("</httpStatusCode>/n");
+            buf.append("  <message>").append(exceptionPojo.getMessage()).append("</message>/n");
+            buf.append("  <stackTrace>/n");
+            for (String line : exceptionPojo.getStackTrace()) {
+                buf.append("    <stackTraceElement>").append(line).append("    </stackTraceElement>/n");
+            }
+            buf.append("  </stackTrace>/n");
+            buf.append("</exception>");
 
+            return buf.toString();
+        }
+    }
 }
diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RuntimeExceptionMapper.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RuntimeExceptionMapper.java
index 8553166..bdf3866 100644
--- a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RuntimeExceptionMapper.java
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RuntimeExceptionMapper.java
@@ -19,12 +19,16 @@
 package org.apache.isis.viewer.restfulobjects.server;
 
 import java.util.List;
+
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.ResponseBuilder;
 import javax.ws.rs.ext.ExceptionMapper;
 import javax.ws.rs.ext.Provider;
+
 import com.google.common.base.Throwables;
+
 import org.jboss.resteasy.spi.Failure;
+
 import org.apache.isis.core.commons.exceptions.ExceptionUtils;
 import org.apache.isis.core.runtime.system.context.IsisContext;
 import org.apache.isis.core.runtime.system.session.IsisSessionFactory;
@@ -38,13 +42,14 @@ public class RuntimeExceptionMapper implements ExceptionMapper<RuntimeException>
 
     @Override
     public Response toResponse(final RuntimeException ex) {
-        // since have rendered...
+
         final IsisTransaction currentTransaction = getIsisSessionFactory().getCurrentSession()
                 .getPersistenceSession().getTransactionManager().getCurrentTransaction();
 
         final Throwable rootCause = Throwables.getRootCause(ex);
         final List<Throwable> causalChain = Throwables.getCausalChain(ex);
         for (Throwable throwable : causalChain) {
+
             if(throwable == rootCause) {
                 currentTransaction.clearAbortCause();
             }
diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/webapp/IsisTransactionFilterForRestfulObjects.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/webapp/IsisTransactionFilterForRestfulObjects.java
index b93d82a..e9ef941 100644
--- a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/webapp/IsisTransactionFilterForRestfulObjects.java
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/webapp/IsisTransactionFilterForRestfulObjects.java
@@ -52,7 +52,11 @@ public class IsisTransactionFilterForRestfulObjects implements Filter {
             final boolean inTransaction = isisSessionFactory.inTransaction();
             if(inTransaction) {
                 // user/logout will have invalidated the current transaction and also persistence session.
-                isisTransactionManager.endTransaction();
+                try {
+                    isisTransactionManager.endTransaction();
+                } catch (Exception ex) {
+                    // ignore.  Any exceptions will have been mapped into a suitable response already.
+                }
             }
         }
     }

-- 
To stop receiving notification emails like this one, please contact
"commits@isis.apache.org" <co...@isis.apache.org>.