获取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