jsr342-experts@javaee-spec.java.net

[jsr342-experts] Issues with interceptors bound to a component lifecycle callback

From: Bill Shannon <bill.shannon_at_oracle.com>
Date: Tue, 02 Apr 2013 15:22:45 -0700

As we got further into implementing support for interceptors bound to
lifecycle methods such as @PostConstruct, some problems were discovered.
Here's a description of the problem and some proposed solutions, written
by Marina Vatkina, Interceptors spec lead.

We'd like to get your feedback by Friday, April 5.

--- begin Marina ---

I. The example:

Assuming that interceptors @A and @B have proper interceptor bindings, define
@PostConstruct method each, and Priority reflects the alphabetical order of
their names:

@A
public class FooSuper {

    @PostConstruct
    private void pc1() {...}

}

public class FooSub extends FooSuper {

    @PostConstruct
    @B
    private void pc2() {...}

}


II. Possible options for an invocation order:

a) independent per-method ordering: A{pc1}->A{B{pc2}}

b) global ordering: A{B{pc1->pc2}}

c) mixed ordering: A{pc1->B{pc2}}

Where

"{" means ctx.proceed() call by an interceptor
"->" means the container calling the next method (there is no proceed in the LC
methods defined on the component itself)
"}" unwind to the previous level

III. Problems with the above options:

- Option (a) while the cleanest, would call A.postConstruct twice, so it's not
backward compatible.
- Option (b) would execute pc1 in the context that it didn't specify.
- Option (c) is not possible if priority is reversed
- If options (a) and (c) had their sub-chains unwind before calling the next
(i.e. the chain unwinds completely in (a) and one level in (c)) and if @B was a
transactional interceptor (that can only be REQUIRES_NEW if not by the attribute
value, then by the behavior specified in the JTA spec), pc1 and pc2 would never
be executed in the same transactional context.

As you can see, we didn't like any of the options (a) - (c)...

IV. Possible solutions for Java EE 7 and implications for the JTA spec:

1) Revert support for interceptors bound to a component lifecycle callback
method. This would require the JTA spec either (i) to remove support for
transactional interceptors on the lifecycle callbacks, or (ii) to always execute
lifecycle callbacks in the transaction defined by the class level interceptor,
or (iii) allow to use UserTransaction to do any transactional work in the
lifecycle callback events.

2) Allow interceptors bound to a component lifecycle callback method only if
there is a single callback event type defined in the component class hierarchy.
This would make it impossible to add transactional support for lifecycle
callbacks for existing components that declare more than one such callback.

2a) A possible workaround for option (2):

@A
public class FooSuper {

    //_at_PostConstruct
    protected void pc1() {...}

}

public class FooSub extends FooSuper {

    @B
    @PostConstruct
    protected void pc2() { pc1(); ...}

}

--- end Marina ---

As we discussed this issue further, we realized there are other workarounds
possible, such as moving some of the code that would've been in the
@PostConstruct method into another bean, injecting that bean into the
original bean, and calling a business method on the other bean in the
original bean's @PostConstruct method. The business method on the other
bean can be annotated and interceptors applied to it.

Given how late we are in this release, the consensus so far seems to be
to choose the safest option that doesn't restrict our choices in the future.
That's option #1 above - don't allow interceptors to be bound to lifecycle
methods, but allow use of UserTransaction in these methods.

If you have any concerns with this choice, please let us know ASAP.

And especially if you see any other options we should consider, let us know.

Thanks.