简体   繁体   English

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

[英]Resolving an OSGi service instance from a static method

I have a legacy Java enterprise application which registers a bunch of services as Spring beans and registers them with JNDI. 我有一个遗留的Java企业应用程序,它将一堆服务注册为Spring bean并使用JNDI注册它们。 I'd like to convert this to use Spring with OSGi instead. 我想将其转换为使用带OSGi的Spring。

Previously, the service class was just included in the classpath of any other class that needed it, and had a static method looking something like this: 以前,服务类只包含在需要它的任何其他类的类路径中,并且有一个静态方法,如下所示:

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...
}

Wherever the service is used, we have code like this: 无论使用何种服务,我们都有这样的代码:

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

Now, I realize the "correct" way to convert this would be to remove getInstance() completely and force all users of the interface to have their own reference to the implementation, supplied by Spring and configured in the xml files in META-INF. 现在,我意识到转换它的“正确”方法是完全删除getInstance()并强制接口的所有用户拥有自己的实现引用,由Spring提供并在META-INF的xml文件中配置。 However, this change would be too big to make all at once, as the system is already in production). 但是,由于系统已经投入生产,因此这种变化太大而无法立即完成。

Is there any way to fetch an instance of an OSGi service analogous to the JNDI approach above? 有没有办法获取类似于上面的JNDI方法的OSGi服务实例?

Update: some clarification 更新:一些澄清
Just to be extra clear on my goals here - I know that this is not a good approach in general. 只是为了清楚我的目标 - 我知道这不是一般的好方法。 This is, however, a large enterprise application which is in production, and changing the entire thing in one go to adjust to the "ideal" OSGi structure is simply too large a change to do all at once. 然而,这是一个正在生产中的大型企业应用程序,并且一次性改变整个事物以适应“理想的”OSGi结构只是一次性改变太大了。

What I'm trying to accomplish here is to break out a small part of the application and make it ready to be served as a separate OSGi bundle. 我在这里要完成的是打破应用程序的一小部分,并准备好作为单独的OSGi包服务。 But since the rest of the application - the "client code", if you will - is not ready for this change yet, I must have an intermediate step which lets me use this code both in the old way, and as an OSGi service. 但是,由于应用程序的其余部分- ,“客户端代码”如果你-是不是准备好这种变化的是,我必须有一个中间步骤,它可以让我用这个代码, 无论是在旧的方式, 作为一个OSGi服务。 Over time, the rest of the appilcation will also be modularized and OSGi-ified, and eventually these static factory methods will be removed entirely. 随着时间的推移,其余的appilcation也将模块化和OSGi-ified,最终这些静态工厂方法将被完全删除。

Until then, however, remarks that "this is the wrong way to do OSGi" aren't very helpful to me - I know it isn't, but this isn't even my final form... 然而,在那之前,“这是做错OSGi的方式”的评论对我来说并不是很有帮助 - 我知道它不是,但这甚至不是我的最终形式......

You are trying to push the square peg through a round hole. 你正试图将方形钉穿过一个圆孔。 Every developers learns in computer science 101 that global variables are bad, so I would say the quotes around "correct" are very misplaced since statics are prime global variables. 每个开发人员都在计算机科学101中学习全局变量是坏的,所以我会说“正确”的引用是非常错误的,因为静态是主要的全局变量。

Because OSGi never relied on global variables (statics in java) you can run OSGi in OSGi in OSGi inside a WAR file on a App server running on top of OSGi. 因为OSGi从不依赖于全局变量(java中的静态),所以可以在OSGi中的OSGi中运行OSGi中的OSGi,该OSGi位于运行在OSGi之上的App服务器上的WAR文件中。 Statics are evil (first spoken by Anselm Baird, the author of ServiceSpace, the predecessor of OSGi). 静态是邪恶的(首先由Anselm Baird说,它是OSGi的前身ServiceSpace的作者)。 The service model was developed to address the problem you run into; 开发服务模型是为了解决您遇到的问题; any service implementation can reference another service by its interface and/or properties from anywhere: 任何服务实现都可以通过其接口和/或属性从任何地方引用其他服务:

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

In a modular system, a central God XML is an anathema. 在模块化系统中,中央神XML是一种诅咒。

