GlassfishでWebアプリケーションからOSGiバンドルを利用する
NetBeans IDE の Maven を使用した OSGi 宣言型サービス
このサンプルを動かしてみました。Glassfishは通常のWebアプリケーション(war/ear)と同じようにOSGiバンドルがディプロイできます。このサンプルは、GlassfishにディプロイしたOSGiバンドルが公開する宣言型サービス(DS:Declarative Service)を、別のWebアプリケーションから利用するというものです。宣言型サービスのためのOSGiバンドルとして、
- サービスAPIバンドル
- サービスバンドル
の二つのバンドルを作成します。サービスAPIバンドルではインタフェースのみ公開し、サービスバンドルで実装クラスを公開します。WebアプリケーションはOSGiバンドルではなく通常のwar形式でディプロイします。
サービスAPIバンドルの作成
NetBeansでは、Mavenを利用してOSGiバンドル用のプロジェクトが作成できます。新規プロジェクトの作成 -> Maven OSGiバンドルを選択します。
生成されたpom.xmlは、以下のようにpackagingがbundleとなり成果物としてOSGiバンドルが作成できるようになります。
<artifactId>MavenHelloServiceAPI</artifactId> <version>1.0-SNAPSHOT</version> <packaging>bundle</packaging> <name>MavenHelloServiceAPI OSGi Bundle</name>
まず、サービスのインタフェースを以下の内容で作成します。
package com.azuki3.mavenhelloserviceapi; public interface HelloService { public String sayHello(String name); }
そして、プロジェクトのプロパティ->パッケージをエクスポート からパッケージを公開する設定にします。
これによりpom.xmlのmaven-bundle-pluginの設定に、Export-Packageの記述が追加されます。
<plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <version>2.0.1</version> <extensions>true</extensions> <configuration> <instructions> <Export-Package>com.azuki3.mavenhelloserviceapi</Export-Package> <Private-Package>com.azuki3.mavenhelloserviceapi.*</Private-Package> </instructions> </configuration> </plugin>
作成したバンドルのMANIFEST.MFは以下のようになります。
Manifest-Version: 1.0 Export-Package: com.azuki3.mavenhelloserviceapi Build-Jdk: 1.6.0_21 Bundle-Version: 1.0.0.SNAPSHOT Tool: Bnd-0.0.357 Bundle-Name: MavenHelloServiceAPI OSGi Bundle Bnd-LastModified: 1307453703590 Created-By: Apache Maven Bundle Plugin Bundle-ManifestVersion: 2 Bundle-SymbolicName: com.azuki3.MavenHelloServiceAPI Import-Package: com.azuki3.mavenhelloserviceapi
サービスバンドルの作成
このインタフェースを実装するサービスのためのプロジェクトを同様に作成します。そしてプロジェクトの依存リソースとして、
を追加します。org.apache.felix.scr.annotationsは、DSを定義するためのXMLファイルを自動生成するツール用のアノテーションです。実際のクラスは以下のようになります。@Componentと@ServiceがDSのためのアノテーションです。
package com.azuki3.mavenhelloservice.impl; import com.azuki3.mavenhelloserviceapi.HelloService; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Service; @Component(name="hello-service") @Service public class HelloImpl implements HelloService { @Override public String sayHello(String name) { return "Hello " + name; } }
次にpom.xmlに、このアノテーションを処理するためのscrプラグインを追加します。
<plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-scr-plugin</artifactId> <executions> <execution> <id>generate-scr-scrdescriptor</id> <goals> <goal>scr</goal> </goals> </execution> </executions> </plugin>
この状態でビルドするとOSGI-INFフォルダにDSの定義が記述されたserviceComponents.xmlが作成されます。
<?xml version="1.0" encoding="UTF-8"?> <components xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0"> <scr:component enabled="true" name="hello-service"> <implementation class="com.azuki3.mavenhelloservice.impl.HelloImpl"/> <service servicefactory="false"> <provide interface="com.azuki3.mavenhelloserviceapi.HelloService"/> </service> <property name="service.pid" value="hello-service"/> </scr:component> </components>
OSGiバンドルを使用するWebアプリケーションの作成
NetBeansでMaven Webアプリケーションを選択して新規プロジェクトを作成します。プロジェクトの依存リソースにサービスAPIバンドを追加しscopeをprovidedに変更します(OSGiバンドルとして利用するため)。
<dependency> <groupId>${project.groupId}</groupId> <artifactId>MavenHelloServiceAPI</artifactId> <version>${project.version}</version> <scope>provided</scope> </dependency>
次に、HelloServiceを利用するサーブレットを作成します。@Resourceアノテーションで、DSを定義する際にname属性で指定した値をmappedNameで指定します。
package com.azuki3.mavenhellowebclient; import com.azuki3.mavenhelloserviceapi.HelloService; import java.io.IOException; import java.io.PrintWriter; import javax.annotation.Resource; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(name="HelloClient", urlPatterns={"/HelloClient"}) public class HelloClient extends HttpServlet { @Resource(mappedName = "hello-service") HelloService helloService; protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); try { out.println("<h1>Servlet HelloClient at " + request.getContextPath() + "</h1>"); out.println(helloService.sayHello("Duke")); out.println("</body>"); out.println("</html>"); } finally { out.close(); } } // ...以下省略 }
OSGiバンドルのディプロイ
GlassfishへのOSGiバンドルのディプロイは以下のいずれかの方法で行います。
- asadminコマンド
- autodeployフォルダへのjarのコピー
今回はautodeployフォルダにコピーします。NetBeansからGlassfishを起動している場合、autodeployフォルダは以下の場所になります。ここに各バンドルのプロジェクトのtargetフォルダに作成されたjarファイルをコピーします。
C:\Users\ユーザー名\.netbeans\6.9\config\GF3\domain1\autodeploy\bundles
felixの管理コンソール*1から確認すると、以下のようにバンドルがActiveになっていることがわかります。また、NetBeansのGlassfishのコンソールにもログが出力されます。
-> find Hello START LEVEL 1 ID State Level Name [ 286] [Active ] [ 1] MavenHelloService OSGi Bundle (1.1.0.SNAPSHOT) [ 287] [Active ] [ 1] MavenHelloServiceAPI OSGi Bundle (1.0.0.SNAPSHOT)
warを作成してGlassfishにディプロイし、Webアプリケーションにアクセスします。
"hello duke"と表示されたら、正しくDSが呼び出されています。
まとめ
通常のWebアプリケーションから、OSGiの宣言型サービスを簡単に利用することができました。これにより、依存性解決をある程度までOSGiにゆだねることができることになり、いままでのjarによる依存性解決の問題が軽減されます。ただし、依然としてWebアプリケーション自体はOSGiベースではないので以下のような制約もあるようです。
- アプリケーション起動時に依存ライブラリが利用可能になっている必要がある
- 動的にバンドル(DS)を差し替えることができない
これは、今回の例でのHelloClientへのHelloServiceのインジェクションについての制約で、OSGi上ではうまくやってくれると思います。
次回は、WebアプリケーションでのOSGiのその他の使い方を試してみたいと思います。
今回のソースは以下にあります。
kenichiro22 / glassfish-osgi-sample / source – Bitbucket