現在可謂是萬事具備,只欠Helloworld了。為了將OSGi架構的三個層次都涵蓋到,這個Helloworld可能會比其他你見到的OSGi Helloworld程式要複雜一點點。如果對代碼中的一些API感到生疏,記得回到之前的入門篇中找到對應的內容,這樣對你理解代碼會有協助。裡面的關鍵代碼已經用黃色高亮顯示。(出於篇幅考慮,代碼中的import語句都省略) 3.1 HelloWorld的定義與實現
首先我們建立一個工程org.serc.helloworld,在這個工程裡面,我們建立一個包含sayHello方法的介面,準備作為服務介面: [java] view plain copy package org.serc.helloworld; public interface Hello { void sayHello(); }
然後,對這個介面進行實現:
[java] view plain copy package org.serc.helloworld.impl; public class HelloImpl implements Hello{ final String helloString; public HelloImpl(String helloString){ this.helloString = helloString; } public void sayHello(){ System.out.println(this.helloString); } }
這個類實現的sayHello所做的工作就是輸出一個在物件建構的時候得到的helloString 字串。 為了將這個介面暴露出來,我們需要在MANIFEST檔案中加入如下條目: [java] view plain copy Export-Package: org.serc.helloworld;version="1.0"
接下來,為了把這個服務註冊到架構中,我們定義了一個Activator: [java] view plain copy package org.serc.helloworld.activator; public class Activator implements BundleActivator { private List<ServiceRegistration> registrations = new ArrayList<ServiceRegistration>(); public void start(BundleContext ctx) { registrations.add(ctx.registerService(Hello.class.getName(),new HelloImpl("Hello, OSGi"), null)); } public void stop(BundleContext ctx) { for (ServiceRegistration registration : registrations) { System.out.println("unregistering: "+ registration); registration.unregister(); }
我們為這個HelloImpl傳入了"Hello, OSGi"的字串 為了讓這個Activator能夠工作,需要在MANIFEST檔案中做如下定義: [java] view plain copy Bundle-Activator: org.serc.helloworld.activator.Activator
這個bundle 最終的MANIFEST內容如下: [java] view plain copy Bundle-ManifestVersion: 2 Bundle-SymbolicName: org.serc.helloworld Bundle-Version: 1.0 Bundle-Activator: org.serc.helloworld.activator.Activator Import-Package: org.osgi.framework Export-Package: org.serc.helloworld;version="1.0"
你的Eclipse工程中現在應該是這樣: 3.2 獲得並執行SayHello服務
建立一個工程org.serc.helloworld.client,建立一個叫HelloUser的BundleActivator,其中的start方法會獲得介面為Hello的服務物件,並且通過這個對象來調用sayHello方法: [java] view plain copy package org.serc.helloworld.client; public class HelloUser implements BundleActivator { public void start(BundleContext ctx) { ServiceReference ref = ctx.getServiceReference(Hello.class.getName()); if (ref != null) { Hello hello = null; try { hello = (Hello) ctx.getService(ref); if (hello != null) hello.sayHello(); else System.out.println("Service:Hello---object null"); } catch (RuntimeException e) { e.printStackTrace(); } finally { ctx.ungetService(ref); hello = null; } } else { System.out.println("Service:Hello---not exists"); } } public void stop(BundleContext ctx) throws Exception { } }
為了獲得Hello這個介面的定義,我們還需要在MANIFEST檔案中import Hello所在的package:
[html] view plain copy Bundle-ManifestVersion: 2 Bundle-SymbolicName: org.serc.helloworld.client Bundle-Version: 1.0 Bundle-Activator: org.serc.helloworld.client.HelloUser Import-Package: org.serc.helloworld;version="[1.0,2.0)",org.osgi.framework 3.3 HelloWorld程式的流程
可能光看代碼會比較不容易看清楚程式的執行流程,下圖表示了這幾個類的各個功能的相互依賴關係,整個關係從Hello介面的定義開始;然後到Hello介面被實現,得到HelloImpl;再到Activator將HelloImpl註冊為架構中的一個服務,再到HelloUser通過與架構互動得到剛才註冊的服務,並且使用這個服務從而輸出字串;最後一個可選流程是當我們stop org.serc.helloworld這個bundle的時候,程式會將之前註冊的服務登出掉。
3.4 程式的執行
通過上面的工作,我們得到了兩個自己定義的bundle: org.serc.helloworldorg.serc.helloworld.client 現在開啟Run configurations介面,我們會看見Bundles標籤裡面多出來了這兩個bundle:
也就是說,OSGi架構在啟動的時候,會自動install和start這2個bundle,我們點擊Run按鈕,看看會有什麼結果: 3.5利用命令列查看程式執行過程中架構狀態的變化
3.4其實只給出了一個結果,如果你還不太清楚這個結果具體是怎麼出來的,那麼這一節的內容應該能夠協助你更好的理解輸出結果的過程。下面我們通過Equinox的一些命令列來一步一步安裝和執行bundle,並且查看過程中架構的狀態變化,來讓你們搞清楚這個結果是怎麼來的。
首先在Run configuration中取消兩個helloworldbundle的自動啟動:
然後點擊Run,這時候就不會立即輸出Hello, OSGi字串了,現在我們先用“ss”命令查看bundle的狀態:
可見兩個bundle並不是出於ACTIVE狀態,說明並沒有啟動,現在我們執行“start 8”來啟動org.serc.helloworld這個bundle:
在用services命令查看當前已經註冊的服務,我們會看到一大堆的系統服務中多出來如下一項服務:
這顯然是我們在start以後註冊上去的,但是現在還沒有任何一個bundle在使用這個服務。 接下來我們start 9號bundle,也就是我們用來調用服務的bundle:
這時就輸出了“Hello,OSGi”的字串。
那麼如果我們先啟動9號bundle而不啟動8號bundle會怎麼樣呢。大家可以試一試,因為我們在代碼中已經對沒有服務的異常情況做了處理,屆時會有相應的輸出。我們先停止8號bundle(這裡的圖中bundleID增加了,大家對號入座):
大家可以看見剛才註冊的服務已經被登出了,現在我們執行refresh 11(也就是剛才的9號bundle)來重新執行其中BundleActivator的start方法:
可見Hello服務已經不複存在了。從這裡我們可以看出來,其實Bundle的啟動順序也是一個需要注意的環節,有時候你所定義的bundle是具有順序敏感性的,必須要某些前置bundle啟動了以後,後面的bundle才能正確啟動。