Chasing an OSGi vision

OSGi技术的研究和讨论

2007年7月21日 #

OSGi介绍(七)bundle和service(续)

接上一篇的例子,为了更具体一点,我们考虑这样的case,
假设房地产开发商construction A采纳了规划公司design A的方案,打算建造公寓类型的房子CityApartment
然后客户A买了一套房子

用ooa方式分析这个case,
我抽象这几个实体:规划公司,图纸类型,开发商,房子,买房人
然后描述这几件事情:开发商选择设计图纸,盖楼然后销售;买房人根据图纸买房并使用房子

下面是我用java语言来简单描述它:

design A 公司

package design.a;
interface Apartment {
..
//方法省略先
}

 

开发商construction A

package construction.a

import design.a.*;//开发商要按照图纸盖楼

class CityApartment implements Apartment {
.
//方法省略先
}


class Construction implements BundleActivator{

}



客户 costumer A

package customer.a

import design.a.*;//客户要按照图纸选择房子

class Customer implements BundleActivator {
.
//方法省略先
}



然后我们把他们分别做成bundle
Bundle A : design A
其manifest中这样描述

BundleSymbolicName: design A
Export
-Package: design.a


Bundle B : construction A
其manifest中这样描述

BundleSymbolicName: construction A
Import
-Package: design.a
Bundle
-Activator: construction.a.Construction


Bundle C : customer C
其manifest中这样描述

BundleSymbolicName: customer C
Import
-Package: design.a
Bundle
-Activator: customer.c.Customer



这样,装入到framework后,framework就会把BundlB和C与BundleA关联起来,正好描述开发商A选择design A的图纸,客户A也不得不选择design A的图纸啦
可是,单从这里,我们看不出来,开发商和客户拿同一份图纸干什么。那我们得必须在BundleB和C的实现里面写点东西来说明。

这里给出开发商construction.a.Construction的伪代码:

class Construction implements BundleActivator {

 
public void start(BundleContext context){
  CityApartment apartment 
= null;
  Hashtable properties 
= null;
  
for(int i = 0; i < 100; i ++){
   properties 
= new Hashtable();
   properties.put(
"price",new Integer(1000 + i*5));//开发商为房子定价
   apartment = new CityApartment();//一套房子盖好
   
//把房子按照公寓注册出去并打广告,等待客户来购买,framework就相当于一个售楼处兼房屋中介
   context.registerService(Apartment.class.getName()/*公寓类型*/,apartment/*房子作为服务对象*/,properties/*与房子相关的附带信息*/);
   
  }

  
//这样开发商一共注册一百套房子
 }

}



而客户的代码可以如下:

class Customer implements BundleActivator {
 
public void start(BundleContext context){
  Apartment apartment 
= null;
  ServiceReference ref 
= context.getServiceReference(Apartment.class.getName,"(price=1050)");//先签署购房合同,而且指明选择Apartment类型,价格为1050的房子。
  apartment = (Apartment)context.getService(ref);//然后买到房子
  
//买房人就可以使用房子apartment对象进行日常生活了
 }

}


这样,我们就很清楚的看出,Design A为Construction A和Customer A提供了共同的Apartment定义,才使得他们有交易的可能。于此同时,Construction A和Customer A之间的耦合是非常松的,因为,如果有另外一个开发商onstruction B加入进来也构造了Apartment的对象,Customer就可以通过改变选择条件,轻易的获得B的房子,而客户本身不关心房子是A还是B盖的,这个是典型的面向对象的多态应用。

总的说来,Bundle在framework的帮助下,使得其他bundle使用其类型定义成为可能。service就是在这些共享的类型定义基础上产生的具体对象,而使用这些service对象的bundle,必然也是对应共享类型的使用者。
这种类型共享,在osgi里面叫做"class space". framework运行时通过关联bundle之间的类型定义,可以构成一个或多个"class space",而某个bundle在framework里面,只能处在一个"class space"里面,不能同时出现在多个"class space"中。
怎么理解这个话呢?请看下一篇,外星人入侵了。

 

posted @ 2007-07-21 22:28 勤劳的蜜蜂 阅读(351) | 评论 (0)编辑 收藏

2007年7月16日 #

OSGi 介绍(七)bundle和service的关系

