获取PackageAdmin

OSGi框架的核心包已经将PackageAdmin注册为服务,只要OSGi框架没有停止,该服务就可以被安全的获取

    ServiceReference<PackageAdmin> reference = bundleContext.getServiceReference(PackageAdmin.class);
    if(reference != null) {
        try {
            PackageAdmin packageAdmin = bundleContext.getService(reference);
            Bundle[] fragments = packageAdmin.getFragments(bundleContext.getBundle());
            if(fragments != null) {
                for(Bundle fragment : fragments) {
                    // 读取Fragment的MANIFEST.MF文件内容
                    String bundleInstaller = fragment.getHeaders().get(BUNDLE_INSTALLER);
                    if(bundleInstaller != null) {
                        // Do something
                    }
                }
            }
                
        }finally {
            // 使用完需要unget,已保证内部的计数器平衡
            bundleContext.ungetService(reference);
        }
    }

BundleTracker/BundleTrackerCustomizer

  • 跟踪Bundle的启动和停止
  • addingBundle/removeBundle应成对使用
  • addingBundle返回为null时,removeBundle绝对不会被调用
  • 可用于Bundle的监测,BundleClassLoader的缓存等
  • 做Bundle缓存时,为了线程可见性,建议使用volatile Map或ConcurrentMap

BundleTracker在测试方面的应用

步骤
1 跟踪每个Bundle的启动

  • 读取Bundle的MANIFEST.MF文件,探测Bundle是否导入了org.junit包
  • 如果导入了org.junit包,则使用Javassist扫描标记了@RunWith注解的测试用例
  • 反射初始化测试用例或从Spring上下文中查找实例
  • 在新线程中执行测试用例

BundleTrackerCustomizer实现片段

@Override
public ApplicationContextTracker addingBundle(Bundle bundle, BundleEvent event) {
    if(needTest(bundle)) { // 判断Bundle是否包含测试用例
        Set<Class<?>> testClasses = findTestClasses(bundle);
        if(testClasses != null && !testClasses.isEmpty()) {
            boolean requireApplicationContext = false;
            for(Class<?> testClass : testClasses) {
                RunWith runWith - testClass.getAnnotation(RunWith.class);
                if(SpringOsgiTestRunner.class.equals(runWith).value()) {
                    requireApplicationContext = true;
                    break;
                }
            }
            if(requireApplicationContext) {
                ApplicationContextTracker applicationContextTracker = new ApplicationContextTracker(bundle);
                applicationContextTracker.open();
                
                sharedExecutor.execute(new TestExecutor(bundle.getBundleId(), testClasses, runListenerClasses));
                return applicationContextTracker;
            }esle{
                sharedExecutor.execute(new TestExecutor(bundle.getBundleId(), testClasses, runListenerClasses));
            }
        }
        
    }
    return null;
}
@Override
public void removeBundle(Bundle bundle, BundleEvent event, ApplicationContextTracker applicationContextTracker) {
    applicationContextTracker.close();
}

ServiceTracker/ServiceTrackerCustomizer

  • addingService/removeService应成对使用
  • 作服务缓存时,为了线程可见性,建议使用volatile Map或ConcurrentMap
  • ServiceTracker.waitForService(long timeout)不可在BundleActivator的start/stop方法中使用,可能会阻塞OSGi框架“主线程”
  • waitForService在超时后返回null而不是抛出异常

ServiceTracker在提供扩展方面的应用

  • 本例介绍了表达式执行管理器(ExpressionExecutorManager)如何跟踪表达式执行器(ExpressionExecutor)
  • 用户实现ExpressionExecutor接口并发布为服务时,将被纳入ExpressionExecutorManager管理范围

ExepressionExecutorManager片段

