[cdi-dev] Destroying beans from an interceptor

arjan tijms arjan.tijms at gmail.com
Fri Nov 14 11:27:18 EST 2014


Hi,

On Friday, November 14, 2014, Jozef Hartinger <jharting at redhat.com
<javascript:_e(%7B%7D,'cvml','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.


It looks like the issue may be even more general.

I have to do some more tests to confirm, but I think I observed a situation
where thread 1 at the end of a request destroyed session scoped bean Foo in
a Servlet. At that point thread 2 was already using bean Foo, and it kept
getting the same instance from the proxy and could even destroy the bean
again. Meanwhile, while thread 2 was getting the destroyed bean instance,
thread 3 that started after the instance was destroyed by thread 1 did get
a new instance.

It looked like some TLS caching issue, but as said I have to do some more
tests to see if this can be reproduced.

Kind regards,
Arjan Tijms



>> 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.CombinedInterceptorAndDecorato
>>>>> rStackMethodHandler.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.
>>>
>>>
>>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/cdi-dev/attachments/20141114/ac70ce62/attachment-0001.html 


More information about the cdi-dev mailing list