首页 文章

OSGi:没有生命周期管理的服务绑定

提问于
浏览
1

我正在Equinox OSGi框架上构建Java应用程序,我一直在使用DS(声明性服务)来声明引用和提供的服务 . 到目前为止,我实施的所有服务消费者也恰好都是服务提供商,因此我很自然地将它们变为无状态(这样它们可以被多个消费者重用,而不是被一个消费者连接)让他们成为由框架实例化(默认构造函数,在我的代码中无处调用) .

现在我有一个不同的情况:我有一个类 MyClass 引用服务 MyService 但它本身不是服务提供者 . 我需要能够自己实例化 MyClass ,而不是让OSGi框架实例化它 . 然后我希望框架将现有的 MyService 实例传递给 MyClass 实例 . 像这样的东西:

public class MyClass {

    private String myString;
    private int myInt;

    private MyService myService;

    public MyClass(String myString, int myInt) {
        this.myString = myString;
        this.myInt= myInt;
    }

    // bind
    private void setMyService(MyService myService) {
        this.myService = myService;
    }

    // unbind
    private void unsetMyService(MyService myService) {
        this.myService = null;
    }

    public void doStuff() {
        if (myService != null) {
            myService.doTheStuff();
        } else {
            // Some fallback mechanism
        }
    }

}
public class AnotherClass {

    public void doSomething(String myString, int myInt) {
        MyClass myClass = new MyClass(myString, myInt);

        // At this point I would want the OSGi framework to invoke
        // the setMyService method of myClass with an instance of
        // MyService, if available.

        myClass.doStuff();
    }

}

我的第一次尝试是使用DS为 MyClass 创建组件定义并从那里引用 MyService

<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="My Class">
    <implementation class="my.package.MyClass"/>
    <reference bind="setMyService" cardinality="0..1" interface="my.other.package.MyService" name="MyService" policy="static" unbind="unsetMyService"/>
</scr:component>

但是, MyClass 实际上并不是一个组件,因为我不希望它的生命周期被管理 - 我想自己处理实例化 . 正如Neil Bartlett指出here

例如,您可以说您的组件“依赖于”特定服务,在这种情况下,只有在该服务可用时才会创建和激活组件 - 并且当服务变得不可用时它也将被销毁 .

这不是我想要的 . 我想要没有生命周期管理的绑定 . [注意:即使我将基数设置为 0..1 (可选和一元),框架仍将尝试实例化 MyClass (并且由于缺少no-args构造函数而失败)]

所以,我的问题是:有没有办法使用DS来实现我正在寻找的“仅绑定,无生命周期管理”功能?如果DS无法做到这一点,有哪些替代方案,您会推荐什么?


更新:使用ServiceTracker(Neil Bartlett建议)

IMPORTANT: I've posted an improved version of this below as an answer. I'm just keeping this here for "historic" purposes.

在这种情况下,我不确定如何应用 ServiceTracker . 你会使用如下所示的静态注册表吗?

public class Activator implements BundleActivator {

    private ServiceTracker<MyService, MyService> tracker;

    @Override
    public void start(BundleContext bundleContext) throws Exception {
        MyServiceTrackerCustomizer customizer = new MyServiceTrackerCustomizer(bundleContext);
        tracker = new ServiceTracker<MyService, MyService>(bundleContext, MyService.class, customizer);
        tracker.open();
    }

    @Override
    public void stop(BundleContext bundleContext) throws Exception {
        tracker.close();
    }

}
public class MyServiceTrackerCustomizer implements ServiceTrackerCustomizer<MyService, MyService>  {

    private BundleContext bundleContext;

    public MyServiceTrackerCustomizer(BundleContext bundleContext) {
        this.bundleContext = bundleContext;
    }

    @Override
    public MyService addingService(ServiceReference<MyService> reference) {
        MyService myService = bundleContext.getService(reference);
        MyServiceRegistry.register(myService); // any better suggestion?
        return myService;
    }

    @Override
    public void modifiedService(ServiceReference<MyService> reference, MyService service) {
    }

    @Override
    public void removedService(ServiceReference<MyService> reference, MyService service) {
        bundleContext.ungetService(reference);
        MyServiceRegistry.unregister(service); // any better suggestion?
    }

}
public class MyServiceRegistry {

