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 2013/02/28 18:10:59 UTC

git commit: ISIS-351: exceptions on edit page now rendered via its feedback

Updated Branches:
  refs/heads/master 1e84d8507 -> a30c737d1


ISIS-351: exceptions on edit page now rendered via its feedback

... similar to how we handle actions with args.


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

Branch: refs/heads/master
Commit: a30c737d180356427df5c92ddbe56f7178ed9370
Parents: 1e84d85
Author: Dan Haywood <da...@apache.org>
Authored: Thu Feb 28 17:10:34 2013 +0000
Committer: Dan Haywood <da...@apache.org>
Committed: Thu Feb 28 17:10:34 2013 +0000

----------------------------------------------------------------------
 .../jdo/datanucleus/DataNucleusObjectStore.java    |   42 ++---
 .../integration/wicket/WebRequestCycleForIsis.java |   15 ++-
 .../wicket/ui/components/actions/ActionPanel.java  |   67 ++++----
 .../entity/properties/EntityPropertiesForm.java    |  131 +++++++++++----
 .../system/transaction/IsisTransaction.java        |   16 ++-
 5 files changed, 171 insertions(+), 100 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/a30c737d/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/DataNucleusObjectStore.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/DataNucleusObjectStore.java b/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/DataNucleusObjectStore.java
index f8016ea..490cdb4 100644
--- a/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/DataNucleusObjectStore.java
+++ b/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/DataNucleusObjectStore.java
@@ -330,38 +330,30 @@ public class DataNucleusObjectStore implements ObjectStoreSpi {
         ensureOpened();
         ensureInTransaction();
 
-        if (LOG.isDebugEnabled()) {
-            LOG.debug("execute " + commands.size() + " commands");
-        }
+        // no longer check if there are no commands; it could be that
+        // DataNucleus has some dirty objects anyway that don't have
+        // commands wrapped around them...
 
-        if (commands.size() <= 0) {
-            if (LOG.isDebugEnabled()) {
-                LOG.debug("no commands");
-            }
-            return;
-        }
+//        if (LOG.isDebugEnabled()) {
+//            LOG.debug("execute " + commands.size() + " commands");
+//        }
+//
+//        if (commands.size() <= 0) {
+//            if (LOG.isDebugEnabled()) {
+//                LOG.debug("no commands");
+//            }
+//            return;
+//        }
 
         executeCommands(commands);
     }
 
     private void executeCommands(final List<PersistenceCommand> commands) {
         
-        // there's no need to do any exception handling here, because we are called
-        // from IsisTransaction.flush() that will catch exceptions and set its abortCause
-        // if need be.
-        
-//        try {
-            for (final PersistenceCommand command : commands) {
-                command.execute(null);
-            }
-            getPersistenceManager().flush();
-//        } catch (final RuntimeException e) {
-//            LOG.warn("Failure during execution - aborting transaction and rethrowing", e);
-//            
-//            abortTransaction();
-//            
-//            throw e;
-//        }
+        for (final PersistenceCommand command : commands) {
+            command.execute(null);
+        }
+        getPersistenceManager().flush();
     }
 
     // ///////////////////////////////////////////////////////////////////////

http://git-wip-us.apache.org/repos/asf/isis/blob/a30c737d/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/wicket/WebRequestCycleForIsis.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/wicket/WebRequestCycleForIsis.java b/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/wicket/WebRequestCycleForIsis.java
index f5d7b0e..8d547b7 100644
--- a/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/wicket/WebRequestCycleForIsis.java
+++ b/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/wicket/WebRequestCycleForIsis.java
@@ -49,7 +49,7 @@ import org.apache.wicket.request.cycle.RequestCycle;
  * automatically opening a {@link IsisSession} at the beginning of the request
  * and committing the transaction and closing the session at the end.
  */
-public class WebRequestCycleForIsis /*extends WebRequestCycle*/ extends AbstractRequestCycleListener {
+public class WebRequestCycleForIsis extends AbstractRequestCycleListener {
 
     private static final Logger LOG = Logger.getLogger(WebRequestCycleForIsis.class);
 
@@ -91,6 +91,13 @@ public class WebRequestCycleForIsis /*extends WebRequestCycle*/ extends Abstract
                 // an abort will cause the exception to be thrown.
                 getTransactionManager().endTransaction();
             } catch(Exception ex) {
+                if(handler instanceof RenderPageRequestHandler) {
+                    RenderPageRequestHandler requestHandler = (RenderPageRequestHandler) handler;
+                    if(requestHandler.getPage() instanceof ErrorPage) {
+                        // do nothing; 
+                        return;
+                    }
+                }
                 throw new RestartResponseException(errorPageProviderFor(ex), RedirectPolicy.ALWAYS_REDIRECT);
             }
         }
