5

Imagine you have a filter that starts a database transaction, processes the request, and then then attempts to commit the transaction.

doFilter(...) {
    ...
    transaction.begin();
    filterChain.doFilter(request, response);
    transaction.commit();
}

Using Jersey, there are some problems:

  1. Using a Filter, the Jersey Servlet Container commits/flushes the response before execution returns to your filter. So, if the commit fails, you can't modify the return code to be a failure. Also, exceptions won't be caught by a JAX-RS ExceptionMapper.
  2. Using ContainerRequestFilter/ContainerResponseFilter.

    public ContainerRequest filter(ContainerRequest request) { ... }
    public ContainerResponse filter(ContainerRequest request, ContainerResponse response) { ... }

This allows exceptions to bubble up to an ExceptionMapper, but splits logic over 2 separate methods/interfaces. The problem is that if there's an exception that doesn't map to a response, the ContainerResponseFilter is never called, so you can't clean up.

What's the preferred way to handle this in a JAX-RS environment? Is there a way to configure the flushing of the response, or is there a class or interface that I'm overlooking?

Shaun
  • 2,300
  • 5
  • 26
  • 38
  • What you describe is commonly known as the "open session in view" (anti-)pattern. See [this SO question](http://stackoverflow.com/q/1103363/131929) for a discussion. Also turn to JBoss (Hibernate/Seam) for more discussions e.g. https://community.jboss.org/docs/DOC-13954 – Marcel Stör May 13 '13 at 21:07
  • There's certainly some debate over what exactly a "view" constitutes, but this is just a REST application. The jax-rs resource methods are the service layer, and they use persistence directly. The response object is the result of the service call. I'm trying to avoid having transaction management in every method, and the answer is either something resembling a filter, or I wire up CDI interception (which I'd like to avoid at this point in the project, if I can). – Shaun May 14 '13 at 09:38

1 Answers1

3

I've been researching this a bit as well for a JAX-RS/RESTEasy application. The two options I was considering before reading this question:

  1. Write an ExceptionMapper<Throwable> (or ExceptionMapper<DaoException> with a custom DaoException) and handle it there or in the ContainerResponseFilter that will get executed because of the ExceptionMapper<?> handling all exceptions.
  2. Before transaction.begin(), check the current state of the transaction, and roll back there if needed.

There are problems with both options.

  1. I find an ExceptionMapper<Throwable> too broad, while the ExceptionMapper<DaoException> might miss some other exception, again leaving the transaction not cleaned up.
  2. Rolling back the transaction on the next request might take a long time, possibly causing locking issues for other transactions.

So after reading your question, I'm currently thinking:

  • Using a ContainerRequestFilter to start transactions (in combination with the @NameBinding annotation construction).
  • Using a ContainerResponseFilter to commit transactions (if the resource method has not yet closed it).
  • Using a Filter to make sure the transaction was closed, and if not, roll it back.
drvdijk
  • 5,506
  • 2
  • 26
  • 47
  • How do you mean "ExceptionMapper too broad"? If you just check the session for an active transaction in an ExceptionMapper, you can be certain something went wrong and rollback. If no active transaction, do nothing. The problem I see is if the transaction.commit() fails in the ContainerResponseFilter, the response object would have to be changed. Not sure what happens if an exception is thrown in the ContainerResponseFilter, will it get caught by the ExceptionMapper? – joscarsson Oct 09 '13 at 21:01
  • Too broad in the sense that you might also have an `ExceptionMapper`. Which `ExceptionMapper>` will handle the exception? First the one for the `SpecificException` and then the one for `Throwable`? Or will the latter swallow a `SpecificException`? – drvdijk Oct 10 '13 at 17:08