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



我最近开始研究OSGi框架。我有一个包,名叫bundle-A。我想从我的主应用程序调用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罐子里的课。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);
        }
    }
}

我不确定做这件事的正确方法是什么?我知道一种方法是在我使用基于maven的项目时,在我的主要应用程序pom.xml文件中添加Bundle-A的依赖项。但我认为这不是正确的做法。因为最终,我会有更多的捆绑包,所以应该有其他我不知道的方法。

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

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

您有几个选择:

动态导入包

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

使用反射

您可以使用反射调用所需的方法,如下所示(示例草案):

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

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

使用接口和OSGi服务

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

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

为了不需要将bundle A作为对主bundle的依赖项添加,接口应该被放入第三个bundle中,这是主bundle和A bundle两者的依赖项。

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

一个例子:

创建一个接口,并将其放入一个新的捆绑包中,如goldenframeworkapi。

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

goldenframework api将是main bundle和bundle-a的依赖项。主捆绑包将使用它,而捆绑包-A将实现它

以下是捆绑包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处于活动状态时,您可以确保您需要的服务已注册。然而,在未来,您应该考虑基于事件的工作(例如使用ServiceTracker)。我的意思是这将是一个糟糕的做法:):

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

这可能会暂时解决你的问题,你可以继续你的工作。然而,请考虑阅读一本像《OSGi在行动》这样的书,对OSGi捆绑包和服务生命周期有一种感觉,这样你就可以重新设计你的框架。

最新更新