java - 在 JSF 中,被调用的 EJB 抛出的自定义异常被视为 EJBTransactionRolledBackException 或 NullPointerException 或 ServletException

标签 java jsf-2 exception ejb custom-exceptions

问题是: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)

在异常堆栈的客户端(底部)我看到了这个:

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)

当然,在浏览器中我会收到一个通用的 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);
    }
  }

自定义异常:

public class LoginPreviouslyConfirmedException extends RuntimeException {

    public LoginPreviouslyConfirmedException(String msg, Throwable cause) {
        super(msg, cause);
    }

    public LoginPreviouslyConfirmedException(String msg) {
        super(msg);
    }
}

这是在 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();
      }

这是日志显示的内容:

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

然后是一些追踪:

SEVERE: Including this to see if the previously confirmed exception was skipped over
javax.ejb.EJBTransactionRolledbackException



根据 BalusC 的建议,我将 ManagedBean 中的代码更改为:

public void fetchChallengeResponse() throws LoginPreviouslyConfirmedException{
     crespList = regFacade.fetchChallengeResponse(confirmUuid);
     this.setChallengeQuestion();
  }

这是在 web.xml 中

<error-page>
    <exception-type>com.extremelatitudesoftware.els_commons.exceptions.LoginPreviouslyConfirmedException</exception-type>
    <location>/errorpages/already_confirmed.xhtml</location>
</error-page>

But I still get this(This is an image of the error page that gets displayed).

最佳答案

这是我今天通过研究找到的最简单和最好的答案:用@ApplicationException(rollback=true) 注解装饰自定义异常。

例如

@ApplicationException(rollback=true)
public class LoginPreviouslyConfirmedException extends Exception {...

根本问题是,在将异常传递给客户端之前,EJB 容器至少将一些异常包装在“EJBTransactionRolledBackException”包装器中。我不确定,但它似乎类似于 JSF 希望将异常包装在“ServletException”中的方式,以解决那里引发的任何问题。在我看来,隐藏真正的应用程序错误是非常有缺陷的想法。在任何情况下,它都会阻止客户端“看到”您可能抛出的任何类型的特殊情况异常,原因是您或您的用户可以在不中止整个操作的情况下更正这些异常。也就是说,它只会看到用于包装大量异常的 EJBTransactionRolledBackException。这样做使得无法轻松调试或处理异常。幸运的是,在最新一期的 JEE 中,他们包含了 @ApplicationException 注释来抵消这种行为。尽管我不明白他们为什么不完全停止包装异常。

在任何情况下,@ApplicationException(rollback=true) 都会告诉服务器不要将异常包装在 EJBTransactionRolledBackException 中并将其按原样传递回客户端。这允许客户端看到真正的异常是什么,这样它就有机会优雅地处理它。指定“rollback=true”告诉服务器回滚当前事务。如果你不这样做,它默认为 false 并且会把所有东西都放在那里。以任何状态结束一切。

如果自定义异常继承自 Exception,并且 EJB session 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/
How to use custom Exception in Session Beans?

无论如何这都行得通。真希望我能早点在 Google 上找到正确的搜索词。

关于java - 在 JSF 中,被调用的 EJB 抛出的自定义异常被视为 EJBTransactionRolledBackException 或 NullPointerException 或 ServletException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10906155/

相关文章:

eclipse - 操作值与导航案例结果不匹配

c - 浮点异常(核心转储)

java - 保存网站 Activity 的更好选择

java - HashMap 不通过引用更新其值

java - "Unknow type constant pool at position X"在自 java 8 以来的 tomcat 日志中

jsf - 制作p :commandButton work like h:button

java - 如何在 Java 中获取特定类型的异常

c++ - 我可以在 C++ 中嵌套 try-catch block 吗?

java - Intellij IDEA 工具栏可用性

java - 如何在后台运行matlab?