|
||||||||||||||||||||||||
SpringSource dm Serverの紹介
はじめにSpringSource dm Serverは、Springフレームワークの開発元のSpringSourceが提供するJavaのアプリケーションサーバーです。2008年9月に1.0がリリースされました。まだメジャーではありませんが、徐々に注目を集めてくると思います。今回の記事では、SpringSource dm Server(以後、dm Server)の概要を紹介します。網羅的な説明ではなく、主な特徴に絞って説明します。また、OSGI(dm Serverの基盤となる技術)を意識せずに説明しますので、OSGIの前提知識がなくても読むことができます。 dm Serverの特徴dm Serverの一番の特徴は、プログラムのデプロイの単位を細かくできる点でしょう。従来のアプリケーションサーバーはプレゼンテーション層からデータアクセス層のプログラムを一括してデプロイします。そして、その単位にコンテキストパスが割り当てられます。 デプロイ単位が異なるプログラム間での連携はできません。 dm Serverはデプロイの単位を任意に決めることができます。例えば層ごとにデプロイの単位を分けることができます。コンテキストパスはプレゼンテーション層のプログラムに割り当てます。
バンドルdm Serverにデプロイするプログラムの単位を「バンドル」と言います。各バンドルは、公開する型(クラス・インターフェース)・オブジェクトを定義します。内部的に使用する型やオブジェクトは隠蔽します。あるバンドルが他のバンドルを利用する際は、実装時に公開された型を参照し、実行時に公開されたオブジェクトを参照します。 バンドルの構成物バンドルの中身は、Javaのプログラム・マニフェストファイル・Bean定義ファイルで構成されます。
※ オブジェクトはBeanとして定義します。 dm Serverと開発ツールのインストールdm Serverを実際にインストールして動かしてみましょう。 dm Serverのインストールdm Serverを以下のサイトからダウンロードします。 http://www.springsource.com/download/dmserver コミュニティ版とエンタープライズ版がありますが、今回はコミュニティ版を使用します。ダウンロードボタンをクリックするとユーザ登録フォームが表示されます。登録の必要がなければフォーム上部の「No thanks, I'd like to go straight to the download.」をクリックします。dm Serverをダウンロードしたら任意のフォルダで解凍します。dm Serverのインストールは以上です。
開発ツールのインストール次にdm Server用の開発ツールをインストールします。開発ツールはeclipseのプラグインとして提供されています。まずはJavaEE版のeclipseが必要です。eclipseは以下のサイトからダウンロードできます。 http://www.eclipse.org/downloads/ 開発ツールのプラグインをインストールします。開発ツールのプラグインをインストールするにはSpring IDEプラグインが必要です。eclipseのSoftware Updateを使って、以下のアップデートサイトから開発ツールとSpring IDEのプラグインをインストールして下さい(2・3分要します)。
http://static.springsource.com/projects/sts-dm-server/update
http://springide.org/updatesite
開発ツールの設定開発ツールのGUIを使ってdm Serverの起動・停止やバンドルのデプロイを行うことができます。開発ツールの設定方法を説明します。 SpringSource配下の「SpringSource dm Server v1.0」を選択します。 インストールしたdm Serverのフォルダを指定します。 「Finish」を押下します。Serverビューに「SpringSource dm Server・・・」が表示されることを確認します。 開発ツールの設定は以上です。 dm Serverの起動Serversビューで「SpringSource dm Server・・・」を選択し、スタートボタンを押下するとdm Serverが起動します。 サンプルプログラムサンプルプログラムを作ってみましょう。以下のような書籍データの一覧をHTMLで表示するWebアプリケーションを作ります。 バンドルの構成を以下に示します。 各バンドル毎のクラス一覧を以下に示します。
バンドルの作成dtoバンドルから作ってみましょう。はじめにバンドルプロジェクトを作成します。ファイルメニューからNew → Other... → SpringSource dm Server → Bundle Projectを選択します。 プロジェクト名に「sample-dto」と入力し、Next → Finiesh ボタンを押下するとプロジェクトが作成されます。 プロジェクトのプロパティダイアログを開き「Targeted Runtime」パネルで「SpringSource dm Server・・・」を選択しOKを押下します。 srcフォルダ配下のMETA-INF/MANIFEST.MF ファイルがマニフェストファイルです。ファイルを開き、エディタ下部の「MANIFEST.MF」タブを押下するとファイルの中身が表示されます。Bundle-SymbolicNameがバンドルの識別子で、Bundle-Versionがバンドルのバージョンです。 sample.dtoパッケージを作成しBookクラスを作成します。Bookクラスのソースを以下に示します。
public class Book {
private Long id;
private String title;
private String auther;
private String isbn;
private Integer price;
--getter、setterメソッドの記述は省略--
}
マニフェストファイルに公開するパッケージを記述します。Export-Packageヘッダを使用し、sample.dtoパッケージを指定します。 次にdaoバンドルを作ります。sample-dtoと同様にバンドルプロジェクトを新規に作成します。プロジェクト名は「sample-dao」にします。 daoバンドルはdtoバンドルを参照しますので、プロジェクトのプロパティダイアログを開き「Project References」パネルでsample-dtoプロジェクトを選択します。 「Targeted Runtime」パネルで「SpringSource dm Server・・・」を選択します。 sample.daoパッケージにBookDaoインターフェースを作成します。BookDaoインターフェースのソースを以下に示します。
public interface BookDao {
List<Book> getAllBookList();
}
BookDaoインターフェースの実装クラスを作成します。sample.dao.implパッケージにBookDaoImplクラスを作成します。BookDaoImplクラスのソースを以下に示します。
public class BookDaoImpl implements BookDao {
private int count = 0;
@Override
public List<Book> getAllBookList() {
List<Book> list = new ArrayList<Book>();
Book b = new Book();
b.setId(1L);
b.setTitle("タイトル1 count="+count);
b.setAuther("著者1");
b.setIsbn("ISBN xxx-xxx 1");
b.setPrice(1000);
list.add(b);
b = new Book();
b.setId(2L);
b.setTitle("タイトル2 count="+count);
b.setAuther("著者2");
b.setIsbn("ISBN xxx-xxx 2");
b.setPrice(2000);
list.add(b);
b = new Book();
b.setId(3L);
b.setTitle("タイトル3 count="+count);
b.setAuther("著者3");
b.setIsbn("ISBN xxx-xxx 3");
b.setPrice(3000);
list.add(b);
count++;
return list;
}
}
便宜的にデータをハードコーディングで作成しています。また、「count」フィールドを保持しメソッドが呼ばれる度にインクリメントしています。countの値を書籍のタイトルに付加しています。
daoバンドルは他のバンドルに対しDAOのオブジェクトを公開します。META-INFフォルダ配下にspringフォルダを作成しBean定義ファイルを作成します(Bean定義ファイルのファイル名は任意です)。今回はbean.xmlというファイル名で作成しました。中身を以下に示します。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:osgi="http://www.springframework.org/schema/osgi"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
">
<osgi:service ref="bookDao"
interface="sample.dao.BookDao" />
<bean id="bookDao" class="sample.dao.impl.BookDaoImpl"/>
</beans>
Springフレームワークを利用してプログラムを作成するときと同様にbeanタグでオブジェクトを定義します。osgi:serviceタグで公開するオブジェクトを指定しています。 特に入力せずにNextを押下します。 Finishを押下するとプロジェクトが作成されます。 プロジェクトのプロパティダイアログを開き「Project References」パネルでsample.dtoプロジェクトとsample.daoプロジェクトを選択します。 「Targeted Runtime」パネルで「SpringSource dm Server・・・」を選択します。 マニフェストファイルを以下のように記述します。 Import-Bundleヘッダで、sample_dtoバンドルとsample_daoバンドルの他にサーブレットAPIのバンドル(com.springsource.javax.servlet)とJSTLのバンドル(com.springsource.org.apache.taglibs.standard)を指定しています。こららのバンドルはSpringSourceがサードパーティのライブラリをバンドルの形式にしたものです。他にもStrutsやiBATISなどのバンドルもあります。 以下にlistBook.jspファイルのソースを示します。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
本の一覧
<hr>
<table width="60%" style="background-color:white" border="1" cellpadding="2" cellspacing="0">
<tr><th>ID</th><th>タイトル</th><th>著者</th><th>ISBN</th><th>値段</th></tr>
<c:forEach items="${bookList}" var="book">
<tr><td>
<c:out value="${book.id}"/>
</td><td>
<c:out value="${book.title}"/>
</td><td>
<c:out value="${book.auther}"/>
</td><td>
<c:out value="${book.isbn}"/>
</td><td>
<c:out value="${book.price}"/>
</td></tr>
</c:forEach>
</table>
</html>
sample.webパッケージにListBookControllerクラスを作成します。ListBookControllerクラスのソースを以下に示します。
public class ListBookController extends AbstractController {
BookDao bookDao;
public void setBookDao(BookDao dao) {
bookDao = dao;
}
String viewName;
public void setViewName(String name) {
viewName = name;
}
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest req,
HttpServletResponse res) throws Exception {
List<Book> result = bookDao.getAllBookList();
ModelAndView mav = new ModelAndView(viewName);
mav.addObject("bookList", result);
return mav;
}
}
Spring MVCのAPIを使用した作りになっています。Spring MVCを利用したことがない方にとっては違和感があるかもしれませんが、何をやってるかが大体読み取れれば十分です。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:osgi="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
">
<osgi:reference id="bookDao" interface="sample.dao.BookDao" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<bean name="/listBook.go" class="sample.web.ListBookController">
<property name="bookDao" ref="bookDao" />
<property name="viewName" value="listBook" />
</bean>
</beans>
Spring MVCを利用した記述になっています。Spring MVCを利用したことがない方にとっては違和感があるかもしれませんが、何をやってるかが大体読み取れれば十分です。sample_webバンドルはsample_daoバンドルが公開するbookDaoオブジェクトを参照します。<osgi:reference>タグでbookDaoオブジェクトを指定し、ListBookControllerクラスのオブジェクトにDIしています。 バンドルのデプロイdm Serverにバンドルをデプロイします。dm Serverが起動していることを確認してください。ServerビューのSpringSource dm Server・・・を右クリックして、「Add and Remove Projects...」を選択します。 参照される側のバンドルから順番にデプロイする必要があります。ダイアログで順番を指定することができないので、順番に沿って1つづつ追加する必要があります。順番はsample_dtoバンドル・sample_daoバンドル・sample_webバンドルの順です。左側のパネルでsample_dtoを選択し「Add」を押下して右側のパネルに移し「Finish」を押下します。
--(日付など)-- Deployment of 'sample_dto' version '1' completed. と表示されればsample_dtoのデプロイは成功です。 --(日付など)-- Deployment of 'sample_dao' version '1' completed. --(日付など)-- Creating web application '/bookstore'. --(日付など)-- Starting web application '/bookstore'. --(日付など)-- Deployment of 'sample_web' version '1' completed. と表示されればデプロイは成功です。 動作確認ブラウザを開き、 http://localhost:8080/bookstore/listBook.go にアクセスします。以下の画面が表示される事を確認します。 異なるコンテキストパスのバンドル間で同じバンドルを共有する従来のアプリケーションサーバは、コンテキストパスが異なるプログラム間で連携できません。dm Serverでは異なるコンテキストパスのバンドル間で同じバンドルを共有することができます。ですのでコンテキストパスが異なるプログラム間で連携できることになります。 sample_web_extは3000円未満の書籍の一覧を表示します。sample_web_extバンドルもsample_dao・sample_dtoバンドルを参照します。プロジェクト名は「sample-web-ext」にします。sample_webバンドルの時と同様にモジュールタイプがWebのプロジェクトを作成し、「Targeted Runtime」と「Project Reference」を設定します。 MODULE-INFフォルダの構成を以下に示します。 filterBook.jspファイルのソースを示します。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
フィルタされた本の一覧
<hr>
<table width="60%" style="background-color:white" border="1" cellpadding="2" cellspacing="0">
<tr><th>ID</th><th>タイトル</th><th>著者</th><th>ISBN</th><th>値段</th></tr>
<c:forEach items="${bookList}" var="book">
<tr><td>
<c:out value="${book.id}"/>
</td><td>
<c:out value="${book.title}"/>
</td><td>
<c:out value="${book.auther}"/>
</td><td>
<c:out value="${book.isbn}"/>
</td><td>
<c:out value="${book.price}"/>
</td></tr>
</c:forEach>
</table>
</html>
sample.web_extパッケージを作成しFilterBookControllerクラスを作成します。FilterBookControllerクラスのソースを以下に示します。
public class FlterBookController extends AbstractController {
BookDao bookDao;
public void setBookDao(BookDao dao) {
bookDao = dao;
}
String viewName;
public void setViewName(String name) {
viewName = name;
}
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest arg0,
HttpServletResponse arg1) throws Exception {
List<Book> result = bookDao.getAllBookList();
List<Book> filtered = new ArrayList<Book>();
for (Book book : result) {
if (book.getPrice() < 3000) {
filtered.add(book);
}
}
ModelAndView mav = new ModelAndView(viewName);
mav.addObject("bookList", filtered);
return mav;
}
}
Bean定義ファイル(src/META-INF/spring/bean.xml)を示します。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:osgi="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
">
<osgi:reference id="bookDao" interface="sample.dao.BookDao" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<bean name="/filterBook.go" class="sample.web_ext.FlterBookController">
<property name="bookDao" ref="bookDao" />
<property name="viewName" value="filterBook" />
</bean>
</beans>
sample_web_extの作成は完了です。デプロイします。 コンソールに --(日付など)-- Deployment of 'sample_web_ext' version '1' completed. と表示されればデプロイ成功です。 http://localhost:8080/bookstore-ext/filterBook.go にアクセスします。以下の画面が表示される事を確認します。 タイトルに埋め込まれたcountの値が0ではないことに注目してください。この値はsample_daoバンドルのオブジェクトが保持しているものです。sample_webバンドルで使用されてるsample_daoバンドルのオブジェクトがsample_web_extバンドルでも使われるためcountは初期値の0ではありません。 http://localhost:8080/bookstore/listBook.go と http://localhost:8080/bookstore-ext/filterBook.go を交互にアクセスするとcountが共有されてるのがわかります。これにより異なるコンテキストパスのバンドル間で同じバンドルが共有されているのが確認できました。 バージョン違いのバンドルを共存させるdm Serverは異なるバージョンのバンドルが共存させることができます。今回はsample_daoバンドルのバージョン違いで2つ共存させ、sample_webバンドルとsample_web_extバンドルにバージョン違いのsample_daoバンドルを使用させます。 eclipseのProject Explorerでsample-daoプロジェクトをコピー&ペーストします。プロジェクト名はsample-dao-v2にし、OKを押下します。 BookDaoImplクラスで書籍のタイトルを設定している箇所を以下のように修正します。
b.setTitle("タイトル1 count="+count);
b.setTitle("タイトル1 count="+count+" v2");
※ タイトル2・3についても同じです。
Bundle-Version: 1.0.0
Bundle-Version: 2.0.0 バンドルをデプロイします。 コンソールに --(日付など)-- Deployment of 'sample_dao' version '2' completed. と表示されればデプロイ成功です。
Import-Bundle: sample_dao, sample_dto,
Import-Bundle: sample_dao;version="2.0.0", sample_dto, sample-web-extプロジェクトの「Project Reference」を以下のように設定します。 (sample-daoプロジェクトのチェックを外しsample-dao-v2にチェック) sample_web_extバンドルが自動的に再デプロイされます。 コンソールに --(日付など)-- Deployment of 'sample_web_ext' version '1' completed. と表示されれば再デプロイ成功です。 http://localhost:8080/bookstore-ext/filterBook.go にアクセスすると以下の画面が表示されます。 書籍のタイトルの値から、新しいバージョンのsample_daoバンドルが参照されたのが確認できます。 http://localhost:8080/bookstore/listBook.go にアクセスすると以下の画面が表示されます。 書籍のタイトルの値から、古いバージョンのsample_daoバンドルが参照されたのが確認できます。 バージョン違いのsample_daoバンドルが共存できていることになります。 再デプロイ時の他のバンドルへの影響バンドルの再デプロイ時に他のバンドルに影響がでてくるケースがあります。 逆に再デプロイするバンドルが他のバンドルのオブジェクトを(Bean定義ファイルでDIして)参照していた場合、再デプロイ後も参照が引き継がれます。 メリットdm Serverを使用するメリットを開発面・運用面から考えたいと思います。 開発面のメリットバンドル間で公開する型を制限するので、カプセル化(隠蔽化)が促進され祖結合なプログラムを作成しやすくなることが期待できます。別の開発チームやサードパーティが作成したライブラリを読み込むと従来はすべてのクラスが参照できてしまいましたが、バンドルの場合は公開された型しか参照できないように規制されます。開発ツールでは公開された型しかコードアシストしません。
運用面のメリット実行中のプログラムを停止せずに再デプロイできる点があげられます。例えば、以下のようなバンドルの構成でプログラムが実行されているとします。 マスタデータメンテナンスwebバンドルを修正して再デプロイする際、オンラインwebバンドルを停止する必要はありません。
さいごにデプロイの単位を細かくできるdm Serverの特徴は魅力的です。しかし、導入する際は従来の開発・運用方法と勝手が違うため負担も多いと思います。開発面では、バンドル毎に効率よく分業して開発するためにバンドル間のインターフェースを早めに決めなければなりません。インターフェース駆動の開発は理想的ですが、実際の適用は簡単ではないと思います。また、単体テストの際、参照するバンドルの実装クラスが公開されていなければモッククラスを作らなければなりません。理想的にはそうあるべきですが、実際には生産性を低くするケースもあると思います。運用面では、特定のバンドルを変更した後のシステムテストの方法を考慮する必要があります。全体への影響がないことを確認するために本番環境と同じ環境をテスト用に用意できればよいですが、用意できないケースもあると思います。上記のような懸念への施策を確立することが今後の課題だと思います。 |
||||||||||||||||||||||||
|
©2009 現場指向
|
||||||||||||||||||||||||