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

doebele pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/empire-db.git


The following commit(s) were added to refs/heads/master by this push:
     new 36984b5f EMPIREDB-431 Page: improved action exception handling
36984b5f is described below

commit 36984b5f84259c6e94da1a8e0d52c47cc39637d3
Author: Rainer Döbele <[email protected]>
AuthorDate: Sun Oct 20 01:10:10 2024 +0200

    EMPIREDB-431
    Page: improved action exception handling
---
 .../apache/empire/jakarta/app/WebApplication.java  |  37 ++++++-
 .../java/org/apache/empire/jakarta/pages/Page.java | 121 +++++++++++---------
 .../org/apache/empire/jsf2/app/WebApplication.java |  37 ++++++-
 .../java/org/apache/empire/jsf2/pages/Page.java    | 123 ++++++++++++---------
 4 files changed, 209 insertions(+), 109 deletions(-)

diff --git 
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/WebApplication.java
 
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/WebApplication.java
index a1d77833..b95aafc8 100644
--- 
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/WebApplication.java
+++ 
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/WebApplication.java
@@ -42,6 +42,7 @@ import 
org.apache.empire.jakarta.controls.TextAreaInputControl;
 import org.apache.empire.jakarta.controls.TextInputControl;
 import org.apache.empire.jakarta.impl.FacesImplementation;
 import org.apache.empire.jakarta.impl.ResourceTextResolver;
+import org.apache.empire.jakarta.pages.Page;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -54,6 +55,8 @@ import jakarta.faces.component.UIComponent;
 import jakarta.faces.component.UIViewRoot;
 import jakarta.faces.context.ExternalContext;
 import jakarta.faces.context.FacesContext;
+import jakarta.faces.event.ExceptionQueuedEvent;
+import jakarta.faces.event.ExceptionQueuedEventContext;
 import jakarta.servlet.ServletContext;
 import jakarta.servlet.http.HttpServletRequest;
 
@@ -271,6 +274,38 @@ public abstract class WebApplication
         }
         return false;
     }
+    
+    /**
+     * Handles an exeption, that could not be handled on the page level
+     * The application should redirect to the error page.
+     * @param context the faces context
+     * @param page the page from which the exception originated
+     * @param the Exception
+     */
+    public void handleException(FacesContext context, Page source, Throwable e)
+    {
+        // log source
+        String origin = (source!=null ? 
source.getPageDefinition().getPageBeanName() : "[Unknown]");
+        log.error("Fatal error of type {} from \"{}\": {}: {}", 
e.getClass().getName(), origin, e.getMessage());
+
+        // For page errors, give the ExceptionHandler a chance to handle
+        if (source!=null)
+        {
+            // Queue event 
+            ExceptionQueuedEventContext event = new 
ExceptionQueuedEventContext(context, e, null, context.getCurrentPhaseId());
+            event.getAttributes().put 
(ExceptionQueuedEventContext.IN_BEFORE_PHASE_KEY, Boolean.TRUE);
+            context.getApplication().publishEvent (context, 
ExceptionQueuedEvent.class, event);
+            
+            // Handle Exception
+            context.getExceptionHandler().handle(); 
+            if (context.getResponseComplete())
+                return;
+        }
+            
+        // If all has failed, redirect to ContextPath (root)
+        String contextPath = 
context.getExternalContext().getRequestContextPath();
+        FacesUtils.redirectDirectly(context, contextPath);
+    }
 
     /**
      * Logs an exception as Error with additional information 
@@ -305,7 +340,7 @@ public abstract class WebApplication
     }
     
     /**
-     * Override this to provide additonal session info for an exception
+     * Override this to provide additional session info for an exception
      * @param sessionMap
      * @return the additional session info
      */
diff --git 
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/pages/Page.java
 
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/pages/Page.java
index be275df8..f2e087bc 100644
--- 
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/pages/Page.java
+++ 
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/pages/Page.java
@@ -54,7 +54,7 @@ public abstract class Page // *Deprecated* implements 
Serializable
     private static final Logger log              = 
LoggerFactory.getLogger(Page.class);
 
     private String              action           = null;
