You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lo...@apache.org on 2018/11/01 09:04:59 UTC

[myfaces-tobago] branch master updated: TOBAGO-1946: Better ExceptionHandling for applications

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

lofwyr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git


The following commit(s) were added to refs/heads/master by this push:
     new b98f7b9  TOBAGO-1946: Better ExceptionHandling for applications
b98f7b9 is described below

commit b98f7b9e5e09360b67cabda206ef4f5f4a84d591
Author: Udo Schnurpfeil <lo...@apache.org>
AuthorDate: Thu Nov 1 10:03:45 2018 +0100

    TOBAGO-1946: Better ExceptionHandling for applications
---
 .../tobago/context/TobagoExceptionHandler.java     | 129 +++++++++++++++++++++
 .../context/TobagoExceptionHandlerFactory.java     |  19 ++-
 .../META-INF/resources/tobago/error.xhtml          |  28 +++++
 .../tobago/example/demo/TestExceptionHandler.java  |  87 --------------
 .../src/main/webapp/WEB-INF/faces-config.xml       |   2 +-
 .../src/main/webapp/WEB-INF/web.xml                |   2 +-
 6 files changed, 174 insertions(+), 93 deletions(-)

diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/context/TobagoExceptionHandler.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/context/TobagoExceptionHandler.java
new file mode 100644
index 0000000..67094f4
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/context/TobagoExceptionHandler.java
@@ -0,0 +1,129 @@
+/*
+ * 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.myfaces.tobago.context;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.faces.FacesException;
+import javax.faces.application.FacesMessage;
+import javax.faces.application.NavigationHandler;
+import javax.faces.application.ViewExpiredException;
+import javax.faces.application.ViewHandler;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExceptionHandler;
+import javax.faces.context.ExceptionHandlerWrapper;
+import javax.faces.context.FacesContext;
+import javax.faces.event.ExceptionQueuedEvent;
+import javax.faces.event.ExceptionQueuedEventContext;
+import javax.faces.event.PhaseId;
+import javax.faces.event.PreRenderViewEvent;
+import javax.faces.view.ViewDeclarationLanguage;
+import javax.servlet.http.HttpServletResponse;
+import java.io.FileNotFoundException;
+import java.util.Iterator;
+
+public class TobagoExceptionHandler extends ExceptionHandlerWrapper {
+
+  private static final Logger LOG = LoggerFactory.getLogger(TobagoExceptionHandler.class);
+
+  private ExceptionHandler wrapped;
+
+  public TobagoExceptionHandler(final ExceptionHandler wrapped) {
+    this.wrapped = wrapped;
+  }
+
+  @Override
+  public ExceptionHandler getWrapped() {
+    return wrapped;
+  }
+
+  @Override
+  public void handle() throws FacesException {
+
+    final Iterator<ExceptionQueuedEvent> iterator = getUnhandledExceptionQueuedEvents().iterator();
+    while (iterator.hasNext()) {
+      final ExceptionQueuedEvent event = iterator.next();
+      final ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
+      final Throwable cause = this.getWrapped().getRootCause(context.getException());
+      final FacesContext facesContext = FacesContext.getCurrentInstance();
+      final NavigationHandler nav = facesContext.getApplication().getNavigationHandler();
+
+      if (cause instanceof ViewExpiredException
+          || cause != null && cause.getCause() instanceof ViewExpiredException) {
+        final ViewExpiredException viewExpiredException = (ViewExpiredException)
+            (cause instanceof ViewExpiredException ? cause : cause.getCause());
+        try {
+          facesContext.addMessage(null,
+              new FacesMessage(FacesMessage.SEVERITY_WARN,
+                  "The view has been expired!",
+                  "Please check the given data or try to start from the beginning."));
+          nav.handleNavigation(facesContext, null, viewExpiredException.getViewId());
+          facesContext.renderResponse();
+          LOG.debug("Handling ViewExpiredException on viewId: {}", viewExpiredException.getViewId());
+        } finally {
+          iterator.remove();
+        }
+      } else {
+        try {
+          final boolean error404 = cause instanceof FileNotFoundException
+              || cause != null && cause.getCause() instanceof FileNotFoundException;
+          final FacesMessage message;
+          if (error404) {
+            message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
+                "The page was not found!",
+                "The requested page was not found!");
+            facesContext.getExternalContext().setResponseStatus(HttpServletResponse.SC_NOT_FOUND);
+            LOG.warn("Handling 404 exception.");
+          } else {
+            message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
+                "An unknown error has occurred!",
+                "An unknown error has occurred!");
+            facesContext.getExternalContext().setResponseStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            LOG.warn("Handling 500 exception.", cause);
+          }
+          facesContext.addMessage(null, message);
+          final String viewId = "/tobago/error.xhtml";
+
+          // when the rendering was not yet started, we can forward to an error page
+          if (event.getContext().getPhaseId().getOrdinal() < PhaseId.RENDER_RESPONSE.getOrdinal()) {
+            nav.handleNavigation(facesContext, null, viewId);
+            facesContext.renderResponse();
+          } else {
+            final HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
+            response.resetBuffer(); // undo rendering, if you can.
+            final ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
+            final ViewDeclarationLanguage vdl = viewHandler.getViewDeclarationLanguage(facesContext, viewId);
+            final UIViewRoot viewRoot = viewHandler.createView(facesContext, viewId);
+            vdl.buildView(facesContext, viewRoot);
+            facesContext.getApplication().publishEvent(facesContext, PreRenderViewEvent.class, viewRoot);
+            vdl.renderView(facesContext, viewRoot);
+            facesContext.responseComplete();
+          }
+        } catch (Exception e) {
+          LOG.error("Exception while exception handling!", e);
+        } finally {
+          iterator.remove();
+        }
+      }
+    }
+    getWrapped().handle();
+  }
+}
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/TestExceptionHandlerFactory.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/context/TobagoExceptionHandlerFactory.java
similarity index 66%
rename from tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/TestExceptionHandlerFactory.java
rename to tobago-core/src/main/java/org/apache/myfaces/tobago/context/TobagoExceptionHandlerFactory.java
index a6cb346..a97c0d2 100644
--- a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/TestExceptionHandlerFactory.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/context/TobagoExceptionHandlerFactory.java
@@ -17,23 +17,34 @@
  * under the License.
  */
 
