8.12. Managing exceptions
JSF has a limited ability to handle exceptions. To work around this problem, Seam alows you to define treatment of an exception class through annotation, or through declaration in an XML file. This combines with the EJB3-standard
@ApplicationException annotation, which specifies whether the exception should cause a transaction rollback.
8.12.1. Exceptions and transactions
EJB specifies well-defined rules to control whether an exception immediately marks the current transaction for rollback, when the exception is generated by a business method of the bean. System exceptions always cause a transaction rollback. Application exceptions do not cause a rollback by default, but they cause a rollback if
@ApplicationException(rollback=true) is specified. (An application exception is any checked exception, or any unchecked exception annotated @ApplicationException. A system exception is any unchecked exception without an @ApplicationException annotation.)
Note
Marking a transaction for rollback is not the same as actually rolling back the transaction. The exception rules say that the transaction should only be marked for rollback, but it may still be active after the exception is generated.
Seam also applies the EJB3 exception rollback rules to Seam JavaBean components. These rules apply only in the Seam component layer. When an exception occurs outside the Seam component layer, Seam rolls back active transactions.
8.12.2. Enabling Seam exception handling
To enable Seam's exception handling, the master Servlet filter must be declared in
web.xml:
<filter> <filter-name>Seam Filter</filter-name> <filter-class>org.jboss.seam.servlet.SeamFilter</filter-class> </filter> <filter-mapping> <filter-name>Seam Filter</filter-name> <url-pattern>*.seam</url-pattern> </filter-mapping>
For the exception handlers to execute, disable Facelets development mode in
web.xml and Seam debug mode in components.xml.
8.12.3. Using annotations for exception handling
The following exception results in an HTTP 404 error whenever it propagates outside the Seam component layer. The exception does not roll back the current transaction immediately, but the transaction is rolled back if the exception is not caught by another Seam component.
@HttpError(errorCode=404) public class ApplicationException extends Exception { ... }
This exception results in a browser redirect whenever it propagates outside the Seam component layer. It also ends the current conversation. It causes an immediate rollback of the current transaction.
@Redirect(viewId="/failure.xhtml") @ApplicationException(rollback=true) public class UnrecoverableApplicationException extends RuntimeException { ... }
Note
Seam cannot handle exceptions that occur during JSF's
RENDER_RESPONSE phase, as it is not possible to perform a redirect once writing to the response has begun.
You can also use EL to specify the
viewId to redirect to. When this exception propagates outside the Seam component layer, it results in a redirect and a message to the user. It also immediately rolls back the current transaction.
@Redirect(viewId="/error.xhtml", message="Unexpected error") public class SystemException extends RuntimeException { ... }
8.12.4. Using XML for exception handling
As annotations cannot be added to all exception classes, Seam allows you to specify this functionality in
pages.xml.
<pages> <exception class="javax.persistence.EntityNotFoundException"> <http-error error-code="404"/> </exception> <exception class="javax.persistence.PersistenceException"> <end-conversation/> <redirect view-id="/error.xhtml"> <message>Database access failed</message> </redirect> </exception> <exception> <end-conversation/> <redirect view-id="/error.xhtml"> <message>Unexpected failure</message> </redirect> </exception> </pages>
The final
<exception> declaration does not specify a class, and acts as catch-all for any exception without specified handling through annotations or in pages.xml.
You can also use EL to specify the
view-id to redirect to. You can also access the handled exception instance through EL. Seam places the exception in the conversation context. For example, to access the exception message:
... throw new AuthorizationException("You are not allowed to do this!"); <pages> <exception class="org.jboss.seam.security.AuthorizationException"> <end-conversation/> <redirect view-id="/error.xhtml"> <message severity="WARN"> #{org.jboss.seam.handledException.message} </message> </redirect> </exception> </pages>
The
org.jboss.seam.handledException holds the nested exception that was handled by an exception handler. The outermost (wrapper) exception is also available as org.jboss.seam.caughtException.
8.12.4.1. Suppressing exception logging
For the exception handlers defined in
pages.xml, it is possible to declare the level at which the exception is logged, or to suppress exception logging altogether. The log and log-level attributes are used to control exception logging. No log message is generated if the specified exception occurs when log="false" is set, as shown here:
<exception class="org.jboss.seam.security.NotLoggedInException" log="false"> <redirect view-id="/register.xhtml"> <message severity="warn"> You must be a member to use this feature </message> </redirect> </exception>
If the
log attribute is not specified, then it defaults to true — that is, the exception will be logged. Alternatively, you can specify the log-level to control the level at which the exception is logged:
<exception class="org.jboss.seam.security.NotLoggedInException" log-level="info"> <redirect view-id="/register.xhtml"> <message severity="warn"> You must be a member to use this feature </message> </redirect> </exception>
The acceptable values for
log-level are: fatal, error, warn, info, debug, and trace. If the log-level is not specified, or if an invalid value is configured, log-level defaults to error.
8.12.5. Some common exceptions
If you are using JPA:
<exception class="javax.persistence.EntityNotFoundException"> <redirect view-id="/error.xhtml"> <message>Not found</message> </redirect> </exception> <exception class="javax.persistence.OptimisticLockException"> <end-conversation/> <redirect view-id="/error.xhtml"> <message> Another user changed the same data, please try again </message> </redirect> </exception>
If you are using the Seam Application Framework:
<exception class="org.jboss.seam.framework.EntityNotFoundException"> <redirect view-id="/error.xhtml"> <message>Not found</message> </redirect> </exception>
If you are using Seam Security:
<exception class="org.jboss.seam.security.AuthorizationException"> <redirect> <message>You do not have permission to do this</message> </redirect> </exception> <exception class="org.jboss.seam.security.NotLoggedInException"> <redirect view-id="/login.xhtml"> <message>Please log in first</message> </redirect> </exception>
And, for JSF:
<exception class="javax.faces.application.ViewExpiredException"> <redirect view-id="/error.xhtml"> <message>Your session has timed out, please try again</message> </redirect> </exception>
A
ViewExpiredException occurs when a user posts to a page after the session has expired. The conversation-required and no-conversation-view-id settings in the Seam page descriptor, discussed in Section 9.4, “Requiring a long-running conversation”, allow finer-grained control over session expiration while accessing a page used within a conversation.