osgi系列已经发表了有将近2年的时间了,很高兴这期间得到了许多朋友的关注,你们和我的讨论切磋都让我兴奋无比。而过去很长的时间里,由于灵感枯竭外加精力有限,不能给大家分享更多osgi的精彩,实在辜负大家的期望,还请谅解。不过,根据这段时间大家和我私下的讨论,发现很多人都苦恼于分不清楚bundle和service的关系,而我的osgi 5-6又臭又长,而且还非常依赖技术,实在是坑害入门者的必备武器。这个不足,犹如头上方圆半尺盘旋的苍蝇,困扰着我,解释的邮件也不知写多少,估计收效甚微。于是我决定再次出山,写一个惊世骇俗的分析文章,力图让更多读者都能够把这两个疯马牛理清楚。

开讲之前,还是用老办法,给大家举个例子。放心,这个例子一点都不技术,而且我相信你可能比我还清楚。大家都知道房地产商是怎么运作楼盘的吧。据我浅显的认识,他们都会先进行一些图纸上的规划,里面包含小区的整体规模,楼房外观以及广大疾苦民众最为关心和渴求的户型图。据说,很多房地产商在搞定政府某些关键部门(个人意见仅供参考)拿到一块地后,根本不用费心思去自己规划这些自己都一窍不通的东西,只要请一个有资质的第三方公司写写画画,甚至照搬某西方发达国家的某社区概念,冠于中国特色云云,就能使广大民众趋之若鹜,倾囊抢购。ok,你可能受不了我又愤世嫉俗了,这个到底和osgi有啥关系?别着急,天色已经很晚,下次某个时间请继续关注osgi(七)续,其间,请大家先考虑一下,如何用ooa的方式来描述人们如何购房,以及在房子里进行日常生活这样的简单场景。

posted @ 2007-07-16 23:31 勤劳的蜜蜂 阅读(996) | 评论 (1)编辑 收藏

2007年5月14日 #

OSGi on mobile phone !

如果你一直关注osgi在embedded的发展,并且还能到旧金山参加正在举行的java one,那我真是羡慕死你了.

