OSGi#2:编程方式使用Equinox

今天来看下怎么在程序中使用Equinox容器。

startup

上一篇文章中,我们通过下面的命令启动了容器,

> java -jar org.eclipse.osgi_3.10.2.v20150203-1939.jar -console

通过查看org.eclipse.osgi_3.10.2.v20150203-1939.jar这个jar包的MANIFEST.MF找到Main-Class

Main-Class: org.eclipse.core.runtime.adaptor.EclipseStarter

EclipseStartermain方法中有如下注释,

    /**
     * This is the main to start osgi.
     * It only works when the framework is being jared as a single jar
     */

所以不能通过main方法来启动。Google了一把,在SO上面发现有人问过这个问题,里面说到可以通过startup方法来启动,EclipseStarter.startup((new String[]{"-console"}), null);等同于上面jar包启动的方式。

EquinoxConfiguration

以编程方式启动,同样的,需要configuration/config.ini文件与org.eclipse.osgi_3.10.2.v20150203-1939.jar在同一目录下,但这只是默认的配置,可以通过设置system property来改变。

容器的配置主要都存在EquinoxConfiguration中,而关于目录这样的location配置则单独抽了出来,放在EquinoxLocations中。二者都是在EquinoxContainer中初始化,

    public EquinoxContainer(Map<String, ?> configuration) {
        this.equinoxConfig = new EquinoxConfiguration(configuration, new HookRegistry(this));
        this.equinoxLocations = new EquinoxLocations(this.equinoxConfig);
        loadConfig(equinoxConfig, equinoxLocations);
        ...
    }

EquinoxConfiguration将所有配置到保存到了一个Properties中,

private final Properties configuration;

而由EquinoxContainer传入的那个Map则是initialConfig,最终也会被存到configuration当中。这个initialConfig是由EclipseStarter#getConfiguration构造出来的,

    private synchronized static Map<String, String> getConfiguration() {
        if (configuration == null) {
            configuration = new HashMap<String, String>();
            // TODO hack to set these to defaults for EclipseStarter
            // Note that this hack does not allow these properties to be specified in config.ini
            configuration.put(EquinoxConfiguration.PROP_USE_SYSTEM_PROPERTIES, System.getProperty(EquinoxConfiguration.PROP_USE_SYSTEM_PROPERTIES, "true")); //$NON-NLS-1$
            configuration.put(EquinoxConfiguration.PROP_COMPATIBILITY_BOOTDELEGATION, System.getProperty(EquinoxConfiguration.PROP_COMPATIBILITY_BOOTDELEGATION, "true")); //$NON-NLS-1$
        }
        return configuration;
    }

注意,EquinoxConfiguration.PROP_USE_SYSTEM_PROPERTIES的配置默认是true,会将所有的system property存入EquinoxConfiguration中,

    this.configuration = useSystemProperties ? System.getProperties() : new Properties();

EquinoxLocations

说完EquinoxConfiguration接下来看看EquinoxLocationsEquinoxLocations会在构造函数中,使用EquinoxConfiguration,初始化各种位置信息。我们来看下config.ini的位置是怎么得出来的。

config.ini文件在EquinoxContainer#loadConfig方法中使用,

    private static void loadConfig(EquinoxConfiguration equinoxConfig, EquinoxLocations equinoxLocations) {
        if (Boolean.TRUE.toString().equals(equinoxConfig.getConfiguration(EquinoxConfiguration.PROP_IGNORE_USER_CONFIGURATION)))
            return;
        Location configArea = equinoxLocations.getConfigurationLocation();
        if (configArea == null)
            return;

        URL location = null;
        try {
            location = new URL(configArea.getURL().toExternalForm() + CONFIG_FILE);
        } catch (MalformedURLException e) {
            // its ok.  This should never happen
        }
        equinoxConfig.mergeConfiguration(loadProperties(location, equinoxConfig));
    }

