You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@openwebbeans.apache.org by David Blevins <da...@visi.com> on 2009/10/15 11:18:20 UTC
Interceptors and Decorators
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
Re: Interceptors and Decorators
Posted by David Blevins <da...@visi.com>.
Great info! Some form of all this could make for great javadoc at the
top of the InterceptorHandler class. At minimum a few "@see" pointers
to the code mentioned.
On Oct 15, 2009, at 5:45 AM, Gurkan Erdogdu wrote:
> 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.
This would be great code for the OpenWebBeansEjbInterceptor.
Essentially it would have an @AroundInvoke method and be placed last
in the EJB interceptor stack. In its @AroundInvoke method it would
call the "web beans" Interceptors, call all the Decorators, and then
finally call invocationContext.proceed() which effectively hands
control back to the ejb container who then invokes the EJB bean
instance's target method. I left out a lot of tricky details, but
that's how it could work at a high level.
> 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)".
Spec note that @InterceptorBinding is moving into the
javax.interceptor package and will be part of the EJB Interceptor
spec. The actual definition of it now will still remain in 299 and
EJB 3.1 containers won't be required to support it, but in the future
they likely will.
So in the future the line between "EJB" Interceptor and "WebBeans"
interceptor will get pretty blurry -- more so than it already is.
There'll be pretty much no line :) Hopefully Decorators will be added
to EJB.next as well (I wanted them in EJB 3.1, but there just wasn't
enough bandwidth to address it), in which case the overlap between EJB
and WebBeans will get even bigger. Might be an opportunity for code
reuse between OpenEJB and OpenWebBeans down the line.
> "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!).
No worries there, it's an obscure requirement even we (OpenEJB) didn't
find till we started running the TCK. We (OWB) can add it easily
later without much change to the way the code is written.
> 3* Invocation of Interceptors and Decorators
>
> Invocation is handled by the "InterceptorHandler" class [...] After
> that it filters
> interceptor stack for calling method (Current design of filtering
> may not be
> optimal!).
I gave that chunk of code a good long mental scrubbing and even
started to write some notes on changing it, but I like it more and
more. Checking equals on a small number of objects is pretty quick,
usually faster than an array copy. There usually aren't a large
number of interceptors and if there are they are typically defined at
the class level or more generallly -- at the method level is less
common. Any scenario involving starting with an empty array
(ArrayList) and adding items to it could involve growing that
ArrayList. If we start with the full list, copy it, then yank out the
values that don't apply, we guarantee that the first array created is
the right size. So the logic involves no hash lookups and exactly one
array copy. The filtering is mostly boolean comparisons until we
reach the entries that apply to specific methods and start
calling .equals().
The only other way for it to be faster is to go the exact opposite
approach and build a full stack for each method in advance, so the
entire stack is one hashmap lookup away and the method is the key.
Building one stack per method can be bad. I've found that doing that
kind of processing in advance can significantly raise the deployment
cost. Doing special processing for every method can really drag
startup as the number of beans and methods increase. This way,
there's no actual growing cost when beans/methods increase, just when
the number of interceptors increase and that's almost always going to
be a smaller number.
I'm very tempted to go back and rewrite the related OpenEJB code.
A small note is that InterceptorHandler actually doesn't do the list
copy in they way I describe -- it copies one item at a time which
could grow the original array in the new ArrayList -- but that's a one
line change. Can just pass the read-only array into the ArrayList
constructor when creating the temp list. Then what I mention is true.
Good small focused change if someone wants to throw that in real quick.
> 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.
Right, exactly.
-David
Re: Interceptors and Decorators
Posted by Gurkan Erdogdu <cg...@gmail.com>.
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 <da...@visi.com>
> 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
Re: Interceptors and Decorators
Posted by Mark Struberg <st...@yahoo.de>.
If it helps, I think I enabled cobertura for the site plugin.
I will do a site deploy-staged to my people@ao in the afternoon.
The public site needs to be updated too I think.
LieGrue,
strub
----- Original Message ----
> From: David Blevins <da...@visi.com>
> To: openwebbeans-dev@incubator.apache.org
> Sent: Thu, October 15, 2009 11:18:20 AM
> Subject: Interceptors and Decorators
>
> 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