由于OSGI技术优秀的模块化技术分离,在不同的bundle之间进行交互的方式有两种:一是包(package)的导出和导入,二是bundle向外部发布service。第二种方式是osgi中推荐的方式,同时这种方式具有SOA的特点。但是,这种模块化的组织方式,特别是通过服务在不同的bundle间进行交互,使得osgi环境中的测试,必须在osgi container中进行,增加了测试环境搭建的难度。 在spring dm中,提供了一种启动osgi container的方式,通过springdm、osgi container、junit的组合,进行osgi的集成的测试。但是这种方式的设置比较复杂,特别是对于每一个bundle到要在测试程序和pom中设置,容易引起错误。
因此,基于对OSGI container的分析(主要是Felix和Equinox),可以通过在传统的程序中,嵌入(embedded)一个osgi的环境,可以利用Junit象测试普通的java程序一样测试bundle提供的service。
一、建立一个OSGI的嵌入环境
这里以建立Felix的嵌入式环境为例子进行解释,对于Equinox的环境,基本的建立方法相同。在建立Osgi环境过程中,关键的问题有两个:一是对于OSGI 环境配置文件的读取和处理,并把它设置到启动的嵌入式环境中;二是对于自动启动的bundle的读取和启动。工程的目录结构如图所示:
首先是对于Felix配置文件的处理, 读取配置文件config.properties,并把读入的内容加载到Properties中。
InputStream input = null;
try {
input = new FileInputStream("conf/config.properties");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Properties props = new Properties();
try {
props.load(input);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
其次是读取自动启动的bundles,放在工程的bundles目录下,子目录以数字命名,对应于Felix中bundle的启动级别,在Felix中,对于自动启动的bundle的设置属性为:"felix.auto.start",通过程序中读取这个目录下的bundle,设置到properties:
File dir = new File("bundles");
String[] list = dir.list();
for(int i= 0;i< list.length;i++)
{
File jarDir = new File("bundles/"+list[i]);
String runJars = this.getFileName(" file:", jarDir);
props.setProperty(AUTO_START_PROP+"."+list[i], runJars);
}
private String getFileName(String rootPath,File file)
{
String bundlesPath="";
if(!file.getAbsolutePath().contains(".svn"))
{
if(file.isDirectory())
{
String[] files = file.list();
for(int i=0;i<files.length;i++)
{
bundlesPath = bundlesPath+getFileName(rootPath,new File(file.getPath()+"/"+files[i]));
}
}
else
{
bundlesPath=rootPath+file;
}
}
return bundlesPath;
}
最终,把完整的属性设置传给Felix,然后以嵌入式方式启动OSGI环境。完整的Felix Host代码如下:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.apache.felix.framework.Felix;
import org.apache.felix.main.AutoProcessor;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
public class FelixHostApplication
{
// private HostActivator m_activator = null;
private static Felix m_felix = null;
private final String AUTO_START_PROP ="felix.auto.start";
public Object getBunndleContext()
{
return m_felix.getBundleContext();
}
private String getFileName(String rootPath,File file)
{
String bundlesPath="";
if(!file.getAbsolutePath().contains(".svn"))
{
if(file.isDirectory())
{
String[] files = file.list();
for(int i=0;i<files.length;i++)
{
bundlesPath = bundlesPath+getFileName(rootPath,new File(file.getPath()+"/"+files[i]));
}
}
else
{
bundlesPath=rootPath+file;
}
}
return bundlesPath;
}
public FelixHostApplication()
{
InputStream input = null;
try {
input = new FileInputStream("conf/config.properties");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Properties props = new Properties();
try {
props.load(input);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
File dir = new File("bundles");
String[] list = dir.list();
for(int i= 0;i< list.length;i++)
{
File jarDir = new File("bundles/"+list[i]);
String runJars = this.getFileName(" file:", jarDir);
props.setProperty(AUTO_START_PROP+"."+list[i], runJars);
}
// Create a configuration property map.
Map config = new HashMap(props);
// Create host activator;
/* m_activator = new HostActivator();
List list1 = new ArrayList();
list1.add(m_activator);
config.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, list1);
*/
try
{
// Now create an instance of the framework with
// our configuration properties.
m_felix = new Felix(config);
m_felix.init();
AutoProcessor.process(config, m_felix.getBundleContext());
// m_felix.getBundleContext().registerService(ShareInterface.class.getName(), new ShareImpl(), null);
// Now start Felix instance.
m_felix.start();
Thread.sleep(10*1000);
System.out.println("hello");
}
catch (Exception ex)
{
System.err.println("Could not create framework: " + ex);
ex.printStackTrace();
}
}
public Bundle[] getInstalledBundles()
{
// Use the system bundle activator to gain external
// access to the set of installed bundles.
return this.m_felix.getBundleContext().getBundles();
}
public void shutdownApplication()
{
// Shut down the felix framework when stopping the
// host application.
try {
m_felix.stop();
m_felix.waitForStop(0);
} catch (BundleException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
二、在Junit Test Case中启动OSGI 环境
建立一个Junit Test Case,重载它的setup方法,生成一个OSGI环境:
host = new FelixHostApplication();
然后通过反射的方法(见文《在osgi容器外应用osgi bundle提供的服务》),就可以获得注册的服务了。