简体   繁体   English

如何隐藏 OSGi 服务以便其他捆绑包无法找到它们?

[英]How do I hide OSGi services so that other bundles can't find them?

I am developing an application that is build on top of Apache Felix and JavaFX.我正在开发一个构建在 Apache Felix 和 JavaFX 之上的应用程序。 The application can be extended by 3rd party bundles that implement a specific Interface and make it available to the OSGi Runtime Service Registry.应用程序可以通过实现特定接口的 3rd 方包进行扩展,并使其可用于 OSGi 运行时服务注册表。

The problem is that those bundles (or plugins) should not be able to retrieve any of the services that are just used internally by my application.问题是那些包(或插件)不应该能够检索我的应用程序内部使用的任何服务。 An example would be a PersistenceService that is used to save the processed data.一个例子是用于保存处理过的数据的 PersistenceService。 Plugins are (in my application) by definition not allowed to store any data through my service but are allowed to save them through a specific service designed for the plugins only.插件(在我的应用程序中)根据定义不允许通过我的服务存储任何数据,但允许通过仅为插件设计的特定服务来保存它们。

I had the idea of using the FindHook Interface offered by OSGi to filter out those requests but that didn't work good.我有使用 OSGi 提供的 FindHook 接口来过滤这些请求的想法,但效果不佳。 Obviously, to make it work, the bundle needs to me loaded at the very start, eve before my core application gets loaded.显然,要使其工作,捆绑包需要在我的核心应用程序加载前夕一开始就加载。 I ensured this happens by specifying the start level for this bundle using the felix.auto.deploy.install.1 = "file\\:bundles/de/zerotask/voices-findhook/0.1-SNAPSHOT/voices-findhook-0.1-SNAPSHOT.jar"我通过使用felix.auto.deploy.install.1 = "file\\:bundles/de/zerotask/voices-findhook/0.1-SNAPSHOT/voices-findhook-0.1-SNAPSHOT.jar"

As far as I understood, the start level of the system bundle will be 1 which means that my bundle should always be loaded right after the system bundle.据我所知,系统包的起始级别是 1,这意味着我的包应该总是在系统包之后加载。

Here is my implementation of the FindHook interface:这是我对 FindHook 接口的实现:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.hooks.service.FindHook;

/**
 *
 * @author PositiveDown
 */
public class VoicesFindHook implements FindHook {

private static Logger log = LoggerFactory.getLogger(VoicesFindHook.class);

private static final String[] INTERNAL_BUNDLE_TABLE = new String[]{
    "de.zerotask.voices-core-actions",
    "de.zerotask.voices-findhook",
    "de.zerotask.voices-interfaces-persistable",
    "de.zerotask.voices-models",
    "de.zerotask.voices-models-actions",
    "de.zerotask.voices-services-configuration-internal",
    "de.zerotask.voices-services-input-internal",
    "de.zerotask.voices-services-licenses-internal",
    "de.zerotask.voices-services-modelsmanager-internal",
    "de.zerotask.voices-services-persistence-internal",
    "de.zerotask.voices-services-window-internal",
    "de.zerotask.voices-ui-dialogs-about",
    "de.zerotask.voices-ui-dialogs-newprofile",
    "de.zerotask.voices-ui-dockable-listview",
    "de.zerotask.voices-ui-dockable-properties",
    "de.zerotask.voices-ui-layout",
    "de.zerotask.voices-utils-io",
    "de.zerotask.voices-utils-services",
    "de.zerotask.voices-utils-ui"
};

private static final String[] INTERNAL_SERVICES_TABLE = new String[]{
    // model services
    // configuration service
    "de.zerotask.voices.services.configuration.IConfiguration",
    // window service
    "de.zerotask.voices.services.window.IWindowService",
    // persistence services
    "de.zerotask.voices.services.persistence.IPathResolver",
    "de.zerotask.voices.services.persistence.IPersistenceService"
};

private static final Set<String> INTERNAL_BUNDLES = new HashSet<>(Arrays.asList(INTERNAL_BUNDLE_TABLE));

private static final Set<String> INTERNAL_SERVICES = new HashSet<>(Arrays.asList(INTERNAL_SERVICES_TABLE));

@Override
public void find(BundleContext context, String name, String filter, boolean allServices, Collection<ServiceReference<?>> references) {
    // only allow the usage of internal interfaces from internal packages
    String symbolicName = context.getBundle().getSymbolicName();
    // debug
    log.debug("Processing Bundle {} and service {}", symbolicName, name);
    // if the service is one of the internal ones, proceed
    if (INTERNAL_SERVICES.contains(name)) {
        // retrieve the bundle id
        log.debug("Service {} is in internal table", name);
        // if the name is not in the internal bundle table, remove all service references
        if (!INTERNAL_BUNDLES.contains(symbolicName)) {
            log.debug("Bundle {} not in internal table => removing service references...", symbolicName);
            // remove them
            references.clear();
        }
    }
}
}

