Bil*_*mus 2 java ejb exception custom-exceptions jsf-2
问题如下:EJB 抛出此异常(来自 glassfish 日志):
SEVERE: Attempting to confirm previously confirmed login using confirmation UUID: b90b33ca-dc69-41c1-9c60-99152810c89b
com.extremelatitudesoftware.els_commons.exceptions.LoginPreviouslyConfirmedException: Attempting to confirm previously confirmed login using confirmation UUID: b90b33ca-dc69-41c1-9c60-99152810c89b
at com.extremelatitudesoftware.security.auth.CredentialsController.checkForPreviousConfirmation(CredentialsController.java:244)
Run Code Online (Sandbox Code Playgroud)
在异常堆栈的客户端(在底部)我看到:
WARNING: StandardWrapperValve[Faces Servlet]: PWC1406: Servlet.service() for servlet Faces Servlet threw exception
java.lang.NullPointerException
at com.extremelatitudesoftware.accesscontrol.registration.RegistrationConfirmationBean.setChallengeQuestion(RegistrationConfirmationBean.java:61)
at com.extremelatitudesoftware.accesscontrol.registration.RegistrationConfirmationBean.fetchChallengeResponse(RegistrationConfirmationBean.java:51)
Run Code Online (Sandbox Code Playgroud)
当然,在浏览器中我收到一个通用 500 异常错误,表示存在空指针异常。
我想要一个自定义错误页面来检测“com.extremelatitudesoftware.els_commons.exceptions.LoginPreviouslyConfirmedException”并说“哎呀,你已经确认了这一点!”
有人可以为我指出正确的方向,让客户端/JSF 层正确地“看到”异常,以便可以完成此操作。
这是该问题的用例:
我在 EJB 层中有一个方法,用于检查用户之前是否已确认其登录帐户。如果他们还没有,它会处理他们确认新帐户的请求。如果他们说回到旧电子邮件并说,嗯,让我们再次单击该电子邮件,后端将检测到这一点,并抛出异常,以便 JSF/客户端层拾取该异常,并且将通知用户该帐户已被确认。
添加示例代码:
方法检查条件并在必要时抛出异常。请注意,大多数情况下不会发生这种情况,因此我想使用此机制,而不是每次有人确认其帐户时都进行不必要的调用。
...
private void checkForPreviousConfirmation(Credentials cr)
throws LoginPreviouslyConfirmedException {
if (!(CredentialsStatusType.PENDING.equals(cr.getStatus()))
|| !(CredentialsDispositionType.WAITING.equals(cr.getDisposition()))) {
String msg = "Attempting to confirm previously confirmed login using "
+ "confirmation UUID: " + cr.getConfirmationUuid();
Logger.getLogger(CredentialsController.class.getName()).log(Level.INFO,
msg);
throw new LoginPreviouslyConfirmedException(msg);
}
}
Run Code Online (Sandbox Code Playgroud)
自定义异常:
public class LoginPreviouslyConfirmedException extends RuntimeException {
public LoginPreviouslyConfirmedException(String msg, Throwable cause) {
super(msg, cause);
}
public LoginPreviouslyConfirmedException(String msg) {
super(msg);
}
}
Run Code Online (Sandbox Code Playgroud)
它位于 JSF 层的 ManagedBean 中。它是通过以下方式调用的: preRenderView
...
public void fetchChallengeResponse() {
try {
crespList = regFacade.fetchChallengeResponse(confirmUuid);
} catch (LoginPreviouslyConfirmedException ex) {
String msg = "Attempting to confirm previously confirmed login using " + "confirmation UUID: " + getConfirmUuid();
Logger.getLogger(RegistrationConfirmationBean.class.getName()).log(Level.SEVERE, msg, ex);
FacesContext facesContext = FacesContext.getCurrentInstance();
Application application = facesContext.getApplication();
NavigationHandler navigationHandler = application.getNavigationHandler();
navigationHandler.handleNavigation(facesContext, null, "faces/errorpages/already_confirmed.xhtml");
facesContext.renderResponse();
}catch (Exception ex){
String msg = "Including this to see if the previously confirmed exception was skipped over";
Logger.getLogger(RegistrationConfirmationBean.class.getName()).log(Level.SEVERE, msg, ex);
}
this.setChallengeQuestion();
}
Run Code Online (Sandbox Code Playgroud)
这是日志显示的内容:
INFO: Attempting to confirm previously confirmed login using confirmation UUID: b90b33ca-dc69-41c1-9c60-99152810c89b
WARNING: EJB5184:A system exception occurred during an invocation on EJB CredentialsController, method: public java.util.ArrayList com.extremelatitudesoftware.security.auth.CredentialsController.fetchChallengeResponseByCredentialUuid(java.lang.String) throws com.extremelatitudesoftware.els_commons.exceptions.LoginPreviouslyConfirmedException
WARNING: javax.ejb.TransactionRolledbackLocalException: Exception thrown from bean
Run Code Online (Sandbox Code Playgroud)
然后有一些线索:
SEVERE: Including this to see if the previously confirmed exception was skipped over
javax.ejb.EJBTransactionRolledbackException
Run Code Online (Sandbox Code Playgroud)
根据 BalusC 的建议,我将 ManagedBean 中的代码更改为:
public void fetchChallengeResponse() throws LoginPreviouslyConfirmedException{
crespList = regFacade.fetchChallengeResponse(confirmUuid);
this.setChallengeQuestion();
}
Run Code Online (Sandbox Code Playgroud)
这是在 web.xml 中
<error-page>
<exception-type>com.extremelatitudesoftware.els_commons.exceptions.LoginPreviouslyConfirmedException</exception-type>
<location>/errorpages/already_confirmed.xhtml</location>
</error-page>
Run Code Online (Sandbox Code Playgroud)
这是我今天通过研究找到的最简单、最好的答案:使用 @ApplicationException(rollback=true) 注释装饰自定义异常。
例如
@ApplicationException(rollback=true)
public class LoginPreviouslyConfirmedException extends Exception {...
Run Code Online (Sandbox Code Playgroud)
根本问题是,在将异常传递给客户端之前,EJB 容器至少将一些异常包装在“EJBTransactionRolledBackException”包装器中。我不确定,但它似乎类似于 JSF 想要将异常包装在“ServletException”中以应对在那里引发的任何异常的方式。在我看来,隐藏真正的应用程序错误是什么的想法是相当有缺陷的。在任何情况下,它都会阻止客户端“看到”您可能抛出的任何类型的特殊情况异常,原因是您或您的用户可以在不中止整个操作的情况下更正它。也就是说,它将看到的只是 EJBTransactionRolledBackException,它用于包装大量异常。这样做会导致无法轻松调试或处理异常。幸运的是,在最新一期的 JEE 中,他们包含了 @ApplicationException 注释来抵消这种行为。尽管我不明白为什么他们不完全停止包装异常。
在任何情况下,@ApplicationException(rollback=true) 都会告诉服务器不要将异常包装在 EJBTransactionRolledBackException 中并将其按原样传递回客户端。这使客户端能够看到真正的异常是什么,以便有机会优雅地处理它。指定“rollback=true”告诉服务器回滚当前事务。如果不这样做,它默认为 false 并且会丢弃所有内容。无论事情处于什么状态,都结束这一切。
如果自定义异常继承自 Exception,并且 EJB 会话 bean 中的方法抛出它,那么 JSF ManagedBean 中执行调用的方法也必须抛出它。如果它继承自RuntimeException,则不会。
在任何情况下,异常都不会被 EJBTransactionRolledBackException 包装,因此当它返回到 JSF 层时,将调用 web.xml 中为自定义异常定义的异常页面。无需捕获托管 bean 中的异常。无需过滤。不需要特殊的导航规则。不过,如果你想抓住它,它就可以抓住。如果没有@ApplicationException,它就不会(因为它被包装了)。
此处找到的信息(以及其他地方):
http://docs.oracle.com/javaee/5/api/javax/ejb/ApplicationException.html
http://openejb.apache.org/examples-trunk/applicationexception/
如何在会话 Bean 中使用自定义异常?
无论如何,这有效。只是希望我能早点在谷歌上找到正确的搜索词。
归档时间: |
|
查看次数: |
3397 次 |
最近记录: |