public final class ExpressionExecutorManager implements BundleContextAware, InitializingBean, DisposableBean {
    private volatile Map<String, ExpressionExecutor> expressionExecutors = new HashMap<String, ExpressionExecutor>();
    private ServiceTracker<ExpressionExecutor, ExpressionExecutor> serviceTracker;
    private BundleContext bundleContext;

    @Override
    public void setBundleContext(BundleContext bundleContext) {
        this.bundleContext = bundleContext;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        serviceTracker = new ServiceTracker<ExpressionExecutor, ExpressionExecutor>(bundleContext,
            ExpressionExecutor.class, null) {
            @Override
            public ExpressionExecutor addingService(ServiceReference<ExpressionExecutor> reference) {
                ExpressionExecutor executor = super.addingService(reference);
                expressionExecutors.put(executor.getName(), executor);
                return executor;
            }

            @Override
            public void removedService(ServiceReference<ExpressionExecutor> reference, ExpressionExecutor executor) {
                expressionExecutors.remove(executor.getName());
                super.removedService(reference, executor);
            }
        };
        serviceTracker.open();
    }
    @Override
    public void destroy() throws Exception {
        serviceTracker.close();
    }
    public ExpressionExecutor getExpressionExecutor(String name) {
        if (MvelExpressionExecutor.getInstance().getName().equals(name)) {
            return MvelExpressionExecutor.getInstance();
        }
        return expressionExecutors.get(name);
    }
}

ServiceFactory服务工厂

  • 针对每个Bundle,单独发布一个服务,在创建该服务的时候,可以获得试用服务的Bundle对象,从而进行客户化
  • 样例,假设针对不同的Bundle发布不同的javax.sql.DataSource服务
public void start(BundleContext bundleContext) throws Exception {
    bundleContext.registerService(DataSource.class.getName(),
        new DataSourceFactory(), null);
}

private static class DataSourceFactory implements ServiceFactory<DataSource> {
    @Override
    public DataSource getService(Bundle bundle, ServiceRegistration<DataSource>   
        registration) {
        return createDataSource(bundle);
    }

    @Override
    public void ungetService(Bundle bundle, ServiceRegistration<DataSource> 
        registration, DataSource dataSource) {
        if (dataSource instanceof Destroyable) {
            ((Destroyable) dataSource).destroy();
        }
    }
}

ManagedServiceFactory/ConfigurationAdmin

  • ManagedServiceFactory于ConfigurationAdmin通常配合使用
  • 可动态更新参数
  • 动态配置的代码非常复杂,涉及监听器、多线程等复杂的操作,如果有需求,应避免直接使用OSGi CM API,而开发一个通用Bundle来支持
  • 建议参考Gemini Blueprint Core中CM部分源码
  • 建议参考Apache Felix File Install的源码

服务注册

  • BundleContext.registerService
  • 该方法的不同重载版本,第三个属性均为过滤器,为K/V键值对格式
  • 获取服务时,可使用过滤器来匹配
  • BundleContext.getServiceReferences第二个参数就是LDAP过滤器字符串
  • ServiceTracker构造方法包含一个带Filter(可用FrameworkUtil工具类创建)过滤器对象的版本

LDAP过滤器字符串

  • org.osgi.framework.FrameworkUtil.createFilter(String filter)可以创建Filter对象
  • LDAP过滤器
  • 过滤器最好用String.format("(|(%s=%s)(%s=%s))")这样的方式进行创建,否则容易出错
格式 含义
(!(filter)) 不包含
(&(filter1)...(filterN)) 并且
(|(filter1)...(filterN)) 或者

根据类获取Bundle

  • FrameworkUtil里面有一个getBundle(Class<?> clazz)方法,可以根据类获取Bundle
  • 前提,类所在的Bundle必须处于Starting或Active状态,否则将得到null
  • 如果一定要用这种方式获取Bundle,必须确保该Bundle已经启动,例如可以在子Bundle内调用该方法,Class采用一个父Bundle的类,一定可以获取到父Bundle

标签: Java, OSGi

添加新评论