所以是通过EquinoxLocations#configurationLocation来得出config.ini的具体位置。configurationLocation的计算逻辑,上面已经说了,在EquinoxLocations的构造函数中,

        mungeConfigurationLocation();
        // compute a default but it is very unlikely to be used since main will have computed everything
        temp = buildLocation(PROP_CONFIG_AREA_DEFAULT, null, "", false, false, null); //$NON-NLS-1$
        defaultLocation = temp == null ? null : temp.getURL();
        if (defaultLocation == null && equinoxConfig.getConfiguration(PROP_CONFIG_AREA) == null)
            // only compute the default if the configuration area property is not set
            defaultLocation = buildURL(computeDefaultConfigurationLocation(), true);
        configurationLocation = buildLocation(PROP_CONFIG_AREA, defaultLocation, "", false, false, null); //$NON-NLS-1$

也就是说如果没有设置PROP_CONFIG_AREA,就会使用默认的配置,

    private String computeDefaultConfigurationLocation() {
        // 1) We store the config state relative to the ‘eclipse‘ directory if possible
        // 2) If this directory is read-only
        //    we store the state in <user.home>/.eclipse/<application-id>_<version> where <user.home>
        //    is unique for each local user, and <application-id> is the one
        //    defined in .eclipseproduct marker file. If .eclipseproduct does not
        //    exist, use "eclipse" as the application-id.

        URL installURL = computeInstallConfigurationLocation();
        if (installURL != null && "file".equals(installURL.getProtocol())) { //$NON-NLS-1$
            File installDir = new File(installURL.getFile());
            File defaultConfigDir = new File(installDir, CONFIG_DIR);
            if (!defaultConfigDir.exists())
                defaultConfigDir.mkdirs();
            if (defaultConfigDir.exists() && StorageUtil.canWrite(defaultConfigDir))
                return defaultConfigDir.getAbsolutePath();
        }
        // We can‘t write in the eclipse install dir so try for some place in the user‘s home dir
        return computeDefaultUserAreaLocation(CONFIG_DIR);
    }
    private URL computeInstallConfigurationLocation() {
        String property = equinoxConfig.getConfiguration(PROP_INSTALL_AREA);
        if (property != null)
            return LocationHelper.buildURL(property, true);
        return null;
    }

所以默认的配置是使用了PROP_INSTALL_AREA,而PROP_INSTALL_AREA是在EquinoxConfiguration#initializeProperties中设置的,

        // initialize some framework properties that must always be set
        if (getConfiguration(PROP_FRAMEWORK) == null || getConfiguration(EquinoxLocations.PROP_INSTALL_AREA) == null) {
            ProtectionDomain pd = EquinoxConfiguration.class.getProtectionDomain();
            CodeSource cs = pd == null ? null : pd.getCodeSource();
            URL url = cs == null ? null : cs.getLocation();
            if (url == null) {
                IOException cause = null;
                // try to determine by loading a resource we know we have
                URL java6Profile = EquinoxConfiguration.class.getResource("/JavaSE-1.6.profile"); //$NON-NLS-1$
                if (java6Profile != null && "jar".equals(java6Profile.getProtocol())) { //$NON-NLS-1$
                    try {
                        url = ((JarURLConnection) java6Profile.openConnection()).getJarFileURL();
                    } catch (IOException e) {
                        cause = e;
                    }
                }
                if (url == null) {
                    throw new IllegalArgumentException(NLS.bind(Msg.ECLIPSE_STARTUP_PROPS_NOT_SET, PROP_FRAMEWORK + ", " + EquinoxLocations.PROP_INSTALL_AREA), cause); //$NON-NLS-1$
                }
            }

            // allow props to be preset
            if (getConfiguration(PROP_FRAMEWORK) == null) {
                String externalForm = getFrameworkPath(url.toExternalForm(), false);
                setConfiguration(PROP_FRAMEWORK, externalForm);
            }
            if (getConfiguration(EquinoxLocations.PROP_INSTALL_AREA) == null) {
                String filePart = getFrameworkPath(url.getFile(), true);
                setConfiguration(EquinoxLocations.PROP_INSTALL_AREA, filePart);
            }
        }
        // always decode these properties
        setConfiguration(PROP_FRAMEWORK, decode(getConfiguration(PROP_FRAMEWORK)));
        setConfiguration(EquinoxLocations.PROP_INSTALL_AREA, decode(getConfiguration(EquinoxLocations.PROP_INSTALL_AREA)));

妥妥的,原来是使用了ProtectionDomain里面的CodeSource,关于他俩,之前有文章介绍过了。所以正如上面所说的,configuration/config.iniorg.eclipse.osgi_3.10.2.v20150203-1939.jar默认情况下需要在同一目录。这个PROP_INSTALL_AREA会被很多其他location用到,具体可以看看EquinoxLocations的构造函数。

