Access to an EJB3 bean in a bank from an independently deployed war (not packaged in the ear)

For some reason, I would like to deploy my application as two separate artifacts: Users-ejb.jar and Users-war.war , not packaged in the same ear (but still, deployed in the same instance of JBoss AS 7.1). In Users-war.war , I have bean support (annotated as a JSF managed bean), where I want to introduce EJB3 packaged in Users-ejb.jar . A simple @EJB injection that worked when everything was packed in one ear no longer works when Users-ejb.jar and Users-war.war are deployed separately.

The following is a narrowed down simplified example of my installation:

EJB3 bean

import javax.ejb.*; (...) @Stateless(name="userFacade") @Local(IUserFacadeLocal.class) @Remote(IUserFacadeRemote.class) public class UserFacade extends AbstractFacade<User> implements IUserFacadeLocal, IUserFacadeRemote { 

Bean support

 import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; import javax.ejb.EJB; import entities.User; import facades.IUserFacadeRemote; import facades.IUserFacadeLocal; @ManagedBean(name="indexBackingBean") @SessionScoped public class IndexBackingBean implements Serializable { @EJB(beanName="userFacade") private IUserFacadeLocal userFacade; 

I tried various combinations, such as declaring an EJB3 bean in the bean library as IUserFacadeRemote (unlike IUserFacadeLocal), but they all fail with the same exception when the User Warfare. war :

 Caused by: org.jboss.as.server.deployment.DeploymentUnitProcessingException: JBAS014543: No EJB found with interface of type 'facades.IUserFacadeLocal' and name 'userFacade' for binding controllers.IndexBackingBean/userFacade 

Ejb.jar users are deployed to JBoss AS 7.1 without any complaints, but when Users-war.war is deployed , JBoss complains that it cannot find the bean that it needs to enter.

However, I can get a link to the EJB3 bean using JNDI using:

 String jndiName = "java:global/Users-ejb/userFacade!facades.IUserFacadeRemote"; this.userFacade = (IUserFacadeRemote) new InitialContext().lookup(jndiName); 

Despite this, the @EJB injection does not work.

UPDATE: I followed the sentence below Tom Anderson , and the injection that works is:

 @EJB(mappedName = "java:global/Users-ejb/userFacade!facades.IUserFacadeRemote") 

which, if I understand correctly, uses the mappedName attribute for a particular provider. I could not get the injection to work in a vendor-independent manner.

+7
source share
3 answers

I would like me to understand this area of ​​the EE specification well enough to give you a definitive answer, but I do not.

The JBoss EJB documentation has this to say:

  • The @EJB annotation also has the mappedName () attribute. The specification leaves this vendor-specific metadata, but JBoss recognizes mappedName () as the global JNDI name of the EJB you are referring to. If you specified mappedName (), then all other attributes are ignored, and this global JNDI name is used for binding.
  • If you specify @EJB without the attributes defined [...] then the following rules apply:
    • The bean in the EJB byte searches for the EJB with the interface used to inject @EJB. If there is more than one EJB that publishes the same business interface, an exception is thrown. If there is only one bean with this interface, then this one is used.
    • Search for EARs for EJBs that publish this interface. If there are duplicates, an exception is thrown. Otherwise, the corresponding bean is returned.
    • Search globally in JBoss for the EJB of this interface. Again, if duplicates are thrown, an exception is thrown.
  • @ EJB.beanName () matches. If beanName () is defined, then use the same algorithm as @EJB without attributes, except for using beanName () as the key in the search. An exception to this rule is the use of the syntax "#" ejb-link. The syntax "#" allows you to put the relative path in the bank in the EAR, where you refer to EJB to life. See the specification for more details.

"A worldwide search in JBoss for the EJB of this interface" certainly assumes that an injection like the one you wrote should work. Indeed, it should work without beanName . However, my suspicion is that, in terms of the component in the WAR, the component in the EJB-JAR is remote, and so you will need to use a remote interface.

So the first thing I will try:

 @EJB private IUserFacadeRemote userFacade; 

Without beanName , in case of problems. Looks like you tried this.

If the normal approach to injection does not work, I can return to trying to inject via mappedName , which in JBoss is the global JNDI name. So:

 @EJB(mappedName = "java:global/Users-ejb/userFacade!facades.IUserFacadeRemote") private IUserFacadeRemote userFacade; 

This is obviously pretty ugly.

Anyway, good luck!

EDIT . Something else you could try is to use a qualified relative beanName , which explicitly calls the EJB-JAR:

 @EJB(beanName = "Users-ejb.jar#userFacade") private IUserFacadeRemote userFacade; 

Since WAR and EJB-JARs are not packaged in EARs, this could be:

 @EJB(beanName = "../Users-ejb.jar#userFacade") private IUserFacadeRemote userFacade; 

But at this point, I’m just guessing.

CHANGE BACK ARROWS . Perhaps we missed something very simple. The @EJB annotation lookup @EJB allows @EJB to specify "a portable search string containing the JNDI name for the target EJB component", therefore

 @EJB(lookup = "java:global/Users-ejb/userFacade!facades.IUserFacadeRemote") private IUserFacadeRemote userFacade; 

Can work. This is essentially a portable version of JBoss-specific use of mappedName .

+4
source

I tested this script in Wildfly and found that it will work with local interfaces as described above if there is jboss-deployment-structure.xml inside the war pointing to ejb. Otherwise, a ClassNotFoundException is thrown, because the aforementioned war cannot "know" about ejbs classes due to loading the module class in JBoss and Wildfly. The contents of the file should be:

 <jboss-deployment-structure> <deployment> <dependencies> <module name="deployment.Users-ejb.jar" /> </dependencies> </deployment> </jboss-deployment-structure> 

And then the JSF bean can use:

 @EJB(lookup = "java:global/Users-ejb/userFacade!facades.IUserFacadeRemote") private IUserFacadeLocal userFacade; 
0
source

As @TomAnderson said, the standard way to achieve cross-artifact search is to lookup the @EJB annotation @EJB .

Here's a complete Maven project to illustrate how this works:
https://github.com/mrts/remote-ejb-injection

You do not need to use the name attribute of the EJB class, provided that the class name in the search is sufficient. Quoting from the above example:

 // in API JAR @Remote public interface HelloService { ... } // in EJB JAR @Stateless public class HelloServiceImpl implements HelloService { ... } // in WAR @WebServlet("/hello") public class HelloServlet extends HttpServlet { @EJB(lookup = "java:global/service-ear/service-ejb-impl/HelloServiceImpl!" + "ee.mrts.service.HelloService") private HelloService helloService; ... } 

(So, using HelloServiceImpl directly in lookup Just Works β„’.)

0
source

All Articles