    // I'm not sure about using a Set here... What if the MyService instances
    // don't have proper equals and hashCode methods? But I need some way to
    // compare services in isActive(MyService). Should I just express this
    // need to implement equals and hashCode in the javadoc of the MyService
    // interface? And if MyService is not defined by me, but is 3rd-party?
    private static Set<MyService> myServices = new HashSet<MyService>();

    public static void register(MyService service) {
        myServices.add(service);
    }

    public static void unregister(MyService service) {
        myServices.remove(service);
    }

    public static MyService getService() {
        // Return whatever service the iterator returns first.
        for (MyService service : myServices) {
            return service;
        }
        return null;
    }

    public static boolean isActive(MyService service) {
        return myServices.contains(service);
    }

}
public class MyClass {

    private String myString;
    private int myInt;

    private MyService myService;

    public MyClass(String myString, int myInt) {
        this.myString = myString;
        this.myInt= myInt;
    }

    public void doStuff() {
        // There's a race condition here: what if the service becomes
        // inactive after I get it?
        MyService myService = getMyService();
        if (myService != null) {
            myService.doTheStuff();
        } else {
            // Some fallback mechanism
        }
    }

    protected MyService getMyService() {
        if (myService != null && !MyServiceRegistry.isActive(myService)) {
            myService = null;
        }
        if (myService == null) {
            myService = MyServiceRegistry.getService();
        }
        return myService;
    }

}

这是你怎么做的?您能否对我在上述评论中提出的问题发表评论?那是:

  • 如果服务实现未正确实现 equalshashCode ,则 Set 出现问题 .

  • 竞争条件:我的 isActive 检查后服务可能会变为非活动状态 .

