[cdi-dev] Destroying beans from an interceptor
Mark Struberg
struberg at yahoo.de
Fri Nov 14 05:01:18 EST 2014
I got your point Jozef, and I fear you are right. This is just pretty dangerous in highly concurrent scenarios :/
LieGrue,
strub
> On Friday, 14 November 2014, 10:47, Jozef Hartinger <jharting at redhat.com> wrote:
> >
> On 11/14/2014 09:14 AM, Mark Struberg wrote:
>> I did not say it cannot work but that it is not guaranteed to work.
> It's just totally up to implementation details of the container and your
> actual situation. Basically it's non-portable at best.
>>
>>
>> E.g. consider the case that you are NOT the outermost interceptor but there
> are 2 other decorators and interceptors. The decorators will probably not hurt
> much as they are defined to be called _after_ interceptors. But if there is an
> interceptor in addition to yours then you will probably kill em and after
> returning from the chain you might end up in a dead bean (the other
> interceptor).
> You are right Mark but I think this is not a problem of whether you call
> AlterableContext.destroy() from within an interceptor or not. This is a
> more general problem of destroying instances. Say you have an
> intercepted @ApplicationScoped bean Foo. Thread 1 is calling an
> intercepted method and is currently in the middle of the interceptor
> chain. Thread 2 calls AlterableContext.destroy(Foo) in a Servlet (not
> from an interceptor!). Foo is destroyed together with its interceptors
> by Thread 2. Thread 1 finds itself executing a dead interceptor chain
> for a destroyed bean.
>
>>
>> There are just so many things which can go wrong. Even though I like the
> general idea what you like to do with that interceptor. But I suggest you
> probably use another trick. Every CDI bean must (as per the interceptors spec)
> support 'self-interception'. Means you only need to add an @AroundInvoke
> method and do the re-setup of your CONNECTION inside your @ApplicationScoped
> bean (with full access to the underlying business infrastructure).
>>
>>
>> This is fundamentally different to your approach as I do not ditch the
> whole service but only fix the thing which broke in it.
>>
>>
>> LieGrue,
>> strub
>>
>>
>>
>> On Thursday, 13 November 2014, 16:28, arjan tijms
> <arjan.tijms at gmail.com> wrote:
>>> Hi,
>>>
>>> On Wednesday, November 12, 2014, Jozef Hartinger
> <jharting at redhat.com> wrote:
>>>
>>> Hi Arjan,
>>>> there is a bug in Weld (WELD-1785) preventing this from working
> which is going to be fixed in the next release. What you are doing should work
> IMO as long as the interceptor does not call any other methods on the target
> instance.
>>>
>>> That's great to hear really.
>>>
>>>
>>> I'm slightly confused through why Mark thinks this cannot really
> work, while you say it should.
>>>
>>>
>>> Is there something in the spec that may need to be clarified here? Ie
> some words about what an interceptor is at least allowed to do and what is
> definitely not allowed?
>>>
>>>
>>>
>>> In addition it must count with the target instance being destroyed
> within the instance.destroy() call.
>>>
>>>
>>> Sorry, I don't fully follow this. You mean something must be
> counted?
>>>
>>>
>>>> Perhaps a nicer way of doing this would be:
>>>>
>>>> @Inject
>>>> @Intercepted
>>>> private Bean<?> bean;
>>>>
>>>> Context context = manager.getContext(bean.getScope());
>>>> if (!(context instanceof AlterableContext)) {
>>>> throw new IllegalStateException("Context does not
> support removal of instances");
>>>> }
>>>> AlterableContext alterableContext =
> AlterableContext.class.cast(context);
>>>> alterableContext.destroy(bean);
>>>
>>> I tried something close to that as well, just used the bean manager to
> resolve a Bean from the target object. Thanks for the suggestion!
>>>
>>>
>>> Kind regards,
>>> Arjan
>>>
>>>
>>>
>>>
>>>> On 11/10/2014 02:59 PM, arjan tijms wrote:
>>>>
>>>> Hi,
>>>>> I wonder if it would be allowed according to the CDI spec to
> destroy a
>>>>> bean instance from within an interceptor.
>>>>>
>>>>> To test this (on Weld) I used the following code:
>>>>>
>>>>> @Interceptor
>>>>> @DestroyOnError
>>>>> @Priority(APPLICATION)
>>>>> public class DestroyOnErrorInterceptor implements Serializable
> {
>>>>>
>>>>> private static final long serialVersionUID = 1L;
>>>>>
>>>>> @AroundInvoke
>>>>> public Object tryInvoke(InvocationContext ctx) throws
> Exception {
>>>>>
>>>>> try {
>>>>> return ctx.proceed();
>>>>> } catch (Exception e) {
>>>>> destroy(ctx.getMethod().getDeclaringClass());
>>>>> throw e;
>>>>> }
>>>>> }
>>>>>
>>>>> private <T> void destroy(Class<T> clazz)
> {
>>>>> Instance<T> instance =
> CDI.current().select(clazz);
>>>>> instance.destroy(instance.get());
>>>>> }
>>>>>
>>>>> }
>>>>>
>>>>>
>>>>> When I use this interceptor on a SessionScoped bean:
>>>>>
>>>>> @SessionScoped
>>>>> public class TestBean implements Serializable {
>>>>>
>>>>> private static final long serialVersionUID = 1L;
>>>>>
>>>>> @DestroyOnError
>>>>> public void test() {
>>>>> throw new IllegalStateException();
>>>>> }
>>>>> }
>>>>>
>>>>> And then inject said bean in say a Servlet and call the test()
> method,
>>>>> destruction of the bean happens partially, but as soon as Weld
> tried
>>>>> to invocate a preDestroy method, it goes through the bean proxy
> again,
>>>>> detects that "the" interceptor handler is already
> active, promptly
>>>>> skips its attempt to call a preDestroy method and then to add
> insult
>>>>> to injury tries to call a "proceed" method which is
> always null and
>>>>> thus throws a NPE.
>>>>>
>>>>> (this happens in
>>>>>
> org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler.invoke)
>>>>>
>>>>> I tried some alternative methods to destroy the bean such as:
>>>>>
>>>>>
>>>>> Bean<T> bean = resolve(beanManager, beanClass);
>>>>>
>>>>> AlterableContext context = (AlterableContext)
>>>>> beanManager.getContext(bean.getScope());
>>>>> context.destroy(bean);
>>>>>
>>>>> with resolve being:
>>>>>
>>>>> public static <T> Bean<T> resolve(BeanManager
> beanManager, Class<T> beanClass) {
>>>>> Set<Bean<?>> beans =
> beanManager.getBeans(beanClass);
>>>>>
>>>>> for (Bean<?> bean : beans) {
>>>>> if (bean.getBeanClass() == beanClass) {
>>>>> return (Bean<T>)
>>>>>
> beanManager.resolve(Collections.<Bean<?>>singleton(bean));
>>>>> }
>>>>> }
>>>>>
>>>>> return (Bean<T>) beanManager.resolve(beans);
>>>>> }
>>>>>
>>>>> But this resulted in the same problem.
>>>>>
>>>>> Any idea?
>>>>>
>>>>> Kind regards,
>>>>> Arjan Tijms
>>>>> _______________________________________________
>>>>> cdi-dev mailing list
>>>>> cdi-dev at lists.jboss.org
>>>>> https://lists.jboss.org/mailman/listinfo/cdi-dev
>>>>>
>>>>> Note that for all code provided on this list, the provider
> licenses the code under the Apache License, Version 2
> (http://www.apache.org/licenses/LICENSE-2.0.html). For all other ideas provided
> on this list, the provider waives all patent and other intellectual property
> rights inherent in such information.
>>>>>
>>> _______________________________________________
>>> cdi-dev mailing list
>>> cdi-dev at lists.jboss.org
>>> https://lists.jboss.org/mailman/listinfo/cdi-dev
>>>
>>> Note that for all code provided on this list, the provider licenses the
> code under the Apache License, Version 2
> (http://www.apache.org/licenses/LICENSE-2.0.html). For all other ideas provided
> on this list, the provider waives all patent and other intellectual property
> rights inherent in such information.
>>>
>>>
>
More information about the cdi-dev
mailing list