@@ -118,9 +125,11 @@ public class WebRequestCycleForIsis /*extends WebRequestCycle*/ extends Abstract
         // previously we had a handler in here.  However, it seems to be sufficient to just
         // use the exception handling in the onRequestHandlerExecuted(...) callback
         // which fires before the onEndRequest(...) callback.
+        //return super.onException(cycle, ex);
         
-        //return new RenderPageRequestHandler(errorPageProviderFor(ex));
-        return super.onException(cycle, ex);
+        // hmm, maybe not.  making in edit page do a flush seems to belie that.
+        
+        return new RenderPageRequestHandler(errorPageProviderFor(ex), RedirectPolicy.ALWAYS_REDIRECT);
     }
 
     protected PageProvider errorPageProviderFor(Exception ex) {

http://git-wip-us.apache.org/repos/asf/isis/blob/a30c737d/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 d519f00..a397f33 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
@@ -23,12 +23,7 @@ import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 
-import org.apache.wicket.MarkupContainer;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.model.Model;
-
 import org.apache.isis.applib.ApplicationException;
-import org.apache.isis.applib.annotation.ActionSemantics;
 import org.apache.isis.applib.services.exceprecog.ExceptionRecognizer;
 import org.apache.isis.applib.services.exceprecog.ExceptionRecognizerComposite;
 import org.apache.isis.core.commons.authentication.MessageBroker;
@@ -39,7 +34,6 @@ 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;
 import org.apache.isis.core.runtime.system.context.IsisContext;
-import org.apache.isis.core.runtime.system.transaction.IsisTransaction;
 import org.apache.isis.core.runtime.system.transaction.IsisTransactionManager;
 import org.apache.isis.viewer.wicket.model.common.SelectionHandler;
 import org.apache.isis.viewer.wicket.model.isis.PersistenceSessionProvider;
@@ -55,6 +49,10 @@ import org.apache.isis.viewer.wicket.ui.app.registry.ComponentFactoryRegistry;
 import org.apache.isis.viewer.wicket.ui.pages.BookmarkedPagesModelProvider;
 import org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage;
 import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
+import org.apache.wicket.Component;
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.model.Model;
 
 import com.google.common.base.Throwables;
 import com.google.common.collect.Iterables;
@@ -194,31 +192,19 @@ public class ActionPanel extends PanelAbstract<ActionModel> implements ActionExe
 
         } catch (RuntimeException ex) {
 
-            // see if the exception is recognized as being a non-serious error
-            // (nb: similar code in WebRequestCycleForIsis, as a fallback)
-            List<ExceptionRecognizer> exceptionRecognizers = getServicesInjector().lookupServices(ExceptionRecognizer.class);
-            String message = new ExceptionRecognizerComposite(exceptionRecognizers).recognize(ex);
-            if(message != null) {
-                // recognized
-                if(feedbackForm != null) {
-                    feedbackForm.error(message);
-                } else {
-                     // use notification mechanism otherwise
-                     getMessageBroker().setApplicationError(message);
-
-                     // forward on instead to void page
-                     final ResultType resultType = ResultType.determineFor(null);
-                     resultType.addResults(this, null);
+            String message = recognizeException(ex, feedbackForm);
+            
+            if (message != null) {
+                if(feedbackForm == null) {
+                    // forward on instead to void page
+                    // (otherwise, we'll have rendered an action parameters page 
+                    // and so we'll be staying on that page)
+                    final ResultType resultType = ResultType.determineFor(null);
+                    resultType.addResults(this, null);
                 }
                 
-
-                // there's no need to abort the transaction, it will have already been done
-                // (in IsisTransactionManager#executeWithinTransaction(...)).
-                
-                getTransactionManager().getTransaction().clearAbortCause();
-                
                 return false;
-            } 
+            }
             
             // not handled, so propagate
             throw ex;
@@ -226,6 +212,28 @@ public class ActionPanel extends PanelAbstract<ActionModel> implements ActionExe
 
     }
 
+    private String recognizeException(RuntimeException ex, Component feedbackComponent) {
+        // see if the exception is recognized as being a non-serious error
+        // (nb: similar code in WebRequestCycleForIsis, as a fallback)
+        List<ExceptionRecognizer> exceptionRecognizers = getServicesInjector().lookupServices(ExceptionRecognizer.class);
+        String message = new ExceptionRecognizerComposite(exceptionRecognizers).recognize(ex);
+        if(message != null) {
+            // recognized
+            if(feedbackComponent != null) {
+                feedbackComponent.error(message);
+            } else {
+                 // use notification mechanism otherwise
+                 getMessageBroker().setApplicationError(message);
+            }
+
+            // there's no need to abort the transaction, it will have already been done
+            // (in IsisTransactionManager#executeWithinTransaction(...)).
+            getTransactionManager().getTransaction().clearAbortCause();
+
+        }
+        return message;
+    }
+
     /**
      * Executes the action, handling any {@link ApplicationException}s that
      * might be encountered.
@@ -446,9 +454,6 @@ public class ActionPanel extends PanelAbstract<ActionModel> implements ActionExe
         return IsisContext.getTransactionManager();
     }
 
-    /**
-     * Factored out so can be overridden in testing.
-     */
     protected ServicesInjector getServicesInjector() {
         return IsisContext.getPersistenceSession().getServicesInjector();
     }

http://git-wip-us.apache.org/repos/asf/isis/blob/a30c737d/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/properties/EntityPropertiesForm.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/properties/EntityPropertiesForm.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/properties/EntityPropertiesForm.java
index c317438..168d5cc 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/properties/EntityPropertiesForm.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/properties/EntityPropertiesForm.java
@@ -21,35 +21,24 @@ package org.apache.isis.viewer.wicket.ui.components.entity.properties;
 import java.util.List;
 import java.util.Map;
 
-import org.apache.wicket.Component;
-import org.apache.wicket.Session;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.feedback.FeedbackMessage;
-import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.html.form.Button;
-import org.apache.wicket.markup.html.form.Form;
-import org.apache.wicket.markup.html.form.FormComponent;
-import org.apache.wicket.markup.html.form.validation.AbstractFormValidator;
-import org.apache.wicket.markup.html.panel.ComponentFeedbackPanel;
-import org.apache.wicket.markup.html.panel.FeedbackPanel;
-import org.apache.wicket.markup.repeater.RepeatingView;
-import org.apache.wicket.model.Model;
-import org.apache.wicket.util.visit.IVisit;
-import org.apache.wicket.util.visit.IVisitor;
-
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.filter.Filter;
 import org.apache.isis.applib.filter.Filters;
+import org.apache.isis.applib.services.exceprecog.ExceptionRecognizer;
+import org.apache.isis.applib.services.exceprecog.ExceptionRecognizerComposite;
+import org.apache.isis.core.commons.authentication.MessageBroker;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager.ConcurrencyChecking;
 import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException;
+import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAssociationFilters;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 import org.apache.isis.core.progmodel.facets.object.validate.ValidateObjectFacet;
 import org.apache.isis.core.runtime.memento.Memento;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.core.runtime.system.transaction.IsisTransactionManager;
 import org.apache.isis.viewer.wicket.model.mementos.PropertyMemento;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
@@ -61,6 +50,22 @@ import org.apache.isis.viewer.wicket.ui.panels.AjaxButtonWithPreSubmitHook;
 import org.apache.isis.viewer.wicket.ui.panels.ButtonWithPreSubmitHook;
 import org.apache.isis.viewer.wicket.ui.panels.FormAbstract;
 import org.apache.isis.viewer.wicket.ui.util.EvenOrOddCssClassAppenderFactory;
+import org.apache.wicket.Component;
+import org.apache.wicket.Session;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.feedback.FeedbackMessage;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Button;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.FormComponent;
+import org.apache.wicket.markup.html.form.validation.AbstractFormValidator;
+import org.apache.wicket.markup.html.panel.ComponentFeedbackPanel;
+import org.apache.wicket.markup.html.panel.FeedbackPanel;
+import org.apache.wicket.markup.repeater.RepeatingView;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.util.visit.IVisit;
+import org.apache.wicket.util.visit.IVisitor;
 
 class EntityPropertiesForm extends FormAbstract<ObjectAdapter> {
 
@@ -195,28 +200,40 @@ class EntityPropertiesForm extends FormAbstract<ObjectAdapter> {
 
             @Override
             public void onSubmit() {
-                if (!getForm().hasError()) {
-                    final ObjectAdapter object = getEntityModel().getObject();
-                    final Memento snapshotToRollbackToIfInvalid = new Memento(object);
-                    // to perform object-level validation, we must apply the
-                    // changes first
-                    // contrast this with ActionPanel (for validating action
-                    // arguments) where
-                    // we do the validation prior to the execution of the
-                    // action
-                    getEntityModel().apply();
-                    final String invalidReasonIfAny = getEntityModel().getReasonInvalidIfAny();
-                    if (invalidReasonIfAny != null) {
-                        getForm().error(invalidReasonIfAny);
-                        snapshotToRollbackToIfInvalid.recreateObject();
-                        return;
-                    } else {
-                        getEntityModel().resetPropertyModels();
-                        toViewMode(null);
-                    }
-                } else {
+                if (getForm().hasError()) {
                     // stay in edit mode
+                    return;
+                } 
+                
+                final ObjectAdapter object = getEntityModel().getObject();
+                final Memento snapshotToRollbackToIfInvalid = new Memento(object);
+                // to perform object-level validation, we must apply the
+                // changes first
+                // contrast this with ActionPanel (for validating action
+                // arguments) where
+                // we do the validation prior to the execution of the
+                // action
+                getEntityModel().apply();
+                final String invalidReasonIfAny = getEntityModel().getReasonInvalidIfAny();
+                if (invalidReasonIfAny != null) {
+                    getForm().error(invalidReasonIfAny);
+                    snapshotToRollbackToIfInvalid.recreateObject();
+                    return;
+                }
+                
+                try {
+                    EntityPropertiesForm.this.getTransactionManager().flushTransaction();
+                } catch(RuntimeException ex) {
+                    String message = recognizeException(ex, EntityPropertiesForm.this);
+                    if(message == null) {
+                        throw ex;
+                    }
+                    return;
                 }
+
+                getEntityModel().resetPropertyModels();
+
+                toViewMode(null);
             }
 
         };
@@ -263,6 +280,28 @@ class EntityPropertiesForm extends FormAbstract<ObjectAdapter> {
         cancelButton.setOutputMarkupPlaceholderTag(true);
     }
 
+    private String recognizeException(RuntimeException ex, Component feedbackComponent) {
+        // see if the exception is recognized as being a non-serious error
+        // (nb: similar code in WebRequestCycleForIsis, as a fallback)
+        List<ExceptionRecognizer> exceptionRecognizers = getServicesInjector().lookupServices(ExceptionRecognizer.class);
+        String message = new ExceptionRecognizerComposite(exceptionRecognizers).recognize(ex);
+        if(message != null) {
+            // recognized
+            if(feedbackComponent != null) {
+                feedbackComponent.error(message);
+            } else {
+                 // use notification mechanism otherwise
+                 getMessageBroker().setApplicationError(message);
+            }
+
+            // there's no need to abort the transaction, it will have already been done
+            // (in IsisTransactionManager#executeWithinTransaction(...)).
+            getTransactionManager().getTransaction().clearAbortCause();
+
+        }
+        return message;
+    }
+
     private void requestRepaintPanel(final AjaxRequestTarget target) {
         if (target != null) {
             target.add(owningPanel);
@@ -341,6 +380,7 @@ class EntityPropertiesForm extends FormAbstract<ObjectAdapter> {
 
     private void addFeedbackGui() {
         final FeedbackPanel feedback = addOrReplaceFeedback();
+        feedback.setEscapeModelStrings(false);
 
         final ObjectAdapter adapter = getEntityModel().getObject();
         if (adapter == null) {
@@ -354,4 +394,21 @@ class EntityPropertiesForm extends FormAbstract<ObjectAdapter> {
         addOrReplace(feedback);
         return feedback;
     }
+    
+    ///////////////////////////////////////////////////////
+    // Dependencies (from context)
+    ///////////////////////////////////////////////////////
+    
+    protected IsisTransactionManager getTransactionManager() {
+        return IsisContext.getTransactionManager();
+    }
+
+    protected ServicesInjector getServicesInjector() {
+        return IsisContext.getPersistenceSession().getServicesInjector();
+    }
+
+    protected MessageBroker getMessageBroker() {
+        return getAuthenticationSession().getMessageBroker();
+    }
+
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/a30c737d/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 d20f791..5f89c1c 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
@@ -303,10 +303,18 @@ public class IsisTransaction implements TransactionScopedComponent {
     // ////////////////////////////////////////////////////////////////
 
     public synchronized final void flush() {
-        if(commands.isEmpty()) {
-            // nothing to do
-            return;
-        }
+        // have removed the guard below because not every objectstore necessarily 
+        // wraps up every change inside a command.
+        
+        // for example, the JDO object store just lets DataNucleus do the change tracking
+        // itself
+        
+//        if(commands.isEmpty()) {
+//            // nothing to do
+//            return;
+//        }
+        
+        
         ensureThatState(getState().canFlush(), is(true), "state is: " + getState());
         if (LOG.isDebugEnabled()) {
             LOG.debug("flush transaction " + this);