如何正确启动和关闭 OSGi 容器?

Posted

技术标签:

【中文标题】如何正确启动和关闭 OSGi 容器?【英文标题】:How to start and shutdown an OSGi container properly? 【发布时间】:2015-03-18 02:32:38 【问题描述】:

我制作了一个包含 Activator 文件的包,该文件基本上按正确的顺序启动其他包。我需要这个,因为框架会在它们的依赖项完成启动之前启动一些包。例如,取决于我的日志服务的我的包将在它之前启动,因此不会为这些包存储日志消息。有了这个激活器,我可以选择谁先开始,因为在 felix 中没有明确的方法。

代码在这里:

package lumina.launcher;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;

public final class Launcher implements BundleActivator 

    private BundleContext context;

    private final String relativePath = "./bundle/";

    private final void print(String msg) 
        // System.out.println(msg);
    

    private Bundle install(String path) throws BundleException 
        Bundle bundle = this.context.installBundle("file:/" + path);
        print("\tInstalling: " + bundle.getSymbolicName());
        return bundle;
    

    private boolean isJar(String fileName) 
        final String[] tokens = fileName.split("\\.");
        return tokens.length != 0 && "jar".equals(tokens[tokens.length - 1]);
    

    private String[] listFilesForFolder(final File folder)
            throws BundleException 
        final List<String> bundles = new ArrayList<String>();
        for (final File fileEntry : folder.listFiles()) 
            if (fileEntry.isDirectory()) 
                listFilesForFolder(fileEntry);
             else 
                if (isJar(fileEntry.getName())) 
                    bundles.add(fileEntry.getAbsolutePath().replace(
                            File.separatorChar, '/'));
                
            
        
        return bundles.toArray(new String[0]);
    

    @Override
    public void start(BundleContext context) throws Exception 
        this.context = context;
        installStart("third-party");
        installStart("shared-components");
        installStart("lumina");
        installStart("webconsole");
        System.out
                .println("-> You can access Felix WebConsole at http://localhost:8080/system/console");
        Thread.sleep(2000);
        installStart("plugins");
    

    private final void installStart(String path) throws BundleException 
        print("Installing " + path);
        final String[] targetBundles = listFilesForFolder(new File(relativePath
                + path));
        final Bundle[] bundles = new Bundle[targetBundles.length];
        for (int i = 0; i < targetBundles.length; i++) 
            bundles[i] = install(targetBundles[i]);
        
        print("\tStarting installed bundles...");
        for (Bundle b : bundles)
            b.start();
    

    @Override
    public void stop(BundleContext arg0) throws Exception 
    


这就是我在 felix 的 bundle/ 文件夹中组织我的包的方式。

C:\felix\BUNDLE
│   .gitignore
│   lumina.launcher-1.0.0-SNAPSHOT.jar
│
├───lumina
│       .gitignore
│       lumina.api-0.0.4-SNAPSHOT.jar
│
├───plugins
│       .gitignore
│       lumina.assembler-0.0.1-SNAPSHOT.jar
│       lumina.extensions.drivers.ip-0.0.1-SNAPSHOT.jar
│       lumina.rest-0.0.4-SNAPSHOT.jar
│       shared.extensions.base.logger-1.0.2-SNAPSHOT.jar
│       smartcampuskndriver.gateway-0.0.1-SNAPSHOT.jar
│       smartcampusmetersip.gateway-0.0.1-SNAPSHOT.jar
│
├───shared-components
│       codebase-2.0.0.jar
│       shared.osgi.services-1.3.0-SNAPSHOT.jar
│       shared.osgi.services.logger-1.0.0-SNAPSHOT.jar
│       shared.properties.api-5.2.1-SNAPSHOT.jar
│
├───third-party
│       com.googlecode.json-simple_1.1.0.jar
│       org.apache.felix.bundlerepository-2.0.2.jar
│       org.apache.servicemix.bundles.joda-time-2.3_1.jar
│       org.apache.servicemix.bundles.junit-4.11_1.jar
│       org.osgi.compendium-1.4.0.jar
│       org.osgi.core-1.4.0.jar
│       org.restlet.ext.json_2.1.0.M1.jar
│       org.restlet.jse.org.restlet.lib.org.json_2.0.0.jar
│       org.restlet_2.1.0.M1.jar
│       OSGiJMX.jar
│
├───unused
│       org.apache.felix.gogo.command-0.14.0.jar
│       org.apache.felix.gogo.runtime-0.12.1.jar
│       org.apache.felix.gogo.shell-0.10.0.jar
│
└───webconsole
        org.apache.felix.http.api-2.3.2.jar
        org.apache.felix.http.jetty-2.3.2.jar
        org.apache.felix.http.servlet-api-1.0.1.jar
        org.apache.felix.webconsole-4.2.4-all.jar

请注意,lumina.launcher-1.0.0-SNAPSHOT.jar 是我的 Activator 所在的位置,并且是唯一由 felix 自动启动的包。

顺便说一句,这很好用,不知道是否有更聪明的方法来做到这一点。所以上面代码的思路是按照以下顺序从每个目录启动所有的bundle:third-party、shared-components、lumina、webconsole和plugins。

另一方面,当我在 OSGi 容器运行时在终端中点击 ^C 时,它开始一个接一个地停止所有捆绑包。这没关系,但是日志服务是最先停止的捆绑包之一,我希望它成为最后一个捆绑包之一。有没有办法设置 OSGi 容器关闭时停止捆绑的顺序?

谢谢!

【问题讨论】:

【参考方案1】:

我需要这个,因为框架会在它们的依赖项完成启动之前启动一些包。

OSGi 的一条规则是,所有的 bundle 都应该以启动顺序无关紧要的方式实现。

如果您确实需要起始顺序,则应为您的捆绑包设置起始级别。请参阅 OSGi 核心规范的第 10.5 章以获取有关启动级别的更多信息。

【讨论】:

我要设置一个顺序,我觉得不设置启动顺序是不现实的。在我的情况下,我在包 (A) 中有一个日志服务注册表,然后我在另一个包 (B) 中有一些记录器实现,然后我有一个记录内容的包 (C)。 OSGi永远不会做的一件事是在启动A之前启动B。但是它可以在启动B之前启动C。所以C将在我安装任何记录器实现之前开始记录东西。 在我的情况下,OSGi 以 A、C 和 B 的顺序启动捆绑包,而我只想让它像 A、B 和 C 一样启动。关闭也是如此,正确的顺序是停止捆绑包将是 C、B 和 A。如何在 Felix 框架中定义开始级别? 您可以配置 felix 以启动具有特定启动级别的捆绑包。请参阅felix.apache.org/site/… 的配置设置 felix.auto.start. 的描述。另外,请参阅提及 startlevel 的其他配置选项的说明。 如果你使用例如声明式服务(例如,通过使用 Apache Felix SCR 注释),然后您可以根据 C 中服务的要求声明日志服务。然后框架将确保服务以正确的顺序启动。 是的。如果启动顺序因服务启动而重要,这是使捆绑启动顺序独立的最常见方法。 wiki.osgi.org/wiki/Avoid_Start_Order_Dependencies 有一篇关于启动订单问题的好文章

以上是关于如何正确启动和关闭 OSGi 容器?的主要内容,如果未能解决你的问题,请参考以下文章

Oracle12cr1新特性之容器数据库(CDB)和可插拔数据库(PDB) 的启动和关闭

容器中用shell 脚本启动如何优雅关闭

如何避免Docker容器启动脚本运行后自动退出

Tomcat 和 OSGi

Docker - 重新启动关闭的容器

Docker容器意外(非正常)关闭后无法正常启动