java - 如何在 Bundle-A jar 文件安装到 OSGi 容器后调用其中的方法之一

标签 java maven osgi osgi-bundle

我最近开始研究 OSGi 框架。我有一个名为 Bundle-A 的 bundle 。我想从我的主应用程序调用 Bundle-A jar 中的方法之一。

我已经从我的主应用程序加载并安装了 Bundle-A。下面是我在其中安装 Bundle-A 的主应用程序的代码。

private void initializeModelFramework() {

    try {

        FileUtils.deleteDirectory(new File("felix-cache"));
        FrameworkFactory frameworkFactory = ServiceLoader.load(FrameworkFactory.class).iterator().next();

        Framework framework = frameworkFactory.newFramework(new HashMap<String, String>());
        framework.start();

        BundleContext bundleContext = framework.getBundleContext();

        modulesNameVersionHolder.put("Bundle-A", "1.0.0");

        List<Bundle> installedBundles = new LinkedList<Bundle>();

        String basePath = "C:\\ClientTool\\LocalStorage";

        for (Map.Entry<String, String> entry : modulesNameVersionHolder.entrySet()) {
            String version = entry.getValue();
            final String filename = name + Constants.DASH + version + Constants.DOTJAR;
            final String localFilename = GoldenModulesConstants.FILE_PROTOCOL + basePath+ File.separatorChar + filename;

            installedBundles.add(bundleContext.installBundle(localFilename));
        }   

        for (Bundle bundle : installedBundles) {
            bundle.start();// this will start bundle A
        }

        // After starting the Bundle-A, now I need to call one of the methods in Bundle-A
        for(int i=0; i<=10; i++) {
            //call processingEvents method of Bundle-A class GoldenModelFramework
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

现在 Bundle-A 已经启动。下面是我的 Bundle-A 的 Activator 类。

public class Activator implements BundleActivator {

    private static final String BUNDLE_VERSION_KEY = "Bundle-Version";
    private static Logger s_logger = Logger.getLogger(Activator.class.getName());

    @Override
    public void start(BundleContext context) throws Exception {

        final Bundle bundle = context.getBundle();
        final String bundleName = bundle.getSymbolicName();
        final String bundleVersion = (String) bundle.getHeaders().get(BUNDLE_VERSION_KEY);

        System.out.println(bundleName+" - "+bundleVersion);
    }

    @Override
    public void stop(BundleContext context) throws Exception {
            System.out.println("Bye.!");
    }
}

下面是我在 Bundle-A jar 中的类。 Bundle-A 启动后,我需要从上面的主应用程序代码中调用 processingEvents 方法。

public class GoldenModelFramework {

    private static final Logger LOGGER = Logger.getLogger(GoldenModelFramework.class.getName());
    private static final long checkingAfterEveryXMinutes = 15L;


    public GoldenModelFramework() {
        // following the traditions
    }

    public static void processingEvents(final String item) {

        for (BundleRegistration.HolderEntry entry : BundleRegistration.getInstance()) {
            final String response = entry.getPlugin().process(item);
            System.out.println(response);
        }
    }
}

我不知道什么是正确的做法?我知道一种方法是在我的主应用程序 pom.xml 文件中添加此 Bundle-A 的依赖项,因为我正在使用基于 Maven 的项目。但我认为这不是正确的做法。因为最终,我将拥有更多 bundle ,因此应该有其他我不知道的方法。

我应该在这里使用 ServiceListener 还是 ServiceTracker ?任何基于我的代码的简单示例都将帮助我更好地理解。谢谢。

我希望这个问题足够清楚。我正在尝试在加载并安装 Bundle-A 后调用其中的方法之一。

最佳答案

您有多种选择:

动态导入包

您可以使用 DynamicImport-Package 代替 Import-Package。在这种情况下,当主 bundle 启动时, bundle A 不必处于 Activity 状态。虽然这有效,但我不推荐此解决方案,因为我不喜欢 DynamicImport-Package。当然,在这种情况下,bundle A 必须是主bundle 的依赖项。

使用反射

您可以通过反射调用您想要的方法,如下所示(草稿示例):

Class<GoldenModelFramework> clazz = bundleA.loadClass("GoldenModelFramework");
Method m = clazz.getMethod("processingEvents", String.class);
m.execute(null, myParam);

这有点好,但是这个解决方案仍然有点模糊,我不会说这是一个干净的代码。

使用接口(interface)和OSGi服务

最干净的方法需要一些重构。在这种情况下,您应该创建一个接口(interface),并在 bundle A 的激活器中您应该注册基于该接口(interface)的服务。在主包中,您应该使用服务跟踪器来捕获该服务并调用其方法。

如果您确实想让您的方法 processEvent 静态,则注册的服务对象(基于接口(interface))应该简单地调用内部的静态方法。

不必将 bundle A 作为主 bundle 的依赖项添加到接口(interface)中,应该将接口(interface)纳入第三个 bundle ,该第三个 bundle 是主 bundle 和 A bundle 的依赖项。

虽然这个解决方案似乎是最复杂的,但我建议使用这个。

一个例子:

创建一个接口(interface)并将其放入新的包中,例如goldenframework-api。

public interface GoldenModelFrameworkOSGi {
    void processingEvents(final String item);
}

goldenframework-api 将成为主包和包-A 的依赖项。主 bundle 将使用它,而 bundle -A 将实现它。

以下是 bundle A 的实现方式:

public class GoldenFrameworkOSGiImpl {
    public void processingEvents(final String item) {
        GoldenModelFramework.processEvents(item);
    }
}

在bundle-A中创建一个Activator类(我将在该激活器中省略您的代码以减少输入):

public class Activator {
  private ServiceRegistration goldenFrameworkSR;

  @Override
  public void start(BundleContext context) {
    goldenFrameworkSR = context.registerService(GoldenFrameworkOSGi.class, new GoldenFrameworkOSGi(), new HashTable());
  }

  @Override
  public void stop(BundleContext context) {
    goldenFrameworkSR.unregister();
  }
}

当你知道 Bundle-A 的代码时,你可以稍微作弊一下。当bundle-A 处于 Activity 状态时,您可以确保您需要的服务已注册。但是,将来您应该考虑基于事件进行工作(例如使用 ServiceTracker)。我的意思是这将是一个不好的做法:):

ServiceReference sr = context.getServiceReference(GoldenServiceOSGi.class);
GoldenServiceOSGi gs = context.getService(sr);
gs.processEvents(...);
context.ungetService(sr);

这可能暂时解决您的问题,您可以继续工作。但是,请考虑阅读《OSGi in Action》之类的书,以了解 OSGi bundle 和服务生命周期,以便您可以重新设计框架。

关于java - 如何在 Bundle-A jar 文件安装到 OSGi 容器后调用其中的方法之一,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18295784/

相关文章:

java - 如何在 php web 脚本中获取 java 应用程序的输出?

java - Libgdx 和谷歌 IAP

java - 使用 JProgressBar 以百分比显示线程的进度

java - Spring Data JPA 'jpaMappingContext' 错误,IllegalStateException : Expected to be able to resolve a type but got null

maven - 如何为 Apache Karaf 设置代理

java - OSGi 容器中的 RMI 客户端

java - 有没有一种简单的方法可以将日志记录从依赖项插入到类中?

maven - 使用Spring Boot进行Gretty构建失败

java - 有没有办法找出哪些 bundle 正在使用我的 bundle ?

java - Eclipse 扩展和声明式服务