Local EJBs and Spring Framework’s @EJB annotation
July 23, 2008 – 9:13 amLately I have been working with Glassfish v2 and local EJB3. Everything worked perfectly until I have decided to leverage the Spring Framework for my web component.
Glassfish only supports @EJB annotation on limited types of classes, namely Servlets and other EJBs. Injection in POJOs are not supported:
“Injection is not supported for POJO classes, so the POJO needs to look up the local EJB reference within the private namespace (java:comp/env) of the component within whose scope it is executing”. Source: Glassfish EJB FAQ / POJOLocalEJB
So when you try to use @EJB nothing happens, you just get the dreadful NullPointerException. But according to documentation Spring supports EJBs and @EJB annotiation via CommonAnnotationBeanPostProcessor.
Finally, this post-processor also supports the EJB 3 EJB annotation, analogous to Resource as well, with the capability to specify both a local bean name and a global JNDI name for fallback retrieval. The target beans can be plain POJOs as well as EJB 3 Session Beans in this case.
This is my setup. Let’s start with Java classes:
// Local EJB
@Stateless
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
public class ForumMapperBean implements ForumMapper {
...
// Spring's Controller
public class ForumListController extends AbstractController {
@EJB
ForumMapper forumMapper;
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest req, ...) throws Exception {
...
And now application context bean needed to get @EJB going:
Unfortunately this setup fails on deployment due to BeanCreationException: “Error creating bean with name ‘forumListController’: Injection of resource fields failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [pl.aie.mapper.ForumMapper] is defined (…).
But why on earth is Spring trying to inject it’s bean on EJB annotation? I don’t get it. Well, ok. How do I stop it from doing so? Quick glance at the documentation on CommonAnnotationBeanPostProcessor and everything is getting clearer. Docs say that for direct JNDI access, resolving resource names as JNDI resource references within the Java EE application’s “java:comp/env/” namespace
one has to use alwaysUseJndiLookup property:
The central element is the Resource annotation for annotation-driven injection of named beans, by default from the containing Spring BeanFactory, with only mappedName references resolved in JNDI. The “alwaysUseJndiLookup” flag enforces JNDI lookups equivalent to standard Java EE 5 resource injection for name references and default names as well. The target beans can be simple POJOs, with no special requirements other than the type having to match.
Okay, let’s check it out:
<property name=”alwaysUseJndiLookup” value=”true” />
Still no good, we now get “No bean named ‘forumMapper’ is defined: not found in JNDI environment”. Okay, so it gets the wrong interface name. Let’s try forcing it to use correct one:
// Spring's Controller
public class ForumListController extends AbstractController {
@EJB(name = "ForumMapper")
ForumMapper forumMapper;
...
And voila! Everything works as it should now
Tags: annotation, ejb3, java, spring framework