简体   繁体   English

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

[英]EJB3 - Self Remote Injection / EJB Plugins

I want to implement a kind of "plugin mechanism" using EJB3. 我想使用EJB3实现一种“插件机制”。 I have two+ war's, each containing own bean types using Remote Interfaces defined in a separate project. 我有两个以上的战争,每个战争都使用在单独的项目中定义的远程接口包含自己的Bean类型。 Basically, I want my main Bean (Product) to always be deployed and provide a mechanism for other Beans (n different Project Beans) to register against. 基本上,我希望始终部署主Bean(产品),并为其他Bean(n个不同的Project Bean)提供注册机制。 It is important that arbitrary Beans may register, as long as they have knowledge of the Product's Remote Interface. 只要任意Bean都知道产品的远程接口,就可以注册,这一点很重要。 Sample code: 样例代码:

Product 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();
    }

}

Project 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");
    }
}

Product Interface 产品介面

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

Project Interface 项目界面

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

Unfortunately, when I try to deploy the Project I get a ClassNotFoundException: 不幸的是,当我尝试部署项目时,我收到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]"}}

So my question is: is there any way to realize such a functionality? 所以我的问题是:有没有办法实现这种功能? Or is it entirely impossible because of the different classpaths for each war? 还是由于每次战争的阶级路径不同而完全不可能吗?

I think the first problem is that even though you're sharing your interface, the client implementation class is still not available with the other project, and it's instance was loaded with a different classloader (ie, another war container). 我认为第一个问题是,即使您共享界面,客户端实现类在其他项目中仍然不可用,并且其实例已使用其他类加载器(即另一个war容器)加载。

You could implement things using a message bean (JMS) design, which will allow separation of concerns, a quasi-registration (to a queue/topic), and allow you to deploy any implementation you want. 您可以使用消息bean(JMS)设计来实现事物,这将允许关注点分离,准注册(到队列/主题),并允许您部署所需的任何实现。 Check out how I'm working with JMS and ACtiveMQ . 了解我如何使用JMS和ACtiveMQ

Another solution would be to use a delegate pattern, as such: 另一个解决方案是使用委托模式,如下所示:

Assuming that you went from two separate war deployments to a single EAR deployment, you're project will have the following modules. 假设您从两个单独的战争部署迁移到一个EAR部署,那么您的项目将具有以下模块。

myLibrary 我的图书馆

EJB interface: EJB接口:

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

MyClient annotation: MyClient批注:

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

}

MyDefault annotation: MyDefault批注:

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

}

myClientEJB myClientEJB

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

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

myDefaultEJB 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("...");
}
}

Lastly, you might want to consider OSGi . 最后,您可能需要考虑OSGi I haven't done anything with that, but it might be what you're looking for as far registering resources. 我没有做任何事情,但这可能是您正在寻找的注册资源。 Of course, you could always consider a resource adapter depending on your problem domain. 当然,您始终可以根据问题域来考虑使用资源适配器

A colleague of mine gave me the right hint about what I did wrong in my code - the way I wrote it I was trying to get my implementation injected. 我的一个同事给了我正确的提示,告诉我我在代码中做错了什么—我编写代码的方式是试图注入实现 Obviously, this does not work because of the different classpaths (and JVMs). 显然,由于不同的类路径(和JVM),此方法不起作用。 Instead, I had to pass the reference to the proxy object created for the remote interface into the registration method. 相反,我必须将对为远程接口创建的代理对象引用传递注册方法。

So, in order to get the above code working, two changes hav been made to Project.java: 因此,为了使上述代码正常工作,对Project.java进行了两项更改:

  1. Add an EJB for itself: 为自己添加一个EJB:

     @EJB private ProjectRemote self; 
  2. Change the @PostConstruct method to reference the EJB 更改@PostConstruct方法以引用EJB

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

This way, the actual implementation of the class does not need to be known by the class that is using it. 这样,使用该类的类无需知道该类的实际实现。 The container knows where to pass the information based on the proxy object created for the EJB. 容器基于为EJB创建的代理对象知道将信息传递到哪里。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM