从静态方法解析OSGi服务实例

时间:2013-07-24 12:28:10

标签: java spring osgi jndi

我有一个遗留Java企业应用程序,它将一堆服务注册为Spring bean并使用JNDI注册它们。我想将其转换为使用带OSGi的Spring。

以前,服务类只包含在需要它的任何其他类的类路径中,并且有一个静态方法,如下所示:

public class SomeService {
    // private fields...

    public static SomeService getInstance() {
        SomeService svc = null;
        try {
            InitialContext ctx = new InitialContext();
            svc = (SomeService)ctx.lookup("java:/SomeService");
        } catch (NamingException ex) {
            logger.info("Exception in getNamedObject", ex);
        }
        return svc;
    }

    // Setters and getters, some of which are filled-in with Spring beans

    // Other methods etc...
}

无论在何处使用该服务,我们都有这样的代码:

SomeService svc = SomeService.getInstance();
// or even
SomeObject results = SomeService.getInstance().getSomeObject();

现在,我意识到转换它的“正确”方法是完全删除getInstance()并强制接口的所有用户拥有自己的实现引用,由Spring提供并在xml文件中配置在META-INF中。但是,由于系统已投入生产,因此这种变化太大而无法立即生成。

有没有办法获取类似于上面JNDI方法的OSGi服务实例?

更新:澄清
只是为了清楚我的目标 - 我知道这不是一般的好方法。然而,这是一个正在生产中的大型企业应用程序,并且一次性改变整个事物以适应“理想的”OSGi结构,这一切都变得太大而无法一次完成。

我在这里要完成的是打破应用程序的一小部分,并准备好作为单独的OSGi包服务。但是,由于应用程序的其余部分 - “客户端代码”(如果您愿意)尚未准备好进行此更改,我必须有一个中间步骤,允许我以旧方式使用此代码两者作为OSGi服务。随着时间的推移,其余的appilcation也将模块化和OSGi-ified,最终这些静态工厂方法将被完全删除。

然而,在那之前,“这是做错OSGi的方式”的评论对我来说并不是很有帮助 - 我知道它不是,但这甚至不是我的最终形式......

4 个答案:

答案 0 :(得分:5)

你正试图将方形钉穿过一个圆孔。每个开发人员都在计算机科学101中学习全局变量是坏的,所以我会说“正确”的引用是非常错误的,因为静态是主要的全局变量。

因为OSGi从不依赖于全局变量(java中的静态),所以可以在OSGi中的OSGi中运行OSGi中的OSGi,该OSGi位于OSGi上运行的App服务器上的WAR文件中。静态是邪恶的(首先由Anselm Baird说,它是OSGi的前身ServiceSpace的作者)。开发服务模型是为了解决您遇到的问题;任何服务实现都可以通过其接口和/或属性从任何地方引用另一个服务:

 @Reference
 void setSomeService(SomeService s) {
     this.s = s;
     ...
 }

在模块化系统中,中心神XML是一种诅咒。

您的问题无法解决,因为您不可避免地会遇到排序问题。在OSGi中,不保证服务可用(其最强大和最容易被误解的功能之一)。您的服务提供商可以随时出入。因为你的静态模型不处理依赖关系,所以它会虚假地失败,因为它假定是隐式排序。现在很多人都做这些静态解决方案,因为它大多数时间都可以工作;我认为“大多数时候”应该不适合计算机软件。如果我们要对我们的产品负责,不要认为我们会这样做......

假设您使用Eclipse或其他具有重构功能的IDE,那么我很难理解为什么更改代码库以注入此服务会很困难?

OSGi不是一种临时饮食,也不像大多数Java库那样神奇,它是一个成熟的模块化系统,需要你的应用程序才能真正实现模块化,即改变生活方式。试图使用一个框架,然后反对它的流程是一种导致我的经历非常悲痛的策略。

答案 1 :(得分:2)

在任何情况下,您都希望将服务作为OSGi服务发布。问题只是如何访问它,对客户端代码的影响最小。

可能会使用一个方法:

FrameworkUtil.getBundle(YourClass).getBundleContext(); 这允许以静态方法访问bundle上下文。从那里你可以访问该服务。

另一种方法可能是使用aries jndi。它允许您使用jndi检索OSGi服务。所以这也应该有所帮助。

当然,如彼得所述,这应该只是暂时的解决方案。

答案 2 :(得分:1)

我必须克服一个类似的问题,即发布了一个静态界面,该界面提供了用户使用的服务,因此我不能强迫他们使用完整的OSGI。以下带注释的代码对我来说效果很好,因此也许其他人也可以受益。

@Component(immediate=true, service=ServiceProvider.class, scope=ServiceScope.SINGLETON)
public class ServiceProvider {

    private static FooService fooService;

    @Reference(cardinality=ReferenceCardinality.MANDATORY)
    public void setFooService(FooService fooService) {
        ServiceProvider.fooService = fooService;
    }

    /**
     * This is the existing Method which should return
     * a FooService Object.
     * @return The 'static' FooService
     */
    public static FooService getFooService() {
        return fooService;
    }
}

在具有插件开发功能的Eclipse(4.8 Photon)中->启用了1.3版的DS注释,这会生成文件

OSGI-INF/your.package.name.ServiceProvider.xml

包含以下OSGI服务声明

<scr:component ...>
    <service scope="singleton">
        <provide interface="your.package.name.ServiceProvider"/>
    </service>
    <reference bind="setFooService" cardinality="1..1" interface="foo.service.package.FooService" name="FooService"/>
    <implementation class="your.package.name.ServiceProvider"/>
</scr:component>

以及MANIFEST.MF中的以下条目

Service-Component: OSGI-INF/your.package.name.ServiceProvider.xml

在运行时,

setFooService(...)
首先调用

方法,并按预期方式注入FooService对象,因此需要外部调用

ServiceProvider.getFooService();

也将像以前一样工作。

答案 3 :(得分:0)

我想我已经找到了一种方法 - 我正在测试它,但从文档来看,这种方法看起来很有希望。

我创建了一个解析器类,它负责从OSGi实际获取服务,并通过实现BundleContextAware来实现。这样,我希望注入BundleContext,我可以从中获取服务实例:

public class SomeServiceResolver implements BundleContextAware {
    private BundleContext _cxt;

    @Override
    public void setBundleContext(BundleContext context) {
        _cxt = context;
    }

    public SomeService resolveService() {
        // TODO: Add error handling if service isn't available
        ServiceReference<SomeService> svcref = _ctx.getServiceReference(SomeService.class);
        SomeService svc = _ctx.getService(svcref);
        return svc;
    }
}

然后静态getInstance()方法变得简单

public static SomeService getInstance() {
    return new SomeServiceResolver().resolveService();
}

正如我所说,我还没有测试过,所以我们会看看它是否真的有效。