OSGi開發環境建立和Hello World

0 前言

看了前面三篇入門篇以後是不是躍躍欲試呢?這篇文檔介紹如何使用OSGi框架的一個實現——Equinox來教你如何配置一個簡單的OSGi開發環境,並且在這個環境上開發一個HelloWorld程序,這其中會涵蓋前面的入門篇講到的三個層次的內容,讓你在實踐的同時鞏固之前瞭解的內容。話不多說,我們開始吧!

1 OSGi開發環境的建立

1.1 Equinox是什麼

從代碼角度來看,Equinox其實就是OSGi核心標準的完整實現,並且還在這個基礎上增加了一些額外的功能(比如爲框架增加了命令行和程序執行的入口)。我們在之前入門篇講解的都東西其實都是OSGi核心標準的一小部分。其實它的核心就是一個jar包,這個jar包既能執行(作爲標準Java包的特性),也是一個bundle(Manifest裏面含有OSGi bundle特有的元數據)。
現在你需要知道的就是,我們能夠利用Equinox項目的代碼來運行一個實實在在的OSGi框架,框架啓動後,你就可以將你開發好bundle放到裏面運行。

1.2 下載Equinox

Equinox在http://download.eclipse.org/equinox上有官方下載,裏面列出了各個版本供我們選擇:

在這裏,我們使用3.7版本的Equinox,下載好以後放在一個單獨的文件夾下(這裏我的路徑是E:\OSGi framework\equinox):

1.3 從命令行啓動框架

如果啓動這個框架的話,有了上面的jar包就足夠了,我們進入命令行輸入如下命令:java –jar org.eclipse.osgi_3.7.0.v20110613.jar -console ,然後就會進入Equinox的控制檯:

如果出現osgi>的提示符,就說明啓動成功了。
Equinox的控制檯的部分基本命令如下(區分大小寫):

install [URL] 將URL表示的bundle安裝到框架中
uninstall [bundleID] 將id=bundleID的bundle卸載
start [bundleID] 啓動一個bundle
stop [bundleID] 停止一個bundle
refresh [bundleID] 刷新bundle
update [bundleID] 更新bundle 的內容
ss 簡單顯示所有bundle的狀態
status 展示安裝的bundle和註冊的服務
headers [bundleID] 展示bundle 的manifest中的元數據

1.4 在Eclipse中建立開發環境

在上一節中大家看到啓動和控制框架的方法,是相當簡單的一個過程。不過單單只是運行環境還不夠,我們還需要開發環境。

1.4.1 設置

首先我們不需要安裝必須的插件,只要你有較新版本的Eclipse就行了。然後進入Eclipse的window->preferences界面,選中Plug-in Development下的Target Platform:

現在右邊只有一個Runing Platform的,我們任務是點擊“Add…”按鈕來增加一個我們自己的的platform,進入如下界面:

http://assets.osgi.com.cn/article/7289228/New<em>Target</em>Definition.png

選擇默認的第一個就好,點擊next。

http://assets.osgi.com.cn/article/7289228/New<em>Target</em>Definition_1.png

再點擊這裏的“Add…”:

http://assets.osgi.com.cn/article/7289228/Add_Content.png

選擇“Directory”:

http://assets.osgi.com.cn/article/7289228/Add<em>Content</em>1.png

選擇你的Equinox的jar包所在的路徑,然後點擊finish,回到剛纔的界面:

http://assets.osgi.com.cn/article/7289228/Edit<em>Target</em>Definition.png

這時候你就會發現裏面多出來了你剛剛設置的路徑,路徑後面描述的“1 plug-ins available”則就是說的我們放置的Equinox的jar包。
繼續點擊finish,回到最開始的界面:

這時候多出來了一個新的target platform,勾選上,然後確定。

1.4.2 啓動

打開菜單項Run->Run configurations…,在OSGi Framework項中,新建一個Run configuration:

這裏面現在已經自動包含了Equinox的jar包了,點擊Run,看看運行的效果:

Eclipse的控制檯中也出現來了osgi的提示符,說明你已經成功啓動了。