Nokia在她的N800上demo运行了osgi(此消息来源于david beers对BJ在osgi alliance blog上的评论,关于demo可以看这个link http://thehereweb.googlepages.com/)
同时美国运行商Sprint也宣布采用osgi的手机平台将会在年底面世.多么值得期待的事情!

posted @ 2007-05-14 10:11 勤劳的蜜蜂 阅读(777) | 评论 (0)编辑 收藏

2006年9月16日 #

framework implementation updated

瞎整了半年多,克服了重重困难,终于把原来framework做了更新,如果有兴趣,还可以按照原来的Link下载。
framework下载后,解压并运行startframework.bat就可以启动framework了。
目前该framework只支持在内存中存储bundle.

另外,多增加了一个管理bundle,它为framework提供了简单的图形化操作界面,可以在这里下载。

http://www.blogjava.net/Files/Ferrari4000/bundlemanagement.zip

下载后,最好先把名字改为bundlemanagement.jar
然后可以这样安装,假设该文件下载到d:\bundles下,则可以在framework的shell下输入
in file:d:/bundles/bundlemanagement.jar
安装该bundle,安装成功后输入stt 1(注意:1是数字一),启动该bundle就可以出现图形操作界面了。

图形界面提供bundle的安装(只支持本地安装),启动,停止,升级和删除等简单功能。
其他功能会在后面陆续增加。目前这个bundle只能运行在j2se环境下。

给的源代码没有很好的build文件,等我有时间了,再写一个。

感谢毕嘉兄弟的支持,他帮助设计了bundle存储模块以及实现了bundle在内存存储的第一版。

有啥问题,可以直接给我发邮件,jerrylee.li@gmail.com

posted @ 2006-09-16 23:40 勤劳的蜜蜂 阅读(984) | 评论 (0)编辑 收藏

2006年3月3日 #

OSGi的曙光?

前两天,看到Peter的blog里说,由ibm牵头发起了jsr291(http://www.jcp.org/en/jsr/detail?id=291),
要把osgi的core应用到目前的j2se上,以填补jsr277发布前的需求空白。

osgi和277的pk开始了!我投osgi一票,呵呵,你买谁?

posted @ 2006-03-03 13:51 勤劳的蜜蜂 阅读(828) | 评论 (1)编辑 收藏

2006年2月16日 #

OSGi Alliance开设面向公众的邮件列表

继开设Blog后(http://www.osgi.org/blog/index.html),昨天OSGi Alliance又宣布开设一个public的mail list,每个对OSGi感兴趣的人都可以加入到这个列表中,通过这个列表可以讯问OSGi的相关的各种问题以及订阅列表的邮件。
请到http://bundles.osgi.org/mailman/listinfo/osgi-dev这里注册。

下面是BJ Hargrave的代表OSGi Alliance发的announcement


Hello,

As part of the OSGi evangelism work, I would like to announce that OSGi now has a new public mail list for OSGi technical questions and discussion. This mail list is a public list and is open to anyone to participate. This new list is mainly for non-members to discuss OSGi technology and ask question about the technology. ......

The new mail list address is:

osgi-dev@bundles.osgi.org

You can subscribe to the list here:
http://bundles.osgi.org/mailman/listinfo/osgi-dev or by sending an e-mail to osgi-dev-subscribe@bundles.osgi.org.

So please go ahead and subscribe to the new osgi-dev mail list. Your participation there can help educate others about the OSGi technology.

BJ Hargrave
Senior Technical Staff Member, IBM
OSGi Fellow and CTO of the OSGi Alliance

posted @ 2006-02-16 10:20 勤劳的蜜蜂 阅读(679) | 评论 (0)编辑 收藏

2006年2月15日 #

framework implementation

这里是我目前的成果。
http://www.blogjava.net/Files/Ferrari4000/framework.zip包含了framework的jar文件。
解压后,直接运行.bat文件就可以了。这个framework实现了r4core的大部分api(除一些支持local和安全的外,secrurity admin和conditional permission这两个服务也没有实现,url service也没有实现),输入help可以看到可以运行的命令(不过全是英文的,还是我写的,将就一下吧)。

http://www.blogjava.net/Files/Ferrari4000/src.zip包含所有源码。代码有点乱,hoho

要成功编译代码,您还需要下载这个http://www.blogjava.net/Files/Ferrari4000/osgi.rar
解压并后得到4个jar,并把他们放入到build path中,import顺序保证它们优先于jre就可以了。

我打算重新编写了。把framework的实现重新规划一下,然后实现core的所有内容,进而再实现cmpn的所有服务。
如果你对开发osgi framework感兴趣,一起来吧!

给我发信:jerrylee.li@gmail.com

posted @ 2006-02-15 09:15 勤劳的蜜蜂 阅读(995) | 评论 (8)编辑 收藏

2006年2月14日 #

OSGi介绍(六)OSGi的service

在给出采用service方式实现的“扶贫助手”之前,我们稍微回顾一下上一篇的成果。
在(五)中,我们看到程序被分成多个bundle后,程序的模块程度得到提高,而控制模块间的耦合度由Import-Package和Export-Package来控制,相对比较灵活。另一方面程序的更新和升级的粒度变小了。谁都知道只更新部分要比全部更新强,尤其当更新发生在一些需要建立昂贵的连接时,细粒度会节省不少花销。除了这些,我们看不到其他新鲜的东西。说白了,也就是挖空心思想一些design pattern来划分程序模块。
 
好了,马上就新鲜了。下面你会看到通过采用service方式来改造(五)中的程序,gui bundle在某些情况下不用重新启动,就能直接某些适应需求的变更!
先给出model bundle的代码,该bundle包含两个java package,分别是:
com.bajie.test.family.model
com.bajie.test.family.model.impl
在com.bajie.test.family.model这个package中包含如下的class和interface:
package com.bajie.test.family.model;
import java.util.List;
import javax.swing.table.AbstractTableModel;
public abstract class FamilyInfoDatabase extends AbstractTableModel{
   
    public abstract void sort(SortingFamilyInfoCriteria sortField) throws IllegalArgumentException;
   
    public abstract void addEntry(List columns, List values) throws IllegalArgumentException;
    public abstract void deleteEntry(String familyName);
    public abstract void update(String familyName,List columns, List values)throws IllegalArgumentException;
}

这是database的model,与(五)定义成interface不同,我们直接让它继承了AbstractTableModel,这是因为我们希望当数据或显示需求变化时,gui上的JTable能获得通知,并显示更新的结果。SortingFamilyInfoCriteria这个类型下文会给出说明。
 
package com.bajie.test.family.model;
public class FamilyInfoEntry {
    private String familyName;
    private int population;
    private int incomePerYear;
   
    public FamilyInfoEntry(String familyName,int population,int income){
        this.familyName = familyName;
        this.population = population;
        this.incomePerYear = income;
    }
   
    public String getFamilyName() {
        return familyName;
    }
    public int getIncomePerYear() {
        return incomePerYear;
    }
    public int getPopulation() {
        return population;
    }
}

这个类的结构和在(五)中完全一样,用来纪录一条家庭信息。唯一不同的是,在(五)中我们把它放入了实现(.impl)package中,在后面给出bundle的manifest文件时,我将解释为什么要这样改。
 
package com.bajie.test.family.model;
public interface FamilyInfoColumn {
    public Object getColumnValue(FamilyInfoEntry entry);
   
    public String getColumnName();
}
这个类用来描述table中的某个列。
package com.bajie.test.family.model;
import java.util.Comparator;
public interface SortingFamilyInfoCriteria extends Comparator{
    public String getSortFieldString();
}
这个类将用于对家庭纪录按某一列的值进行排序。
在com.bajie.test.family.model.impl这个package中包含上面抽象类和interface的实现:
package com.bajie.test.family.model.impl;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import com.bajie.test.family.model.FamilyInfoColumn;
import com.bajie.test.family.model.FamilyInfoDatabase;
import com.bajie.test.family.model.FamilyInfoEntry;
import com.bajie.test.family.model.SortingFamilyInfoCriteria;
public class FamilyDatabase extends FamilyInfoDatabase implements  BundleActivator,
        ServiceListener {
    private LinkedList familyEntryList = new LinkedList();
    private Object[] sortedValues = null;
    private LinkedList columns = new LinkedList();
    private BundleContext context;
    public int getColumnCount() {
        return this.columns.size();
    }
    public String getColumnName(int index) {
        return ((FamilyInfoColumn)columns.get(index)).getColumnName();
    }
   
    public Object getValueAt(int row, int column) {
        FamilyInfoEntry entry = (FamilyInfoEntry) this.sortedValues[row];
        if(column >= this.familyEntryList.size()){
            return null;
        }
        return ((FamilyInfoColumn) this.columns.get(column))
                .getColumnValue(entry);
    }
    public int getRowCount() {
        return this.familyEntryList.size();
    }
    public void addEntry(List columns, List values)
            throws IllegalArgumentException {
    }
    public void deleteEntry(String familyName) {
    }
    public void update(String familyName, List columns, List values)
            throws IllegalArgumentException {
    }
    public void sort(SortingFamilyInfoCriteria sortField) {
        Arrays.sort(this.sortedValues, sortField);
    }
    public void start(BundleContext context) throws Exception {
        this.context = context;
        this.familyEntryList.add(new FamilyInfoEntry("Zhang", 3, 1200));
        this.familyEntryList.add(new FamilyInfoEntry("Li", 6, 1800));
        this.familyEntryList.add(new FamilyInfoEntry("Liu", 5, 1500));
        this.familyEntryList.add(new FamilyInfoEntry("Wang", 4, 1300));
       
        this.sortedValues = this.familyEntryList.toArray();
 //向framework注册一个类型为FamilyInfoDatabase的服务
        context.registerService(FamilyInfoDatabase.class.getName(),this,null);
 //向framework注册三个服务,每个服务的类型既为FamilyInfoColumn,也是SortingFamilyInfoCriteria
        String[] clazzes = new String[] {FamilyInfoColumn.class.getName(),SortingFamilyInfoCriteria.class.getName()};
        context.registerService(clazzes,new FamilyNameColumn(),null);
        context.registerService(clazzes,new FamilyPopulationColumn(),null);
        context.registerService(clazzes,new FamilyIncomeColumn(),null);
       
 //向framework查找所有注册类型为FamilyInfoColumn的服务
 //先获得服务的引用
        ServiceReference[] columnRefs = context.getServiceReferences(
                FamilyInfoColumn.class.getName(), null);
        FamilyInfoColumn column = null;
        for (int i = 0; i < columnRefs.length; i++) {
            System.out.println(i + ":" + ((String[])(columnRefs[i].getProperty(Constants.OBJECTCLASS)))[0]);
     //通过引用获得具体的服务对象,每一个对象都将转化成gui中table的一列
            column = (FamilyInfoColumn) context.getService(columnRefs[i]);
            if (column != null) {
                this.columns.add(column);
            }else{
                System.out.println("null service object.");
            }
        }

 //注册服务侦听器,该侦听器专门侦听FamilyInfoColumn服务对象的动态(主要是增加和删除)
        context.addServiceListener(this,"(" + Constants.OBJECTCLASS + "="
                + FamilyInfoColumn.class.getName() + ")");
    }
    public void stop(BundleContext context) throws Exception {
    }
    public void serviceChanged(ServiceEvent event) {
        switch (event.getType()) {
        case ServiceEvent.MODIFIED:
            return;
        case ServiceEvent.REGISTERED://表明有新的列产生了。
            ServiceReference ref = event.getServiceReference();
            Object service = this.context.getService(ref);
            this.columns.add(service);
            this.fireTableStructureChanged();//通知gui,表结构发生变化
            return;
        case ServiceEvent.UNREGISTERING://表明有些列将被删除
            ref = event.getServiceReference();
            service = this.context.getService(ref);
            this.columns.remove(service);
            this.fireTableStructureChanged();//通知gui,表结构发生变化
            return;
        }
    }

    //这个类定义一个“Family Name”这个列,以及如何按这个列的值进行排序
    class FamilyNameColumn implements FamilyInfoColumn,SortingFamilyInfoCriteria {
        private static final String COLUMNNAME = "Family Name";
       
        public Object getColumnValue(FamilyInfoEntry entry) {
            return entry.getFamilyName();
        }
       
       
        public String getColumnName() {
            return FamilyNameColumn.COLUMNNAME;
        }
       
        public String getSortFieldString() {
            return FamilyNameColumn.COLUMNNAME;
        }
        public int compare(Object obj1, Object obj2) {
            if (obj1 == obj2) {
                return 0;
            }
            FamilyInfoEntry en1 = (FamilyInfoEntry)obj1;
            FamilyInfoEntry en2 = (FamilyInfoEntry)obj2;
           
            return en1.getFamilyName().compareTo(en2.getFamilyName());
        }
       
    }
    //这个类定义一个“Family Population”这个列,以及如何按这个列的值进行排序
    class FamilyPopulationColumn implements FamilyInfoColumn, SortingFamilyInfoCriteria {
        private static final String COLUMNNAME = "Family Population";
        public Object getColumnValue(FamilyInfoEntry entry) {
            return new Integer(entry.getPopulation());
        }
        public String getColumnName() {
            return FamilyPopulationColumn.COLUMNNAME;
        }
       
        public String getSortFieldString() {
            return FamilyPopulationColumn.COLUMNNAME;
        }
       
        public int compare(Object obj1, Object obj2) {
            if (obj1 == obj2) {
                return 0;
            }
            FamilyInfoEntry en1 = (FamilyInfoEntry)obj1;
            FamilyInfoEntry en2 = (FamilyInfoEntry)obj2;
           
            return en1.getPopulation() - en2.getPopulation();
        }
    }
   
    //这个类定义一个“Family Income”这个列,以及如何按这个列的值进行排序
    class FamilyIncomeColumn implements FamilyInfoColumn, SortingFamilyInfoCriteria {
        private static final String COLUMNNAME = "Family Income";
        public Object getColumnValue(FamilyInfoEntry entry) {
            return new Integer(entry.getIncomePerYear());
        }
        public String getColumnName() {
            return FamilyIncomeColumn.COLUMNNAME;
        }
       
       
        public String getSortFieldString() {
            return FamilyIncomeColumn.COLUMNNAME;
        }
        public int compare(Object obj1, Object obj2) {
            if (obj1 == obj2) {
                return 0;
            }
            FamilyInfoEntry en1 = (FamilyInfoEntry)obj1;
            FamilyInfoEntry en2 = (FamilyInfoEntry)obj2;
           
            return en1.getIncomePerYear() - en2.getIncomePerYear();
        }
       
    }
}
 
与(五)相比,最大的不同就是表结构的“列”是通过查找所有类型为FamilyInfoColumn的服务对象而组成的。而通过framework提供的服务侦听机制(即实现ServiceListener接口并注册到framework中),bundle能够获得该类服务对象的动态事件通知,如果该事件是新服务注册,则添加一个显示列,如果是服务被注销,则删除对应的显示列。
 
下面是bundle的manifest文件
Manifest-Version: 1.0
Bundle-SymbolicName: com.bajie.test.family.model
Bundle-Name: family model
Bundle-Version: 1.0
Bundle-Vendor: LiMing
Bundle-Activator: com.bajie.test.family.model.impl.FamilyDatabase
Import-Package: org.osgi.framework;version=1.3,com.bajie.test.family.model
Export-Package: com.bajie.test.family.model;version=1.0
从中我们看到com.bajie.test.family.model这个package被export出来,这样其他bundle就能够import这个package,并根据FamilyInfoEntry所提供的基本内容提供一些额外的处理结果,从而产生新列(FamilyInfoColumn)以及排序方法(SortingFamilyInfoCriteria),比如家庭人均年收入。

下面来看看gui bundle,它只包含一个package
package com.bajie.test.family.gui;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Hashtable;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import com.bajie.test.family.model.FamilyInfoDatabase;
import com.bajie.test.family.model.SortingFamilyInfoCriteria;
public class FamilyInfoGui implements BundleActivator, ActionListener,
        ItemListener, ServiceListener {
    private JFrame mainFrame;
    private JPanel contentPanel;
    private JTable familiesTable;
    private JScrollPane familiesTableScrollPane;
    private JPanel sortedByPanel = new JPanel(new GridLayout(1, 2));
    private JLabel sortedByLabel = new JLabel("Sorted By: ");
    private JComboBox sortedByList = null;
    private JPanel commandPanel = new JPanel(new GridLayout(1, 3));
    private JButton addEntry = new JButton("Add");
    private JButton deleteEntry = new JButton("Delete");
    private JButton updateEntry = new JButton("Update");
    private Hashtable sortingFields = new Hashtable();
    private BundleContext context;
    FamilyInfoDatabase database = null;
    public void start(BundleContext context) throws Exception {
        this.context = context;
        //查找所有注册类型为FamilyInfoDatabase的服务对象。在我们这个例子,它是由上面给出的model bundle注册的
        ServiceReference databaseServiceRef = context
                .getServiceReference(FamilyInfoDatabase.class.getName());
        if (databaseServiceRef == null) {
            System.out.println("No database service is registered.");
            return;
        }
 //这个服务对象将成为JTable的数据model
        this.database = (FamilyInfoDatabase) context
                .getService(databaseServiceRef);
        if (this.database == null) {
            System.out.println("Can not get database object");
            return;
        }
        //查找所有注册类型为SortingFamilyInfoCriteria的服务对象。
        ServiceReference[] sortingCriteria = context.getServiceReferences(
                SortingFamilyInfoCriteria.class.getName(), null);
        sortedByList = new JComboBox();
        SortingFamilyInfoCriteria criterion = null;
        if (sortingCriteria != null) {
            for (int i = 0; i < sortingCriteria.length; i++) {
                criterion = (SortingFamilyInfoCriteria) context
                        .getService(sortingCriteria[i]);
                if (criterion != null) {
      //每个服务对象将对应一种排序方法,并加入到下拉列表中
                    sortedByList.addItem(criterion.getSortFieldString());
                    this.sortingFields.put(criterion.getSortFieldString(),
                            criterion);
                }
            }
        }
 //注册服务侦听器,该侦听器专门侦听SortingFamilyInfoCriteria服务对象的动态(主要是增加和删除)
        context.addServiceListener(this, "(" + Constants.OBJECTCLASS + "="
                + SortingFamilyInfoCriteria.class.getName() + ")");
        sortedByList.addItemListener(FamilyInfoGui.this);
        //construct gui
        Runnable r = new Runnable() {
            public void run() {
                contentPanel = new JPanel();
                familiesTableScrollPane = new JScrollPane();
  //获得的FamilyInfoDatabase对象成为gui中JTable的model
                familiesTable = new JTable(database);
                familiesTableScrollPane.setViewportView(familiesTable);
                sortedByPanel.add(sortedByLabel);
                sortedByPanel.add(sortedByList);
                commandPanel.add(addEntry);
                commandPanel.add(deleteEntry);
                commandPanel.add(updateEntry);
                contentPanel.add(sortedByPanel, BorderLayout.NORTH);
                contentPanel.add(familiesTableScrollPane, BorderLayout.CENTER);
                contentPanel.add(commandPanel, BorderLayout.SOUTH);
                mainFrame = new JFrame();
                mainFrame.setContentPane(contentPanel);
                mainFrame.setSize(new Dimension(500, 600));
                mainFrame.show();
            }
        };
        Thread t = new Thread(r);
        t.start();
    }
    public void stop(BundleContext context) throws Exception {
        if (this.mainFrame != null)
            this.mainFrame.dispose();
    }
    public void actionPerformed(ActionEvent event) {
    }
    public void itemStateChanged(ItemEvent event) {
        if (event.getSource() == this.sortedByList) {
            SortingFamilyInfoCriteria criterion = (SortingFamilyInfoCriteria) this.sortingFields
                    .get(event.getItem());
            if (criterion == null)
                return;
            this.database.sort(criterion);
            this.familiesTable.repaint();
        }
    }
    public void serviceChanged(ServiceEvent event) {
        switch (event.getType()) {
        case ServiceEvent.MODIFIED:
            return;
        case ServiceEvent.REGISTERED://有新的排序方法注册到framework当中
            ServiceReference ref = event.getServiceReference();
            SortingFamilyInfoCriteria criterion = (SortingFamilyInfoCriteria) this.context
                    .getService(ref);
            if (criterion != null) {
  //把新的排序方法加入到下拉列表中
                sortedByList.addItem(criterion.getSortFieldString());
                this.sortingFields.put(criterion.getSortFieldString(),
                        criterion);
            }
            return;
        case ServiceEvent.UNREGISTERING://一个现有的排序方法将被从framework被取消
            ref = event.getServiceReference();
            criterion = (SortingFamilyInfoCriteria) this.context
                    .getService(ref);
            if (criterion != null) {
  //把该排序方法从下拉列表中删除
                sortedByList.removeItem(criterion.getSortFieldString());
                this.sortingFields.remove(criterion);
            }
            return;
        }
    }
}
 
与(五)相比不同的地方是,这个gui的table model以及排序的方法,都是通过查询service对象获得。
 
manifest文件如下:
Manifest-Version: 1.0
Bundle-SymbolicName: com.bajie.test.family.gui
Bundle-Name: family gui
Bundle-Version: 1.0
Bundle-Vendor: LiMing
Bundle-Activator: com.bajie.test.family.gui.FamilyInfoGui
Import-Package: org.osgi.framework;version=1.3,com.bajie.test.family.model
 
然后我们生成bundle的jar文件。分别为familymodel.jar和familygui.jar,之后我们用“in”命令把两个bundle装入framework。
接着我们先启动model bundle,然后再启动gui bundle,我们会看到JTable中有3列,而排序方法列表中也有3个选项,完全和程序的逻辑符合。
 
接下来,我们假设客户需要添加显示每个家庭的人均年收入并按其排列纪录。要满足这个需求,我们可以参考在(五)中做法,就是在model bundle里面再添加一个同时实现了FamilyInfoColumn和SortingFamilyInfoCriteria的类,并在bundle的启动中作为服务注册到framework中?不过这样就得更新model bundle然后调用rfr命令来刷新。为什么不再装一个补丁bundle,在这个bundle中包含了同时实现FamilyInfoColumn和SortingFamilyInfoCriteria的类,并在这个新bunle启动时注册产生该类的新对象作为服务注册到framework中,这样gui和model bundle都能侦听到该新服务的到来(他们都实现了服务侦听接口ServiceListener),gui上马上就能有所体现。

这个新bundle的代码如下:
package com.bajie.test.family.model.impladd;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import com.bajie.test.family.model.FamilyInfoColumn;
import com.bajie.test.family.model.FamilyInfoEntry;
import com.bajie.test.family.model.SortingFamilyInfoCriteria;
public class FamilyIncomePerPerson implements BundleActivator {
    public void start(BundleContext context) throws Exception {
 //注册一个新的服务,服务的类型既为FamilyInfoColumn,也是SortingFamilyInfoCriteria
        String[] clazzes = new String[] {FamilyInfoColumn.class.getName(),SortingFamilyInfoCriteria.class.getName()};
        context.registerService(clazzes,new FamilyIncomePerPersonColumn(),null);
       
    }
    public void stop(BundleContext context) throws Exception {
    }
    //这个类实现了“Income Per Person”这个列以及按该列排序的方法。
    class FamilyIncomePerPersonColumn implements FamilyInfoColumn,SortingFamilyInfoCriteria {
        private static final String COLUMNNAME = "Income Per Person";
       
        public Object getColumnValue(FamilyInfoEntry entry) {
            return new Integer(entry.getIncomePerYear()/entry.getPopulation());
        }
       
       
        public String getColumnName() {
            return FamilyIncomePerPersonColumn.COLUMNNAME;
        }
       
        public String getSortFieldString() {
            return FamilyIncomePerPersonColumn.COLUMNNAME;
        }
        public int compare(Object obj1, Object obj2) {
            if (obj1 == obj2) {
                return 0;
            }
            FamilyInfoEntry en1 = (FamilyInfoEntry)obj1;
            FamilyInfoEntry en2 = (FamilyInfoEntry)obj2;
           
            return en1.getIncomePerYear()/en1.getPopulation() - en2.getIncomePerYear()/en2.getPopulation();
        }
       
    }
}
 
manifest文件如下:
Manifest-Version: 1.0
Bundle-SymbolicName: com.bajie.test.family.modeladd
Bundle-Name: family model add
Bundle-Version: 1.0
Bundle-Vendor: LiMing
Bundle-Activator: com.bajie.test.family.model.impladd.FamilyIncomePerPerson
Import-Package: org.osgi.framework;version=1.3,com.bajie.test.family.model
 
打包安装到framework后,启动该bundle,我们就会在gui上看到新的列已经被添加,而且排序列表中增加了一个新的排序选项。
这个结果,完全符合需求的意图。
如果我们用stp命令停止这个bundle,我们在gui上就会发现,新列消失,而且排序列表中对应选项也没有了。这就是service带来的动态效果。不过,如果我们的model发生了一些实质的变化,比如FamilyInfoEntry需要添加一个“地址”列,那么model bundle就要更新,进而gui bundle以及使用到这个类型的bundle都需要通过rfr命令刷新。
 
好了,对扶贫助手的分析就此打住,我们总结一下,通过程序可以看到注册服务一点都不复杂。最简单的情况我们只需要提供一个java类型名称,以及实现这个类型的一个java对象就可以了,
不需要提供复杂的类型描述,比如xml描述文件。而使用服务的bundle通过类型名称就轻而易举的查找到相关的服务对象。
 
到此,osig介绍系列就要结束了,只希望这个系列能够把你引入到osgi的门口,其后面的精彩世界就看你的兴趣了。
就我个人的关注和理解,今年是osgi很重要的一年。JSR249今年应该投票,如果osgi入选,那么osgi将成为高端手机中java体系结构的重要组成部分。
在汽车领域,siemensVDO已经推出了基于osgi的解决方案,听说已经配备在BMW serials 5里面了。应该还会有更多的应用......
 
如果你是osgi的粉丝,欢迎你来信jerrylee.li@gmail.com拍砖交流。

posted @ 2006-02-14 16:08 勤劳的蜜蜂 阅读(1792) | 评论 (10)编辑 收藏

OSGi介绍(五)两个bundle

     摘要: (四)中提到的直接型改造法实际上和一个传统的java应用程序没有区别。因此客户的需求发生变化,通常是牵一发而动全身。那么我们现在就看看如果在osgi framework中,用多个bundle来实现的效果吧。 我的想法是用两个bundle来配合实现“扶贫助手”的功能。一个bundle专门负责录入和显示纪录,一个bundle专门负责纪录的数据结构和对数据的处理,用时下时髦的说法就是使用了mvc,只是...  阅读全文

posted @ 2006-02-14 16:02 勤劳的蜜蜂 阅读(1970) | 评论 (1)编辑 收藏

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 =