1 Reply Latest reply on Dec 5, 2011 1:32 PM by lightguard

    Handle and throw with Seam Catch 3.0

    dahm

      Hi,


      I'm not absolutely sure how to handle the following situation with Seam Catch (nice work, by the way!)


      We'd like to catch and handle ConstraintViolationException which are caused by DB constraints. However, the client code (web controller) does not know the specific cause and I do not want to put
      too much exception handling logic in there.


      The situation in more detail is that when the user wants to delete a certain entity, but that entity may be referenced in
      a large number of different collections. It would be very cumbersome to check all those collections via JPA whether it is safe to delete the entity.


      Instead, I would like to handle the ConstrainViolationException and raise a new client-specific Exception that the client code can catch and display a message with.


      We currently use the following ExceptionHandler, which does not quite do what I want, because the rethrow() is never seen by the client. If I move the rethrow() to be the last
      statement the default exception handler catches the original exception. To sum up: I need an expert :-)




      package com.meyle.deva.web.exceptions;
      
      import java.io.IOException;
      import java.io.PrintWriter;
      import java.io.StringWriter;
      import java.sql.SQLException;
      
      import javax.faces.application.ViewExpiredException;
      import javax.faces.context.FacesContext;
      import javax.inject.Inject;
      import javax.persistence.Cache;
      import javax.persistence.EntityManager;
      import javax.persistence.OptimisticLockException;
      import javax.persistence.PersistenceContext;
      import javax.validation.ConstraintViolationException;
      
      import org.apache.commons.lang3.exception.ExceptionUtils;
      import org.hibernate.LazyInitializationException;
      import org.hibernate.StaleObjectStateException;
      import org.jboss.logging.Logger;
      import org.jboss.seam.exception.control.CaughtException;
      import org.jboss.seam.exception.control.Handles;
      import org.jboss.seam.exception.control.HandlesExceptions;
      import org.jboss.seam.exception.control.TraversalMode;
      
      import com.meyle.deva.common.exceptions.DevaConstraintValidationException;
      import com.meyle.deva.common.exceptions.DevaSQLException;
      import com.meyle.deva.web.debug.Debug;
      import com.meyle.deva.web.security.LogoutController;
      import com.meyle.deva.web.viewconfig.DevAViewConfig;
      
      @HandlesExceptions
      public class DevaExceptionHandler {
        @Inject
        private LogoutController _logoutController;
      
        @Inject
        private Logger _log;
      
        @Inject
        private FacesContext _facesContext;
      
        @Inject
        private Debug _debug;
      
        @PersistenceContext
        private EntityManager _entityManager;
      
        // Das ist zwar nicht ganz korrekt, aber ohne logout bleibt die Exception hängen
        public void lazyInitializationHandler(@Handles final CaughtException<LazyInitializationException> event) {
          _log.warn("Exception handled by lazyInitializationHandler", event.getException());
          redirect(DevAViewConfig.URL_VIEW_EXPIRED);
      
          setHandled(event);
          clearCache();
      
          _logoutController.logout();
        }
      
        public void transactionBugHandler(@Handles final CaughtException<IllegalStateException> event) {
          _log.warn("Exception handled by transactionBugHandler", event.getException());
      
          if (event.getException().getMessage().startsWith("BaseTransaction.checkTransactionState")) {
            redirect(DevAViewConfig.URL_VIEW_EXPIRED);
            setHandled(event);
            clearCache();
            _logoutController.logout();
          }
        }
      
        public void constraintViolationHandler(
            @Handles(precedence = 100, during = TraversalMode.BREADTH_FIRST) final CaughtException<ConstraintViolationException> event) {
          handleConstraintViolation(event);
        }
      
        public void hibernateConstraintViolationHandler(
            @Handles(precedence = 100, during = TraversalMode.BREADTH_FIRST) final CaughtException<org.hibernate.exception.ConstraintViolationException> event) {
          handleConstraintViolation(event);
        }
      
        private void handleConstraintViolation(final CaughtException<? extends Exception> event) {
          _log.warn("Exception handled by constraintViolationHandler", event.getException());
      
          event.rethrow(new DevaConstraintValidationException(event.getException()));
          setHandled(event);
          clearCache();
        }
      
        public void sqlExceptionHandler(@Handles(precedence = 50, during = TraversalMode.BREADTH_FIRST) final CaughtException<SQLException> event) {
          final SQLException exception = event.getException();
          _log.warnv("Exception handled by sqlExceptionHandler: {0}, {1}, {2}", exception.getMessage(), exception.getErrorCode(),
              exception.getSQLState());
          _log.warn("SQL exception", event.getException());
      
          event.rethrow(new DevaSQLException(event.getException()));
          setHandled(event);
          clearCache();
        }
      
        public void viewExpiredHandler(@Handles final CaughtException<ViewExpiredException> event) {
          _log.warn("Exception handled by viewExpiredHandler", event.getException());
          redirect(DevAViewConfig.URL_VIEW_EXPIRED);
      
          setHandled(event);
          clearCache();
      
          _logoutController.logout();
        }
      
        public void optimisticLockHandler(@Handles final CaughtException<OptimisticLockException> event) {
          handleOptimisticLock(event);
        }
      
        public void staleObjectExceptionHandler(@Handles final CaughtException<StaleObjectStateException> event) {
          handleOptimisticLock(event);
        }
      
        private void handleOptimisticLock(final CaughtException<? extends Throwable> event) {
          _log.warn("Exception handled by optimisticLockHandler", event.getException());
          redirect(DevAViewConfig.URL_OPTIMISTIC_LOCK);
      
          clearCache();
          setHandled(event);
        }
      
        // @WebRequest
        public void throwableHandler(@Handles final CaughtException<Throwable> event) {
          final long bugId = System.currentTimeMillis();
          final Throwable exception = event.getException();
          final String stackTrace = getStackTrace(exception);
      
          _log.error("Bug-Id:" + bugId + ": Error occured handled by throwableHandler", exception);
          _debug.setCurrentBugId(bugId);
          _debug.setStackTrace(stackTrace);
      
          redirect(DevAViewConfig.URL_ERROR);
          setHandled(event);
        }
      
        private String getStackTrace(final Throwable exception) {
          final StringWriter out = new StringWriter();
          ExceptionUtils.printRootCauseStackTrace(exception, new PrintWriter(out));
          final String stackTrace = out.toString();
          return stackTrace;
        }
      
        private void setHandled(final CaughtException<? extends Throwable> event) {
          event.unmute();
          event.handled();
        }
      
        private void clearCache() {
          try {
            final Cache cache = _entityManager.getEntityManagerFactory().getCache();
      
            if (cache != null) {
              cache.evictAll();
              _log.info("Cache cleared");
            } else {
              _log.warn("No cache configured!");
            }
          } catch (final Throwable e) {
            _log.warn("Could not clear cache", e);
          }
        }
      
        private void redirect(final String viewId) {
          try {
            // facesContext.getExternalContext().responseReset();
            _facesContext.getExternalContext().redirect(viewId);
          } catch (final IOException e) {
            _log.warn("Redirect failed", e);
          }
        }
      }
      
      



      BTW. Throwing an exception in the handler itself does not work, either (which is OK I think...).


      java.lang.IllegalArgumentException: Handler method public void com.meyle.deva.web.exceptions.DevaExceptionHandler.dhibernateConstraintViolationHandler(org.jboss.seam.exception.control.CaughtException) throws com.meyle.deva.common.exceptions.DevaConstraintValidationException must not throw exceptions
      


      Cheers
        Markus