Thursday, 21 April 2011

Qualifying @Ejb Annotation to Resolve Injection Problems

A few days ago, I wrote a blog about simplifying EJB3 and Spring by using Spring’s SpringBeanAutowiringInterceptor which allows you to dispense with a certain amount of boilerplate code. In writing my sample code (remember the golden rule: never use a technique, api, pattern or algorithm professionally until you’ve used it to write some sample code) I did some rough refactoring of an existing EJB3 sample with the result that I had, in my JAR file, two implementations of an @Remote interface. Without thinking I quickly deployed my sample only to get large list of error messages that went something like:
[J2EE:160199]Error resolving ejb-ref 'marin.tips.ejb3.stateless.hasa.OrchestratorBean/wineSearch' from module 'marin-tips-ejb3-hello-world-1.0-SNAPSHOT.jar' of application 'marin-tips'. The ejb-ref does not have an ejb-link and the JNDI name of the target bean has not been specified. Attempts to automatically link the ejb-ref to its target bean failed because multiple EJBs in the application were found to implement the 'marin.tips.ejb3.stateless.winesearchcommon.WineSearchFacade' interface. Please specify a qualified ejb-link for this ejb-ref to indicate which EJB is the target of this ejb-ref.

        at weblogic.ejb.container.deployer.EJBModule.activate(EJBModule.java:501)
The failure resulted from trying to inject multi-implemented @Remote interface into another EJB by type:

  @EJB()
 
public void setWineSearch(WineSearchFacade wineSearch) {
   
this.wineSearch = wineSearch;
 
}

The reason for the failure is pretty obvious: as there’s two implementation of my @Remote interface, the Weblogic container doesn’t know which one to inject.

The fix to this isn’t at all obvious, it’s quite obvious that you need to specify which of the implementation you wish to inject, but how to specify which one, that’s another matter.

In Weblogic land, the eventual fix is easy. Weblogic seems to force you to add name and mappedName attributes to your @Stateless annotation:

@Stateless(name = "WineSearchFacade", mappedName = "WineSearch")
public class WineSearchFacadeBean implements WineSearchFacade, WineSearchFacadeLocal { etc...

and you can use these in your injection annotation to reference the correct @Remote implementation:

  @EJB(name = "WineSearchFacade", mappedName = "WineSearch")
 
public void setWineSearch(WineSearchFacade wineSearch) {

   
this.wineSearch = wineSearch;
 
}

1 comment:

Anonymous said...

Helped a lot, thank you!