2009年11月2日月曜日

Apache Felix + Spring DM

Apache Felix + Spring DM の Getting Started。

前にApache Felix について書いたポストと同じように、名前を受け取って挨拶を返すサービスと、それを標準出力に表示するクライアントを作ってみる。ただし、Spring DM も併用するので、OSGi 定義のクラスの派生ではなく POJO でサービスを記述するという事になる。こういった、ちょっと新しいプロダクトの組み合わせは、最初期の段階でかなりハマる事がたまにあるので、この Getting Started で技術リスクを今のうちに減らしておきたい。

便宜上、下表のようなディレクトリを作業場所とした。

c:\work2\ルート作業ディレクトリ
  ├ client\クライアント用バンドル作業ディレクトリ
  ├ service\サービス用バンドル作業ディレクトリ
  └ felix\実験用 Felix 環境

■ 用意

  • felix を落としてどっかに展開しておく(以降 {felix base}と書く)
  • spring-osgi-1.2.0-with-dependencies.zipを落として、どっかに展開する。あとでspring-osgi-1.2.0の中身を使う。
  • jcl104-over-slf4j-1.4.3.jarをダウンロードしておく
※どれもググればすぐに在り処がわかる

■ 環境作り

解凍直後の もともとの felix の構成がゴチャゴチャすると嫌なので、c:\work2\felix\を実験用 Felix 環境とする。以下は c:\work2\felix\ 下での作業。必要なものは適宜 {felix base} からオリジナルをコピーする事にする。

  • {felix base}/bin/ から、felix.jar を持ってくる。
  • {felix base}/から以bundle/とconf/を持ってくる
  • spring-dm フォルダを作って以下のものを置く。
    • spring-osgi-1.2.0/lib 下から、以下をコピーする
      • com.springsource.org.aopalliance-1.0.0.jar
      • com.springsource.slf4j.api-1.5.0.jar
      • com.springsource.slf4j.log4j-1.5.0.jar
      • com.springsource.slf4j.org.apache.commons.logging-1.5.0.jar
      • log4j.osgi-1.2.15-SNAPSHOT.jar
      • org.apache.felix.main-1.4.1.jar
      • org.springframework.aop-2.5.6.A.jar
      • org.springframework.beans-2.5.6.A.jar
      • org.springframework.context-2.5.6.A.jar
      • org.springframework.core-2.5.6.A.jar
    • ダウンロードしておいた jcl104-over-slf4j を入れる
    • spring-osgi-1.2.0/dist 下から、以下をコピーする
      • spring-osgi-core-1.2.0.jar
      • spring-osgi-extender-1.2.0.jar
      • spring-osgi-io-1.2.0.jar
  • conf/config.properties のファイル末尾に以下を追加
    felix.auto.start.1=\
     file:bundle/org.apache.felix.shell-1.4.1.jar \
     file:bundle/org.apache.felix.shell.tui-1.4.1.jar \
     file:bundle/org.apache.felix.bundlerepository-1.4.2.jar \
     file:spring-dm/com.springsource.org.aopalliance-1.0.0.jar \
     file:spring-dm/jcl104-over-slf4j-1.4.3.jar \
     file:spring-dm/com.springsource.slf4j.org.apache.commons.logging-1.5.0.jar \
     file:spring-dm/log4j.osgi-1.2.15-SNAPSHOT.jar \
     file:spring-dm/org.apache.felix.main-1.4.1.jar \
     file:spring-dm/com.springsource.slf4j.api-1.5.0.jar \
     file:spring-dm/com.springsource.slf4j.log4j-1.5.0.jar \
     file:spring-dm/org.springframework.aop-2.5.6.A.jar \
     file:spring-dm/org.springframework.beans-2.5.6.A.jar \
     file:spring-dm/org.springframework.context-2.5.6.A.jar \
     file:spring-dm/org.springframework.core-2.5.6.A.jar \
     file:spring-dm/spring-osgi-core-1.2.0.jar \
     file:spring-dm/spring-osgi-extender-1.2.0.jar \
     file:spring-dm/spring-osgi-io-1.2.0.jar
    
  • log4j.properties を適当に書く(例えば以下のように)
    log4j.rootCategory=INFO, LOGFILE
    
    log4j.appender.LOGFILE=org.apache.log4j.FileAppender
    log4j.appender.LOGFILE.File=felix.log
    log4j.appender.LOGFILE.Append=true
    log4j.appender.LOGFILE.Threshold=INFO
    log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
    log4j.appender.LOGFILE.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
  • 立ち上げて確認してみる。
    C:\work2\felix>java -Dlog4j.configuration=file:log4j.properties -Dfelix.config.properties=file:config.properties -jar felix.jar
    
    Welcome to Felix
    ================
    
    Auto-properties start: org.osgi.framework.BundleException: Fragment bundles can not be started.
    -> ps
    START LEVEL 1
       ID   State         Level  Name
    [   0] [Active     ] [    0] System Bundle (2.0.1)
    [   1] [Active     ] [    1] Apache Felix Bundle Repository (1.4.2)
    [   2] [Active     ] [    1] Apache Felix Shell Service (1.4.1)
    [   3] [Active     ] [    1] Apache Felix Shell TUI (1.4.1)
    [   4] [Active     ] [    1] AOP Alliance API (1.0.0)
    [   5] [Active     ] [    1] jcl104-over-slf4j (1.4.3)
    [   6] [Active     ] [    1] SLF4J Jakarta Commons Logging Over SLF4J Binding (1.5.0)
    [   7] [Active     ] [    1] log4j.osgi (1.2.15.SNAPSHOT)
    [   8] [Active     ] [    1] Apache Felix (1.4.1)
    [   9] [Active     ] [    1] SLF4J API (1.5.0)
    [  10] [Resolved   ] [    1] SLF4J Log4J Binding (1.5.0)
    [  11] [Active     ] [    1] Spring AOP (2.5.6.A)
    [  12] [Active     ] [    1] Spring Beans (2.5.6.A)
    [  13] [Active     ] [    1] Spring Context (2.5.6.A)
    [  14] [Active     ] [    1] Spring Core (2.5.6.A)
    [  15] [Active     ] [    1] spring-osgi-core (1.2.0)
    [  16] [Active     ] [    1] spring-osgi-extender (1.2.0)
    [  17] [Active     ] [    1] spring-osgi-io (1.2.0)
    ->
    
