原文地址:http://www.openlogic.com/wazi/bid/259014/How-to-add-exception-handling-to-JSF-applications
Everyone who codes Java EE web applications needs to pay attention to exception handling. When a program encounters an error, developers can display friendly messages for end users, which increases their trust in the application. Also, by adding adequate exception handling, you can troubleshoot and debug application defects. Since version 2.0 the JavaServer Faces framework has supported an exception handling mechanism to provide a centralized place for handling exceptions in JSF applications. Here's how you can utilize it to write professional applications.
A good example to illustrate everything that goes into proper exception handling is the guessNumber application, in which the application generates a random number within a specific range, and the user is asked to enter a number in this range. After the user enters a valid number, the application can provide three possible responses:
- If the entered number is equal to the generated number, the application congratulates the user and asks if he wants to try again.
- If the entered number is less than the generated number, the application asks the user to enter a number greater than the entered number.
- If the entered number is greater than the generated number, the application asks the user to enter a number less than the entered number.
To code the guessNumber application, we can use three pages:
- input.xhtml, in which the user enters a number.
- congratulations.xhtml, which displays the congratulations message if the user succeeds in guessing the correct number.
- error.xhtml, which is displayed if the application has an internal error.
The following code snippet shows the input.xhtml page:
<?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head> <title>#{bundle['guessNumber.input_page.title']}</title> </h:head> <h:body> <h:form> <h:panelGrid columns="3"> <h:outputText value="#{bundle['guessNumber.input_number']}"/> <h:inputText id="inputNumber" value="#{randomNumber.number}" required="true"> <f:validateLongRange minimum="0" maximum="10"/> </h:inputText> <h:message for="inputNumber"/> </h:panelGrid> <h:commandButton value="#{bundle['guessNumber.guess_now']}" action="#{randomNumber.guessNumber}"/> <h:messages /> </h:form> </h:body> </html>
Here a form contains an input text ("inputNumber")
in which the user enters a number, and a command button ("Guess Now")
that the user clicks in order to send the guess. The input text is required and is validated to be in the range from 0 to 10. When the command button is clicked, #{randomNumber.guessNumber}
(the guessNumber
action method of the RandomNumber
managed bean) is executed. The code below shows the RandomNumber
managed bean.
package com.wazi.guessnumber.model; import java.io.Serializable; import java.util.ResourceBundle; import javax.annotation.PostConstruct; import javax.faces.application.FacesMessage; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; import javax.faces.context.FacesContext; import javax.servlet.http.HttpSession; @ManagedBean @SessionScoped public class RandomNumber implements Serializable { private Integer number; private Integer generatedNumber; public String guessNumber() { if (generatedNumber == number) { return "congratulations?faces-redirect=true"; } FacesContext context = FacesContext.getCurrentInstance(); ResourceBundle bundle = ResourceBundle.getBundle("com.wazi.guessnumber.messages", context.getViewRoot().getLocale()); String tip = ""; if (generatedNumber > number) { tip = bundle.getString("guessNumber.use_bigger_number"); } else { tip = bundle.getString("guessNumber.use_smaller_number"); } context.addMessage(null, new FacesMessage(tip)); return null; } public String reset() { FacesContext context = FacesContext.getCurrentInstance(); HttpSession session = (HttpSession) context.getExternalContext().getSession(false); session.invalidate(); return "input?faces-redirect=true"; } public Integer getNumber() { return number; } public void setNumber(Integer number) { this.number = number; } @PostConstruct public void initialize() { generatedNumber = (int) (Math.random() * 10); System.out.println ("Initializing random generated number: " + generatedNumber); } }
The RandomNumber
class is marked as a managed bean using the JSF @ManagedBean
annotation and is set in the session scope using the @SessionScoped
annotation. Using the @PostConstruct
annotation, the initialize()
method of the RandomNumber
managed bean is called after the managed bean class is instantiated in order to initialize the managed bean. In the initialize()
method, the generatedNumber
property is set to a random number from 0 to 10.
In the guessNumber()
action method, if the user-entered a number is equal to the generatedNumber
property, the user is redirected to the congratulations.xhtml page. If the entered number is less than or greater than the generatedNumber
property, the user is advised to enter a number that is less than or greater than the entered number.
You can add messages to be displayed by the <h:messages>
tag from the JSF action methods using the FacesContext.getCurrentInstance().addMessage()
API, specifying two parameters. The first parameter represents the client ID with which this message is associated (if no client ID is available you can set this parameter to null) and the second represents the FacesMessage
object.
In the reset()
action method, the user session is invalidated and the user is redirected to the input.xhtml page. This method is called in the congratulations.xhtml page when the user clicks on the ("Try again")
command button. The following code shows the congratulations.xhtml page:
<?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head> <link href="#{request.contextPath}/css/styles.css" rel="stylesheet" type="text/css"/> <title>#{bundle['guessNumber.cong_page.title']}</title> </h:head> <h:body> <h:form> <h:outputFormat value="#{bundle['guessNumber.correct_input']}" class="highlighted infoMessage"> <f:param value="#{randomNumber.number}"/> </h:outputFormat> <br/><br/> <h:commandButton value="#{bundle['guessNumber.try_again']}" action="#{randomNumber.reset}"/> </h:form> </h:body> </html>
In the congratulations.xhtml page, the congratulations message is displayed and the user has the option to try again by clicking on the ("Try Again")
button, which calls the reset
method of the RandomNumber
managed bean.
Applying the JSF exception-handling mechanism
Now we can apply the centralized JSF exception-handling mechanism on the guessNumber application. After we do so, the application will be able to handle different exceptions in a centralized place and display the exception information in an error page. In order to create a custom exception handler in JSF applications we need to do three things:
- Create a custom exception handler class that handles the application exceptions. This handler class should extend an exception handling wrapper class (such as the
ExceptionHandlerWrapper
class). - Create a custom exception handler factory class that is responsible for creating the instances of the exception handler class. The custom exception handler class should extend the JSF
ExceptionHandlerFactory
class. - Finally, register the custom exception handler factory class in the faces-config.xml file.
The following code shows the CustomExceptionHandler
class, which extends the ExceptionHandlerWrapper
class:
package com.wazi.guessnumber.exceptions; import java.util.Iterator; import javax.faces.FacesException; import javax.faces.application.NavigationHandler; import javax.faces.context.ExceptionHandler; import javax.faces.context.ExceptionHandlerWrapper; import javax.faces.context.FacesContext; import javax.faces.context.Flash; import javax.faces.event.ExceptionQueuedEvent; import javax.faces.event.ExceptionQueuedEventContext; public class CustomExceptionHandler extends ExceptionHandlerWrapper { private ExceptionHandler wrapped; public CustomExceptionHandler(ExceptionHandler wrapped) { this.wrapped = wrapped; } @Override public ExceptionHandler getWrapped() { return wrapped; } @Override public void handle() throws FacesException { Iterator iterator = getUnhandledExceptionQueuedEvents().iterator(); while (iterator.hasNext()) { ExceptionQueuedEvent event = (ExceptionQueuedEvent) iterator.next(); ExceptionQueuedEventContext context = (ExceptionQueuedEventContext)event.getSource(); Throwable throwable = context.getException(); FacesContext fc = FacesContext.getCurrentInstance(); try { Flash flash = fc.getExternalContext().getFlash(); // Put the exception in the flash scope to be displayed in the error page if necessary ... flash.put("errorDetails", throwable.getMessage()); System.out.println("the error is put in the flash: " + throwable.getMessage()); NavigationHandler navigationHandler = fc.getApplication().getNavigationHandler(); navigationHandler.handleNavigation(fc, null, "error?faces-redirect=true"); fc.renderResponse(); } finally { iterator.remove(); } } // Let the parent handle the rest getWrapped().handle(); } }
The most important method of the CustomExceptionHandler
class is the handle()
method, which is responsible for handling JSF application exceptions. The getUnhandledExceptionQueuedEvents()
method gets all the unhandled exceptions in the JSF application. Every item in the returned Iterable
object of this method represents an ExceptionQueuedEvent
object. From the ExceptionQueuedEvent
object you can get the ExceptionQueuedEventContext
object, from which you can retrieve the Throwable
object. Using the Throwable
object, you can verify the exceptions you want to handle in the applications.
In our custom exception handler, we get the exception message from throwable.getMessage()
. It is set to be used in the error.xhtml page in an "errorDetails"
attribute that is defined in the flash scope. The flash scope, which was introduced in JSF 2.0, makes objects available only for the next request of the same browser window, which makes it useful if you want to keep information for a short time for the next request only, whether the next request will result from an HTTP redirect or a JSF form postback or simply an HTTP GET for a new page.
The NavigationHandler
redirects the response to the application error page (error.xhtml), and the ExceptionQueuedEvent
is removed from the Iterable
object. The following code shows the error.xhtml page:
<?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <link href="#{request.contextPath}/css/styles.css" rel="stylesheet" type="text/css"/> <title>#{bundle['guessNumber.error_page.title']}</title> </h:head> <h:body> <div class="highlighted errorMessage"> <h:outputText escape="false" value="#{bundle['guessNumber.error_page.content']}"/> </div> <br/><br/> <div class="errorDetails"> Error details: <br/> #{flash.keep.errorDetails} </div> </h:body> </html>
It mainly displays the error details message (which was set from the exception handler class) using #{flash.keep.errorDetails}
.
Next, we need to create CustomExceptionHandlerFactory
, the custom exception handler factory class that is responsible for creating the instances of the CustomExceptionHandler
class:
package com.wazi.guessnumber.exceptions; import javax.faces.context.ExceptionHandler; import javax.faces.context.ExceptionHandlerFactory; public class CustomExceptionHandlerFactory extends ExceptionHandlerFactory { private ExceptionHandlerFactory parent; public CustomExceptionHandlerFactory(ExceptionHandlerFactory parent) { this.parent = parent; } @Override public ExceptionHandler getExceptionHandler() { ExceptionHandler result = new CustomExceptionHandler(parent.getExceptionHandler()); return result; } }
Finally, we need to register the custom exception handler factory class in the faces-config.xml file of the application, as shown below:
<?xml version='1.0' encoding='UTF-8'?> <faces-config version="2.1" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd"> <factory> <exception-handler-factory> com.wazi.guessnumber.exceptions.CustomExceptionHandlerFactory </exception-handler-factory> </factory> <!—other configurations here --> </faces-config>
Testing the exception handling mechanism
After setting up this exception handling mechanism in the guessNumber application, if an exception is thrown from the guessNumber application for whatever reason, the error.xhtml page will be displayed. The following screenshot shows the error page if, for example, the guessNumber
action method throws a RuntimeException
.
And here's the error page you see if the guessNumber application throws the common JSF ViewExpiredException
, which can result from the user session timeout that causes the JSF not able to restore the view after the JSF form postback.
The guessNumber application is a Maven application. You can build it from its POM file using the command mvn clean install
, then deploy it on Apache Tomcat or another application server (if you specify the correct dependencies). After deploying the application on a local Tomcat server you should be able to try the application by navigating to http://localhost:8080/guessNumber-1.0/.
Follow @openlogic
Follow @OSCloudServices
This work is licensed under a Creative Commons Attribution 3.0 Unported License
相关推荐
The Definitive Guide to JSF in Java EE 8 Building Web Applications with JavaServer Faces 英文epub 本资源转载自网络,如有侵权,请联系上传者或csdn删除 查看此书详细信息请在美国亚马逊官网搜索此书
Pro JSF and HTML5 shows you how to leverage the full potential of JavaServer Faces (JSF) and HTML5. This book is for Java developers who aspire to build sophisticated, enterprise-grade web experiences...
This innovative book arms you with the tools to utilize JavaServer Faces (JSF), a new standard that will make building user interfaces for J2EE(TM) applications a lot easier. The authors begin by ...
Introduction to Python Programming and developing GUI applications with PyQT.pdf 作者B.M. Harwani © 2012 Course Technology, a part of Cengage Learning. ISBN-13: 978-1-4354-6097-3 ISBN-10: 1-4354-6097...
jsf实例 JSF学习 JSF jar包 JSF jsf实例 JSF学习 JSF jar包 JSFjsf实例 JSF学习 JSF jar包 JSF jsf实例 JSF学习 JSF jar包 JSF
The Definitive Guide to JSF in Java EE 8 Building Web Applications with JavaServer Faces 英文无水印原版pdf pdf所有页面使用FoxitReader、PDF-XChangeViewer、SumatraPDF和Firefox测试都可以打开 本资源转载...
**JSF(JavaServer Faces)** 是一个Java平台上的用户界面框架,用于构建Web应用程序。它为开发人员提供了一种模型-视图-控制器(MVC)架构,简化了前端和后端之间的交互。JSF提供了组件库,使得创建动态、交互式的...
**JSF(JavaServer Faces)** 是一个Java平台上的用户界面框架,用于构建Web应用程序。它简化了开发人员创建交互式、数据驱动的Web界面的过程。JSF提供了一个组件模型,允许开发者通过拖放组件的方式来构建用户界面...
在JavaServer Faces (JSF)框架中,分页是一种常用的技术,用于处理大量数据时提供更好的用户体验。当数据集过大,一次性加载所有记录到页面上会导致性能下降且用户界面响应变慢。通过分页,我们可以将数据分成多个...
《JSF_实战》非常好的JSF学习书《JSF_实战》非常好的JSF学习书《JSF_实战》非常好的JSF学习书《JSF_实战》非常好的JSF学习书《JSF_实战》非常好的JSF学习书《JSF_实战》非常好的JSF学习书《JSF_实战》非常好的JSF...
JavaServer Faces(JSF)是Java平台上的一种用于构建Web应用程序的MVC(Model-View-Controller)框架。它提供了一种声明式的方式来构建用户界面,简化了开发过程,并且与Java EE平台无缝集成。本系列资料包括《JSF...
JavaServer Faces (JSF) 是一个用于构建用户界面的Java Web框架,它简化了创建和维护Web应用程序的复杂性。JSF的核心理念是提供一种组件化的编程模型,将UI元素与业务逻辑分离,使得开发者可以专注于应用的逻辑部分...
**JSF(JavaServer Faces)** 是一种Java平台上的Web应用程序开发框架,它提供了一种组件化和事件驱动的方式来创建用户界面。JSF的核心概念包括组件、事件、渲染器和生命周期,这些元素共同构建了一个强大的MVC...
JSF是一种用于构建Java Web 应用程序的标准框架(是Java Community Process 规定的JSR-127标准)。JSF(Java Server Faces)技术为开发基于网络用户界面的Java开发者提供了标准的编程接口API以及标签库。就像Struts框架...
jsf 视频 java faces jsf 视频 java faces jsf 视频 java faces
JSF(JavaServer Faces)是Java平台上用于构建用户界面的Web框架,尤其在处理表单和数据交互方面表现强大。本项目聚焦于JSF的文件上传功能,特别是针对大文件的上传,允许用户上传最大可达1.99GB的文件。在实际应用...
**JSF 1.2核心详解** JavaServer Faces (JSF) 是Java平台上的一个用于构建用户界面的组件模型框架,特别适用于Web应用程序的开发。JSF 1.2是该框架的一个重要版本,它在JSF 1.1的基础上进行了一系列的改进和增强,...