Is this pattern safe?
Consider the following stateless bean with a resource-injected DataSource. All of the methods in the bean want to use a connection acquired from the DataSource; we would like to avoid having to code this explicitly in every method, so we write an interceptor instead that sets a private connection field in the bean, and closes it after the method has finished executing:
@WebService
@Stateless(name ="MyExampleEJB")
@Interceptors({MyExampleBean.MyExampleInterceptor.class})
publicclass MyExampleBean{
@Resource(mappedName="jdbc/myDataSource")private DataSource ds;
private Connection connection;
publicstaticclass MyExampleInterceptor{
@AroundInvoke
public Object acquireConnection(InvocationContext ic)throws Exception{
MyExampleBean source = (MyExampleBean) ic.getTarget();
source.connection = source.ds.getConnection();
try{
return ic.proceed();
}finally{ source.connection.close(); source.connection=null;}
}
}
// ...methods which want to use this connection...
}
Questions:
i) Is this safe? Can we guarantee that no more than one attempt at a time will be made to execute any method on the same bean.
ii) Is this sensible? Is there a much simpler way of accomplishing the same thing?
# 1
Hi Dominic,
i) Yes, it's safe. There is guaranteed to be a separate instance of the interceptor class for
each bean instance.The ejb container will always prevent concurrent access to the
same bean instance, so there's no need for any additional synchronization.
ii) Yes, it's sensible. @AroundInvoke methods are a good place for code that applies
to many methods of the same bean.There isn't a much simpler way of doing the
connection retrieval/close on each method invocation, other than moving up a level
of abstraction and using the Java Persistence API instead :-)
--ken
# 3
There's no specific API to get the InvocationContext from a business method since
the design center for InvocationContext is as a communication conduit between
the interceptors themselves.So, the best approach is just to pull out any applicable
contextData within the AroundInvoke method and set it on the bean instance.That's a
bit cleaner than just setting the InvocationContext itself on the bean instance, since that
object is considered a special container object that is not intended to be passed around.
Forgot to mention that you can also implement an AroundInvoke method on the
bean class itself.It's a good option for cases where the logic in the interceptor is
tightly coupled to a specific bean.It saves you having to implement a
separate inner class and makes it easier to access bean instance state.
--ken