The idea is to have a table of internal bundles``` and internal services```.这个想法是有一个internal bundles``` and内部服务的表。 Each time a service is looked up, the hook will check if it is an internal service.每次查找服务时,钩子都会检查它是否是内部服务。 If this is the case, it will also check if the caller bundle is an internal bundle.如果是这种情况,它还会检查调用者包是否是内部包。 If that's not true, the hook will remove all services found from the collection.如果这不是真的,钩子将删除从集合中找到的所有服务。

I am by far no OSGi expert but this method should work because it is based on the SymbolicName s which are unique in each container.我目前还不是 OSGi 专家,但这种方法应该有效,因为它基于每个容器中唯一的SymbolicName

I have tested the above code with two small test bundles.我已经用两个小的测试包测试了上面的代码。 One providing the interface + implementation and the other one consuming it.一个提供接口 + 实现,另一个使用它。 I changed the hook so it will not return any services for the consumer bundle (to just simply check if it works).我改变了钩子,所以它不会为消费者包返回任何服务(只是简单地检查它是否有效)。

No my problem is, the consumer bundle gets somehow loaded first.不,我的问题是,消费者包首先以某种方式加载。 I have no idea why.我不知道为什么。 By doing this it basically breaks my loading property set in the properties file.通过这样做,它基本上打破了我在属性文件中设置的加载属性。 I am not sure if this helps but the provider bundle's name starts with an 'y', the consumer one with an 't' and the hook one with an 'v'.我不确定这是否有帮助,但提供者捆绑包的名称以“y”开头,消费者以“t”开头,钩子以“v”开头。 The funny thing is, Felix is loading them in alphabetically order.有趣的是,Felix 正在按字母顺序加载它们。

I would really appreciate any help here.我真的很感激这里的任何帮助。

Services are implicitly available to every bundle – that is the purpose of services after all.服务对每个捆绑包都隐式可用——这毕竟是服务的目的。

You can work around this with various hacks like FindHooks etc, but as you have already discovered you are constantly fighting against the true nature of the OSGi Framework and services.您可以使用 FindHooks 等各种 hack 来解决这个问题,但是正如您已经发现的那样,您一直在与 OSGi 框架和服务的真实本质作斗争。

It sounds more like you are creating an isolation system between a kernel and a user space, so that you cannot accidentally pollute the user area with kernel services and vice versa .听起来更像是您在内核和用户空间之间创建了一个隔离系统,这样您就不会意外地用内核服务污染用户区,反之亦然 The proper way (IMHO) to achieve this is with a separate OSGi Framework instance for the two areas.实现这一点的正确方法(恕我直言)是为这两个区域使用单独的 OSGi 框架实例。 It's quite simple to run up a new Framework using the FrameworkFactory API.使用FrameworkFactory API 运行新框架非常简单。 Then you can expose select packages and services from the kernel using the BundleContext of the system bundle of the user-area Framework.然后,您可以使用用户区框架的系统包的BundleContext从内核公开选择的包和服务。

However as BJ points out in comments, you may be over-engineering this.但是,正如 BJ 在评论中指出的那样,您可能对此进行了过度设计。 What's the worst that can happen if the plugins can see your system services?如果插件可以看到您的系统服务,最糟糕的情况是什么? If those services are well designed then the answer should be "not a lot".如果这些服务设计得很好,那么答案应该是“不多”。

I see two options:我看到两个选项:

or或者

  • ServiceFactory , you decide what bundle can get the real service. ServiceFactory ,您决定哪个 bundle 可以获得真正的服务。 Others receive a fake implementation.其他人收到虚假的实现。

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

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