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