问题
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