你可以試試剛纔講的那些命令,看看能輸出些什麼(比如上圖中我輸入了ss)。

1.4.3 新建一個project

打開新建project的界面,選擇Plug-in Project:

然後輸入project的名字,TargetPlatform處選擇an OSGi framework->Equinox或者standard都行,點擊下一步:

這裏實際上是對bundle的Manifest文件的設置,其中的ID就是Bundle-SymbolicName,Version就是bundle的版本號,下面還能決定是否定義BundleActivator,點擊finish就創建了一個project:

至此,開發環境已經建立完畢(這個project只是爲了演示怎麼建立,不會在接下來的內容中用到,可刪之)。

2 HelloWorld

現在可謂是萬事具備,只欠Helloworld了。爲了將OSGi框架的三個層次都涵蓋到,這個Helloworld可能會比其他你見到的OSGi Helloworld程序要複雜一點點。如果對代碼中的一些API感到生疏,記得回到之前的入門篇中找到對應的內容,這樣對你理解代碼會有幫助。裏面的關鍵代碼已經用黃色高亮顯示。(出於篇幅考慮,代碼中的import語句都省略)

2.1 HelloWorld的定義與實現

首先我們創建一個工程org.serc.helloworld,在這個工程裏面,我們創建一個包含sayHello方法的接口,準備作爲服務接口:

package org.serc.helloworld;

public interface Hello {
void sayHello();
}

然後,對這個接口進行實現:

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文件中加入如下條目:

Export-Package:  org.serc.helloworld;version="1.0"

接下來,爲了把這個服務註冊到框架中,我們定義了一個Activator:

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文件中做如下定義:

Bundle-Activator: org.serc.helloworld.activator.Activator

這個bundle 最終的MANIFEST內容如下:

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工程中現在應該是這樣:

2.2 獲得並執行SayHello服務

創建一個工程org.serc.helloworld.client,創建一個叫HelloUser的BundleActivator,其中的start方法會獲得接口爲Hello的服務對象,並且通過這個對象來調用sayHello方法:

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:

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

2.3 HelloWorld程序的流程

可能光看代碼會比較不容易看清楚程序的執行流程,下圖表示了這幾個類的各個功能的相互依賴關係,整個關係從Hello接口的定義開始;然後到Hello接口被實現,得到HelloImpl;再到Activator將HelloImpl註冊爲框架中的一個服務,再到HelloUser通過與框架交互得到剛纔註冊的服務,並且使用這個服務從而輸出字符串;最後一個可選流程是當我們stop org.serc.helloworld這個bundle的時候,程序會將之前註冊的服務註銷掉。

2.4 程序的執行

通過上面的工作,我們得到了兩個自己定義的bundle:org.serc.helloworldorg.serc.helloworld.client現在打開Run configurations界面,我們會看見Bundles標籤裏面多出來了這兩個bundle:

也就是說,OSGi框架在啓動的時候,會自動install和start這2個bundle,我們點擊Run按鈕,看看會有什麼結果:

2.5 利用命令行查看程序執行過程中框架狀態的變化

2.4其實只給出了一個結果,如果你還不太清楚這個結果具體是怎麼出來的,那麼這一節的內容應該能夠幫助你更好的理解輸出結果的過程。下面我們通過Equinox的一些命令行來一步一步安裝和執行bundle,並且查看過程中框架的狀態變化,來讓你們搞清楚這個結果是怎麼來的。
首先在Run configuration中取消兩個helloworld bundle的自動啓動:

然後點擊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才能正確啓動。

3 小結

這篇文檔是入門篇的最後一章了,希望讀者在花時間看完這4篇文檔並且動手實踐後能夠有所收穫,並且對OSGi框架的工作原理及其優勢能有一個比較清晰的瞭解和認識。如果你依然有興趣深入瞭解OSGi的世界,或者覺得這些東西對你來說有點小兒科了,那就讓我們在進階篇中見面吧。




發佈了0 篇原創文章 · 獲贊 2 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章