前回は、チュートリアルに従ってサンプルアプリケーションGreenPagesを、STSからVirgoを起動して動作させるところまでやりました。
今回は、GreenPages内の各アプリケーションのOSGi Bundleとしての関係を見ていこうと思います。インポートしたプロジェクトは
- greenpages.web
- greenpages.app
- greenpages.jpa
- greenpages.db
の4つなのですが、各プロジェクトの依存関係は、以下のようになっているそうです。
webがフロントエンド、dbがDataSourceを提供…と見えるのですが、appとjpaは?jpaは名前的にJPAを使った機能を提供しそうですが。
とりあえず、OSGi BundleといえばMANIFEST.MFのようなので、各プロジェクトのMANIFEST.MFを見ていきましょう。
MANIFEST.MF from greenpages.web
Manifest-Version: 1.0 Import-Bundle: com.springsource.org.apache.taglibs.standard;version="[ 1.1.2,1.3)" Bundle-Version: 2.3.0 Tool: Bundlor 1.0.0.RELEASE Bundle-Name: GreenPages Web Import-Library: org.springframework.spring;version="[3.0, 3.1)" Bundle-ManifestVersion: 2 Bundle-SymbolicName: greenpages.web Web-ContextPath: greenpages Import-Package: freemarker.cache;version="[2.3.15,2.3.15]",greenpages; version="[2.3, 2.4)",javax.servlet.jsp.jstl.core;version="[1.1.2,1.2. 0)",javax.sql,org.apache.commons.dbcp,org.eclipse.virgo.web.dm;versio n="[2.0.0, 3.0.0)",org.springframework.beans.factory.annotation;versi on="[3.0, 3.1)",org.springframework.core.io;version="[3.0, 3.1)",org. springframework.stereotype;version="[3.0, 3.1)",org.springframework.w eb.bind.annotation;version="[3.0, 3.1)",org.springframework.web.conte xt;version="[3.0, 3.1)",org.springframework.web.servlet;version="[3.0 , 3.1)",org.springframework.web.servlet.mvc.annotation;version="[3.0, 3.1)",org.springframework.web.servlet.view.freemarker;version="[3.0, 3.1)"
MANIFEST.MF from greenpages.app
Manifest-Version: 1.0 Export-Package: greenpages;version="2.3.0" Bundle-Vendor: SpringSource Inc. Bundle-Version: 2.3.0 Tool: Bundlor 1.0.0.RELEASE Bundle-Name: GreenPages Service Bundle-ManifestVersion: 2 Bundle-SymbolicName: greenpages Import-Package: org.springframework.stereotype;version="[3.0, 3.1)"
MANIFEST.MF from greenpages.jpa
Manifest-Version: 1.0 Import-Bundle: com.springsource.org.eclipse.persistence;version="[1.0. 0, 1.0.0]",com.springsource.org.eclipse.persistence.jpa;version="[1.0 .0, 1.0.0]" Bundle-Vendor: SpringSource Inc. Bundle-Version: 2.3.0 Tool: Bundlor 1.0.0.RELEASE Bundle-Name: GreenPages JPA Bundle-ManifestVersion: 2 Bundle-SymbolicName: greenpages.jpa Import-Package: greenpages;version="[2.3, 2.4)",javax.persistence;vers ion="[1.0.0, 1.0.0]",javax.sql;version="0",org.apache.commons.dbcp;ve rsion="[1.2.2.osgi, 1.2.2.osgi]",org.springframework.beans.factory.an notation;version="[3.0, 3.1)",org.springframework.context.weaving;ver sion="[3.0, 3.1)",org.springframework.core.io;version="[3.0, 3.1)",or g.springframework.dao.annotation;version="[3.0, 3.1)",org.springframe work.orm.jpa;version="[3.0, 3.1)",org.springframework.orm.jpa.vendor; version="[3.0, 3.1)",org.springframework.stereotype;version="[3.0, 3. 1)",org.springframework.transaction.annotation;version="[3.0, 3.1)",o rg.springframework.transaction.aspectj;version="[3.0, 3.1)"
MANIFEST.MF from greenpages.db
Manifest-Version: 1.0 Bundle-Vendor: SpringSource Inc. Bundle-Classpath: . Bundle-Version: 2.3.0 Tool: Bundlor 1.0.0.RELEASE Bundle-Name: GreenPages DataSource Bundle-ManifestVersion: 2 Bundle-SymbolicName: greenpages.db Import-Package: javax.sql;version="0",org.apache.commons.dbcp;version= "[1.2.2.osgi, 1.2.2.osgi]",org.h2;version="[1.0.71, 1.0.71]"
いろいろ書いてますが、OSGi Bundleが外部にパッケージを公開するためには、「Export-Package」と書いてパッケージ名を書いていき、他のOSGi Bundleがそれを参照するためには「Import-Package」と書かねばならないらしいです。この4つのプロジェクトの中で、パッケージをエクスポートしているのはgreenpages.appプロジェクトのみなので、これがやっぱり中心にいるっぽいですね。
なお、エクスポートしているパッケージは「greenpages」です。ちょっと同梱されているソースは…と。
$ cd $GREENPAGES_HOME/solution/greenpages.app $ find ./src -type f ./src/main/resources/META-INF/MANIFEST.MF ./src/main/resources/META-INF/spring/osgi-context.xml ./src/main/resources/META-INF/spring/module-context.xml ./src/main/java/greenpages/internal/DirectoryImpl.java ./src/main/java/greenpages/internal/ImmutableListing.java ./src/main/java/greenpages/Directory.java ./src/main/java/greenpages/Listing.java
// ./src/main/java/greenpages/Directory.java public interface Directory { // ./src/main/java/greenpages/Listing.java public interface Listing { // ./src/main/java/greenpages/internal/DirectoryImpl.java public class DirectoryImpl implements Directory { // ./src/main/java/greenpages/internal/ImmutableListing.java final class ImmutableListing implements Listing {
へぇ、公開しているのはインターフェースだけですねぇ。greenpages.internalパッケージには実装しかない…ってことは、これでインターフェースと実装体を分離して、インターフェースパッケージだけエクスポートしようってこと?
続いて、greenpages.jpaプロジェクトを見てみます。
$ cd $GREENPAGES_HOME/solution/greenpages.jpa $ find ./src -type f ./src/test/resources/META-INF/spring/test-context.xml ./src/test/java/greenpages/jpa/TestDataPopulator.java ./src/test/java/greenpages/jpa/JpaDirectorySpringContextTests.java ./src/main/resources/META-INF/persistence.xml ./src/main/resources/META-INF/orm.xml ./src/main/resources/META-INF/MANIFEST.MF ./src/main/resources/META-INF/spring/osgi-context.xml ./src/main/resources/META-INF/spring/module-context.xml ./src/main/java/greenpages/jpa/JpaListing.java ./src/main/java/greenpages/jpa/JpaDirectory.java
// ./src/main/java/greenpages/jpa/JpaDirectory.java final class JpaDirectory implements Directory { // ./src/main/java/greenpages/jpa/JpaListing.java public class JpaListing implements Listing {
greenpages.appでエクスポートしたインターフェースを実装してますねぇ…。そして「Import-Package」でgreenpagesパッケージをインポートしてます、と。
つまり、greenpages.appってWeb層以下のインターフェースを規定するためのOSGi Bundleってことかな?んで、greenpages.jpaはその実装体のひとつです、と。なお、greenpages.dbにはホントにDataSourceの定義しかなかったです…。
ということは、インターフェースをかっちり決めてエクスポートして、実装体の切り替えは各Bundle内のosgi-context.xmlやmodule-context.xmlに書いておいて、SpringのDIを使ってうまいこと依存性を注入っていうのがこのプロダクトのやりたいことなのかな?しかも依存性もバージョンを指定した上でやれますよっと。
この推測が合ってるかどうかちょっとまだ自信ないですが、なんかすごそうですねぇ。DIを見ていた時は、インターフェースと実装の分離といっても実装体と大差ないインターフェースが散らばるばっかりで、あんまり意味無いなぁと思っていたのですが、こういうモジュールレベルでキレイに分離されているとちょっと感動的です。
ただ、実装体は確かにエクスポートしてませんけど、それを使う時ってどうやってんのかなぁ?実装体のクラス自体に直接アクセスできないけど、あくまでインターフェースの型を見ているだけだからOKということでしょうか…。舞台裏の繋ぎ合わせの部分がちょっとまだわかりません。