Primary menu

From time to time, in a system I develop, which uses EJB3 beans as the basic “component model”, I have a need to do some operations using a new entity manager (but still in the same transaction). The problem here is that if I invoke a method on another bean, which has an entity manager injected (using @PersistenceContext EntityManager em), the entity manager will be the original one. There is no way to temporarily replace it or any other dependency, just for the time of one invocation.

That’s why I wanted to see if that would be possible to achieve using the recently released Weld framework, reference implementation of the CDI specification (part of JEE 6). Unlike in Seam, where injection is done whenever a method on a managed bean is invoked (which would theoretically make it easier to do the temporary replacement), in CDI injection happens once, when the objects are constructed. So the only way (at least the only way I see) to implement temporary replacement is to wrap the injected beans in some kind of an interceptor.

In CDI, injected beans can either be “stand-alone”, or come from a producer (factory) method (annotated with @Producer). As EntityManagers are typically created by producer methods, in the example below I assume this scenario. It is possible to slightly change the code so that it works for normal beans, however I didn’t manage to create a nice universal solution (which wouldn’t simply do an “if” to check if the bean is created by a producer method).

To test, we define a simple bean which will have a dependency injected via constructor injection:

The @Alternative annotation makes the bean disabled by default, so that Weld doesn’t try to inject an instance of it when there’s a matching injection point. That’s because we want to produce implementations of ISecondBean using a producer method:

1
2
3
4
5
6
7

@ApplicationScoped
publicclass SecondBeanProducer {
@Produces
public ISecondBean getSecondBean(){returnnew SecondBeanA();// by default we use the A variant}}

@ApplicationScoped
public class SecondBeanProducer {
@Produces
public ISecondBean getSecondBean() {
return new SecondBeanA(); // by default we use the A variant
}
}

A very nice feature of Weld/CDI, especially for evaluation purposes, is that it’s possible to use it very easily in Java SE. To test our beans, all we need to do is run the org.jboss.weld.environment.se.StartMain, putting a jar with our classes in the classpath. One important thing is that the jar must contain a beans.xml file (it can be empty, but it must exist). We can observe the container startup-event to do some work:

Adding an interceptor for a method is easy: if we want to be able to temporarily replace implementations of ISecondBean, all we need to do is annotate the producer method with the new annotation. SecondBeanProducer now takes the form:

1
2
3
4
5
6
7

@ApplicationScoped
publicclass SecondBeanProducer {
@Produces @ReplaceableResult
public ISecondBean getSecondBean(){returnnew SecondBeanA();// by default we use the A variant}}

@ApplicationScoped
public class SecondBeanProducer {
@Produces @ReplaceableResult
public ISecondBean getSecondBean() {
return new SecondBeanA(); // by default we use the A variant
}
}

We can now test the temporary replacement by modifying the Main bean that we used before:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

@ApplicationScoped
publicclass Main {
@Inject
private FirstBean first;publicvoid start(@Observes ContainerInitialized event)throwsException{System.out.println("Welcome to Main!");
first.start(1);// Here we replace the implementation of ISecondBean just for // the duration of one method call.
ReplaceableResultInterceptor.withReplacement(
ISecondBean.class,
new SecondBeanB(), // we are using the B variantnew Callable<Void>(){
@Override
publicVoid call()throwsException{
first.start(2);returnnull;}});
first.start(3);}}

So the dependency in FirstBean was swapped! Which was our goal: for the duration of the bean method invocation, whenever a ISecondBean dependency is injected, the temporary implementation will be used.
Full code of the interceptor:

Overall, CDI/Weld works very well and I’m very satisfied with it. One thing I’m missing is being to able to do programmatic configuration (Guice-style), but it’s already in the list of ideas for future portable extensions. I think that programmatic configuration and the ability to run Weld in JavaSE will create a great mix for “semi-integration” testing.