OSGi#2:编程方式使用Equinox

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

startup

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

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

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

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

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,今天就到这^_^

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值