Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaEE: Perform task at the end of a request/transaction

we have a situation where we want to perform some tasks at the end of a request or transaction. More specifically, we need to collect some data during that request and at the end we use that data to do some automatic database updates.

This process should be as transparent as possible, i.e. users of the EJBs that require this should not have to worry about that.

In addition we can't control the exact call stack since there are multiple entry points to the process.

To achieve our goal, we're currently considering the following concept:

  1. certain low level operations (that are always called) fire a CDI event
  2. a stateless EJB listens for those events and upon receiving one it collects the data and stores it into a scoped CDI bean (either request scope or conversation scope would be fine)
  3. at the end of the request another event is fired which causes the data in the scoped CDI bean to be processed

So far, we managed to get steps 1 and 2 up and running.

However, the problem is step 3:

As I already said, there are multiple entry points to the process (originating from web requests, scheduled jobs or remote calls) and thus we thought of the following approach:

3a. A CDI extension scans all beans and adds an annotation to every EJB.

3b. An interceptor is registered for the added annotation and thus on every call to an EJB method the interceptor is invoked.

3c. The first invocation of that interceptor will fire an event after the invoked method has returned.

And here's the problem (again in the 3rd step :) ):

How would the interceptor know whether it was the first invocation or not?

We thought of the following, but neither worked so far:

  • get a request/conversation scoped bean
    • fails because no context is active
  • get the request/conversation context and activate it (which then should mark the first invocation since in subsequent ones the context should be active)
    • the system created another request context and thus WELD ends up with at least two active request contexts and complained about this
    • the conversion context stayed active or was deactivated prematurely (we couldn't yet figure out why)
  • start a long running conversation and end it after the invocation
    • failed because there was no active request context :(

Another option we didn't try yet but which seems to be discouraged:

  • use a ThreadLocal to either store some context data or at least to use invocation context propagatation as described here: http://blog.dblevins.com/2009/08/pattern-invocationcontext-propagation.html

However, AFAIK there's no guarantee that the request will be handled entirely by the same thread and thus wouldn't even invocation context propagation break when the container decides to switch to another thread?

So, thanks to all who endured with me and read through all that lengthy description.

Any ideas of how to solve this are welcome.

Btw, here are some of the software components/standards we're using (and which we can't switch):

  • JBoss 7.1.0.Final (along with WELD and thus CDI 1.0)
  • EJB 3.1
  • Hibernate 3.6.9 (can't switch to 4.0.0 yet)

UPDATE:

With the suggestions you gave so far, we came up with the following solution:

  1. use a request scoped object to store the data in
  2. the first time an object is stored in that object an event is fired
  3. a listener is invoked before the end of the transaction (using @Observes(during=BEFORE_COMPLETION) - thanks, @bkail)

This works so far but there's still one problem:

We also have MBeans that are managed by CDI and automatically registered to the MBean server. Thus those MBeans can get EJB references injected.

However, when we try and call an MBean method which in turn calls an EJB and thus causes the above process to start we get a ContextNotActiveException. This indicates that within JBoss the request context is not started when executing an MBean method.

This also doesn't work when using JNDI lookups to get the service instead of DI.

Any ideas on this as well?

Update 2:

Well, seems like we got it running now.

Basically we did what I described in the previous update and solved the problem with the context not being active by creating our own scope and context (which activated the first time an EJB method is called and deactivated when the corresponding interceptor finishes).

Normally we should have been able to do the same with request scope (at least if we didn't miss anything in the spec) but since there is a bug in JBoss 7.1 there is not always an active request context when calling EJBs from MBeans or scheduled jobs (which do a JNDI lookup).

In the interceptor we could try to get an active context and on failure activate one of those present in the bean manager (most likely EjbRequestContext in that case) but despite our tests we'd rather not count on that working in every case.

A custom scope, however, should be independent from any JBoss scope and thus should not interfere here.

Thanks to all who answered/commented.

So there's a last problem though: whose answer should I accept as you all helped us get into the right direction? - I'll try to solve that myself and attribute those points to jan - he's got the fewest :)

like image 776
Thomas Avatar asked Feb 20 '23 03:02

Thomas


1 Answers

Do the job in a method which is annotated with @PreDestroy.

@Named
@RequestScoped
public class Foo {

    @PreDestroy
    public void requestDestroyed() {
        // Here.
    }

}

It's invoked right before the bean instance is destroyed by the container.

like image 140
BalusC Avatar answered Feb 27 '23 15:02

BalusC