EJB3 - 自我远程注入/ EJB插件

时间:2015-08-30 21:30:07

标签: java-ee plugins classpath ejb-3.1

我想实现一种"插件机制"使用EJB3。我有两个+ war,每个都使用在单独项目中定义的远程接口包含自己的bean类型。基本上,我希望我的主Bean(产品)总是被部署,并为其他Beans(n个不同的Project Beans)提供注册机制。任意Bean都可以注册,这一点很重要,只要他们了解产品的远程接口即可。示例代码:

产品Bean

@Singleton
@ConcurrencyManagement(BEAN)
public class ProductBean implements ProductRemote, Serializable {
    private static final long serialVersionUID = 4686943363874502919L;

    private ProjectRemote project;

    public void registerProject(ProjectRemote project) {
        this.project = project;
    }

    public void something() {
        if(project != null)
            project.doSomething();
    }

}

项目Bean

@ConcurrencyManagement(BEAN)
@Singleton
@Startup
public class ProjectBean implements ProjectRemote, Serializable {
    private static final long serialVersionUID = -2034521486195622039L;

    @EJB(lookup="java:global/product/ProductBean")
    private ProductRemote product;

    @PostConstruct
    private void registerSelf() {
        product.registerProject(this);
    }

    public void doSomething() {
        System.err.println("FOOBAR");
    }
}

产品界面

@Remote
public interface ProductRemote {
    public void registerProject(ProjectRemote project);
}

项目界面

@Remote
public interface ProjectRemote {
    public void doSomething();
}

不幸的是,当我尝试部署Project时,我得到一个ClassNotFoundException:

22:56:33,146 ERROR [org.jboss.as.controller.management-operation] (XNIO-1 task-7) JBAS014613: Operation ("add") failed - address: ([{"deployment" => "project-0.1-SNAPSHOT.war"}]) - failure description: {"JBAS014671: Failed services" => {"jboss.deployment.unit.\"project-0.1-SNAPSHOT.war\".component.ProjectBean.START" => "org.jboss.msc.service.StartException in service jboss.deployment.unit.\"project-0.1-SNAPSHOT.war\".component.ProjectBean.START: java.lang.IllegalStateException: JBAS011048: Failed to construct component instance
Caused by: java.lang.IllegalStateException: JBAS011048: Failed to construct component instance
Caused by: javax.ejb.EJBException: java.lang.RuntimeException: JBAS014154: Failed to marshal EJB parameters
Caused by: java.lang.RuntimeException: JBAS014154: Failed to marshal EJB parameters
Caused by: java.lang.ClassNotFoundException: com.xxx.test.project.ProjectBean from [Module \"deployment.product-0.1-SNAPSHOT.war:main\" from Service Module Loader]"}}

所以我的问题是:有没有办法实现这样的功能?或者因为每次战争的不同阶级路径完全不可能?

2 个答案:

答案 0 :(得分:0)

我认为第一个问题是,即使您正在共享您的界面,客户端实现类仍然无法与其他项目一起使用,并且它的实例已加载了不同的类加载器(即,另一个战争集装箱)。

您可以使用消息bean(JMS)设计来实现,这将允许分离关注点,准注册(到队列/主题),并允许您部署所需的任何实现。看看我如何使用JMS and ACtiveMQ

另一种解决方案是使用委托模式,如下:

假设您从两个单独的战争部署转到单个EAR部署,您的项目将具有以下模块。

<强>在MyLibrary

EJB接口:

@Local
public interface MyBeanLocal
    public void doSomething(String something);

MyClient注释:

@Documented
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
public @interface MyClient {

}

MyDefault注释:

@Documented
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
public @interface MyDefault {

}

<强> myClientEJB

@Stateless
@MyClient
public class MyBean implements MyBeanLocal {
@Inject
@MyDefault
private MyBeanLocal myDefault;

@Override
public void doSomething(String something) {
    myDefault.doSomething(something);
}
}

<强> myDefaultEJB

@Stateless
@MyDefault
public class MyBean implements MyBeanLocal {

@Override
public void doSomething(String something) {
    // do something
}
}

<强> MYWEB

@Named
@Request
public class MyRequestBean {

@Inject
@MyClient
private MyBeanLocal myClient;

public void something() {
    myClient.doSomething("...");
}
}

最后,您可能需要考虑 OSGi 。我还没有做过任何事情,但它可能是你在注册资源方面寻找的东西。当然,您可以随时考虑资源适配器,具体取决于您的问题域。

答案 1 :(得分:0)

我的同事给了我正确的提示,告诉我在我的代码中我做错了什么 - 我编写它的方式我试图让我的实现注入。显然,由于不同的类路径(和JVM),这不起作用。相反,我必须将为远程接口创建的代理对象的引用传递给注册方法。

因此,为了使上述代码正常工作,对Project.java进行了两处更改:

  1. 为自己添加EJB:

    @EJB
    private ProjectRemote self;
    
  2. 更改@PostConstruct方法以引用EJB

    @PostConstruct
    private void registerSelf() {
        product.registerProject(self);
    }
    
  3. 这样,类的实际实现不需要被使用它的类知道。容器根据为EJB创建的代理对象知道传递信息的位置。