<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Chasing an mobile web vision

    闖蕩在移動互聯網的世界中

    2006年2月14日 #

    移動互聯網時代--Android上的一個例子

    我們來演示一個獲取聯系人,并用網頁展現出來的簡單例子。

     首先,我們在eclipse環境中創建一個Android project,我們的Activity名稱是com.example.RIAExample,并且修改界面的layout文件如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation
    ="vertical"
        android:layout_width
    ="fill_parent"
        android:layout_height
    ="fill_parent"
        
    >
    <WebView android:id="@+id/web"
     android:layout_width
    ="fill_parent" android:layout_height="fill_parent">
    </WebView>
    </LinearLayout>

    可以看到,界面中僅僅包含一個WebView控件。

     接下來,創建一個簡單的java類來描述一個聯系人的信息,它包含聯系人姓名和號碼。

     

    package com.example;

    import java.util.Vector;

    import android.app.Activity;
    import android.os.Bundle;
    import android.webkit.WebView;

    public class RIAExample extends Activity {
        
        
    private WebView web;
        
        
    //模擬號碼簿
        private Vector<Person> phonebook = new Vector<Person>();
        
    /** Called when the activity is first created. */
        @Override
        
    public void onCreate(Bundle savedInstanceState) {
            
    super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            
    this.initContacts();
            web 
    = (WebView)this.findViewById(R.id.web);
            web.getSettings().setJavaScriptEnabled(
    true);//開啟javascript設置,否則WebView不執行js腳本
            web.addJavascriptInterface(this"phonebook");//把RIAExample的一個實例添加到js的全局對象window中,
                                                            
    //這樣就可以使用window.phonebook來調用它的方法
            web.loadUrl("file:///android_asset/phonebook.html");//加載網頁
         
        }

        
        
    /**
         * 該方法將在js腳本中,通過window.phonebook.getContacts()進行調用
         * 返回的JavaArrayJSWrapper對象可以使得在js中訪問Java數組
         * 
    @return
         
    */

        
    public JavaArrayJSWrapper getContacts(){
            System.out.println(
    "fetching contacts data");
            Person[] a 
    = new Person[this.phonebook.size()];
            a 
    = this.phonebook.toArray(a);
            
    return new JavaArrayJSWrapper(a);
        
        }

        
        
    /**
         * 初始化電話號碼簿
         
    */

        
    public void initContacts(){
            Person p 
    = new Person();
            p.name 
    = "Perter";
            p.phone_number 
    = "8888888";
            phonebook.add(p);
            p 
    = new Person();
            p.name 
    = "Jack";
            p.phone_number 
    = "777777";
            phonebook.add(p);
           
        }

        
        
    /**
         * 通過window.phonebook.debugout來輸出js調試信息。
         * 
    @param info
         
    */

        
    public void debugout(String info){
            
            System.out.println(info);
        }

    }

    下面是html文件,它非常簡單。
    <html>
        
    <head>
            
    <script type="text/javascript" src="fetchcontacts.js"/>
    </head>
    <body>
        
    <div id = "contacts">
            
    <p> this is a demo </p>
        
    </div>
    </body>
    </html>

    而主角就是我們的javascript腳本fetchcontacts.js
    window.onload= function(){
        window.phonebook.debugout(
    "inside js onload");//調用RIAExample.debugout
        var persons = window.phonebook.getContacts();//調用RIAExample.getContacts()
        if(persons){//persons實際上是JavaArrayJSWrapper對象
            window.phonebook.debugout(persons.length() + " of contact entries are fetched");
            
    var contactsE = document.getElementById("contacts");
            
    var i = 0
            
    while(i < persons.length()){//persons.length()調用JavaArrayJSWrapper.length()方法
                
                pnode 
    = document.createElement("p");
                tnode 
    = document.createTextNode("name : " + persons.get(i).getName() + " number : " + persons.get(i).getNumber());//persons.get(i)獲得Person對象,然后在js里面直接調用getName()和getNumber()獲取姓名和號碼
                pnode.appendChild(tnode);
                contactsE.appendChild(pnode);
                i 
    ++;
            }

        }
    else{
            window.phonebook.debugout(
    "persons is undefined");
        }

        
    }


     例子很簡單,我加了注釋希望有助大家理解,其他我就不深入解釋了。
    我把例子的源代碼放上來,你可以下來試一試。

    RIADemo

    這個例子說明通過WebView.addJavascriptInterface方法,我們可以擴展JavaScript的API,獲取Android的數據。這樣,JS的粉絲就可以使用Dojo,JQuery,Prototy等這些知名的js框架來搭建android應用程序來展現它們很酷很玄的效果!但是,目前addJavascriptInterface還不夠靈活強大,為什么呢?敬請關注第四篇“what next?”


    posted @ 2009-03-14 16:16 勤勞的蜜蜂 閱讀(5486) | 評論 (6)編輯 收藏

    移動互聯網時代--忽如一夜春風來,web花開各終端

    自從Apple在safari上使用webkit并移植到iphone后,webkit就仿佛是獲得了選秀第一名似的,吸引了眾多眼球。
    其實,我最早聽說webkit是在iphone出來前一兩年,當時有報道說Nokia正在采用webkit為它下一代的Symbian平臺做一個引擎,而且還有專門的社區,但比較封閉,很難進入那個圈子,讓人一點感覺沒有。不過,至少說明Nokia很早就盯上這一塊了。
    iphone發布后,Apple似乎就接管webkit了,從此,webkit社區就有了巨大變化。Apple把能開放的東西都放到了webkit社區里面,速度還相當的快(用Apple自己的話說,他們貢獻了81%的力量,看看webkit社區的主力開發人員,有多少來自Apple!)現在,我們時不時的可以看到webkit的更新,比如對html5的跟進,css的特效等新功能!其中不得不重點提出的是2008年6月2日,webkit社區發布了高性能javascript引擎SquirrelFish!9月3日google chrome發布,采用了V8 javascript引擎,號稱比SquirrelFish還快,兩個禮拜后webkit就發布了SquirrelFish Extreme給予正面還擊。巨頭們牟足了勁爭先把javascript引擎油門踩到底!我不想猜測他們賣力的真正原因,但是,我覺得隨著移動終端能力加強,javascript大面積攻進終端已經指日可待了,iphone和android已經實現了。
    這期間,由于Apple的快速組合拳,Nokia經營的webkit專區很快就倒閉了。但他并沒有放棄對webkit的投入,2008年1月28日Nokia宣布收購了Trolltech公司。現在Trolltech lab作為Nokia的代表仍然活躍在webkit社區之中。就前兩天,他們還在http://planet.webkit.org/發布了一篇標題極其引人的博文“Creating a Google chat client in 15 minutes”,為QWebView做了一把廣告,相信qt的粉絲肯定不會錯過。而本月初發布的qt4.5中,明確指出qt加強了對webkit諸如SquirrelFish等新功能的整合。那么就讓我們期待一下Nokia的新款機器吧。
    到此,我們已經看到Google, Apple, Nokia, Palm等移動終端的領頭羊們所采取的行動,然而,不要忘記軟件廠商!Adobe就是杰出的代表。作為RIA的強烈倡導者,他也選擇把webkit整合到其AIR平臺,你可以想象這個webkit+flash的威力有多強大!而Adobe發起的openscreen項目,更是表明了他要在RIA上爭奪王位的野心!
    好了,不啰嗦那么多了,總之呢,webkit已經成為移動互聯網這出大戲的主角了,回歸到我在上一篇中提到的主題,讓我們來研究一下android.webkit.WebView的功能,來看android上的RIA。
    敬請關注下一篇--使用WebView的小例子。

    posted @ 2009-03-10 22:39 勤勞的蜜蜂 閱讀(2286) | 評論 (0)編輯 收藏

    移動互聯網時代的終端--暫時遺忘OSGi,讓我們去品味一杯android磨出的移動互聯網咖啡吧

    1年多前,揣著在移動終端推廣OSGi的夢想,我離開了原來的公司,來到了一個自認為更利于osgi的地方,在我看來osgi擁有eRCP這樣的粉絲,應該會給它在移動終端上提供廣闊的舞臺。然而半路殺出個程咬金,google android!第一次見到它時,我不相信osgi會輸,于是就廢寢忘食的研究它,結果我沒有進行太多的抵抗,很快就被Android收編了。我得承認它在某些關鍵的因素,確實比osgi更適合移動終端。對此,我只能感嘆google開源的偉大,以及摒棄jcp的雷厲風行!但是,更為重要的是,在移動互聯網時代即將到來的時刻,Android為我們打開了一扇方便之門,這到底為何?

    通過對Android的(java)源碼進行掃描后,我看到了像jsr211,MVM這些老朋友的影子,嗯,我可不想再炒這些舊飯了。還有啥?網上熱炒android用了webkit作為其瀏覽器的引擎,和iphone的safari使用的引擎一樣,這個東西應該不錯,因為我一直覺得eRCP沒有很好的web控件,使得它在移動平臺上遜色不少,于是我翻到了android.webkit這個java包,wow! Google給webkit封裝了很多java的接口,這真是java開發人員的福氣,(聽說很多java開發人員對sun的javafx期待度最高的就是一個傳說中的JWebView控件,但是不知道出來沒,算了,我懶得查證了,因為俺現在不想浪費太多在sun java上,呵呵),不過我覺得這更是廣大互聯網應用開發人員的福氣!想想!用html + css + javascript就能編寫android的類似電話簿,日歷甚至手機桌面的應用,這難道不讓您浮想聯翩嗎?所以,如果說Android為移動終端進入互聯網開了一扇門,那么android.webkit.WebView就是這扇門的金鑰匙,而webkit就是通向互聯網的康莊大道!

    其實android的webview是一個極端復雜的控件,而我個人認為它是可以實現現在熱炒的RIA/webos等概念的基石!雖然我進入這個領域不長,但覺得它非常有意思,很有前途,所以我打算寫一個系列,分享對它的理解,強烈歡迎各位高手指點!

    怎么開始說呢?不知道大家有沒有注意,年初Palm公司在CES上palm pre的基于webkit的webos驚艷之秀,十足掉起了大家的胃口,這是我聽到第一個冠以webos的移動終端,這是不是意味著目前由palm來唱這個webos的獨角戲呢?答案當然不是,其實很多終端廠家以及平臺廠商很早就率領大部隊兵臨webos的城下了,相信很快一場硝煙彌漫的戰爭就要開始了......

    欲知端的,敬請關注移本系列第二篇--忽如一夜春風來,web花開各終端

    posted @ 2009-03-09 22:53 勤勞的蜜蜂 閱讀(3448) | 評論 (7)編輯 收藏

    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 勤勞的蜜蜂 閱讀(1728) | 評論 (1)編輯 收藏

    OSGi 介紹(七)bundle和service的關系

    osgi系列已經發表了有將近2年的時間了,很高興這期間得到了許多朋友的關注,你們和我的討論切磋都讓我興奮無比。而過去很長的時間里,由于靈感枯竭外加精力有限,不能給大家分享更多osgi的精彩,實在辜負大家的期望,還請諒解。不過,根據這段時間大家和我私下的討論,發現很多人都苦惱于分不清楚bundle和service的關系,而我的osgi 5-6又臭又長,而且還非常依賴技術,實在是坑害入門者的必備武器。這個不足,猶如頭上方圓半尺盤旋的蒼蠅,困擾著我,解釋的郵件也不知寫多少,估計收效甚微。于是我決定再次出山,寫一個驚世駭俗的分析文章,力圖讓更多讀者都能夠把這兩個瘋馬牛理清楚。

    開講之前,還是用老辦法,給大家舉個例子。放心,這個例子一點都不技術,而且我相信你可能比我還清楚。大家都知道房地產商是怎么運作樓盤的吧。據我淺顯的認識,他們都會先進行一些圖紙上的規劃,里面包含小區的整體規模,樓房外觀以及廣大疾苦民眾最為關心和渴求的戶型圖。據說,很多房地產商在搞定政府某些關鍵部門(個人意見僅供參考)拿到一塊地后,根本不用費心思去自己規劃這些自己都一竅不通的東西,只要請一個有資質的第三方公司寫寫畫畫,甚至照搬某西方發達國家的某社區概念,冠于中國特色云云,就能使廣大民眾趨之若鶩,傾囊搶購。ok,你可能受不了我又憤世嫉俗了,這個到底和osgi有啥關系?別著急,天色已經很晚,下次某個時間請繼續關注osgi(七)續,其間,請大家先考慮一下,如何用ooa的方式來描述人們如何購房,以及在房子里進行日常生活這樣的簡單場景。

    posted @ 2007-07-16 23:31 勤勞的蜜蜂 閱讀(2488) | 評論 (2)編輯 收藏

    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 勤勞的蜜蜂 閱讀(1793) | 評論 (0)編輯 收藏

    framework implementation updated

    瞎整了半年多,克服了重重困難,終于把原來framework做了更新,如果有興趣,還可以按照原來的Link下載。
    framework下載后,解壓并運行startframework.bat就可以啟動framework了。
    目前該framework只支持在內存中存儲bundle.

    另外,多增加了一個管理bundle,它為framework提供了簡單的圖形化操作界面,可以在這里下載。

    http://m.tkk7.com/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 勤勞的蜜蜂 閱讀(1560) | 評論 (0)編輯 收藏

    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 勤勞的蜜蜂 閱讀(1736) | 評論 (1)編輯 收藏

    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 勤勞的蜜蜂 閱讀(1297) | 評論 (0)編輯 收藏

    framework implementation

    這里是我目前的成果。
    http://m.tkk7.com/Files/Ferrari4000/framework.zip包含了framework的jar文件。
    解壓后,直接運行.bat文件就可以了。這個framework實現了r4core的大部分api(除一些支持local和安全的外,secrurity admin和conditional permission這兩個服務也沒有實現,url service也沒有實現),輸入help可以看到可以運行的命令(不過全是英文的,還是我寫的,將就一下吧)。

    http://m.tkk7.com/Files/Ferrari4000/src.zip包含所有源碼。代碼有點亂,hoho

    要成功編譯代碼,您還需要下載這個http://m.tkk7.com/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 勤勞的蜜蜂 閱讀(1795) | 評論 (8)編輯 收藏

    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 勤勞的蜜蜂 閱讀(5957) | 評論 (13)編輯 收藏

    OSGi介紹(五)兩個bundle

         摘要: (四)中提到的直接型改造法實際上和一個傳統的java應用程序沒有區別。因此客戶的需求發生變化,通常是牽一發而動全身。那么我們現在就看看如果在osgi framework中,用多個bundle來實現的效果吧。 我的想法是用兩個bundle來配合實現“扶貧助手”的功能。一個bundle專門負責錄入和顯示紀錄,一個bundle專門負責紀錄的數據結構和對數據的處理,用時下時髦的說法就是使用了mvc,只是...  閱讀全文

    posted @ 2006-02-14 16:02 勤勞的蜜蜂 閱讀(4391) | 評論 (3)編輯 收藏

    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 {
     }

    }


    看到代碼的區別了嗎?我在不同之處都標注了注釋。其實,從說白了,就是實現了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
    如果你接著問,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的一個例子:

    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)

    然后我們用jdk自帶的jar工具,來生成bundle jar文件。這樣,第一個bundle就完成了,您可以下載一個開源的framework安裝這個bundle試一試。在framework上嘗試對該bundle的啟動和停止,輸出的結果應該和原先的java application是一樣的,然后您還可以在那個start(context)的方法中,再增加一條記錄,重新打包,然后通過framework的update功能,就能夠在不重新啟動framework的情況下升級該bundle,我就暫時偷懶不針對具體framework來給出操作的方法了,先給您自己先摸索了(當然您也可以偷懶,因為后面我會結合具體framework深入講述的)。
    好了,說完代碼的改造,再看看改造所帶來的程序設計結構變化:那~~~就~~~~是~~~~沒變化!因此我把這種原封不動的改造方法稱為“直接型”,用這種直接法,我們可以輕易的把一個java應用程序改造成bundle。而這種改造目前能看到的好處就是bundle的“熱”升級。那怎樣能更漂亮些呢?在下一篇中,我會進一步改造這個扶貧助手成為兩個bundle,看看bundle的合作將會帶來怎樣的精彩效果

    posted @ 2006-02-14 15:46 勤勞的蜜蜂 閱讀(5533) | 評論 (4)編輯 收藏

    OSGi介紹(三)OSGi service platform的體系結構

    先讓我們來看看OSGi service platform的體系結構。另外要說明的是,我在后面的文章中,將采用framework來代替OSGi service platfrom,這樣比較簡便。
    下面這張圖來自OSGi Alliance的主頁(http://www.osgi.org/
     
    OSGi Service Platform Architecture

    層次很分明吧。放到我們假想的案例中,OS&Hardware可以對應為PDA的硬件和操作系統,您可以想象它是Intel xscacle + Microsoft window mobile,或者是Arm + embedded Linux
    而Execution Environment當然是我們上次提到的CVM + CDC + FP + PP,有這個jvm的配置運行framework就綽綽有余了。而再往上,就是我們要重點學習和分析的OSGi framework了。而Modules, Life Cycle, Service Registry, Services和Security是從不同的5個角度來劃分framework所具備的功能,后面我將會從除了Security外的四個方面分別結合我們的假設場景來分析。而體系結構的最上層是符合OSGi framework接口標準的應用程序,也就是OSGi世界中有名的“bundle”。

    下面來看看OSGi規范是如何定義一個bundle的。在r4規范的第27頁中大致這樣描述到:Framework定義了模塊(modularization)的單元,叫作bundle。Bundle實際就是一個具有jar(Java ARchive)格式的文件,其中包含了java的class文件和其他資源文件(比如圖標,配置文件等等)。Bundle可以在自己的manifest文件中說明自己能夠提供哪些java包,其他bundle如果在自己的manifest文件中指定了它需要這個包,那他們之間就可能產生java包的依賴關系,這樣多個bundle之間就可以共享java包。值得注意的是,bundle是能夠在framework中部署的唯一格式。下面給出原文的描述:
    A bundle is a JAR file that:
    ? Contains the resources necessary to provide some functionality. These resources may be class files for the Java programming language, as well as other data such as HTML files, help files, icons, and so on. A bundle JAR file can also embed additional JAR files that are available as resources and classes. This is however not recursive.
    ? Contains a manifest file describing the contents of the JAR file and providing information about the bundle. This file uses headers to specify information that the Framework needs to install correctly and activate a bundle. For example, it states dependencies on other resources, such as Java packages, that must be available to the bundle before it can run.
    ? Can contain optional documentation in the OSGI-OPT directory of the JAR file or one of its sub-directories. Any information in this directory is optional. For example, the OSGI-OPT directory is useful to store the source code of a bundle. Management systems may remove this information to save storage space in the OSGi Service Platform.

    framework的modules這一方面功能將主要負責bundle的安裝部署,更新和卸載,以及bundle在設備的物理存儲(如果有的話)。在這個層次,每個bundle都是獨立的,它的安裝,升級和卸載完全不依賴任何其他bundle,這點framework提供了強大的隔離性。Life Cycle專門負責對bundle的解析(比如關聯兩個有相互依賴關系的bundle),啟動(相當于運行應用程序)和停止(相當于停止應用程序)。這個層次中,bundle間的邏輯關系被創建起來,這些關系能否成功的創建,將會直接影響bundle的成功解析和啟動。Service Registry可以認為是一個數據庫,bundle啟動后,可以向這個數據庫注冊它動態提供的服務。只要bundle不被停止,且bundle不主動撤銷注冊的服務,這個服務將一直保存在這個數據庫中供其它bundle來查詢和使用。而Services就是由bundle運行時提供的具體服務對象,這些服務對象的存在,使得framework具有極其動態的特征,并為framework運行時提供靈活強大的功能。
    另外,根據OSGi Alliance的說法,OSGi的運行平臺包括了j2me(kvm + cldc + midp, cvm + cdc+fp), j2se, j2ee。不過,我個人還是覺得目前的midp規范還太弱,OSGi要想運行在上面,很多功能實現起來都比較不爽。

    好,有了對framework結構層次的皮毛認識,下面我們就開始著手改造那個“扶貧助手”的程序,使其變成OSGi的bundle(s),然后從上面提到的4個方面來分析framework的機制。
    這里,我先給出“扶貧助手”的java application模擬程序代碼:

    package aa.bb.cc;

    public class FamilyInfo {
     
    private String familyName; //家庭名稱
     private int population; //人口數量
     private int incomePerYear; //家庭年收入

      …..
    //省略了getter和setter方法

    //獲得家庭人均年收入
     public int getIncomePerMember(){
      
    return (int)(this.incomePerYear/this.population);
     }


     
    public String toString() {
      
      
    return "Family: " + this.familyName + ", population: " + this.population + ", income: " + this.incomePerYear;
     }

     
    //按家庭年收入又低到高排序
     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;
        }

       }

      }

      
     }


     
    public static void main(String[] args){
      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());
      }

      
     }

    }


     

    posted @ 2006-02-14 15:42 勤勞的蜜蜂 閱讀(5176) | 評論 (3)編輯 收藏

    osgi介紹(二)一個假想的實例

    如何分析OSGi service platform的機制?給出幾個硬生生的例子,然后分析一下
    代碼?那還不如你自己看書看規范好了。因此,我覺得還是結合一個應用實例來分析會更
    容易理解,當然,是一個假想的應用實例。用怎樣一個實例呢?嗯......

    幾個月前,一個中學同學打電話給我說他們要在PDA上開發一個簡單的應用程序來臨時紀
    錄工作的結果,并向我咨詢,這種開發的難度和周期。這事啟發我了,就以PDA的應用為
    背景,讓我們來假想一個場景,從而來比較傳統的應用模型與采用OSGi的應用模型有怎樣
    的區別。

    我這樣想象:
    小李是一個軟件工程師,在一家專門為PDA開發應用程序和解決方案的公司工作。最近,
    他剛為公司的一個客戶開發完成了一套運行在PDA的JAVA應用程序,我們不要關心PDA是
    什么硬件配置,只要知道它配備了JVM(cvm) + CDC以及PP和文件系統(呵呵設備還是比較
    強勁的)。而這個客戶是一個慈善機構,該機構人員攜帶PDA進入偏遠山區收集生活困難
    家庭的信息,以準備進行資助。而這套程序將會暫時把家庭信息保存在PDA中,并隨時供
    用戶查詢修改。用戶使用一個月后,反饋非常好,但是,他們有新需求了,說原來只是想紀錄
    信息就成了,現在希望能給出一些排序功能,比如按家庭年收入對紀錄進行排序.

    接到這個需求,小李一看,這個簡單,只要增加一個排序方法就可以了,讓我們假設他使用了如下
    數據結構來紀錄家庭信息:

    Class FamilyInfo {

     
    private String familyName;//家庭名稱

     
    private int population; //人口數量

     
    private int incomePerYear; //年收入

     .(省略Getter和Setter方法)
    }


     

    為了滿足這個需求,小李決定添加一個靜態的排序方法:

    public static FamilyInfo[] sortByIncomePerYear(FamilyInfo[] familyInfos){
     
    //根據incomePerYear的值進行冒泡排序。
    }



    把相關連部分修改完畢后,小李重新制作了安裝包和啟動腳本,發送給客戶,不管客戶如何操作
    總之,原來的PDA程序必須卸載,新程序必須拷貝到PDA上再次執行安裝,重新啟動運行。

    又過了一陣,客戶說,要求提供按人均年收入進行排序,然后同樣的事情又發生了......

    幾個輪次下來,小李發現,客戶的需求還在增加,他們可能要求增加一個字段,記錄目前該
    家庭得到的資助額,還可能添加按收入范圍查詢紀錄等等,事情還遠沒有結束。

    如何改進這個情況呢?當然,改進涉及多方面,比如從軟件本身出發,可以使用合適的design
    pattern重新設計程序的體系結構,使得程序更易于擴展,關于這一點,有太多的討論了,我就不
    摻和了。還有從部署方面說,配置,安裝和卸載程序,對最終用戶往往是一項mission impossible,
    能否讓應用程序自己升級,而用戶只要點擊一個"升級"來觸發這個過程而已......

    我想你當然知道我給的答案:OSGi,OSGi,OSGi!!!!

    posted @ 2006-02-14 15:39 勤勞的蜜蜂 閱讀(4684) | 評論 (3)編輯 收藏

    osgi介紹(一)什么是osgi

    過于的一年多,在和很多it屆的同學及朋友見面時,他們總會問我最近在做什么。“OSGi!”,我不加思索的回答。到目前為止,對這個單詞得到的反應都沒有超出“這是什么?”,“我沒有聽說過”,“噢,能具體點嗎?”等等。而我的回答更讓他們糊涂,最后,大家干脆放棄這個話題,轉到買房,運動等等更能體現聚會實質的問題上。不過最近,我一直在思考這個問題,下次再遇到這種情況時,該如何去表達才能讓也是it屆的哥們姐們能迅速的理解這個領域的范圍呢?要知道,技術人員往往不善于表達,我們已經習慣了和業內人士用行話交流。

    關于這個問題,我訪問了OSGi Alliance的網站,在里面的faqs中,找到了我想要的東西。實際上,正如faqs中所解答的,OSGi涵蓋了太多的范圍,簡單的兩三句話是無法說清楚的。而我這里指的OSGi從技術的角度,應該說是“OSGi service platform ”,faqs中這樣解釋OSGi service platform(http://www.osgi.org/about/faqs.asp?section=1#q19) :
    The OSGi service platform delivers an open, common architecture for service providers, developers, software vendors, gateway operators and equipment vendors to develop, deploy and manage services in a coordinated fashion. .......(以下省略上千英文單詞)

    好長!不過第一句話就已經能總結陳詞了,“OSGi service platform是一個開放并且提供統一接口標準的體系框架,基于這個體系框架,服務提供商,程序開發人員,軟件提供商,服務網管運營商,設備提供商能夠協調地聯合起來開發,部署以及管理向用戶提供的各種服務。”還需要提到的是OSGi service platform是一個基于Java的platform。

    OSGi的提出和誕生之初,其目的主要是能夠靈活方便并遠程管理互聯的網絡嵌入設備(聽說是1997年左右提出,與Jini有深厚淵源)。隨著硬件設備的能力不斷提高,java技術的日益普及,尤其J2ME的壯大,現實應用的需求也不斷擴大和推進,一個統一的標準變得非常的必要。OSGi Alliance就在這樣的背景下成立了。從1999年成立以來,OSGi Alliance已經針對這個service platform發布了4版規范,其中r4是2005年10月份剛剛發布。

    目前有不少公司對OSGi service platform推出了自己的實現,象ibm的smf(Service Management Framework,嗯,多好的名字,在那么多的platform實現中,我個人最喜歡這個名字,言簡意賅)。

    德國的ProSyst公司(http://www.prosyst.com)是OSGi Alliance中非常活躍的推動者,看看他們的產品列表吧http://www.prosyst.com/products/osgi.html(他們甚至提供了kvm + cldc的OSGi framework)

    開源的Oscar(http://oscar.objectweb.org/),Knopflerfish(http://www.knopflerfish.org/)

    對于OSGi的成功應用,最有名的應該是eclipse了,它就是基于OSGi service platform的產品。還有Apache,據說OSGi將被應用于其新一代的build工具中。這些都是j2se和j2ee的應用,而基于j2me的,手機(對應OSGi Alliance的MEG)和車載設備(對應OSGi Alliance的VEG)是OSGi的主要領域,OSGi Alliance已經有相應的規范,這些領域的應用相信會更加精彩,讓我們拭目以待吧。

    posted @ 2006-02-14 15:32 勤勞的蜜蜂 閱讀(23147) | 評論 (10)編輯 收藏

    主站蜘蛛池模板: 美景之屋4在线未删减免费| 亚洲国产第一页www| 亚洲精品国产av成拍色拍| 亚洲黄色免费在线观看| 久久亚洲伊人中字综合精品| 成人免费ā片在线观看| 国产亚洲?V无码?V男人的天堂| 羞羞漫画登录页面免费| 亚洲成a人在线看天堂无码| 成年人在线免费看视频| 亚洲一区AV无码少妇电影| 曰批全过程免费视频播放网站| 亚洲最大的成网4438| 最近在线2018视频免费观看| 成人黄色免费网址| 亚洲大尺码专区影院| 亚洲国产AV无码专区亚洲AV| ww在线观视频免费观看w| 亚洲精品456在线播放| 亚洲国产成人精品久久久国产成人一区二区三区综 | 亚洲AV无码码潮喷在线观看| 毛片免费视频在线观看| 中文字幕免费在线视频| 亚洲成a人无码亚洲成www牛牛 | 国产在线观看免费完整版中文版| 免费成人在线视频观看| 高潮内射免费看片| 亚洲一级特黄特黄的大片| 亚洲国产精品无码久久久秋霞2| 暖暖在线日本免费中文| 97视频免费观看2区| 国产一级在线免费观看| 色欲aⅴ亚洲情无码AV蜜桃| 亚洲神级电影国语版| 久久精品国产精品亚洲精品| 亚洲国产V高清在线观看| 国产精品黄页在线播放免费| 一个人看的www在线观看免费| 香蕉成人免费看片视频app下载| 又硬又粗又长又爽免费看| 色天使色婷婷在线影院亚洲|