OSGi介绍(四)第一个bundle
先给出“扶贫助手”的第一种改造,我称之为“直接型”,请看:
package
aa.bb.cc;
// 需要import osgi的核心package
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
// 实现了BundleActivator
public class FamilyInfo implements BundleActivator {
private String familyName;
private int population;
private int incomePerYear;
省略了getter和setter方法
public String toString() {
return "Family: " + this.familyName + ", population: " + this.population + ", income: " + this.incomePerYear;
}
public int getIncomePerMember(){
return (int)(this.incomePerYear/this.population);
}
public static void sortByIncomePerYear(FamilyInfo[] families){
FamilyInfo temp = null;
for(int i = 0; i < families.length -1; i ++){
for(int j = i + 1; j < families.length; j ++){
if(families[i].getIncomePerYear() > families[j].getIncomePerYear()){
temp = families[i];
families[i] = families[j];
families[j] = temp;
}
}
}
}
public static void sortByIncomePerMember(FamilyInfo[] families){
FamilyInfo temp = null;
for(int i = 0; i < families.length -1; i ++){
for(int j = i + 1; j < families.length; j ++){
if(families[i].getIncomePerMember() > families[j].getIncomePerMember()){
temp = families[i];
families[i] = families[j];
families[j] = temp;
}
}
}
}
//在framework每次启动该bundle的时候该方法会被framework调用执行。
public void start(BundleContext context) throws Exception {
FamilyInfo[] families = new FamilyInfo[3];
families[0] = new FamilyInfo();
families[0].setFamilyName("Zhang");
families[0].setPopulation(3);
families[0].setIncomePerYear(1200);
families[1] = new FamilyInfo();
families[1].setFamilyName("Li");
families[1].setPopulation(6);
families[1].setIncomePerYear(1800);
families[2] = new FamilyInfo();
families[2].setFamilyName("Liu");
families[2].setPopulation(4);
families[2].setIncomePerYear(1500);
FamilyInfo.sortByIncomePerYear(families);
for(int i = 0; i < families.length; i ++){
System.out.println(families[i].toString());
}
FamilyInfo.sortByIncomePerMember(families);
for(int i = 0; i < families.length; i ++){
System.out.println(families[i].toString());
}
}
//在framework停止该bundle时,该方法将被framework调用
public void stop(BundleContext context) throws Exception {
}
}
// 需要import osgi的核心package
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
// 实现了BundleActivator
public class FamilyInfo implements BundleActivator {
private String familyName;
private int population;
private int incomePerYear;
省略了getter和setter方法
public String toString() {
return "Family: " + this.familyName + ", population: " + this.population + ", income: " + this.incomePerYear;
}
public int getIncomePerMember(){
return (int)(this.incomePerYear/this.population);
}
public static void sortByIncomePerYear(FamilyInfo[] families){
FamilyInfo temp = null;
for(int i = 0; i < families.length -1; i ++){
for(int j = i + 1; j < families.length; j ++){
if(families[i].getIncomePerYear() > families[j].getIncomePerYear()){
temp = families[i];
families[i] = families[j];
families[j] = temp;
}
}
}
}
public static void sortByIncomePerMember(FamilyInfo[] families){
FamilyInfo temp = null;
for(int i = 0; i < families.length -1; i ++){
for(int j = i + 1; j < families.length; j ++){
if(families[i].getIncomePerMember() > families[j].getIncomePerMember()){
temp = families[i];
families[i] = families[j];
families[j] = temp;
}
}
}
}
//在framework每次启动该bundle的时候该方法会被framework调用执行。
public void start(BundleContext context) throws Exception {
FamilyInfo[] families = new FamilyInfo[3];
families[0] = new FamilyInfo();
families[0].setFamilyName("Zhang");
families[0].setPopulation(3);
families[0].setIncomePerYear(1200);
families[1] = new FamilyInfo();
families[1].setFamilyName("Li");
families[1].setPopulation(6);
families[1].setIncomePerYear(1800);
families[2] = new FamilyInfo();
families[2].setFamilyName("Liu");
families[2].setPopulation(4);
families[2].setIncomePerYear(1500);
FamilyInfo.sortByIncomePerYear(families);
for(int i = 0; i < families.length; i ++){
System.out.println(families[i].toString());
}
FamilyInfo.sortByIncomePerMember(families);
for(int i = 0; i < families.length; i ++){
System.out.println(families[i].toString());
}
}
//在framework停止该bundle时,该方法将被framework调用
public void stop(BundleContext context) throws Exception {
}
}
看到代码的区别了吗?我在不同之处都标注了注释。其实,从说白了,就是实现了org.osgi.framework.BundleActivator这个接口。
当然,细心的话,你会发现这个bundle没有public static void main(String[] args)方法了。那么它怎么被启动呢?这个就是bundle的奥秘所在。不过,如果你了解java的class loading机制以及reflection技术,你立马会明白这个bundle的运行机制。这两项技术广泛应用于j2ee(对吧?我得承认,j2ee的经验不多,呵呵)以及java的plugin机制。
简单说来,java.lang.Class这个类有一个方法:
public Object newInstance()throws InstantiationException,IllegalAccessException
针对上面的“扶贫助手”bundle而言,framework只要通过ClassLoader找到aa.bb.cc.FamilyInfo.class并加载后,就可以通过newInstance()方法创建一个BundleActivator的实例,然后调用public void start(BundleContext context)方法,就完成了启动bundle的动作了。之后,调用public
void stop(BundleContext context)方法来停止bundle
简单说来,java.lang.Class这个类有一个方法:
public Object newInstance()throws InstantiationException,IllegalAccessException
针对上面的“扶贫助手”bundle而言,framework只要通过ClassLoader找到aa.bb.cc.FamilyInfo.class并加载后,就可以通过newInstance()方法创建一个BundleActivator的实例,然后调用public void start(BundleContext context)方法,就完成了启动bundle的动作了。之后,调用public
void stop(BundleContext context)方法来停止bundle
如果你接着问,framework怎么知道这个bundle里面的BundleActivator是哪个类呢?嗯,问到点子上了。这就涉及到下面我们要讲的bundle的部署了。在上一篇给出的bundle定义中指出,Jar文件是bundle的唯一格式,也就是说,我们要运行bundle,必须把代码打成jar文件。而jar文件可以带有manifest文件,这个文件对bundle是不可缺少的。OSGi规范里面,通过定义一系列适用于bundle的manifest关键字(bundle manifest header)来扩展manifest文件。
比如,开发人员在manifest中添加下面一行:
Bundle-Activator: aa.bb.cc.FamilyInfo
这样,在bundle被部署到framework后,framework就可以通过读取manifest的关键字来获得BundleActivator的具体实现类名,并通过reflection机制产生BundleActivator的实例。
比如,开发人员在manifest中添加下面一行:
Bundle-Activator: aa.bb.cc.FamilyInfo
这样,在bundle被部署到framework后,framework就可以通过读取manifest的关键字来获得BundleActivator的具体实现类名,并通过reflection机制产生BundleActivator的实例。
这里就给出扶贫助手的manifest的一个例子:
然后我们用jdk自带的jar工具,来生成bundle jar文件。这样,第一个bundle就完成了,您可以下载一个开源的framework安装这个bundle试一试。在framework上尝试对该bundle的启动和停止,输出的结果应该和原先的java application是一样的,然后您还可以在那个start(context)的方法中,再增加一条记录,重新打包,然后通过framework的update功能,就能够在不重新启动framework的情况下升级该bundle,我就暂时偷懒不针对具体framework来给出操作的方法了,先给您自己先摸索了(当然您也可以偷懒,因为后面我会结合具体framework深入讲述的)。
Manifest-Version:
1.0
Bundle-SymbolicName: aa.bb.cc.family //osgi specification 4强制要求的关键字,每个bundle都必须有唯一的symbolic name
Bundle-Name: Family Info Manager //bundle的名称
Bundle-Version: 1.0 //bundle的版本号
Bundle-Activator: aa.bb.cc.FamilyInfo //指明BundleActivator的实现类名
Import-Package: org.osgi.framework ; version=1.3 //列出该bundle需要从其他bundle所引入的
//package(s)(提供该package的bundle必须在其
//manifest中有Export-Package:
//org.osgi.framework ; version=1.3)
Bundle-SymbolicName: aa.bb.cc.family //osgi specification 4强制要求的关键字,每个bundle都必须有唯一的symbolic name
Bundle-Name: Family Info Manager //bundle的名称
Bundle-Version: 1.0 //bundle的版本号
Bundle-Activator: aa.bb.cc.FamilyInfo //指明BundleActivator的实现类名
Import-Package: org.osgi.framework ; version=1.3 //列出该bundle需要从其他bundle所引入的
//package(s)(提供该package的bundle必须在其
//manifest中有Export-Package:
//org.osgi.framework ; version=1.3)
然后我们用jdk自带的jar工具,来生成bundle jar文件。这样,第一个bundle就完成了,您可以下载一个开源的framework安装这个bundle试一试。在framework上尝试对该bundle的启动和停止,输出的结果应该和原先的java application是一样的,然后您还可以在那个start(context)的方法中,再增加一条记录,重新打包,然后通过framework的update功能,就能够在不重新启动framework的情况下升级该bundle,我就暂时偷懒不针对具体framework来给出操作的方法了,先给您自己先摸索了(当然您也可以偷懒,因为后面我会结合具体framework深入讲述的)。
好了,说完代码的改造,再看看改造所带来的程序设计结构变化:那~~~就~~~~是~~~~没变化!因此我把这种原封不动的改造方法称为“直接型”,用这种直接法,我们可以轻易的把一个java应用程序改造成bundle。而这种改造目前能看到的好处就是bundle的“热”升级。那怎样能更漂亮些呢?在下一篇中,我会进一步改造这个扶贫助手成为两个bundle,看看bundle的合作将会带来怎样的精彩效果