There is no solution to your problem since you will inevitably run in ordering problems. 你的问题没有解决方案,因为你不可避免地会遇到排序问题。 In OSGi, a service is not guaranteed to be available (One of its most powerful and misunderstood features). 在OSGi中,不保证服务可用(其最强大和最容易被误解的功能之一)。 Your service provider is free to come and go at any time. 您的服务提供商可以随时出入。 Because your static model does not handle dependencies it will spuriously fail because it assumes implicit ordering. 因为你的静态模型不处理依赖关系,所以它会虚假地失败,因为它假定是隐式排序。 Now lots of people do these static solutions because it tends to work most of the time; 现在很多人都做这些静态解决方案,因为它大多数时间都可以工作; I think "most of the time" should however not be good enough for computer software. 我认为“大多数时候”应该不适合计算机软件。 Don't think we would do this if we would be liable for our products ... 如果我们要对我们的产品负责,不要认为我们会这样做......

Assuming that you work with Eclipse or another IDE with refactoring, then I have a hard time understanding why it would be hard to change the code base to inject this service? 假设您使用Eclipse或其他具有重构功能的IDE,那么我很难理解为什么更改代码库以注入此服务会很困难?

OSGi is not a temporary diet nor a magic sauce like most Java libraries, it is a mature modularity system that requires your app to become truly modular, ie it is a lifestyle change. OSGi不是一种临时饮食,也不像大多数Java库那样神奇,它是一个成熟的模块化系统,需要你的应用程序成为真正的模块化,即改变生活方式。 Trying to use a framework but then go against its flow is a strategy that leads to much grief in my experience. 试图使用一个框架,然后反对它的流程是一种战略,导致我的经历很悲痛。

In any case you want to publish you service as OSGi service. 在任何情况下,您都希望将服务作为OSGi服务发布。 The question is only how to access it with minimal impact on your client code. 问题只是如何访问它,对客户端代码的影响最小。

One aproach might be to use: 一个方法可能是使用:

FrameworkUtil.getBundle(YourClass).getBundleContext(); FrameworkUtil.getBundle(YourClass).getBundleContext(); This allows to access the bundle context in static methods. 这允许以静态方法访问bundle上下文。 From there you can access the service. 从那里您可以访问该服务。

Another aproach could be to use aries jndi . 另一种方法可能是使用aries jndi It allows you to retrieve OSGi services using jndi. 它允许您使用jndi检索OSGi服务。 So this should also help. 所以这也应该有所帮助。

Of course like stated by Peter this should only be a temporary solution. 当然,如彼得所说,这应该只是一个临时解决方案。

I had to overcome a similar Problem, ie a published static Interface which delivers Services used by users, so I couldn't force them to go full OSGI. 我必须克服类似的问题,即发布的静态接口,它提供用户使用的服务,所以我不能强迫他们完全使用OSGI。 The following annotated code worked well for me, so maybe someone else can benefit too. 以下带注释的代码对我来说效果很好,所以也许其他人也可以受益。

@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;
    }
}

In Eclipse (4.8 Photon) with Plug-in Development -> DS Annotations for 1.3 enabled, this generates the file 在带有插件开发的Eclipse(4.8 Photon) - >启用了1.3的DS Annotations时,会生成该文件

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

containing the following OSGI Service declarations 包含以下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>

and the following entry in the MANIFEST.MF 以及MANIFEST.MF中的以下条目

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

At runtime, the 在运行时,

setFooService(...)

method is called first and the FooService object is injected as expected, so an external call to 首先调用方法,并按预期注入FooService对象,因此外部调用

ServiceProvider.getFooService();

will also work as it did before. 也会像以前一样工作。

I think I've found a way to do this - I'm working on testing it, but from the documentation this approach seems promising. 我想我已经找到了这样做的方法 - 我正在测试它,但从文档中这种方法看起来很有希望。

I created a resolver class, which is responsible for actually fetching the service from OSGi, and which does it by means of implementing BundleContextAware . 我创建了一个解析器类,它负责从OSGi实际获取服务,并通过实现BundleContextAware来实现。 That way, I'm hoping a BundleContext will be injected from which I can grab a service instance: 这样,我希望注入一个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;
    }
}

Then the static getInstance() method becomes simply 然后静态getInstance()方法变得简单

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

As I said, I haven't tested this yet, so we'll see if it actually works. 正如我所说,我还没有测试过,所以我们会看看它是否真的有效。

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

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