[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