@InterceptorBinding / CDI/ EJB 3.2 - problems with injection

匆匆过客 提交于 2019-12-24 17:00:14

问题


My dev environment is: WildFly 8.1, CDI, EJB 3.2, JDK 1.7. App is packed as an ear archive (one ejb + one war) because probably it gonna have other web modules in the future.

I'm struggling with a custom @InterceptorBinding type used inside my EJB stateless bean.

@Inherited
@InterceptorBinding
@Target({ TYPE, METHOD })
@Retention(RUNTIME)
@Documented
public @interface DetectIntegrityConstraintsViolation {
}

@javax.annotation.ManagedBean  // make it a CDI bean. @Interceptor itself should be enough, but WildFly 8.1 seems to have a bug, since it doesn't work without this line.
@Interceptor
@DetectIntegrityConstraintsViolation
public class ReferentialIntegrityConstraintViolationInterceptor {

    @PersistenceContext
    private EntityManager em;

    @Resource
    private SessionContext sessionContext;

    // ....
 }

beans.xml:

<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       version="1.1" bean-discovery-mode="annotated">

  <interceptors>
    <class>com.xxx.ejb.ReferentialIntegrityConstraintViolationInterceptor</class>
  </interceptors>
</beans>

When I'm calling my EJB method through REST service I'm getting Error injecting resource into CDI managed bean:

javax.naming.NameNotFoundException: Caused by java.lang.IllegalStateException: JBAS011048: Failed to construct component instance  Caused by: java.lang.IllegalArgumentException: JBAS016081: Error injecting resource into CDI managed bean.
Can't find a resource named java:comp/env/com.xxx.ejb.ReferentialIntegrityConstraintViolationInterceptor/sessionContext defined on private javax.ejb.SessionContext com.xxx.ejb.ReferentialIntegrityConstraintViolationInterceptor.sessionContext   at org.jboss.as.weld.services.bootstrap.WeldResourceInjectionServices.resolveResource(WeldResourceInjectionServices.java:188) [wildfly-weld-8.1.0.Final.jar:8.1.0.Final]

So walking in the dark, I've moved to the ResourceLookup approach:

@ManagedBean
@Interceptor
@DetectIntegrityConstraintsViolation
public class ReferentialIntegrityConstraintViolationInterceptor {

    @PersistenceContext
    private EntityManager em;

    private SessionContext sessionContext;

    @PostConstruct
    public void init(InvocationContext ctx) {
        try {
            InitialContext ic = new InitialContext();
            this.sessionContext = (SessionContext)ic.lookup("java:comp/EJBContext");
        } catch (NamingException ex) {
            throw new RuntimeException(ex.getMessage());
        }
    }

    // .....
}

Then Injection started to work, but I got a new error:

Caused by: org.jboss.weld.exceptions.DefinitionException: WELD-000619: An interceptor for lifecycle callbacks Interceptor [class com.xxx.ejb.ReferentialIntegrityConstraintViolationInterceptor intercepts @DetectIntegrityConstraintsViolation] declares and interceptor binding interface com.xxx.ejb.DetectIntegrityConstraintsViolation with METHOD as its @Target.

So when removed from the DetectIntegrityConstraintsViolation a METHOD target:

@Inherited
@InterceptorBinding
@Target({ TYPE /*, METHOD*/ }) // CRUCIAL
@Retention(RUNTIME)
@Documented
public @interface DetectIntegrityConstraintsViolation {
}

Then it started to work. But why??
And why I cannot have my annotation placed on the method? Do somebody know?

BTW: what is even more strange, when i'm not using @InterceptorBinding, but plain old:

@Override
// @DetectIntegrityConstraintsViolation
@Interceptors(ReferentialIntegrityConstraintViolationInterceptor.class)
public User updateUser(final User user) {
    // ...
}

Interceptor works flawlessly even on a method level.

I find EJB and Weld so awkward to use...


回答1:


One of your error messages is described in the CDI spec:

An interceptor for lifecycle callbacks may only declare interceptor binding types that are defined as @Target(TYPE). If an interceptor for lifecycle callbacks declares an interceptor binding type that is defined @Target({TYPE, METHOD}), the container automatically detects the problem and treats it as a definition error.

You have created a lifecycle callback with @PostConstruct init(InvocationContext ctx). This callback is meant to be run on the construction of the bean you are intercepting, so applying it to a method doesn't make sense.

As for why plain old @Interceptor is working, that's also described in the documentation:

An around-invoke interceptor may be defined to apply only to a specific method of the target class. Likewise, an around-timeout interceptor may be defined to apply only to a specific timeout method of the target class. However, if an interceptor class that defines lifecycle callback interceptor methods is defined to apply to a target class at the method level, the lifecycle callback interceptor methods are not invoked.

As for this:

I find EJB and Weld so awkward to use...

You will have an easier time if you slow down and try to learn as you go. You seem to be trying random things and getting confused by the results, which is to be expected if you're unfamiliar with CDI and EJBs.

I'm also concerned that you're using the @ManagedBean annotation. One, it's practically deprecated, and two, it's for JSF, which you didn't say you're using.




回答2:


Thanks to @DavidS I've managed to make it work. Kudos for him.

He pointed me, that @PostConstruct inside @Interceptor has a different meaning than I thought.

Below correct code with comments:

@Inherited
@InterceptorBinding
@Target({ TYPE, METHOD })
@Retention(RUNTIME)
@Documented
public @interface DetectIntegrityConstraintsViolation {
}


import javax.annotation.ManagedBean;
import javax.annotation.Resource;
import javax.ejb.SessionContext;
import javax.persistence.PersistenceContext;

@ManagedBean // make it a CDI bean. @Interceptor itself should be enough, but WildFly 8.1 seems to have a bug, since it doesn't work without this line.
@Interceptor
@DetectIntegrityConstraintsViolation
public class ReferentialIntegrityConstraintViolationInterceptor {

    @PersistenceContext
    private EntityManager em;

    @Resource(name = "java:comp/EJBContext") // injecting Java EE resource into CDI bean
    private SessionContext sessionContext;

    @AroundInvoke
    public Object processInvocation(InvocationContext ctx) throws Exception {
        // ...
    }
 }

So now I can use inside my EJB beans:

@DetectIntegrityConstraintsViolation
public User updateUser(final User user) {
    // ...
}

instead of:

@Interceptors(ReferentialIntegrityConstraintViolationInterceptor.class)
public User updateUser(final User user) {
    // ...
}

Explanation for peopole coming here in the future:

@InterceptorBinding mechanism comes from a CDI world, so interceptor has to became a CDI bean itself. And it comes with certain consequences:

  • Interceptor has to be explicitly specified in beans.xml
  • Interceptor (if bean-discovery-mode="annotated") has to be made a CDI bean; so annotate it with @javax.interceptor.Interceptor. Unfortunately WildFly 8.1 seems to have some bug, because it refused to work without @javax.annotation.ManagedBean.
  • Some Java EE resources injection (like EJB SessionContext) inside interceptor is done via JNDI


来源:https://stackoverflow.com/questions/30894880/interceptorbinding-cdi-ejb-3-2-problems-with-injection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!