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 2014/02/03 12:28:59 UTC

git commit: ISIS-665: auto-escalate application exceptions to fatal exceptions

Updated Branches:
  refs/heads/master a8d01a60d -> 9032a866c


ISIS-665: auto-escalate application exceptions to fatal exceptions

In addition:
- added RecoverableException, NonRecoverableException, FatalException into the hierarchy
- RecoverableException is synonym for ApplicationException (immediate supertype)
- NonRecoverableException is synonym for FatalException (immediate supertype_
  - basically any other runtime exception thrown by the app

extended ToDoItem to demonstrate usage.


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/9032a866
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/9032a866
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/9032a866

Branch: refs/heads/master
Commit: 9032a866c477bf38a6e19bae3939fecee6d9df68
Parents: a8d01a6
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Mon Feb 3 11:28:40 2014 +0000
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Mon Feb 3 11:28:40 2014 +0000

----------------------------------------------------------------------
 .../jdo/applib/service/publish/IoUtils.java     | 10 ++-
 .../service/support/IsisJdoSupportImpl.java     |  7 +-
 .../viewer/wicket/model/models/ActionModel.java |  8 +-
 .../ui/components/actions/ActionPanel.java      | 12 ++-
 .../cssmenu/ActionLinkFactoryAbstract.java      | 10 ++-
 .../wicket/ui/errors/JGrowlBehaviour.java       | 16 ++--
 .../isis/applib/ApplicationException.java       | 29 ++++++-
 .../org/apache/isis/applib/FatalException.java  | 53 ++++++++++++
 .../isis/applib/NonRecoverableException.java    | 53 ++++++++++++
 .../isis/applib/PersistFailedException.java     | 29 +++----
 .../isis/applib/RecoverableException.java       | 62 +++++++++++++
 .../apache/isis/applib/RepositoryException.java | 28 +++---
 .../org/apache/isis/applib/clock/Clock.java     |  5 +-
 .../isis/applib/fixtures/BaseFixture.java       |  4 +-
 .../services/wrapper/InteractionException.java  |  4 +-
 .../exceptions/IsisApplicationException.java    |  2 +-
 .../core/commons/lang/ThrowableExtensions.java  |  4 +-
 .../runtimecontext/RuntimeContext.java          | 91 +++++++++++++++++++-
 .../noruntime/RuntimeContextNoRuntime.java      | 11 +++
 .../DeveloperUtilitiesServiceDefault.java       |  7 +-
 .../invoke/ActionInvocationFacetFactory.java    | 16 +++-
 .../invoke/ActionInvocationFacetViaMethod.java  | 28 ++++--
 ...ostsPropertyChangedEventFacetAnnotation.java |  5 +-
 .../internal/RuntimeContextFromSession.java     | 16 +++-
 .../background/BackgroundActionExecution.java   |  5 +-
 .../system/transaction/IsisTransaction.java     | 21 +++--
 .../isis/core/tck/dom/refs/ParentEntity.java    |  2 +-
 .../core/tck/dom/refs/ReferencingEntity.java    |  2 +-
 .../isis/core/tck/dom/refs/SimpleEntity.java    |  2 +-
 .../dom/src/main/java/dom/todo/ToDoItem.java    | 37 ++++----
 .../src/main/java/dom/todo/ToDoItem.layout.json | 10 ++-
 .../java/dom/todo/ToDoItemContributions.java    |  6 +-
 .../ToDoItemContributionsTest_priority.java     |  2 +-
 33 files changed, 480 insertions(+), 117 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/IoUtils.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/IoUtils.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/IoUtils.java
index 1d3c976..d7f1ae0 100644
--- a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/IoUtils.java
+++ b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/IoUtils.java
@@ -25,7 +25,9 @@ import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 import java.util.zip.ZipOutputStream;
 
-import org.apache.isis.applib.ApplicationException;
+import org.apache.isis.applib.FatalException;
+import org.apache.isis.applib.RecoverableException;
+import org.apache.isis.applib.NonRecoverableException;
 
 class IoUtils {
 
@@ -44,8 +46,8 @@ class IoUtils {
             final byte[] utf8Bytes = toZip.getBytes(Charset.forName("UTF-8"));
             zos.write(utf8Bytes);
             zos.flush();
-        } catch (IOException e) {
-            throw new ApplicationException(e);
+        } catch (final IOException ex) {
+            throw new FatalException(ex);
         } finally {
             closeSafely(zos);
         }
@@ -72,7 +74,7 @@ class IoUtils {
             }
             return null;
         } catch(IOException ex) {
-            throw new ApplicationException(ex);
+            throw new NonRecoverableException(ex);
         } finally {
             IoUtils.closeSafely(zis);
         }

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/service/support/IsisJdoSupportImpl.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/service/support/IsisJdoSupportImpl.java b/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/service/support/IsisJdoSupportImpl.java
index 279361f..2f7e3b8 100644
--- a/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/service/support/IsisJdoSupportImpl.java
+++ b/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/service/support/IsisJdoSupportImpl.java
@@ -35,7 +35,8 @@ import javax.jdo.datastore.JDOConnection;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
-import org.apache.isis.applib.ApplicationException;
+import org.apache.isis.applib.FatalException;
+import org.apache.isis.applib.RecoverableException;
 import org.apache.isis.applib.annotation.Hidden;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
@@ -170,8 +171,8 @@ public class IsisJdoSupportImpl implements IsisJdoSupport {
                         return null;
                     }
                 });