-    private boolean             initialized      = false;
+    private short               initialized      = -1;   // -1=not 
initialized; 0=pending; 1=initialized
     private PageDefinition      pageDefinition   = null;
     private List<PageElement>   pageElements     = null;
 
@@ -87,7 +87,7 @@ public abstract class Page // *Deprecated* implements 
Serializable
 
     public boolean isInitialized()
     {
-        return initialized;
+        return (initialized>0);
     }
 
     public String getAction()
@@ -103,7 +103,7 @@ public abstract class Page // *Deprecated* implements 
Serializable
 
     public void setAction(String actionParam)
     {
-        if (!initialized)
+        if (initialized>0)
             Page.log.debug("Setting PageBean action {} for bean {}.", action, 
getPageName());
         else
             Page.log.trace("Re-setting PageBeanAction {} for bean {}.", 
action, getPageName());
@@ -135,12 +135,11 @@ public abstract class Page // *Deprecated* implements 
Serializable
 
     public void preRenderPage(FacesContext context)
     {
-        if (this.initialized)
-        {
-            // PageBean.log.error("PageBean {} is already initialized.", 
name());
-            try
-            {
-                Page.log.debug("PageBean {} is already initialized. Calling 
doRefresh().", getPageName());
+        // Check initialized
+        if (initialized>0)
+        {   try
+            {   // refresh
+                log.debug("PageBean {} is already initialized. Calling 
doRefresh().", getPageName());
                 doRefresh();
             }
             catch (Exception e)
@@ -150,6 +149,15 @@ public abstract class Page // *Deprecated* implements 
Serializable
             return; // already Initialized
         }
 
+        // Check pending
+        if (initialized==0)
+        {   // Initialization pending
+            Exception e = new InvalidOperationException("Page Initialization 
pending.");
+            WebApplication.getInstance().handleException(context, this, e);
+        }
+        // Initialization pending
+        initialized=0;
+        
         // Check access
         try
         {
@@ -171,9 +179,6 @@ public abstract class Page // *Deprecated* implements 
Serializable
             return;
         }
 
-        // Initialize
-        this.initialized = true;
-
         // String value of "null"?
         if (this.action!=null && "null".equals(this.action))
         {   log.warn("Invalid action name 'null' for {}", 
getClass().getName());
@@ -181,52 +186,63 @@ public abstract class Page // *Deprecated* implements 
Serializable
         }    
         
         // Execute Action
-        if (this.action != null)
-        {   try
-            {   // Process action
-                log.info("Processing action {} on {}.", 
String.valueOf(action), getPageName());
-                Method method = getClass().getMethod(action);
-                Object result = method.invoke(this);
-                if (result != null)
-                {
-                    String outcome = result.toString();
-                    // Retrieve the NavigationHandler instance..
-                    NavigationHandler navHandler = 
context.getApplication().getNavigationHandler();
-                    // Invoke nav handling..
-                    navHandler.handleNavigation(context, action, outcome);
-                    // Trigger a switch to Render Response if needed
-                    context.renderResponse();
-                    return;
-                }
-                restoreSessionMessage();
-            }
-            catch (NoSuchMethodException nsme)
-            {
-                logAndHandleActionException(action, nsme);
-            }
-            catch (Exception e)
-            {
-                logAndHandleActionException(action, e.getCause());
-            }
-            finally
-            {
+        if (this.action!=null && this.action.length()>0)
+        {   // process action
+            try
+            {   log.debug("Processing action {} on page {}.", action, 
getPageName());
+                processAction(action, context);
+            } finally {
                 // Clear action
-                this.action = null; // Page.INVALID_ACTION;
+                this.action = null;
             }
         }
         else
         {   // call default Action
             try
-            {
-                Page.log.debug("Initializing PageBean {}. Calling doInit()", 
getPageName());
+            {   log.debug("Initializing page {} using doInit()", 
getPageName());
                 doInit();
-                restoreSessionMessage();
+                // if not redirected, restore SessionMessage
+                if (!context.getResponseComplete())
+                    restoreSessionMessage();
             }
             catch (Exception e)
             {
                 logAndHandleActionException("doInit", e);
             }
         }
+        
+        // Initialized unless redirected
+        this.initialized = (context.getResponseComplete() ? (short)-1 : 1);
+    }
+    
+    protected void processAction(String action, FacesContext context)
+    {
+        try
+        {   // Process action
+            Method method = getClass().getMethod(action);
+            Object result = method.invoke(this);
+            if (result != null)
+            {
+                String outcome = result.toString();
+                // Retrieve the NavigationHandler instance..
+                NavigationHandler navHandler = 
context.getApplication().getNavigationHandler();
+                // Invoke nav handling..
+                navHandler.handleNavigation(context, action, outcome);
+                // Trigger a switch to Render Response if needed
+                context.renderResponse();
+                return;
+            }
+            // OK, not redirected
+            restoreSessionMessage();
+        }
+        catch (NoSuchMethodException e)
+        {
+            logAndHandleActionException(action, e);
+        }
+        catch (Exception e)
+        {
+            logAndHandleActionException(action, e.getCause());
+        }
     }
     
     public boolean isHasMessages()
@@ -255,15 +271,14 @@ public abstract class Page // *Deprecated* implements 
Serializable
 
     protected void logAndHandleActionException(String action, Throwable e)
     {
+        // log
         String msg = "Failed to perform action " + action + " on " + 
getPageName();
-        // Message
-        Page.log.error(msg, e);
+        log.error(msg, e);
+        // handle
         if (!handleActionError(action, e))
-        {   // Not handled. Throw again
-            if (e instanceof EmpireException)
-                throw ((EmpireException)e);
-            else
-                throw new InternalException(e);
+        {   // Not handled: Forward to Application
+            FacesContext context = FacesContext.getCurrentInstance();
+            WebApplication.getInstance().handleException(context, this, e);
         }    
     }
     
@@ -290,7 +305,7 @@ public abstract class Page // *Deprecated* implements 
Serializable
         // Set Faces Message
         String msg = extractErrorMessage(e);
         String detail = extractErrorMessageDetail(action, e, 1);
-        log.error(msg + "\r\n" + detail);
+        // log.error(msg + "\r\n" + detail);
         FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_ERROR, 
msg, detail);
         setSessionMessage(facesMsg);
         // Return to parent page
diff --git 
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/app/WebApplication.java 
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/app/WebApplication.java
index 943a2e14..8c4dde9c 100644
--- 
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/app/WebApplication.java
+++ 
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/app/WebApplication.java
@@ -35,6 +35,8 @@ import javax.faces.component.UIComponent;
 import javax.faces.component.UIViewRoot;
 import javax.faces.context.ExternalContext;
 import javax.faces.context.FacesContext;
+import javax.faces.event.ExceptionQueuedEvent;
+import javax.faces.event.ExceptionQueuedEventContext;
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.sql.DataSource;
@@ -53,6 +55,7 @@ import org.apache.empire.jsf2.controls.TextAreaInputControl;
 import org.apache.empire.jsf2.controls.TextInputControl;
 import org.apache.empire.jsf2.impl.FacesImplementation;
 import org.apache.empire.jsf2.impl.ResourceTextResolver;
+import org.apache.empire.jsf2.pages.Page;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -270,6 +273,38 @@ public abstract class WebApplication
         }
         return false;
     }
+    
+    /**
+     * Handles an exeption, that could not be handled on the page level
+     * The application should redirect to the error page.
+     * @param context the faces context
+     * @param page the page from which the exception originated
+     * @param the Exception
+     */
+    public void handleException(FacesContext context, Page source, Throwable e)
+    {
+        // log source
+        String origin = (source!=null ? 
source.getPageDefinition().getPageBeanName() : "[Unknown]");
+        log.error("Fatal error of type {} from \"{}\": {}: {}", 
e.getClass().getName(), origin, e.getMessage());
+
+        // For page errors, give the ExceptionHandler a chance to handle
+        if (source!=null)
+        {
+            // Queue event 
+            ExceptionQueuedEventContext event = new 
ExceptionQueuedEventContext(context, e, null, context.getCurrentPhaseId());
+            event.getAttributes().put 
(ExceptionQueuedEventContext.IN_BEFORE_PHASE_KEY, Boolean.TRUE);
+            context.getApplication().publishEvent (context, 
ExceptionQueuedEvent.class, event);
+            
+            // Handle Exception
+            context.getExceptionHandler().handle(); 
+            if (context.getResponseComplete())
+                return;
+        }
+            
+        // If all has failed, redirect to ContextPath (root)
+        String contextPath = 
context.getExternalContext().getRequestContextPath();
+        FacesUtils.redirectDirectly(context, contextPath);
+    }
 
     /**
      * Logs an exception as Error with additional information 
@@ -304,7 +339,7 @@ public abstract class WebApplication
     }
     
     /**
-     * Override this to provide additonal session info for an exception
+     * Override this to provide additional session info for an exception
      * @param sessionMap
      * @return the additional session info
      */
diff --git 
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/pages/Page.java 
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/pages/Page.java
index 9b09bef7..c2a62ee8 100644
--- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/pages/Page.java
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/pages/Page.java
@@ -35,8 +35,8 @@ import org.apache.empire.commons.StringUtils;
 import org.apache.empire.db.DBRowSet;
 import org.apache.empire.exceptions.EmpireException;
 import org.apache.empire.exceptions.InternalException;
-import org.apache.empire.exceptions.ItemNotFoundException;
 import org.apache.empire.exceptions.InvalidOperationException;
+import org.apache.empire.exceptions.ItemNotFoundException;
 import org.apache.empire.jsf2.app.FacesUtils;
 import org.apache.empire.jsf2.app.TextResolver;
 import org.apache.empire.jsf2.app.WebApplication;
@@ -54,7 +54,7 @@ public abstract class Page // *Deprecated* implements 
Serializable
     private static final Logger log              = 
LoggerFactory.getLogger(Page.class);
 
     private String              action           = null;
-    private boolean             initialized      = false;
+    private short               initialized      = -1;   // -1=not 
initialized; 0=pending; 1=initialized
     private PageDefinition      pageDefinition   = null;
     private List<PageElement>   pageElements     = null;
 
@@ -87,7 +87,7 @@ public abstract class Page // *Deprecated* implements 
Serializable
 
     public boolean isInitialized()
     {
-        return initialized;
+        return (initialized>0);
     }
 
     public String getAction()
@@ -103,7 +103,7 @@ public abstract class Page // *Deprecated* implements 
Serializable
 
     public void setAction(String actionParam)
     {
-        if (!initialized)
+        if (initialized>0)
             Page.log.debug("Setting PageBean action {} for bean {}.", action, 
getPageName());
         else
             Page.log.trace("Re-setting PageBeanAction {} for bean {}.", 
action, getPageName());
@@ -135,12 +135,11 @@ public abstract class Page // *Deprecated* implements 
Serializable
 
     public void preRenderPage(FacesContext context)
     {
-        if (this.initialized)
-        {
-            // PageBean.log.error("PageBean {} is already initialized.", 
name());
-            try
-            {
-                Page.log.debug("PageBean {} is already initialized. Calling 
doRefresh().", getPageName());
+        // Check initialized
+        if (initialized>0)
+        {   try
+            {   // refresh
+                log.debug("PageBean {} is already initialized. Calling 
doRefresh().", getPageName());
                 doRefresh();
             }
             catch (Exception e)
@@ -150,6 +149,15 @@ public abstract class Page // *Deprecated* implements 
Serializable
             return; // already Initialized
         }
 
+        // Check pending
+        if (initialized==0)
+        {   // Initialization pending
+            Exception e = new InvalidOperationException("Page Initialization 
pending.");
+            WebApplication.getInstance().handleException(context, this, e);
+        }
+        // Initialization pending
+        initialized=0;
+        
         // Check access
         try
         {
@@ -171,9 +179,6 @@ public abstract class Page // *Deprecated* implements 
Serializable
             return;
         }
 
-        // Initialize
-        this.initialized = true;
-
         // String value of "null"?
         if (this.action!=null && "null".equals(this.action))
         {   log.warn("Invalid action name 'null' for {}", 
getClass().getName());
@@ -181,52 +186,63 @@ public abstract class Page // *Deprecated* implements 
Serializable
         }    
         
         // Execute Action
-        if (this.action != null)
-        {   try
-            {   // Process action
-                log.info("Processing action {} on {}.", 
String.valueOf(action), getPageName());
-                Method method = getClass().getMethod(action);
-                Object result = method.invoke(this);
-                if (result != null)
-                {
-                    String outcome = result.toString();
-                    // Retrieve the NavigationHandler instance..
-                    NavigationHandler navHandler = 
context.getApplication().getNavigationHandler();
-                    // Invoke nav handling..
-                    navHandler.handleNavigation(context, action, outcome);
-                    // Trigger a switch to Render Response if needed
-                    context.renderResponse();
-                    return;
-                }
-                restoreSessionMessage();
-            }
-            catch (NoSuchMethodException nsme)
-            {
-                logAndHandleActionException(action, nsme);
-            }
-            catch (Exception e)
-            {
-                logAndHandleActionException(action, e.getCause());
-            }
-            finally
-            {
+        if (this.action!=null && this.action.length()>0)
+        {   // process action
+            try
+            {   log.debug("Processing action {} on page {}.", action, 
getPageName());
+                processAction(action, context);
+            } finally {
                 // Clear action
-                this.action = null; // Page.INVALID_ACTION;
+                this.action = null;
             }
         }
         else
         {   // call default Action
             try
-            {
-                Page.log.debug("Initializing PageBean {}. Calling doInit()", 
getPageName());
+            {   log.debug("Initializing page {} using doInit()", 
getPageName());
                 doInit();
-                restoreSessionMessage();
+                // if not redirected, restore SessionMessage
+                if (!context.getResponseComplete())
+                    restoreSessionMessage();
             }
             catch (Exception e)
             {
                 logAndHandleActionException("doInit", e);
             }
         }
+        
+        // Initialized unless redirected
+        this.initialized = (context.getResponseComplete() ? (short)-1 : 1);
+    }
+    
+    protected void processAction(String action, FacesContext context)
+    {
+        try
+        {   // Process action
+            Method method = getClass().getMethod(action);
+            Object result = method.invoke(this);
+            if (result != null)
+            {
+                String outcome = result.toString();
+                // Retrieve the NavigationHandler instance..
+                NavigationHandler navHandler = 
context.getApplication().getNavigationHandler();
+                // Invoke nav handling..
+                navHandler.handleNavigation(context, action, outcome);
+                // Trigger a switch to Render Response if needed
+                context.renderResponse();
+                return;
+            }
+            // OK, not redirected
+            restoreSessionMessage();
+        }
+        catch (NoSuchMethodException e)
+        {
+            logAndHandleActionException(action, e);
+        }
+        catch (Exception e)
+        {
+            logAndHandleActionException(action, e.getCause());
+        }
     }
     
     public boolean isHasMessages()
@@ -255,15 +271,14 @@ public abstract class Page // *Deprecated* implements 
Serializable
 
     protected void logAndHandleActionException(String action, Throwable e)
     {
+        // log
         String msg = "Failed to perform action " + action + " on " + 
getPageName();
-        // Message
-        Page.log.error(msg, e);
+        log.error(msg, e);
+        // handle
         if (!handleActionError(action, e))
-        {   // Not handled. Throw again
-            if (e instanceof EmpireException)
-                throw ((EmpireException)e);
-            else
-                throw new InternalException(e);
+        {   // Not handled: Forward to Application
+            FacesContext context = FacesContext.getCurrentInstance();
+            WebApplication.getInstance().handleException(context, this, e);
         }    
     }
     
@@ -290,7 +305,7 @@ public abstract class Page // *Deprecated* implements 
Serializable
         // Set Faces Message
         String msg = extractErrorMessage(e);
         String detail = extractErrorMessageDetail(action, e, 1);
-        log.error(msg + "\r\n" + detail);
+        // log.error(msg + "\r\n" + detail);
         FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_ERROR, 
msg, detail);
         setSessionMessage(facesMsg);
         // Return to parent page

Reply via email to