Hi David;
Thanks a lot for great comment!
Firstly, I would like to explain how decorators and interceptors of beans
are constructed and invoked by the current implementation. According to the
spec. "Decorator" and "WebBeans Interceptors" are just another beans. In
the early versions of the spec. contains a "fully functional interceptor"
chapter but now it is superseded by the "Interceptor Binding" section. I
wrote most of the interceptor logic code according to the early spec.
requirements, but it looks that it is still valid.
I will try to explain how I implemented decorator and interceptor related
functionality in detailed below. So any tinker can work on an old code :)
Let's dive into the code;
1- Configuration of decorators and interceptors
Decorators and Interceptors are configured from
"WebBeansContainerDeployer" class via methods
"configureInterceptors(scanner)" and "configureDecorators(scanner)". Those
methods further call "defineInterceptor(interceptor class)" and
"defineDecorator(decorator class)" methods. Those methods finally call
"WebBeansUtil#defineInterceptors" and "WebBeansUtil#defineDecorators"
methods for actual configuration.
Let's look at the "WebBeansUtil's" methods;
*defineInterceptors* : This method firstly creates a "Managed Bean" for the
given interceptor with "WebBeansType.INTERCEPTOR" as a type. After checking
some controls, it calls
"WebBeansInterceptorConfig#configureInterceptorClass".
"configureInterceptorClass" method creates a "WebBeansInterceptor" instance
that wraps the given managed bean instance and configuring interceptor's
*Interceptor Binding* annotations. If everything goes well, it adds
interceptor instance into the "BeanManager" interceptor list.
*defineDecorators* : Exactly doing same thing as "defineInterceptors". If
everything goes well, it adds decorator instance into the "BeanManager"
decorator list.
2* Configuring ManagedBean Instance Interceptor and Decorator Stack
Currently interceptors and decorators are supported for the "Managed
Beans". OWB delegates calling of "EJB Beans" interceptors to the EJB
container. It does not provide built-in interceptor and decorator support
for EJB beans. Current implementation supports configuration of the
interceptors on the "Managed Beans" with 2 different scenarios, i.e. it
supports "EJB related interceptors ( defined by EJB specification)" and
"JSR-299 related interceptors (defined by interceptor bindings)".
Lets look at the code for configuring "Managed Beans" interceptor and
decorator stack.
Managed Beans interceptor and decorator stacks are configured after they are
instantiated by the container first time. This method can be found in the
"AbstractInjectionTargetBean" class "afterConstructor()" method. Actual
configuration is done by the
"DefinitionUtil.defineSimpleWebBeanInterceptorStack" and
"DefinitionUtil.defineSimpleWebBeanDecoratorStack".
public static void
defineSimpleWebBeanInterceptorStack(AbstractBean<?> component)
> {
> Asserts.assertNotNull(component, "component parameter can no be
> null");
>
> // @javax.interceptor.Interceptors
> EJBInterceptorConfig.configure(component.getReturnType(),
> component.getInterceptorStack());
>
> // @javax.webbeans.Interceptor
> WebBeansInterceptorConfig.configure(component,
> component.getInterceptorStack());
> }
>
> public static void defineWebBeanDecoratorStack(AbstractBean<?> component,
> Object object)
> {
> WebBeansDecoratorConfig.configureDecarotors(component, object);
> }
>
>
In "DefinitionUtil.defineSimpleWebBeanInterceptorStack", firstly it
configures "EJB spec. interceptors" after that configures "JSR-299 spec.
interceptors."
In "DefinitionUtil.defineSimpleWebBeanDecoratorStack", it configures
decorator stack.
"EJBInterceptorConfig" class is responsible for finding all interceptors for
given managed bean class according to the EJB Specification. (But as you
said, it may not include @AroundInvoke/@PostConstruct etc. disablement
scenario!). "WebBeansInterceptorConfig" class is responsible for finding all
interceptors for a given managed bean class according to the "JSR-299,
spec." It adds all interceptors into the bean's interceptor stack. It first
adds "EJB" related interceptors, after that adds "JSR-299" related
interceptors. For "JSR-299" related interceptors, it orders the interceptors
according to the "InterceptorComparator". Basically, it puts interceptors in
order according to how they are ordered in a "beans.xml" configuration file.
Similarly, it configures managed bean's decorator stack according to the
decorator resolution rules. Also, it orders decorators according to the
"beans.xml" configuration file that contains decorator declarations.
3* Invocation of Interceptors and Decorators
Invocation is handled by the "InterceptorHandler" class (It has an absurd
name, it can be changed to a more meaningful name :)). It works nearly same
as what you have explained. First of all, it checks that calling method is
a business method of a managed bean or not. After that it filters
interceptor stack for calling method (Current design of filtering may not be
optimal!). Firstly it adds EJB interceptor to the list and then adds JSR-299
interceptors. After that, it starts to call all interceptors in order. After
consuming all interceptors it calls decorators. (as you explained, seems
that the logic may not be correct here. Currently, interceptors and
decorators are not related with each other. They are called independently).
Lets say that 2 interceptor and 1 decorator instance on the stack. Currently
it calls interceptor's @AroundInvoke methods, calls actual method of bean
instance and calls decorator stack and return the interceptor execution
result. This seems incorrect.
The correct way may be that after executing last interceptor instance, it
looks for the decorator stack. If decorator stack is not empty, it exhausts
all decorators before calling actual method. (I1 --> I2 --> D1 --> Actual
Method). This issue can be resolved easily by updating *InterceptorHandle*
code.
I hope that it helps!
Thanks;
--Gurkan
2009/10/15 David Blevins <[email protected]>
> As Mark is hammering on the observer code (thanks, Mark!), I moved over to
> Interceptors and am mulling over that code. For those that like to follow
> along, most the code is in InterceptorHandler. Ran into some related
> Decorator stuff as well.
>
> INTERCEPTOR RELATED
>
> First a small note, in the current code there is one interceptor stack per
> component, rather than per method. I see how we copy the list on each
> invoke and filter out the interceptors that do not apply to the method being
> invoked, but I didn't see if we were maintaining order in that list.
> Figured I'd just ask where the original list was created so I could take a
> look -- faster than digging. Pointer is welcome.
>
> A side note on the above, I think we could use some more diversity in the
> related tests. So far we don't have any components that have more than one
> interceptable method, so most the above logic is unchallenged. We should
> definitely add some tests that verify you only get interceptors that were
> declared in your method and not ones from other methods as well as verifying
> the order is as declared on the method. The current code shouldn't have a
> problem with that, but if someone wanted something to do, that'd be a great
> contribution.
>
> One area not implemented is something barely mentioned in the interceptor
> spec, but I know is tested in the EJB spec at least, is the concept of
> "Disable via override." If an @AroundInvoke method is overridden in a
> subclass without also being annotated @AroundInvoke, then this must dissable
> the around invoke method and it should not be called. This applies to
> @AroundInvoke methods in beans and interceptors. It also applies to
> @PostConstruct and @PreDestroy callbacks.
>
> INTERCEPTORS and DECORATORS
>
> It looks as though the part of the spec that says "Decorators are called
> after interceptors" was taken differently than intended. Currently, the
> interceptor stack wraps the target bean and the decorator "stack" more or
> less just gets a notification of the invocation as a separate after thought.
> As well Decorators were implemented as a list that is iterated over, rather
> than as a stack where one decorator calls the next in the chain.
>
> Interceptors and Decorators are completely identical and part of the same
> overall invocation stack. They both wrap an object and delegate to it.
> They both can manipulate the method parameters and both can manipulate the
> return value (or exception). The only difference is Interceptors are very
> reflection-like and Decorators are strongly typed.
>
> So what "Decorators are called after interceptors" means is that one stack
> is created and in that stack interceptors are invoked first, decorators are
> invoked second, and the target bean method is invoked last. The wording is
> unfortunate as it also means that the opposite is true on the way out: the
> bean returns and that value propagates back up the stack to the decorators,
> then the interceptors. So Interceptors and Decorators all wrap each other
> like one big onion. Interceptors are the outer most layers of the onion,
> the decorator layers follow, and finally the bean itself is the center.
>
> From an implementation standpoint what it means is that the last
> interceptor in the interceptor part of the stack is actually going to invoke
> the first decorator in that part of the stack and the invocation will
> continue. Code wise there's no real way for that last interceptor to know
> if its invoking the bean or a decorator that implements the same interface
> and method as the bean. It just calls what it was given. So we just need
> to make an actual stack out of the decorators and pass that into the
> interceptor stack in place of where we pass the bean now.
>
> If someone has time that's another good area for contribution.
>
>
> -David
>
>
>
--
Gurkan Erdogdu
http://gurkanerdogdu.blogspot.com