2 回答

  • -1

    不,这不属于DS的范围 . 如果您想自己直接实例化该类,那么您将不得不使用像 ServiceTracker 这样的OSGi API来获取服务引用 .

    Update:

    请参阅以下建议的代码 . 显然,根据您实际想要实现的目标,有很多不同的方法可以做到这一点 .

    public interface MyServiceProvider {
        MyService getService();
    }
    

    ...

    public class MyClass {
    
        private final MyServiceProvider serviceProvider;
    
        public MyClass(MyServiceProvider serviceProvider) {
            this.serviceProvider = serviceProvider;
        }
    
        void doStuff() {
            MyService service = serviceProvider.getService();
            if (service != null) {
                // do stuff with service
            }
        }
    }
    

    ...

    public class ExampleActivator implements BundleActivator {
    
        private MyServiceTracker tracker;
    
        static class MyServiceTracker extends ServiceTracker<MyService,MyService> implements MyServiceProvider {
            public MyServiceTracker(BundleContext context) {
                super(context, MyService.class, null);
            }
        };
    
        @Override
        public void start(BundleContext context) throws Exception {
            tracker = new MyServiceTracker(context);
            tracker.open();
    
            MyClass myClass = new MyClass(tracker);
            // whatever you wanted to do with myClass
        }
    
        @Override
        public void stop(BundleContext context) throws Exception {
            tracker.close();
        }
    
    }
    
  • 1

    解决方案:使用ServiceTracker(由Neil Bartlett建议)

    注意:如果您想查看downvote的原因,请参阅Neil's answer以及我们的评论中的来回 .

    最后,我使用 ServiceTracker 和静态注册表( MyServiceRegistry )解决了它,如下所示 .

    public class Activator implements BundleActivator {
    
        private ServiceTracker<MyService, MyService> tracker;
    
        @Override
        public void start(BundleContext bundleContext) throws Exception {
            MyServiceTrackerCustomizer customizer = new MyServiceTrackerCustomizer(bundleContext);
            tracker = new ServiceTracker<MyService, MyService>(bundleContext, MyService.class, customizer);
            tracker.open();
        }
    
        @Override
        public void stop(BundleContext bundleContext) throws Exception {
            tracker.close();
        }
    
    }
    
    public class MyServiceTrackerCustomizer implements ServiceTrackerCustomizer<MyService, MyService>  {
    
        private BundleContext bundleContext;
    
        public MyServiceTrackerCustomizer(BundleContext bundleContext) {
            this.bundleContext = bundleContext;
        }
    
        @Override
        public MyService addingService(ServiceReference<MyService> reference) {
            MyService myService = bundleContext.getService(reference);
            MyServiceRegistry.getInstance().register(myService);
            return myService;
        }
    
        @Override
        public void modifiedService(ServiceReference<MyService> reference, MyService service) {
        }
    
        @Override
        public void removedService(ServiceReference<MyService> reference, MyService service) {
            bundleContext.ungetService(reference);
            MyServiceRegistry.getInstance().unregister(service);
        }
    
    }
    
    /**
     * A registry for services of type {@code <S>}.
     *
     * @param <S> Type of the services registered in this {@code ServiceRegistry}.<br>
     *            <strong>Important:</strong> implementations of {@code <S>} must implement
     *            {@link #equals(Object)} and {@link #hashCode()}
     */
    public interface ServiceRegistry<S> {
    
        /**
         * Register service {@code service}.<br>
         * If the service is already registered this method has no effect.
         *
         * @param service the service to register
         */
        void register(S service);
    
        /**
         * Unregister service {@code service}.<br>
         * If the service is not currently registered this method has no effect.
         *
         * @param service the service to unregister
         */
        void unregister(S service);
    
        /**
         * Get an arbitrary service registered in the registry, or {@code null} if none are available.
         * <p/>
         * <strong>Important:</strong> note that a service may become inactive <i>after</i> it has been retrieved
         * from the registry. To check whether a service is still active, use {@link #isActive(Object)}. Better
         * still, if possible don't store a reference to the service but rather ask for a new one every time you
         * need to use the service. Of course, the service may still become inactive between its retrieval from
         * the registry and its use, but the likelihood of this is reduced and this way we also avoid holding
         * references to inactive services, which would prevent them from being garbage-collected.
         *
         * @return an arbitrary service registered in the registry, or {@code null} if none are available.
         */
        S getService();
    
        /**
         * Is {@code service} currently active (i.e., running, available for use)?
         * <p/>
         * <strong>Important:</strong> it is recommended <em>not</em> to store references to services, but rather
         * to get a new one from the registry every time the service is needed -- please read more details in
         * {@link #getService()}.
         *
         * @param service the service to check
         * @return {@code true} if {@code service} is currently active; {@code false} otherwise
         */
        boolean isActive(S service);
    
    }
    
    /**
     * Implementation of {@link ServiceRegistry}.
     */
    public class ServiceRegistryImpl<S> implements ServiceRegistry<S> {
    
        /**
         * Services that are currently registered.<br>
         * <strong>Important:</strong> as noted in {@link ServiceRegistry}, implementations of {@code <S>} must
         * implement {@link #equals(Object)} and {@link #hashCode()}; otherwise the {@link Set} will not work
         * properly.
         */
        private Set<S> myServices = new HashSet<S>();
    
        @Override
        public void register(S service) {
            myServices.add(service);
        }
    
        @Override
        public void unregister(S service) {
            myServices.remove(service);
        }
    
        @Override
        public S getService() {
            // Return whatever service the iterator returns first.
            for (S service : myServices) {
                return service;
            }
            return null;
        }
    
        @Override
        public boolean isActive(S service) {
            return myServices.contains(service);
        }
    
    }
    
    public class MyServiceRegistry extends ServiceRegistryImpl<MyService> {
    
        private static final MyServiceRegistry instance = new MyServiceRegistry();
    
        private MyServiceRegistry() {
            // Singleton
        }
    
        public static MyServiceRegistry getInstance() {
            return instance;
        }
    
    }
    
    public class MyClass {
    
        private String myString;
        private int myInt;
    
        public MyClass(String myString, int myInt) {
            this.myString = myString;
            this.myInt= myInt;
        }
    
        public void doStuff() {
            MyService myService = MyServiceRegistry.getInstance().getService();
            if (myService != null) {
                myService.doTheStuff();
            } else {
                // Some fallback mechanism
            }
        }
    
    }
    

    如果有人想将此代码用于任何目的,请继续 .

相关问题