-            } catch (Exception e) {
-                throw new ApplicationException(e);
+            } catch (final Exception ex) {
+                throw new FatalException(ex);
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/component/viewer/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java b/component/viewer/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java
index e0909d3..e7ef110 100644
--- a/component/viewer/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java
+++ b/component/viewer/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java
@@ -40,7 +40,7 @@ import org.apache.wicket.request.resource.ByteArrayResource;
 import org.apache.wicket.request.resource.ContentDisposition;
 import org.apache.wicket.util.resource.StringResourceStream;
 
-import org.apache.isis.applib.ApplicationException;
+import org.apache.isis.applib.RecoverableException;
 import org.apache.isis.applib.Identifier;
 import org.apache.isis.applib.annotation.ActionSemantics;
 import org.apache.isis.applib.annotation.BookmarkPolicy;
@@ -507,9 +507,9 @@ public class ActionModel extends BookmarkableModel<ObjectAdapter> {
 
     // //////////////////////////////////////
     
-    public static ApplicationException getApplicationExceptionIfAny(Exception ex) {
-        Iterable<ApplicationException> appEx = Iterables.filter(Throwables.getCausalChain(ex), ApplicationException.class);
-        Iterator<ApplicationException> iterator = appEx.iterator();
+    public static RecoverableException getApplicationExceptionIfAny(Exception ex) {
+        Iterable<RecoverableException> appEx = Iterables.filter(Throwables.getCausalChain(ex), RecoverableException.class);
+        Iterator<RecoverableException> iterator = appEx.iterator();
         return iterator.hasNext() ? iterator.next() : null;
     }
 

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.java
index a2a6e51..df2b949 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.java
@@ -28,7 +28,7 @@ import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.model.Model;
 
-import org.apache.isis.applib.ApplicationException;
+import org.apache.isis.applib.RecoverableException;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.bookmark.BookmarkService;
 import org.apache.isis.applib.services.exceprecog.ExceptionRecognizer;
@@ -274,11 +274,11 @@ public class ActionPanel extends PanelAbstract<ActionModel> implements ActionExe
     }
 
     /**
-     * Executes the action, handling any {@link ApplicationException}s that
+     * Executes the action, handling any {@link RecoverableException}s that
      * might be encountered.
      * 
      * <p>
-     * If an {@link ApplicationException} is encountered, then the application error will be
+     * If an {@link RecoverableException} is encountered, then the application error will be
      * {@link MessageBroker#setApplicationError(String) set} so that a suitable message can be 
      * rendered higher up the call stack.
      * 
@@ -292,9 +292,13 @@ public class ActionPanel extends PanelAbstract<ActionModel> implements ActionExe
             return resultAdapter;
 
         } catch (RuntimeException ex) {
+
+            // TODO: some duplication between this code and ActionLinkFactoryAbstract
             
             // see if is an application-defined exception
-            final ApplicationException appEx = ActionModel.getApplicationExceptionIfAny(ex);
+            // if so, is converted to an application error,
+            // equivalent to calling DomainObjectContainer#raiseError(...)
+            final RecoverableException appEx = ActionModel.getApplicationExceptionIfAny(ex);
             if (appEx != null) {
                 getMessageBroker().setApplicationError(appEx.getMessage());
 

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/cssmenu/ActionLinkFactoryAbstract.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/cssmenu/ActionLinkFactoryAbstract.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/cssmenu/ActionLinkFactoryAbstract.java
index 8cf7c9e..bb3803d 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/cssmenu/ActionLinkFactoryAbstract.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/cssmenu/ActionLinkFactoryAbstract.java
@@ -35,7 +35,7 @@ import org.apache.wicket.request.IRequestHandler;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.util.resource.IResourceStream;
 
-import org.apache.isis.applib.ApplicationException;
+import org.apache.isis.applib.RecoverableException;
 import org.apache.isis.applib.annotation.ActionSemantics;
 import org.apache.isis.core.commons.exceptions.IsisApplicationException;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
@@ -187,9 +187,13 @@ public abstract class ActionLinkFactoryAbstract implements ActionLinkFactory {
             return actionModel.getObject();
 
         } catch (RuntimeException ex) {
-            
+
+            // TODO: some duplication between this code and ActionPanel
+
             // see if is an application-defined exception
-            final ApplicationException appEx = ActionModel.getApplicationExceptionIfAny(ex);
+            // if so, is converted to an application error,
+            // equivalent to calling DomainObjectContainer#raiseError(...)
+            final RecoverableException appEx = ActionModel.getApplicationExceptionIfAny(ex);
             if (appEx != null) {
                 IsisContext.getMessageBroker().setApplicationError(appEx.getMessage());
                 return null;

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/JGrowlBehaviour.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/JGrowlBehaviour.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/JGrowlBehaviour.java
index 21ec7c8..54a8b76 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/JGrowlBehaviour.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/JGrowlBehaviour.java
@@ -26,16 +26,15 @@ import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
 
-import org.apache.isis.applib.ApplicationException;
+import org.apache.isis.applib.RecoverableException;
 import org.apache.isis.core.commons.authentication.MessageBroker;
-import org.apache.isis.core.commons.lang.StringExtensions;
 import org.apache.isis.core.runtime.system.context.IsisContext;
 
 
 /**
  * Attach to any Ajax button that might trigger a notification (ie calls
  * {@link MessageBroker#addMessage(String)}, {@link MessageBroker#addWarning(String)},
- * {@link MessageBroker#setApplicationError(String)} or throws an {@link ApplicationException}). 
+ * {@link MessageBroker#setApplicationError(String)} or throws an {@link RecoverableException}). 
  * 
  * <p>
  * Attach using the standard Wicket code:
@@ -50,18 +49,23 @@ public class JGrowlBehaviour extends AbstractDefaultAjaxBehavior {
 
     @Override
     protected void respond(AjaxRequestTarget target) {
-        String feedbackMsg = JGrowlUtil.asJGrowlCalls(IsisContext.getMessageBroker());
+        String feedbackMsg = JGrowlUtil.asJGrowlCalls(getMessageBroker());
         if(!Strings.isNullOrEmpty(feedbackMsg)) {
             target.appendJavaScript(feedbackMsg);
         }
     }
-    
+
     @Override
     public void renderHead(Component component, IHeaderResponse response) {
         super.renderHead(component, response);
-        String feedbackMsg = JGrowlUtil.asJGrowlCalls(IsisContext.getMessageBroker());
+        String feedbackMsg = JGrowlUtil.asJGrowlCalls(getMessageBroker());
         if(!Strings.isNullOrEmpty(feedbackMsg)) {
             response.render(OnDomReadyHeaderItem.forScript(feedbackMsg));
         }
     }
+    
+    protected org.apache.isis.core.runtime.system.transaction.MessageBroker getMessageBroker() {
+        return IsisContext.getMessageBroker();
+    }
+    
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/applib/src/main/java/org/apache/isis/applib/ApplicationException.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/ApplicationException.java b/core/applib/src/main/java/org/apache/isis/applib/ApplicationException.java
index 36bcbd3..5717f17 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/ApplicationException.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/ApplicationException.java
@@ -20,10 +20,31 @@
 package org.apache.isis.applib;
 
 /**
- * Indicates that a problem has occurred within the application, as opposed to
- * within a supporting framework or system.
+ * Indicates that an exceptional condition/problem has occurred within the application's domain logic.
+ * 
+ * <p>
+ * Throwing this exception is equivalent to calling {@link DomainObjectContainer#raiseError(String)}.
+ * The framework will trap the error and display the exception message as a warning.
+ * 
+ * <p>
+ * This exception should only be thrown for &quot;recoverable&quot; exceptions, that is, those which
+ * could be anticipated by the application.  It should not be thrown for fatal, unanticipated exceptions.
+ * 
+ * <p>
+ * The framework attempts to apply some heuristics; if the underlying Isis transaction has been aborted
+ * (for example as the result of a problem persisting some data) but then the application attempts to
+ * throw this exception, the exception will be promoted to a fatal exception.
+ * 
+ * <p>
+ * Note that this exception has identical semantics to {@link RecoverableException}, and can be considered a
+ * synonym.
+ * 
+ * @see RecoverableException
+ * @see NonRecoverableException
+ * @see FatalException
  */
-public class ApplicationException extends RuntimeException {
+public class ApplicationException extends RecoverableException {
+    
     private static final long serialVersionUID = 1L;
 
     public ApplicationException(final String msg) {
@@ -31,7 +52,7 @@ public class ApplicationException extends RuntimeException {
     }
 
     public ApplicationException(final Throwable cause) {
-        this(cause.getMessage(), cause);
+        super(cause);
     }
 
     public ApplicationException(final String msg, final Throwable cause) {

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/applib/src/main/java/org/apache/isis/applib/FatalException.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/FatalException.java b/core/applib/src/main/java/org/apache/isis/applib/FatalException.java
new file mode 100644
index 0000000..7284609
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/FatalException.java
@@ -0,0 +1,53 @@
+/*
+ *  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.applib;
+
+/**
+ * Indicates that an unexpected, non-recoverable (fatal) exception has occurred within
+ * the application logic.
+ * 
+ * <p>
+ * Throwing this exception will (dependent on the viewer) result in some sort of an error page being displayed to the user.
+ *
+ * <p>
+ * Note that this exception has identical semantics to {@link NonRecoverableException}, and can be considered a
+ * synonym.
+ * 
+ * @see RecoverableException
+ * @see ApplicationException
+ * @see NonRecoverableException
+ */
+public class FatalException extends NonRecoverableException {
+    
+    private static final long serialVersionUID = 1L;
+
+    public FatalException(final String msg) {
+        super(msg);
+    }
+
+    public FatalException(final Throwable cause) {
+        this(cause.getMessage(), cause);
+    }
+
+    public FatalException(final String msg, final Throwable cause) {
+        super(msg, cause);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/applib/src/main/java/org/apache/isis/applib/NonRecoverableException.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/NonRecoverableException.java b/core/applib/src/main/java/org/apache/isis/applib/NonRecoverableException.java
new file mode 100644
index 0000000..9649d39
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/NonRecoverableException.java
@@ -0,0 +1,53 @@
+/*
+ *  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.applib;
+
+/**
+ * Indicates that an unexpected, non-recoverable (fatal) exception has occurred within
+ * the application logic.
+ * 
+ * <p>
+ * Throwing this exception will (dependent on the viewer) result in some sort of an error page being displayed to the user.
+ * 
+ * <p>
+ * Note that this exception has identical semantics to {@link FatalException} (of which it is the immediate
+ * superclass) and can be considered a synonym.
+ * 
+ * @see RecoverableException
+ * @see ApplicationException
+ * @see FatalException
+ */
+public class NonRecoverableException extends RuntimeException {
+    
+    private static final long serialVersionUID = 1L;
+
+    public NonRecoverableException(final String msg) {
+        super(msg);
+    }
+
+    public NonRecoverableException(final Throwable cause) {
+        super(cause);
+    }
+
+    public NonRecoverableException(final String msg, final Throwable cause) {
+        super(msg, cause);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/applib/src/main/java/org/apache/isis/applib/PersistFailedException.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/PersistFailedException.java b/core/applib/src/main/java/org/apache/isis/applib/PersistFailedException.java
index c308ca8..5506564 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/PersistFailedException.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/PersistFailedException.java
@@ -21,31 +21,30 @@ package org.apache.isis.applib;
 
 /**
  * Indicates that the persistence of an object failed.
+ * 
+ * <p>
+ * This exception is intended to represent an unexpected and non-recoverable condition (eg a unique/primary key/
+ * foreign key constaint has been violated), and so is a subclass of {@link NonRecoverableException}.
+ * Throwing this exception will therefore result in (some sort of) error page being displayed
+ * to the user.
+ * 
+ * @see NonRecoverableException
+ * @see RecoverableException
  */
-public class PersistFailedException extends RuntimeException {
+public class PersistFailedException extends NonRecoverableException {
+    
     private static final long serialVersionUID = 1L;
-    private Throwable cause;
-
-    public PersistFailedException() {
-        super();
-    }
-
+    
     public PersistFailedException(final String msg) {
         super(msg);
     }
 
     public PersistFailedException(final Throwable cause) {
-        this(cause.getMessage());
-        this.cause = cause;
+        super(cause);
     }
 
     public PersistFailedException(final String msg, final Throwable cause) {
-        this(msg);
-        this.cause = cause;
+        super(msg, cause);
     }
 
-    @Override
-    public Throwable getCause() {
-        return cause;
-    }
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/applib/src/main/java/org/apache/isis/applib/RecoverableException.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/RecoverableException.java b/core/applib/src/main/java/org/apache/isis/applib/RecoverableException.java
new file mode 100644
index 0000000..b3d7a43
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/RecoverableException.java
@@ -0,0 +1,62 @@
+/*
+ *  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.applib;
+
+/**
+ * Indicates that an exceptional condition/problem has occurred within the application's domain logic.
+ * 
+ * <p>
+ * Throwing this exception is equivalent to calling {@link DomainObjectContainer#raiseError(String)}.
+ * The framework will trap the error and display the exception message as a warning.
+ * 
+ * <p>
+ * This exception should only be thrown for &quot;recoverable&quot; exceptions, that is, those which
+ * could be anticipated by the application.  It should not be thrown for fatal, unanticipated exceptions.
+ * 
+ * <p>
+ * The framework attempts to apply some heuristics; if the underlying Isis transaction has been aborted
+ * (for example as the result of a problem persisting some data) but then the application attempts to
+ * throw this exception, the exception will be promoted to a fatal exception.
+ * 
+ * <p>
+ * Note that this exception has identical semantics to {@link ApplicationException} (of which it is the immediate
+ * superclass), and can be considered a synonym.
+ * 
+ * @see ApplicationException
+ * @see NonRecoverableException
+ * @see FatalException
+ */
+public class RecoverableException extends RuntimeException {
+    
+    private static final long serialVersionUID = 1L;
+
+    public RecoverableException(final String msg) {
+        super(msg);
+    }
+
+    public RecoverableException(final Throwable cause) {
+        super(cause);
+    }
+
+    public RecoverableException(final String msg, final Throwable cause) {
+        super(msg, cause);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/applib/src/main/java/org/apache/isis/applib/RepositoryException.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/RepositoryException.java b/core/applib/src/main/java/org/apache/isis/applib/RepositoryException.java
index 786fcc2..d0839aa 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/RepositoryException.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/RepositoryException.java
@@ -21,31 +21,29 @@ package org.apache.isis.applib;
 
 /**
  * Indicates that a repository method has failed.
+ * 
+ * <p>
+ * This exception is intended to represent an unexpected and non-recoverable condition (eg a syntax error in some
+ * JDOQL query syntax or similar), and so is a subclass of {@link NonRecoverableException}.
+ * Throwing this exception will therefore result in (some sort of) error page being displayed
+ * to the user.
+ * 
+ * @see NonRecoverableException
+ * @see RecoverableException
  */
-public class RepositoryException extends RuntimeException {
+public class RepositoryException extends NonRecoverableException {
+    
     private static final long serialVersionUID = 1L;
-    private Throwable cause;
-
-    public RepositoryException() {
-        super();
-    }
 
     public RepositoryException(final String msg) {
         super(msg);
     }
 
     public RepositoryException(final Throwable cause) {
-        this(cause.getMessage());
-        this.cause = cause;
+        super(cause);
     }
 
     public RepositoryException(final String msg, final Throwable cause) {
-        this(msg);
-        this.cause = cause;
-    }
-
-    @Override
-    public Throwable getCause() {
-        return cause;
+        super(msg, cause);
     }
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/applib/src/main/java/org/apache/isis/applib/clock/Clock.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/clock/Clock.java b/core/applib/src/main/java/org/apache/isis/applib/clock/Clock.java
index 3ceca74..48ac2f3 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/clock/Clock.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/clock/Clock.java
@@ -29,7 +29,8 @@ import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.joda.time.LocalDateTime;
 
-import org.apache.isis.applib.ApplicationException;
+import org.apache.isis.applib.FatalException;
+import org.apache.isis.applib.RecoverableException;
 import org.apache.isis.applib.Defaults;
 import org.apache.isis.applib.fixtures.FixtureClock;
 
@@ -129,7 +130,7 @@ public abstract class Clock {
 
     private static void ensureReplaceable() {
         if (!isReplaceable && instance != null) {
-            throw new ApplicationException("Clock already set up");
+            throw new RecoverableException("Clock already set up");
         }
     }
 

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/applib/src/main/java/org/apache/isis/applib/fixtures/BaseFixture.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/fixtures/BaseFixture.java b/core/applib/src/main/java/org/apache/isis/applib/fixtures/BaseFixture.java
index 12caef0..03edfe6 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/fixtures/BaseFixture.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/fixtures/BaseFixture.java
@@ -19,7 +19,7 @@
 package org.apache.isis.applib.fixtures;
 
 import org.apache.isis.applib.AbstractContainedObject;
-import org.apache.isis.applib.ApplicationException;
+import org.apache.isis.applib.RecoverableException;
 import org.apache.isis.applib.clock.Clock;
 
 abstract class BaseFixture extends AbstractContainedObject implements InstallableFixture {
@@ -31,7 +31,7 @@ abstract class BaseFixture extends AbstractContainedObject implements Installabl
         this.fixtureType = fixtureType;
         try {
             clock = FixtureClock.initialize();
-        } catch (final ApplicationException ex) {
+        } catch (final RecoverableException ex) {
             clock = null;
             System.err.println(ex.getMessage());
             System.err.println("calls to change date or time will be ignored");

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/applib/src/main/java/org/apache/isis/applib/services/wrapper/InteractionException.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/wrapper/InteractionException.java b/core/applib/src/main/java/org/apache/isis/applib/services/wrapper/InteractionException.java
index cb8b643..a73f3d1 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/wrapper/InteractionException.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/wrapper/InteractionException.java
@@ -19,11 +19,11 @@
 
 package org.apache.isis.applib.services.wrapper;
 
-import org.apache.isis.applib.ApplicationException;
+import org.apache.isis.applib.RecoverableException;
 import org.apache.isis.applib.Identifier;
 import org.apache.isis.applib.events.InteractionEvent;
 
-public abstract class InteractionException extends ApplicationException {
+public abstract class InteractionException extends RecoverableException {
 
     private static final long serialVersionUID = 1L;
 

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/metamodel/src/main/java/org/apache/isis/core/commons/exceptions/IsisApplicationException.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/commons/exceptions/IsisApplicationException.java b/core/metamodel/src/main/java/org/apache/isis/core/commons/exceptions/IsisApplicationException.java
index 3387e23..64863a6 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/commons/exceptions/IsisApplicationException.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/commons/exceptions/IsisApplicationException.java
@@ -24,7 +24,7 @@ package org.apache.isis.core.commons.exceptions;
  * 
  * <p>
  * The viewer is expected to render the message within the application in a
- * user-friendly fashion.
+ * user-friendly fashion, for example as a growl-like notification.
  */
 public class IsisApplicationException extends IsisException {
 

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/metamodel/src/main/java/org/apache/isis/core/commons/lang/ThrowableExtensions.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/commons/lang/ThrowableExtensions.java b/core/metamodel/src/main/java/org/apache/isis/core/commons/lang/ThrowableExtensions.java
index 571d0e4..5e96785 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/commons/lang/ThrowableExtensions.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/commons/lang/ThrowableExtensions.java
@@ -24,7 +24,7 @@ import java.io.IOException;
 import java.io.PrintStream;
 import java.lang.reflect.InvocationTargetException;
 
-import org.apache.isis.applib.ApplicationException;
+import org.apache.isis.applib.RecoverableException;
 import org.apache.isis.core.commons.exceptions.IsisApplicationException;
 import org.apache.isis.core.metamodel.exceptions.MetaModelException;
 
@@ -48,7 +48,7 @@ public final class ThrowableExtensions {
 
     public static void throwWithinIsisException(final InvocationTargetException e, final String error) {
         final Throwable targetException = e.getTargetException();
-        if (targetException instanceof ApplicationException) {
+        if (targetException instanceof RecoverableException) {
             // an application exception from the domain code is re-thrown as an
             // IsisException with same semantics
             throw new IsisApplicationException(targetException);

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/metamodel/src/main/java/org/apache/isis/core/metamodel/runtimecontext/RuntimeContext.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/runtimecontext/RuntimeContext.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/runtimecontext/RuntimeContext.java
index 44f3061..e5fbc52 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/runtimecontext/RuntimeContext.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/runtimecontext/RuntimeContext.java
@@ -83,7 +83,94 @@ public interface RuntimeContext extends Injectable, ApplicationScopedComponent {
 
     public void setContainer(DomainObjectContainer container);
 
-
-
+    public TransactionState getTransactionState();
+
+    public static enum TransactionState {
+        
+        /**
+         * No transaction exists.
+         */
+        NONE,
+        /**
+         * Started, still in progress.
+         * 
+         * <p>
+         * May {@link IsisTransaction#flush() flush},
+         * {@link IsisTransaction#commit() commit} or
+         * {@link IsisTransaction#abort() abort}.
+         */
+        IN_PROGRESS,
+        /**
+         * Started, but has hit an exception.
+         * 
+         * <p>
+         * May not {@link IsisTransaction#flush()} or
+         * {@link IsisTransaction#commit() commit} (will throw an
+         * {@link IllegalStateException}), but can only
+         * {@link IsisTransaction#abort() abort}.
+         * 
+         * <p>
+         * Similar to <tt>setRollbackOnly</tt> in EJBs.
+         */
+        MUST_ABORT,
+        /**
+         * Completed, having successfully committed.
+         * 
+         * <p>
+         * May not {@link IsisTransaction#flush()} or
+         * {@link IsisTransaction#abort() abort} or
+         * {@link IsisTransaction#commit() commit} (will throw
+         * {@link IllegalStateException}).
+         */
+        COMMITTED,
+        /**
+         * Completed, having aborted.
+         * 
+         * <p>
+         * May not {@link IsisTransaction#flush()},
+         * {@link IsisTransaction#commit() commit} or
+         * {@link IsisTransaction#abort() abort} (will throw
+         * {@link IllegalStateException}).
+         */
+        ABORTED;
+
+        private TransactionState(){}
+
+        /**
+         * Whether it is valid to {@link IsisTransaction#flush() flush} this
+         * {@link IsisTransaction transaction}.
+         */
+        public boolean canFlush() {
+            return this == IN_PROGRESS;
+        }
+
+        /**
+         * Whether it is valid to {@link IsisTransaction#commit() commit} this
+         * {@link IsisTransaction transaction}.
+         */
+        public boolean canCommit() {
+            return this == IN_PROGRESS;
+        }
+
+        /**
+         * Whether it is valid to {@link IsisTransaction#markAsAborted() abort} this
+         * {@link IsisTransaction transaction}.
+         */
+        public boolean canAbort() {
+            return this == IN_PROGRESS || this == MUST_ABORT;
+        }
+
+        /**
+         * Whether the {@link IsisTransaction transaction} is complete (and so a
+         * new one can be started).
+         */
+        public boolean isComplete() {
+            return this == COMMITTED || this == ABORTED;
+        }
+
+        public boolean mustAbort() {
+            return this == MUST_ABORT;
+        }
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/metamodel/src/main/java/org/apache/isis/core/metamodel/runtimecontext/noruntime/RuntimeContextNoRuntime.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/runtimecontext/noruntime/RuntimeContextNoRuntime.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/runtimecontext/noruntime/RuntimeContextNoRuntime.java
index bf8dd81..0417498 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/runtimecontext/noruntime/RuntimeContextNoRuntime.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/runtimecontext/noruntime/RuntimeContextNoRuntime.java
@@ -355,4 +355,15 @@ public class RuntimeContextNoRuntime extends RuntimeContextAbstract {
     public LocalizationProvider getLocalizationProvider() {
         return localizationProvider;
     }
+
+    
+    // ///////////////////////////////////////////
+    // getTransactionState
+    // ///////////////////////////////////////////
+    
+    @Override
+    public TransactionState getTransactionState() {
+        throw new UnsupportedOperationException("Not supported by this implementation of RuntimeContext");
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/devutils/DeveloperUtilitiesServiceDefault.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/devutils/DeveloperUtilitiesServiceDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/devutils/DeveloperUtilitiesServiceDefault.java
index 031ae0b..7239695 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/devutils/DeveloperUtilitiesServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/devutils/DeveloperUtilitiesServiceDefault.java
@@ -35,7 +35,8 @@ import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.Lists;
 
-import org.apache.isis.applib.ApplicationException;
+import org.apache.isis.applib.FatalException;
+import org.apache.isis.applib.RecoverableException;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.services.devutils.DeveloperUtilitiesService;
 import org.apache.isis.applib.value.Blob;
@@ -187,8 +188,8 @@ public class DeveloperUtilitiesServiceDefault implements DeveloperUtilitiesServi
             }
             writer.close();
             return new Blob("layouts.zip", mimeTypeApplicationZip, baos.toByteArray());
-        } catch (IOException e) {
-            throw new ApplicationException("Unable to create zip of layouts", e);
+        } catch (final IOException ex) {
+            throw new FatalException("Unable to create zip of layouts", ex);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/actions/invoke/ActionInvocationFacetFactory.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/actions/invoke/ActionInvocationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/actions/invoke/ActionInvocationFacetFactory.java
index 81c3c58..06a71b1 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/actions/invoke/ActionInvocationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/actions/invoke/ActionInvocationFacetFactory.java
@@ -33,6 +33,8 @@ import org.apache.isis.core.metamodel.facets.actions.exploration.ExplorationFace
 import org.apache.isis.core.metamodel.facets.actions.invoke.ActionInvocationFacet;
 import org.apache.isis.core.metamodel.facets.named.NamedFacet;
 import org.apache.isis.core.metamodel.facets.named.NamedFacetInferred;
+import org.apache.isis.core.metamodel.runtimecontext.RuntimeContext;
+import org.apache.isis.core.metamodel.runtimecontext.RuntimeContextAware;
 import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
 import org.apache.isis.core.metamodel.runtimecontext.ServicesInjectorAware;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
@@ -47,7 +49,7 @@ import org.apache.isis.core.progmodel.facets.MethodPrefixBasedFacetFactoryAbstra
  * and {@link DebugFacet}. In addition a {@link NamedFacet} is inferred from the
  * name (taking into account the above well-known prefixes).
  */
-public class ActionInvocationFacetFactory extends MethodPrefixBasedFacetFactoryAbstract implements AdapterManagerAware, ServicesInjectorAware {
+public class ActionInvocationFacetFactory extends MethodPrefixBasedFacetFactoryAbstract implements AdapterManagerAware, ServicesInjectorAware, RuntimeContextAware {
 
     private static final String EXPLORATION_PREFIX = "Exploration";
     private static final String DEBUG_PREFIX = "Debug";
@@ -56,6 +58,7 @@ public class ActionInvocationFacetFactory extends MethodPrefixBasedFacetFactoryA
 
     private AdapterManager adapterManager;
     private ServicesInjector servicesInjector;
+    private RuntimeContext runtimeContext;
 
     /**
      * Note that the {@link Facet}s registered are the generic ones from
@@ -99,7 +102,7 @@ public class ActionInvocationFacetFactory extends MethodPrefixBasedFacetFactoryA
             final ObjectSpecification typeSpec = getSpecificationLoader().loadSpecification(cls);
             final FacetHolder holder = processMethodContext.getFacetHolder();
 
-            FacetUtil.addFacet(new ActionInvocationFacetViaMethod(actionMethod, typeSpec, returnSpec, holder, getAdapterManager(), getServicesInjector()));
+            FacetUtil.addFacet(new ActionInvocationFacetViaMethod(actionMethod, typeSpec, returnSpec, holder, getRuntimeContext(), getAdapterManager(), getServicesInjector()));
         } finally {
             processMethodContext.removeMethod(actionMethod);
         }
@@ -169,4 +172,13 @@ public class ActionInvocationFacetFactory extends MethodPrefixBasedFacetFactoryA
     private ServicesInjector getServicesInjector() {
         return servicesInjector;
     }
+
+    private RuntimeContext getRuntimeContext() {
+        return runtimeContext;
+    }
+    
+    @Override
+    public void setRuntimeContext(RuntimeContext runtimeContext) {
+        this.runtimeContext = runtimeContext;
+    }
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/actions/invoke/ActionInvocationFacetViaMethod.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/actions/invoke/ActionInvocationFacetViaMethod.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/actions/invoke/ActionInvocationFacetViaMethod.java
index 07daf5f..624f8ac 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/actions/invoke/ActionInvocationFacetViaMethod.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/actions/invoke/ActionInvocationFacetViaMethod.java
@@ -30,6 +30,8 @@ import com.google.common.collect.Lists;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import org.apache.isis.applib.NonRecoverableException;
+import org.apache.isis.applib.RecoverableException;
 import org.apache.isis.applib.annotation.Bulk;
 import org.apache.isis.applib.annotation.Bulk.InteractionContext.InvokedAs;
 import org.apache.isis.applib.services.background.ActionInvocationMemento;
@@ -54,6 +56,7 @@ import org.apache.isis.core.metamodel.facets.actions.reified.ReifiedActionFacet;
 import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet;
 import org.apache.isis.core.metamodel.facets.typeof.ElementSpecificationProviderFromTypeOfFacet;
 import org.apache.isis.core.metamodel.facets.typeof.TypeOfFacet;
+import org.apache.isis.core.metamodel.runtimecontext.RuntimeContext;
 import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
@@ -71,12 +74,14 @@ public class ActionInvocationFacetViaMethod extends ActionInvocationFacetAbstrac
 
     private final AdapterManager adapterManager;
     private final ServicesInjector servicesInjector;
-
+    private final RuntimeContext runtimeContext;
+    
     public ActionInvocationFacetViaMethod(
             final Method method, 
             final ObjectSpecification onType, 
             final ObjectSpecification returnType, 
             final FacetHolder holder, 
+            final RuntimeContext runtimeContext, 
             final AdapterManager adapterManager, 
             final ServicesInjector servicesInjector) {
         super(holder);
@@ -84,6 +89,7 @@ public class ActionInvocationFacetViaMethod extends ActionInvocationFacetAbstrac
         this.paramCount = method.getParameterTypes().length;
         this.onType = onType;
         this.returnType = returnType;
+        this.runtimeContext = runtimeContext;
         this.adapterManager = adapterManager;
         this.servicesInjector = servicesInjector;
     }
@@ -203,12 +209,22 @@ public class ActionInvocationFacetViaMethod extends ActionInvocationFacetAbstrac
         } catch (final IllegalArgumentException e) {
             throw e;
         } catch (final InvocationTargetException e) {
-            if (e.getTargetException() instanceof IllegalStateException) {
-                throw new ReflectiveActionException("IllegalStateException thrown while executing " + method + " " + e.getTargetException().getMessage(), e.getTargetException());
-            } else {
-                ThrowableExtensions.throwWithinIsisException(e, "Exception executing " + method);
-                return null;
+            final Throwable targetException = e.getTargetException();
+            if (targetException instanceof IllegalStateException) {
+                throw new ReflectiveActionException("IllegalStateException thrown while executing " + method + " " + targetException.getMessage(), targetException);
+            } 
+            if(targetException instanceof RecoverableException) {
+                if (!runtimeContext.getTransactionState().canCommit()) {
+                    // something severe has happened to the underlying transaction;
+                    // so escalate this exception to be non-recoverable
+                    final Throwable targetExceptionCause = targetException.getCause();
+                    Throwable nonRecoverableCause = targetExceptionCause != null? targetExceptionCause: targetException;
+                    throw new NonRecoverableException(nonRecoverableCause);
+                }
             }
+
+            ThrowableExtensions.throwWithinIsisException(e, "Exception executing " + method);
+            return null;
         } catch (final IllegalAccessException e) {
             throw new ReflectiveActionException("Illegal access of " + method, e);
         }

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/properties/event/PostsPropertyChangedEventFacetAnnotation.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/properties/event/PostsPropertyChangedEventFacetAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/properties/event/PostsPropertyChangedEventFacetAnnotation.java
index 57d5ff3..406ac36 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/properties/event/PostsPropertyChangedEventFacetAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/properties/event/PostsPropertyChangedEventFacetAnnotation.java
@@ -25,7 +25,8 @@ import java.util.List;
 import com.google.common.base.Objects;
 import com.google.common.collect.Lists;
 
-import org.apache.isis.applib.ApplicationException;
+import org.apache.isis.applib.FatalException;
+import org.apache.isis.applib.RecoverableException;
 import org.apache.isis.applib.services.eventbus.EventBusService;
 import org.apache.isis.applib.services.eventbus.PropertyChangedEvent;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
@@ -110,7 +111,7 @@ public class PostsPropertyChangedEventFacetAnnotation extends PostsPropertyChang
             
             eventBusService.post(event);
         } catch (Exception e) {
-            throw new ApplicationException(e);
+            throw new FatalException(e);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/internal/RuntimeContextFromSession.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/internal/RuntimeContextFromSession.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/internal/RuntimeContextFromSession.java
index 5bc58b7..1f13e00 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/internal/RuntimeContextFromSession.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/internal/RuntimeContextFromSession.java
@@ -21,7 +21,7 @@ package org.apache.isis.core.runtime.persistence.internal;
 
 import java.util.List;
 
-import org.apache.isis.applib.ApplicationException;
+import org.apache.isis.applib.RecoverableException;
 import org.apache.isis.applib.profiles.Localization;
 import org.apache.isis.applib.query.Query;
 import org.apache.isis.applib.services.bookmark.Bookmark;
@@ -60,6 +60,8 @@ import org.apache.isis.core.runtime.persistence.container.DomainObjectContainerR
 import org.apache.isis.core.runtime.system.context.IsisContext;
 import org.apache.isis.core.runtime.system.persistence.PersistenceSession;
 import org.apache.isis.core.runtime.system.session.IsisSession;
+import org.apache.isis.core.runtime.system.transaction.IsisTransaction;
+import org.apache.isis.core.runtime.system.transaction.IsisTransaction.State;
 import org.apache.isis.core.runtime.system.transaction.IsisTransactionManager;
 import org.apache.isis.core.runtime.system.transaction.MessageBroker;
 import org.apache.isis.core.runtime.system.transaction.UpdateNotifier;
@@ -238,7 +240,7 @@ public class RuntimeContextFromSession extends RuntimeContextAbstract {
 
             @Override
             public void raiseError(final String message) {
-                throw new ApplicationException(message);
+                throw new RecoverableException(message);
             }
 
             @Override
@@ -396,6 +398,14 @@ public class RuntimeContextFromSession extends RuntimeContextAbstract {
         return IsisContext.getMessageBroker();
     }
 
-
+    @Override
+    public TransactionState getTransactionState() {
+        final IsisTransaction transaction = getTransactionManager().getTransaction();
+        if(transaction == null) {
+            return TransactionState.NONE;
+        }
+        IsisTransaction.State state = transaction.getState();
+        return state.getRuntimeContextState();
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundActionExecution.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundActionExecution.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundActionExecution.java
index 9fda66f..3bf77dd 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundActionExecution.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundActionExecution.java
@@ -33,6 +33,7 @@ import org.apache.isis.core.metamodel.adapter.oid.RootOidDefault;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.Contributed;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.progmodel.facets.actions.invoke.ReifiableActionUtil;
 import org.apache.isis.core.runtime.services.memento.MementoServiceDefault;
 import org.apache.isis.core.runtime.sessiontemplate.AbstractIsisSessionTemplate;
 
@@ -93,9 +94,9 @@ public abstract class BackgroundActionExecution extends AbstractIsisSessionTempl
             }
             
             final ObjectAdapter[] argAdapters = argAdaptersFor(aim);
-            ObjectAdapter resultAdapter = objectAction.execute(targetAdapter, argAdapters);
+            final ObjectAdapter resultAdapter = objectAction.execute(targetAdapter, argAdapters);
             if(resultAdapter != null) {
-                Bookmark resultBookmark = bookmarkService.bookmarkFor(resultAdapter.getObject());
+                Bookmark resultBookmark = ReifiableActionUtil.bookmarkFor(resultAdapter);
                 backgroundAction.setResult(resultBookmark);
             }
 

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
index 8118e4a..2862f2b 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
@@ -70,6 +70,7 @@ import org.apache.isis.core.metamodel.facets.actions.publish.PublishedActionFace
 import org.apache.isis.core.metamodel.facets.object.audit.AuditableFacet;
 import org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet;
 import org.apache.isis.core.metamodel.facets.object.publish.PublishedObjectFacet;
+import org.apache.isis.core.metamodel.runtimecontext.RuntimeContext.TransactionState;
 import org.apache.isis.core.metamodel.spec.feature.Contributed;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.isis.core.runtime.persistence.ObjectPersistenceException;
@@ -95,6 +96,7 @@ import org.apache.isis.core.runtime.system.context.IsisContext;
  */
 public class IsisTransaction implements TransactionScopedComponent {
 
+
     public static enum State {
         /**
          * Started, still in progress.
@@ -104,7 +106,7 @@ public class IsisTransaction implements TransactionScopedComponent {
          * {@link IsisTransaction#commit() commit} or
          * {@link IsisTransaction#abort() abort}.
          */
-        IN_PROGRESS,
+        IN_PROGRESS(TransactionState.IN_PROGRESS),
         /**
          * Started, but has hit an exception.
          * 
@@ -117,7 +119,7 @@ public class IsisTransaction implements TransactionScopedComponent {
          * <p>
          * Similar to <tt>setRollbackOnly</tt> in EJBs.
          */
-        MUST_ABORT,
+        MUST_ABORT(TransactionState.MUST_ABORT),
         /**
          * Completed, having successfully committed.
          * 
@@ -127,7 +129,7 @@ public class IsisTransaction implements TransactionScopedComponent {
          * {@link IsisTransaction#commit() commit} (will throw
          * {@link IllegalStateException}).
          */
-        COMMITTED,
+        COMMITTED(TransactionState.COMMITTED),
         /**
          * Completed, having aborted.
          * 
@@ -137,9 +139,14 @@ public class IsisTransaction implements TransactionScopedComponent {
          * {@link IsisTransaction#abort() abort} (will throw
          * {@link IllegalStateException}).
          */
-        ABORTED;
+        ABORTED(TransactionState.ABORTED);
+
+        public final TransactionState transactionState;
+        
+        private State(TransactionState transactionState){
+            this.transactionState = transactionState;
+        }
 
-        private State(){}
 
         /**
          * Whether it is valid to {@link IsisTransaction#flush() flush} this
@@ -176,6 +183,10 @@ public class IsisTransaction implements TransactionScopedComponent {
         public boolean mustAbort() {
             return this == MUST_ABORT;
         }
+
+        public TransactionState getRuntimeContextState() {
+            return transactionState;
+        }
     }
 
 

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/tck/tck-dom/src/main/java/org/apache/isis/core/tck/dom/refs/ParentEntity.java
----------------------------------------------------------------------
diff --git a/core/tck/tck-dom/src/main/java/org/apache/isis/core/tck/dom/refs/ParentEntity.java b/core/tck/tck-dom/src/main/java/org/apache/isis/core/tck/dom/refs/ParentEntity.java
index e97dd55..2b6c43c 100644
--- a/core/tck/tck-dom/src/main/java/org/apache/isis/core/tck/dom/refs/ParentEntity.java
+++ b/core/tck/tck-dom/src/main/java/org/apache/isis/core/tck/dom/refs/ParentEntity.java
@@ -93,7 +93,7 @@ public class ParentEntity extends BaseEntity {
     // {{ NotPersisted
     @NotPersisted
     public List<SimpleEntity> getNotPersisted() {
-        throw new org.apache.isis.applib.ApplicationException("unexpected call");
+        throw new org.apache.isis.applib.NonRecoverableException("unexpected call");
     }
     // }}
 

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/tck/tck-dom/src/main/java/org/apache/isis/core/tck/dom/refs/ReferencingEntity.java
----------------------------------------------------------------------
diff --git a/core/tck/tck-dom/src/main/java/org/apache/isis/core/tck/dom/refs/ReferencingEntity.java b/core/tck/tck-dom/src/main/java/org/apache/isis/core/tck/dom/refs/ReferencingEntity.java
index b076705..645e03e 100644
--- a/core/tck/tck-dom/src/main/java/org/apache/isis/core/tck/dom/refs/ReferencingEntity.java
+++ b/core/tck/tck-dom/src/main/java/org/apache/isis/core/tck/dom/refs/ReferencingEntity.java
@@ -87,7 +87,7 @@ public class ReferencingEntity extends BaseEntity {
     // {{ NotPersisted
     @NotPersisted
     public SimpleEntity getNotPersisted() {
-        throw new org.apache.isis.applib.ApplicationException("unexpected call");
+        throw new org.apache.isis.applib.NonRecoverableException("unexpected call");
     }
     // }}
 

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/core/tck/tck-dom/src/main/java/org/apache/isis/core/tck/dom/refs/SimpleEntity.java
----------------------------------------------------------------------
diff --git a/core/tck/tck-dom/src/main/java/org/apache/isis/core/tck/dom/refs/SimpleEntity.java b/core/tck/tck-dom/src/main/java/org/apache/isis/core/tck/dom/refs/SimpleEntity.java
index 1ec2c14..89c3fc4 100644
--- a/core/tck/tck-dom/src/main/java/org/apache/isis/core/tck/dom/refs/SimpleEntity.java
+++ b/core/tck/tck-dom/src/main/java/org/apache/isis/core/tck/dom/refs/SimpleEntity.java
@@ -96,7 +96,7 @@ public class SimpleEntity extends BaseEntity {
     // {{ NotPersisted: int  (nb: throws exception if called)
     @NotPersisted
     public int getNotPersisted() {
-        throw new org.apache.isis.applib.ApplicationException("unexpected call");
+        throw new org.apache.isis.applib.NonRecoverableException("unexpected call");
     }
     // }}
 

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.java b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.java
index df0516f..4d7ac81 100644
--- a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.java
+++ b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.java
@@ -40,6 +40,8 @@ import com.google.common.collect.Ordering;
 import org.joda.time.LocalDate;
 
 import org.apache.isis.applib.ApplicationException;
+import org.apache.isis.applib.NonRecoverableException;
+import org.apache.isis.applib.RecoverableException;
 import org.apache.isis.applib.DomainObjectContainer;
 import org.apache.isis.applib.annotation.ActionSemantics;
 import org.apache.isis.applib.annotation.ActionSemantics.Of;
@@ -277,7 +279,7 @@ public class ToDoItem implements Comparable<ToDoItem> /*, Locatable*/ { // GMAP3
         this.subcategory = subcategory;
     }
 
-    
+
     // //////////////////////////////////////
     // OwnedBy (property)
     // //////////////////////////////////////
@@ -650,25 +652,30 @@ public class ToDoItem implements Comparable<ToDoItem> /*, Locatable*/ { // GMAP3
     // //////////////////////////////////////
     
     static enum DemoExceptionType {
-        APPLICATION_EXCEPTION(ApplicationException.class),
-        RUNTIME_EXCEPTION(RuntimeException.class);
-        private final Class<? extends Exception> type;
-        private DemoExceptionType(Class<? extends Exception> type) {
-            this.type = type;
-        }
-        @Override
-        public String toString() {
-            return type.getName();
-        }
+        RecoverableException,
+        RecoverableExceptionAutoEscalated,
+        NonRecoverableException;
     }
     
     @Prototype
     @ActionSemantics(Of.SAFE)
     public void demoException(final @Named("Type") DemoExceptionType type) {
-        if(type == DemoExceptionType.RUNTIME_EXCEPTION)
-            throw new RuntimeException("Demo throwing runtime exception");
-        else
-            throw new ApplicationException("Demo throwing application exception");
+        switch(type) {
+        case NonRecoverableException:
+            throw new NonRecoverableException("Demo throwing " + type.name());
+        case RecoverableException:
+            throw new RecoverableException("Demo throwing " + type.name());
+        case RecoverableExceptionAutoEscalated:
+            try {
+                // this will trigger an exception (because subcategory cannot be null), causing the xactn to be aborted
+                setSubcategory(null);
+                container.flush();
+            } catch(Exception e) {
+                // it's a programming mistake to throw only a recoverable exception here, because of the xactn's state.
+                // the framework should instead auto-escalate this to a non-recoverable exception
+                throw new RecoverableException("Demo throwing " + type.name(), e);
+            }
+        }
     }
 
     // //////////////////////////////////////

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.layout.json
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.layout.json b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.layout.json
index bc1acb8..da28f6a 100644
--- a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.layout.json
+++ b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.layout.json
@@ -85,9 +85,9 @@
     {
         span: 6,
         memberGroups: {
-            Detail: {
+            Priority: {
                 members: {
-                    priority: {
+                     relativePriority: {
                         actions: {
                             previous: {},
                             next: {}
@@ -95,7 +95,11 @@
                     },
                     dueBy: {
                         cssClass: { value: "x-key" }
-                    },
+                    }
+                }
+            },
+            Other: {
+                members: {
                     cost: {
                         actions: {
                             updateCost:{

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemContributions.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemContributions.java b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemContributions.java
index 4762857..a264349 100644
--- a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemContributions.java
+++ b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemContributions.java
@@ -57,7 +57,7 @@ public class ToDoItemContributions extends AbstractFactoryAndRepository {
     @NotContributed(As.ACTION)
     @Hidden(where=Where.ALL_TABLES)
     @Disabled(reason="Relative priority, derived from due date")
-    public Integer priority(final ToDoItem toDoItem) {
+    public Integer relativePriority(final ToDoItem toDoItem) {
         if(toDoItem.isComplete()) {
             return null;
         }
@@ -106,7 +106,7 @@ public class ToDoItemContributions extends AbstractFactoryAndRepository {
     @ActionSemantics(Of.SAFE)
     @NotContributed(As.ASSOCIATION)
     public ToDoItem next(final ToDoItem item) {
-        final Integer priority = priority(item);
+        final Integer priority = relativePriority(item);
         if(priority == null) {
             return item;
         }
@@ -119,7 +119,7 @@ public class ToDoItemContributions extends AbstractFactoryAndRepository {
     @ActionSemantics(Of.SAFE)
     @NotContributed(As.ASSOCIATION)
     public ToDoItem previous(final ToDoItem item) {
-        final Integer priority = priority(item);
+        final Integer priority = relativePriority(item);
         if(priority == null) {
             return item;
         }

http://git-wip-us.apache.org/repos/asf/isis/blob/9032a866/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/tests/props/ToDoItemContributionsTest_priority.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/tests/props/ToDoItemContributionsTest_priority.java b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/tests/props/ToDoItemContributionsTest_priority.java
index 8fba902..cce36a2 100644
--- a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/tests/props/ToDoItemContributionsTest_priority.java
+++ b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/tests/props/ToDoItemContributionsTest_priority.java
@@ -62,7 +62,7 @@ public class ToDoItemContributionsTest_priority extends ToDoIntegTest {
     }
 
     private void assertPriority(final int n, final int priority) {
-        assertThat(toDoItemContributions.priority(notYetComplete.get(n)), is(Integer.valueOf(priority)));
+        assertThat(toDoItemContributions.relativePriority(notYetComplete.get(n)), is(Integer.valueOf(priority)));
     }
     
 }
\ No newline at end of file