我有一个应用程序,它由战争、一个核心 ejb 和一个 jar 中的许多服务 bean 和另一个 jar 中的远程接口组成。一切都打包在一个耳朵里,并在 Glassfish 4.1 上运行。
现在我想为核心 ejb 添加扩展点或插件支持。
目标是拥有不同的热插拔数据导入服务,它们都共享相同的接口,因为它们从路透社和彭博等供应商那里获取和规范化财务数据。
这些插件应该由核心 ejb jar 中的“插件管理器”bean 检测和管理。插件应支持在运行时加载、卸载和替换。
理想情况下,插件接口位于单独的包中,这样其他人就可以针对它们进行开发,而无需我的应用程序或 Glassfish,即使没有 Java EE 堆栈也可以完美地进行开发。我还想按需部署插件,而不是总是部署整个应用程序。
目前我尝试使用 CDI 实例迭代器,只要它们在核心 ejb 中,它就可以很好地与两个导入服务实现一起工作。如果我将一个实现放入单独的 ejb jar 中,那么 CDI 根本找不到它。我猜问题是 Glassfish 将每个 ejb jar 作为应用程序加载到单独的类加载器中。
现在是我当前的简化代码!
单独的jar包中的插件接口:
package com.photon.extensions;
import java.io.Serializable;
public interface ImportServiceExtension extends Serializable {
String getImportServiceName();
}
未找到的单独 ejb jar 包中的插件实现:
package com.photon.services.extensions.vitrex.services;
import com.photon.extensions.ImportServiceExtension;
import javax.ejb.Remote;
import javax.ejb.Stateless;
@Remote(ImportServiceExtension.class)
@Stateless
public class ReutersImportService implements ImportServiceExtension {
@Override
public String getImportServiceName() {
return "Reuters";
}
}
找到的核心ejb jar包中的插件实现:
package com.photon.services.extensions;
import com.photon.extensions.ImportServiceExtension;
import javax.ejb.Stateless;
@Stateless
public class BloombergImportService implements ImportServiceExtension {
@Override
public String getImportServiceName() {
return "Bloomberg";
}
}
远程接口 jar 中“插件管理器”的远程接口:
package com.photon.services.extensions;
import java.util.List;
import javax.ejb.Remote;
@Remote
public interface ImportServiceExtensionsRemote {
List<String> getImportServiceNames();
}
核心 ejb jar 中的“插件管理器”bean 实现:
package com.photon.services.extensions;
import com.photon.extensions.ImportServiceExtension;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.ejb.Stateless;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
@Stateless
public class ImportersService implements ImportServiceExtensionsRemote {
@Inject private Instance<ImportServiceExtension> importServiceExtensions;
@Override
public List<String> getImportServiceNames() {
Iterator<ImportServiceExtension> iter = importServiceExtensions.iterator();
List<String> names = new ArrayList<>();
while ( iter.hasNext() ) {
ImportServiceExtension extension = iter.next();
names.add(extension.getImportServiceName());
}
return names;
}
}
最后是在战争中将名称呈现给网站的控制器:
package com.photon.website;
import com.photon.services.extensions.ImportServiceExtensionsRemote;
import javax.ejb.EJB;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
@RequestScoped
@Named
public class ImportController implements Serializable {
@EJB private ImportServiceExtensionsRemote importServiceExtensions;
public String getImportServiceNames() {
String names = "";
for ( String name : importServiceExtensions.getImportServiceNames() ) {
names += name;
}
return names;
}
}
最后只渲染了“Bloomberg”。
现在我的问题:
我在正确的轨道上吗?
如果是这样,我在代码中缺少什么?
这个问题是否有更好的解决方案(OSGI、自定义 clazz.forName、...)?