简单起见,我直接将相关jar包及配置扔到%JAVA_HOME%/jre/lib/ext目录下了,

C:\Java\jdk1.7.0_51\jre\lib>tree ext /F
C:\JAVA\JDK1.7.0_51\JRE\LIB\EXT
│  dnsns.jar
│  jaccess.jar
│  localedata.jar
│  org.eclipse.equinox.common_3.6.200.v20130402-1505.jar
│  org.eclipse.osgi_3.10.2.v20150203-1939.jar
│  org.eclipse.update.configurator_3.3.300.v20140518-1928.jar
│  sunec.jar
│  sunjce_provider.jar
│  sunmscapi.jar
│  zipfs.jar
│
├─configuration
│       config.ini
│
└─plugins
        me.kisimple.just4fun.osgi.common_1.0.0.jar
        org.apache.felix.gogo.command_0.10.0.v201209301215.jar
        org.apache.felix.gogo.runtime_0.10.0.v201209301036.jar
        org.apache.felix.gogo.shell_0.10.0.v201212101605.jar
        org.eclipse.equinox.console_1.1.0.v20140131-1639.jar

接下来我们写代码来加载plugins目录下me.kisimple.just4fun.osgi.common_1.0.0.jar这个bundle里面的me.kisimple.just4fun.osgi.HelloOSGi

package me.kisimple.just4fun.osgi;

public class HelloOSGi {
    public void run() {
        System.out.println("Hello,OSGi.");
    }
}

该bundle的MANIFEST.MF如下,

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: common
Bundle-SymbolicName: me.kisimple.just4fun.osgi.common
Bundle-Version: 1.0.0
Bundle-Vendor: KISIMPLE
Bundle-Localization: systembundle
Import-Package: org.osgi.framework
Export-Package: me.kisimple.just4fun.osgi

直接上代码,

import org.eclipse.core.runtime.adaptor.EclipseStarter;
import org.eclipse.osgi.container.ModuleWiring;
import org.eclipse.osgi.internal.framework.EquinoxBundle;
import org.eclipse.osgi.internal.loader.BundleLoader;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;

public class Main {

    public static void main(String[] args) throws Throwable {
//        BundleContext context = EclipseStarter.startup((new String[]{"-console"}), null);
        BundleContext context = EclipseStarter.startup((new String[]{}), null);

        String className = "me.kisimple.just4fun.osgi.HelloOSGi";
        String packageName = BundleLoader.getPackageName(className);

        for (Bundle osgiBundle : context.getBundles()) {
            if(osgiBundle instanceof EquinoxBundle) {
                EquinoxBundle bundle = (EquinoxBundle)osgiBundle;
                ModuleWiring wiring = bundle.getModule().getCurrentRevision().getWiring();
                if (wiring != null) {
                    BundleLoader loader = (BundleLoader)wiring.getModuleLoader();
                    if (loader != null && loader.isExportedPackage(packageName)) {
                        Class<?> klass = bundle.loadClass(className);
                        System.out.println(klass);
                        System.out.println(klass.getClassLoader());
                    }
                }
            }
        }

        EclipseStarter.shutdown();
    }

}

输出如下,

class me.kisimple.just4fun.osgi.HelloOSGi
org.eclipse.osgi.internal.loader.EquinoxClassLoader@54432395[me.kisimple.just4fun.osgi.common:1.0.0(id=3)]

alright,今天就到这^_^

时间: 2024-11-04 15:49:07

OSGi#2:编程方式使用Equinox的相关文章

ASP.NET MVC下的四种验证编程方式[续篇]

在<ASP.NET MVC下的四种验证编程方式>一文中我们介绍了ASP.NET MVC支持的四种服务端验证的编程方式("手工验证"."标注ValidationAttribute特性"."让数据类型实现IValidatableObject或者IDataErrorInfo"),那么在ASP.NET MVC框架内部是如何提供针对这四种不同编程方式的支持的呢?接下来我们就来聊聊这背后的故事. 一.ModelValidator与ModelVal

即时通讯软件开发 几种网络编程方式