※BundleException は、多分、SLF4J Log4J Binding 関連だと思うけど、本筋に関係なさそうなので、差し当たり放置。 と、実験用の Felix 環境はこんな感じ。

■ バンドル作り

前と同じように、Greeting Service と Greeting Service Client を連携させるが、ただし Clientはもっとシンプルに、固定文字列"Client"を Greeting Serviceに渡して結果を得るだけにする。つまり start コマンドでクライアントバンドルを実行すると、Felixのコンソールに固定文字列 "Hello Client"が表示されるのがゴール。

◆ Greeting Service

※以下、c:\work2\service\下の作業 ・以下のような2つのソースを書いて、適切なファイル名で適切なディレクトリに置く

package greeting.service;

public interface GreetingService {
 public String greet(String name);
}
package greeting.service.impl;

import greeting.service.GreetingService;

public class GreetingServiceImpl implements GreetingService {
   public void start() {
      System.out.println("Greeting Service registered");
   }
   public String greet(String name) {
     return "Hello " + name;
   }
}
・コンパイルする。classes ディレクトリを作ってそこに入れる。
$ javac -d classes greeting/service/*.java greeting/service/impl/*.java
・bean 定義ファイルを書いて、META-INF/spring 下に置く greetingservice.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean name="greetingService" 
    class="greeting.service.impl.GreetingServiceImpl" init-method="start"/>
</beans>
greetingservice-osgi.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:osgi="http://www.springframework.org/schema/osgi"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
                      http://www.springframework.org/schema/beans/spring-beans.xsd
                      http://www.springframework.org/schema/osgi
                      http://www.springframework.org/schema/osgi/spring-osgi.xsd">

  <osgi:service id="greetingOSGiService" ref="greetingService"
    interface="greeting.service.GreetingService">
  </osgi:service>
</beans>
・以下のようなmanifest.mf を書く
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Greeting Service
Bundle-SymbolicName: greetingservice
Bundle-Version: 1.0.0
Import-Package: org.springframework.beans.factory.xml,
 org.springframework.aop,
 org.springframework.aop.framework,
 org.aopalliance.aop,
 org.xml.sax,
 org.osgi.framework,
 org.springframework.osgi.service.importer.support,
 org.springframework.beans.propertyeditors,
 org.springframework.osgi.service.exporter.support,
 org.springframework.osgi.service.exporter
Export-Package: greeting.service
・jar にする
$ jar cvfm service.jar manifest.mf META-INF -C classes greeting
・念のため jar tvf service.jar で、怪しいところがないか見ておく

◆ Greeting Service Client

※以下、c:\work2\client\下の作業 ・以下のようなソースを書いて、適切なファイル名で適切なディレクトリに置く

package greeting.client;

import greeting.service.GreetingService;

public class GreetingClient {
   private GreetingService greetingService;

   public void setGreetingService(GreetingService greetingService) {
      this.greetingService = greetingService;
   }
   public void removeService() {
      System.out.println("service removed");
      this.greetingService = null;
   }
   public void start() {
      System.out.println(greetingService.greet("Client"));
   }
}
・サービス側と同様に、spring 定義ファイルと、manifest.mf を書く。 greetingclient.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans 
                      http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean name="greetingClient"
        class="greeting.client.GreetingClient" init-method="start">
      <property name="greetingService" ref="greetingService"/>
  </bean>
</beans>
greetingclient-osgi.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:osgi="http://www.springframework.org/schema/osgi"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
                      http://www.springframework.org/schema/beans/spring-beans.xsd
                      http://www.springframework.org/schema/osgi
                      http://www.springframework.org/schema/osgi/spring-osgi.xsd">
    <osgi:reference id="greetingService" interface="greeting.service.GreetingService"/>
</beans>
manifest.mf
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Greeting Service Client
Bundle-SymbolicName: greetingclient
Bundle-Version: 1.0.0
Import-Package: org.springframework.beans.factory.xml,
 org.springframework.aop,
 org.springframework.aop.framework,
 org.aopalliance.aop,
 org.xml.sax,
 org.osgi.framework,
 org.springframework.osgi.service.importer.support,
 org.springframework.beans.propertyeditors,
 org.springframework.osgi.service.importer,
 org.springframework.osgi.service.exporter.support,
 greeting.service
・コンパイル
$ javac -cp ../service/service.jar -d classes greeting/client/GreetingClient.java
・jar にする
$ jar cvfm client.jar manifest.mf META-INF -C classes greeting
以上、service.jar とclient.jar が作成され、デプロイできる状態になった。

■ 動かす

◆ Greeting Service ・インストールは以下のような感じ

-> install file:../service/service.jar
Bundle ID: 18
・start 18 で以下の出力。ちゃんと bean 定義の init-method で指定した start() が呼ばれたのがわかる。
-> start 18
-> Greeting Service registered

◆ Greeting Service Client ・インストールは以下のような感じ

-> install file:../client/client.jar
Bundle ID: 19
・開始すると、以下のように Greeting Service と協働しているのが分かる。
-> start 19
-> Hello Client

■ まとめ

  • 参考にした記事は→「OSGi と Spring:第 2 回
  • 正直、上の記事のアプリを思い通りに動かすまでちょっと苦労したけど、おかげで大分 Felix + Spring DMに慣れた。
  • 最初は何かと面倒だけど、バンドルが増えてくると段々と効果が出てくるのだと思う。特に POJO でサービスを書く事による、テスト効率向上に期待できそう。
  • 今回使ったSpring DM は 1.x で、Java 1.4ベースという事になる。Tiger ベースの2.x系があるらしい
  • 今回やってみて、やはり Spring の XML がちょっと面倒くさい。Spring DM 2.x 系は未見だけど、できれば EoD の方向でアノテーション・ベースになってくれてたら嬉しい。

0 件のコメント:

コメントを投稿