0

If you inject objects into itself in cdi, all interceptors are left out. That is a big difference to @EJB-Injection. In my project ejb-cdi-unit I try to simulate the injection of Ejbs by injecting them with CDI-means. Therefore I try to extend the already existing CDI-Extension by manipulating the InjectionTarget.

In the Web I found PropertyLoaderExtension as an example, how the InjectionTarget can be manipulated at the time when the actual injection occurs. The Propertymanipulating stuff I do not need, I am interested in activating the interceptors, when the proxy is called. To do that, I found, that the used Weld-Version provides two static functions in InterceptionDecorationContext. So I want to wrap each call through the injected field by calls to InterceptionDecorationContext. To do the actual wrapping I use the cglib-Enhancer.

    public <T> void initializeSelfInit(final @Observes ProcessInjectionTarget<T> pit) {

        final InjectionTarget<T> it = pit.getInjectionTarget();
        InjectionTarget<T> wrapped = new InjectionTarget<T>() {

            @Override
            public void inject(final T instance, CreationalContext<T> ctx) {
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(instance.getClass());
                enhancer.setCallback(new InvocationHandler() {
                    @Override
                    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                        InterceptionDecorationContext.startIfNotEmpty();
                        try {
                            return method.invoke(instance, objects);
                        } catch (Throwable thw) {
                            if (thw instanceof InvocationTargetException) {
                                throw thw.getCause();
                            } else {
                                throw thw;
                            }
                        }
                        finally {
                            InterceptionDecorationContext.endInterceptorContext();
                        }
                    }
                });
                it.inject((T)enhancer.create(), ctx);
            }
            @Override
            public void postConstruct(T instance) {
                it.postConstruct(instance);
            }
            @Override
            public void preDestroy(T instance) {
                it.dispose(instance);
            }
            @Override
            public void dispose(T instance) {
                it.dispose(instance);
            }
            @Override
            public Set<InjectionPoint> getInjectionPoints() {
                return it.getInjectionPoints();
            }
            @Override
            public T produce(CreationalContext<T> ctx) {
                return it.produce(ctx);
            }
        };
        pit.setInjectionTarget(wrapped);
    }

As soon as the instance gets injected, the wrapping should be done and the wrapped object should be injected instead. When anybody calls a method at this wrapped object which happens to be a bean with intercepters, the weld-specific call to InterceptionDecorationContext should activate them. At another place in the code, where the Resource SessionContext is simulated, that works already fine. SessionContextSimulation

@Override
public <T> T getBusinessObject(Class<T> businessInterface) throws IllegalStateException {
    Set<Bean<?>> beans = beanManager.getBeans(businessInterface);
    if (beans.isEmpty() && businessInterface.getName().endsWith("_WeldSubclass")) {
        beans = beanManager.getBeans(businessInterface.getSuperclass());
    }
    Bean<T> bean = (Bean<T>) beanManager.resolve(beans);

    final Object testBean1 = beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean));

    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(businessInterface);
    enhancer.setCallback(new InvocationHandler() {
        @Override
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
            InterceptionDecorationContext.startIfNotEmpty();
            try {
                return method.invoke(testBean1, objects);
            } catch (Throwable thw) {
                if (thw instanceof InvocationTargetException) {
                    throw thw.getCause();
                } else {
                    throw thw;
                }
            }
            finally {
                InterceptionDecorationContext.endInterceptorContext();
            }
        }
    });

    Object proxy = enhancer.create();

    return (T) proxy;
}

But: initializeSelfInit gets called at the right place, the wrapper introduced by it gets called by Weld, but not during the call of the injected object. There must be a misunderstanding in that handling of the InterceptionTarget.

aschoerk
  • 3,333
  • 2
  • 15
  • 29

1 Answers1

0

I found the solution, the misunderstanding was that the parameter instance is not the injected bean, but the bean where the injections happen. InjectionTarget.inject takes instance and fills all the injections correctly. The goal can be achieved by letting Weld (the superclass of InjectionTarget) do the injections and afterwards correct them if necessary by replacing the fields by the wrapped fields.

InjectionTarget<T> wrapped = new InjectionTarget<T>() {

            @Override
            public void inject(final T instance, CreationalContext<T> ctx) {
                it.inject(instance, ctx);
                // After injection replace all fields of self-type by enhanced ones which make sure interception is handled.
                for (AnnotatedField<? super T> f: pit.getAnnotatedType().getFields()) {
                    if (f.getJavaMember().getType().equals(pit.getAnnotatedType().getJavaClass())) {
                        try {
                            final Field javaMember = f.getJavaMember();
                            javaMember.setAccessible(true);
                            final Object currentInstance = javaMember.get(instance);
                            Enhancer enhancer = new Enhancer();
                            enhancer.setSuperclass(currentInstance.getClass());
                            enhancer.setCallback(new InvocationHandler() {
                                @Override
                                public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                                    SessionContextSimulation.startInterceptionDecorationContext();
                                    try {
                                        return method.invoke(currentInstance, objects);
                                    } catch (Throwable thw) {
                                        if (thw instanceof InvocationTargetException) {
                                            throw thw.getCause();
                                        } else {
                                            throw thw;
                                        }
                                    } finally {
                                        InterceptionDecorationContext.endInterceptorContext();
                                    }
                                }
                            });
                            javaMember.setAccessible(true);
                            javaMember.set(instance, enhancer.create());
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
aschoerk
  • 3,333
  • 2
  • 15
  • 29