即时通讯软件开发 几种网络编程方式: ISAPI.CGI.WinInet.Winsock 它们之间的区别: 1)ISAPI主要是开发基于浏览器客户端与服务器端程序.效率比CGI方式高,而且也扩展了CGI没有的一些功能.(基于TCP/IP模型中的应用层) 2) CGI主要是开发基于浏览器客户端与服务器端程序.(基于TCP/IP模型中的应用层) 3) WinInet主要是开发客户端程序.(基于TCP/IP模型中的应用层) 4) Winsock主要是基于socket来开发客户端与服务器端程序.(基于T

获取系统当前的输入法列表并通过编程方式切换输入法

1.获取输入法列表 //保存输入法信息的结构     struct _tagInputMethod     {      char szLanguageId[30];      char szLanguageName[100];      char szLanguageImeFileName[100];      HICON hIcon;     }; _tagInputMethod m_InputMethodList[30];     int m_InputMethodCount; //获取输

编程方式操作WorkFlow

背景: 刚进某地产公司,就是做一个工作流的小Demo,就是画一条采购的工作流,然后代码实现它. 刚开始的时候真的其实挺迷茫的,然后就从自己比较熟悉的实体做起,接着是controller,然后是view,代码都是参考过N份的,最后代码写的差不多了,然后才开始画一条流程.流程图就画了前后有6份,然后改来改去,还好有各位哥哥帮忙,.最后一条流程总算是走了下来.就在上周五的时候,SA突然让我就开始熟悉需求了,没想到这么快就要开始干活了. 简谈 最开始做工作流的时候,感觉它跟职责链模式很像,它的处理都是一

VB 在Visio 2010 以编程方式创建子进程图

在2010年Visio以编程方式创建子进程图 Office 2010  https://msdn.microsoft.com/en-us/library/gg650651.aspx 简介: 学习如何创建子流程图表以编程方式在Microsoft Visio 2010. 最后修改: 2011年4月07日 适用于: Office 2010 | SharePoint Server 2010 | 2010 | Visio Visio溢价2010 在这篇文章中 概述创建一个流程图创建子流程页面移动一个形状子

EF三种编程方式详细图文教程(C#+EF)

开始学习EF,从网上找了好多,都不是自己想要的,于是边学边把自己学习的过程写下来,以供参考. 操作环境:VS2013+SQLServer2012 Entity Framework4.1之前EF支持“Database First”和“Model First”编程方式,从EF4.1开始EF开始支持支持“Code First”编程方式,今天简单看一下EF三种编程方式. 开始介绍这三种EF操作方式之前,首先在Visual Studio 2013中建立一个数据库连接,这里我们以“EFDemo”数据库为例:

ASP.NET MVC下的四种验证编程方式

原文:ASP.NET MVC下的四种验证编程方式 ASP.NET MVC采用Model绑定为目标Action生成了相应的参数列表,但是在真正执行目标Action方法之前,还需要对绑定的参数实施验证以确保其有效性,我们将针对参数的验证成为Model绑定.总地来说,我们可以采用4种不同的编程模式来进行针对绑定参数的验证. 目录 一.手工验证绑定的参数 二.使用ValidationAttribute特性 三.让数据类型实现IValidatableObject接口 四.让数据类型实现IDataError

CodeFirst 的编程方式

第一步:创建控制台项目第二步:添加新建项目→Ado.Net空实体模型第三步:添加实体:Customer,添加几个必要的测试字段第四步:添加实体之间的联系第五步:根据模型生成数据库脚本,并执行sql脚本创建数据库第六步:写增删改查来讲解EF的基本使用 CodeFirst是EntityFramework的一种技术手段,因为传统编程方式都是先建立数据库,然后根据数据库模型为应用程序建模,再进行开发:CodeFirst从字面上理解就是代码先行,先在程序中建立要映射到数据库的实体结构,然后EntityFr

如何以编程方式打印到在 MFC 中的非默认打印机

http://cache.baiducontent.com/c?m=9f65cb4a8c8507ed4fece763105790245b09c0252bd7a74a2485d315d2390f07506694ea7a7d0d5d83d87f6305ac4957f7b86c65377571eb8f8dd50a8bb485582a9f5631671df65663d50edcba5154cb37e12efeae69f0caf625e7aec5a5de4320c944040a9780fb4d7467&p