Hi,
On Friday, November 14, 2014, Jozef Hartinger <jharting(a)redhat.com
<javascript:_e(%7B%7D,'cvml','jharting@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(a)gmail.com>
> wrote:
>
>> Hi,
>>
>> On Wednesday, November 12, 2014, Jozef Hartinger <jharting(a)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(a)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(a)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.
>>
>>
>>