-package org.apache.myfaces.tobago.example.demo;
+package org.apache.myfaces.tobago.context;
 
 import javax.faces.context.ExceptionHandler;
 import javax.faces.context.ExceptionHandlerFactory;
 
-public class TestExceptionHandlerFactory extends ExceptionHandlerFactory {
+/**
+ * To enable the TobagoExceptionHandler insert this class in the faces-config.xml like:
+ * <pre>
+ *   &lt;factory&gt;
+ *     &lt;exception-handler-factory&gt;
+ *       org.apache.myfaces.tobago.example.demo.TobagoExceptionHandlerFactory
+ *     &lt;/exception-handler-factory&gt;
+ *   &lt;/factory&gt;
+ * </pre>
+ *
+ */
+public class TobagoExceptionHandlerFactory extends ExceptionHandlerFactory {
 
   private ExceptionHandlerFactory parent;
 
-  public TestExceptionHandlerFactory(final ExceptionHandlerFactory parent) {
+  public TobagoExceptionHandlerFactory(final ExceptionHandlerFactory parent) {
     this.parent = parent;
   }
 
   @Override
   public ExceptionHandler getExceptionHandler() {
     ExceptionHandler result = parent.getExceptionHandler();
-    result = new TestExceptionHandler(result);
+    result = new TobagoExceptionHandler(result);
     return result;
   }
 
diff --git a/tobago-core/src/main/resources/META-INF/resources/tobago/error.xhtml b/tobago-core/src/main/resources/META-INF/resources/tobago/error.xhtml
new file mode 100644
index 0000000..1946dd5
--- /dev/null
+++ b/tobago-core/src/main/resources/META-INF/resources/tobago/error.xhtml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ * 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.
+-->
+
+<f:view xmlns:f="http://java.sun.com/jsf/core"
+        xmlns:tc="http://myfaces.apache.org/tobago/component">
+
+  <tc:page>
+    <tc:messages/>
+  </tc:page>
+
+</f:view>
+
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/TestExceptionHandler.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/TestExceptionHandler.java
deleted file mode 100644
index ab208c5..0000000
--- a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/TestExceptionHandler.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.myfaces.tobago.example.demo;
-
-import javax.faces.FacesException;
-import javax.faces.application.FacesMessage;
-import javax.faces.application.NavigationHandler;
-import javax.faces.application.ViewExpiredException;
-import javax.faces.context.ExceptionHandler;
-import javax.faces.context.ExceptionHandlerWrapper;
-import javax.faces.context.FacesContext;
-import javax.faces.event.ExceptionQueuedEvent;
-import javax.faces.event.ExceptionQueuedEventContext;
-import javax.servlet.http.HttpServletResponse;
-import java.io.FileNotFoundException;
-import java.util.Iterator;
-
-public class TestExceptionHandler extends ExceptionHandlerWrapper {
-
-  private ExceptionHandler wrapped;
-
-  public TestExceptionHandler(final ExceptionHandler wrapped) {
-    this.wrapped = wrapped;
-  }
-
-  @Override
-  public ExceptionHandler getWrapped() {
-    return wrapped;
-  }
-
-  @Override
-  public void handle() throws FacesException {
-
-    final Iterator<ExceptionQueuedEvent> iterator = getUnhandledExceptionQueuedEvents().iterator();
-    while (iterator.hasNext()) {
-      final ExceptionQueuedEvent event = iterator.next();
-      final ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
-      final Throwable cause = context.getException();
-
-      if (cause instanceof ViewExpiredException) {
-        final FacesContext facesContext = FacesContext.getCurrentInstance();
-        final NavigationHandler nav = facesContext.getApplication().getNavigationHandler();
-        try {
-          facesContext.addMessage(null, new FacesMessage("The view has been expired!"));
-
-          /*
-           * TODO analyse the '/faces'-prefix
-           * actually the viewID should be enough, but if you do so, the FacesMessage won't be shown.
-           */
-          nav.handleNavigation(facesContext, null, ((ViewExpiredException) cause).getViewId());
-          facesContext.renderResponse();
-        } finally {
-          iterator.remove();
-        }
-      } else if (cause instanceof FileNotFoundException
-              || cause != null && cause.getCause() instanceof FileNotFoundException) {
-        try {
-          final FacesContext facesContext = FacesContext.getCurrentInstance();
-          final NavigationHandler nav = facesContext.getApplication().getNavigationHandler();
-          nav.handleNavigation(facesContext, null, "/error/404.xhtml");
-          facesContext.getExternalContext().setResponseStatus(HttpServletResponse.SC_NOT_FOUND);
-          facesContext.renderResponse();
-        } finally {
-          iterator.remove();
-        }
-      }
-    }
-    getWrapped().handle();
-  }
-}
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/WEB-INF/faces-config.xml b/tobago-example/tobago-example-demo/src/main/webapp/WEB-INF/faces-config.xml
index fff55cb..f291fda 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/WEB-INF/faces-config.xml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/WEB-INF/faces-config.xml
@@ -58,7 +58,7 @@
 
   <factory>
     <exception-handler-factory>
-      org.apache.myfaces.tobago.example.demo.TestExceptionHandlerFactory
+      org.apache.myfaces.tobago.context.TobagoExceptionHandlerFactory
     </exception-handler-factory>
   </factory>
 
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/WEB-INF/web.xml b/tobago-example/tobago-example-demo/src/main/webapp/WEB-INF/web.xml
index 09bcad8..8ac66da 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/WEB-INF/web.xml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/WEB-INF/web.xml
@@ -148,7 +148,7 @@
   </servlet-mapping>
 
   <!-- Error handling, see also these classes:
-       org.apache.myfaces.tobago.example.demo.TestExceptionHandler
+       org.apache.myfaces.tobago.example.demo.TobagoExceptionHandler
        org.apache.myfaces.tobago.example.demo.DeltaSpikeClientWindowConfig
    -->