从独立部署的战争中访问jar中的EJB3 bean(未包装在耳中)

时间:2012-08-13 12:10:58

标签: jsf ejb-3.0

由于某些原因,我想将我的应用程序部署为两个单独的工件: Users-ejb.jar Users-war.war ,而不是打包在同一工件中 ear (但仍然部署在相同的 JBoss AS 7.1实例中)。在 Users-war.war 中,我有一个支持bean(注释为JSF托管bean),我想在其中注入一个打包在 Users-ejb.jar 中的EJB3。当 Users-ejb.jar 时,所有内容都打包在单个 ear 中的简单 @EJB 注入不再有效> Users-war.war 是单独部署的。

我的设置的简化示例如下:

EJB3 bean

import javax.ejb.*;

(...)

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

支持bean

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;

我尝试了各种组合,比如在支持bean中将EJB3 bean的类型声明为IUserFacadeRemote(而不是IUserFacadeLocal),但当 Users-war.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

Users-ejb.jar 部署到JBoss AS 7.1,没有任何抱怨但是当部署了 Users-war.war 时,JBoss抱怨它不能找到他应该注射的豆子。

但是,我可以使用JNDI获取对EJB3 bean的引用:

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

尽管如此,@ EJB注射似乎不起作用。

更新 我按照Tom Anderson下面给出的建议进行了说明,并且有效的注射是:

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

如果我理解正确使用特定于供应商的mappedName属性。我无法以与供应商无关的方式使注入工作。

3 个答案:

答案 0 :(得分:4)

我希望我能够理解EE规范的这个区域,以便给出明确的答案,但我不这样做。

JBoss EJB documentation有这样说:

  
      
  • @EJB注释还具有mappedName()属性。规范留下了供应商特定的元数据,但JBoss将mappedName()识别为您引用的EJB的全局JNDI名称。如果已指定mappedName(),则忽略所有其他属性,并使用此全局JNDI名称进行绑定。
  •   
  • 如果指定@EJB没有定义属性[...]则适用以下规则:   
        
    • 在引用bean的EJB jar中搜索带有接口的EJB,用于@EJB注入。如果有多个EJB发布相同的业务接口,则会引发异常。如果只有一个bean具有该接口,则使用该接口。
    •   
    • 在EAR中搜索发布该接口的EJB。如果存在重复项,则抛出异常。否则返回匹配的bean。
    •   
    • 在JBoss中全局搜索该接口的EJB。同样,如果重复,则抛出异常。
    •   
  •   
  • @ EJB.beanName()对应。如果定义了beanName(),则使用与@EJB相同的算法,但不定义任何属性,除非在搜索中使用beanName()作为键。此规则的一个例外是使用ejb-link'#'语法。 '#'语法允许您将相对路径放入EAR中您正在引用的EJB所在的jar。有关详细信息,请参阅规范
  •   

“在JBoss中全局搜索该接口的EJB”肯定表明像你编写的那样注入应该有效。实际上,它应该在没有beanName的情况下工作。但是,我怀疑从WAR中的组件的角度来看,EJB-JAR中的组件是远程的,因此您将需要使用远程接口。

所以,我要尝试的第一件事是:

@EJB
private IUserFacadeRemote userFacade;

没有beanName,如果遇到麻烦。听起来你已经尝试过了。

如果注射的正常方法不起作用,我可能会回退到通过mappedName进行注射,这在JBoss中是全局JNDI名称。所以:

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

这显然很难看。

无论如何,祝你好运!

编辑:您可以尝试的其他方法是使用明确命名EJB-JAR的合格亲属beanName

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

由于WAR和EJB-JAR未打包在EAR中,因此可能需要:

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

但到目前为止我只是在猜测。

编辑罢工:我们可能忽略了一些非常简单的事情。 @EJB注释的lookup属性允许您指定“包含目标EJB组件的JNDI名称的可移植查找字符串”,因此:

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

可能会工作。这基本上是JBoss特定使用mappedName的可移植版本。

答案 1 :(得分:0)

我一直在Wildfly中测试这个场景,发现如果在战争中有一个指向ejb的jboss-deployment-structure.xml,它将与上面描述的本地接口一起工作。否则抛出ClassNotFoundException,因为由于JBoss和Wildfly中的模块化类加载,上面的战争无法真正“知道”ejbs类。该文件的内容应为:

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

然后JSF bean可以使用:

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

答案 2 :(得分:0)

正如@TomAnderson所说,实现跨工件查找的标准方法是@EJB注释的lookup attribute

这是一个完整的Maven项目,用于说明其工作原理:
https://github.com/mrts/remote-ejb-injection

您不需要使用EJB类的name属性,只要在查找中提供类名就足够了。引自上面的例子:

// 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;

    ...
}

(所以,直接在HelloServiceImpl Just Works™中使用lookup。)