はじめに
業務家具業界は、他の業界と同様、企業が使用するソフトウェアに盛り込まなければならない要件の変化が激しいことが特徴としてあげられます。イリノイ州シカゴの RPC Software は、自社製品の基盤にオープンソースソフトウェアを使用することで、この市場での成功を収めています。RPC Software は、競合他社よりも、早く、低コストでソリューションを提供するために、Eclipse RCP (リッチクライアントプラットフォーム)、DotProject、および SugarCRM などの技術を活用しています。この導入事例では、これらの技術の中身、この事例から得られた開発知識、および教訓を紹介します。
対象分野
RPC Software は、家具業界で使用されるERP 注文管理ソフトウェアを開発しています。RPC Software の製品が登場する以前、この業界の企業は、多くの場合、Visual Basic などのMicrosoft Visual Studio 言語でそれぞれ独自に作成したソフトウェア、DOS ベースのソリューション、および CA の Visual Objects を使用していました。こうした企業が現在求めているものは、営業、見積もり、注文入力、時間管理、倉庫業務、会計、レポーティングなど、さまざまな業務活動に包括的に対応できるソリューションです。このような要件を満たすため、ソフトウェアは、スケーラブルで堅牢なだけでなく、モジュール形式という性質を持つことが必要となってきました。
最近では、他業界と同様にこの業界でも、販売店と日常的にやり取りする卸売り業者や顧客が、情報をより透過的に入手できるようにしようとする動きがあります。この変化は、主に 2 つの形で始まりました。まず、この業界の複数の企業がOFDA-XML などのオープンなデータ交換フォーマットに移行しました。さらに、協力会社との情報共有のために、プロジェクト追跡などのビジネスプロセスが Web アプリケーションを通じて報告および公開されるようになりました。
ソリューションの概要
RPC Software の顧客は、それぞれのビジネス要件に短期間で厳密に適合させることのできるソフトウェアを求めています。また、日常業務でアプリケーションを使用する従業員向けに堅牢なクライアント機能を備えていること、さらに、別の方面から業務に関わる関係者やパートナー企業にレポーティング機能を提供できることも要件としています。業界でのこのような要件を考慮した結果、RPC Software は、ソリューションの基盤にオープンソースソフトウェアを起用することを決定しました。RPC Software の製品ラインの中心は、Eclipse RCP と Apache Tomcat を使用したリッチクライアント ERPクライアント/サーバコンポーネント、およびオープンソースの DotProject PHP アプリケーションに基づく Web ベースプロジェクト管理ソリューションです。さらに、オープンソースの SugarCRM を基盤とする Web ベース CRM 製品が近くリリースされる予定です。
プロジェクト管理、および CRM の製品については、販売店、顧客、および卸売り業者の間で大きなクライアントをインストールすることなく共有を実現する必要があるという点から、Web ベースのソリューションが採用されています。ERP 製品については、Eclipse RCP が選択されました。Eclipse RCP は、SWT/JFace ウィジェットセットによる豊富な機能性と、OSGiの基盤によって提供されるモジュール形式のインフラストラクチャを備えています。
Eclipse RCP の中心は、これが基づくOSGi 仕様です。ウィキペディアに定義されているとおり、OSGi フレームワークは以下を提供します。
このフレームワークは完全な動的コンポーネントモデルを実装します。これは、スタンドアロンの Java/VM 環境に欠けている部分です。アプリケーションまたはコンポーネント (バンドルという形式で配布される) は、リモートから、再起動をせずに、インストール、開始、停止、更新、およびアンインストールできます。このために、Java パッケージおよびクラスの管理は、極めて詳細に規定されています。ライフサイクル管理は、リモートから管理ポリシーをダウンロードすることでAPIを経由して行われます。サービスの登録によって、バンドルが新しいサービスや消滅したサービスを検出し、適切に対応できるようになっています (http://ja.wikipedia.org/wiki/OSGi を参照してください)。
CORE 製品は、クライアントと 1 つのサーバで構成されます。どちらも全体にわたって OSGi を活用しており、コードを機能的コンポーネントに編成し、サーバ層とクライアント層でそのコードを再使用しています。Eclipse RCP では、クラスおよびリソースを、OSGi バンドルを含む上位集合であるプラグインと呼ばれるjar ファイルにモジュール化することが可能です。RCP Software のクライアントは、機能分野ごとに複数のコアビジネスプラグインに分割されています。またクライアントは、Hibernate や Jasper Reportsなどのサードパーティ製の API のプラグインも使用します。CORE Business サーバも、プラグインの集合として構成されます。このため、クライアントとサーバでビジネスロジックプラグインを簡単に再使用することができます。
RPC Software は、アプリケーションのデプロイをより簡略化するため、サーバもクライアントも同じインストール環境に含めています。Eclipse RCP は、プラグイン内のXML ファイルと設定ファイルの連携によってエントリポイントを定義するという概念を取り入れています。このフレームワークは、プラグイン間の依存関係メタデータを使用して、特定のエントリポイントで何を開始する必要があるかを判断します。クライアントの場合は、クライアントのエントリポイントを使用して、通常のexe ベースのEclipse RCPスタートアッププロセスが実行されます。このクライアントスタートアップは、サーバベースの機能を提供するプラグインは除外します。これに対して、サーバとしての実行の場合は、オープンソースの JNIWrapper 製品を使用して Windows サービスが作成されます。これは、Tomcat サーバインスタンスパッケージをプラグインとして含む別のエントリポイントを使用して、Eclipse RCP インストール環境を開始します。このエントリポイントは、UI 専用ロジックや、SWT プラグインなど、クライアントに依存するものを含むプラグインは除外します。
CORE Business のクライアントは、提案書の作成、商品の請求書の読み込み、および会計など、一般的な ERP 関連作業に対応します。サーバコンポーネントは、その入力されたデータを基に Web ベースのレポートを提供し、企業の各組織のメンバが活動の概要を確認できるようにします。注文を CORE Business クライアントから入力すると、Core Vision製品のデータベースにも製品 ID が自動的に作成されます。Core Vision でスケジュールを変更すれば、それは Core Businessからも確認することができます。同様に、Core Business で CRM に変更を加えると、それは CORE CRM にも反映されます。逆方向の反映も同様に行われます。RPC Software が、2 つのデータベースの統合を提供しています。企業が、DotProject と SugarCRM など、複数の製品を別々に使用している場合、このような統合は実現されません。
Eclipse RCP クライアント
CORE Business 製品の特徴は、Eclipse RCP リッチクライアントフレームワークを起用している点です。CORE Business クライアントは、会計、管理レポート、プロジェクト価値の算出、創作や提案など、ERP で必要となる日常業務で使用されます。Eclipse RCP は、多くの点で他のテクノロジより優れていたため採用されました。顧客が要望する情報入力機能とデータサイズから考えて、Web ベースのアプリケーションは可能な選択肢ではありませんでした。Eclipse RCP および SWT 以外にも、C# および Swing などのリッチクライアントウィジェットフレームワークがいくつか検討されました。しかし、SWT のプラットフォーム・ネイティブなルック&フィールが決め手となりました。RPC Software にとっては、Eclipse RCP にもともと含まれている、ウィンドウ、メニュー、および設定などの機能性も魅力でした。
RPC Software は、OSGi および Eclipse RCP によって提供されるモジュール性も CORE Business クライアントの中で広く活用しています。顧客は、多くの場合、カスタムなレポートや計算ロジックなどを必要とします。また、時間入力(Time Entry)のように、すべてのクライアントに必要なわけではない機能もあります。RPC Software は、こうした要件に応えるために、Eclipse RCP がプラグインベースのインフラストラクチャであることを活かし、コアアプリケーションプラグインに、個々のクライアント専用のプラグインを追加して配布するという方法を採用しています。
Eclipse RCP フレームワークでは、プラグインが、自身が提供する機能を、XML ベースのフォーマットを使用してコアアプリケーションに通知できることが特徴です。たとえば、RPC Software は、この機能をカスタムレポートに使用しています。次の XML は、カスタムな発注レポートを実行時にクライアント固有の custom.plugin.*.core スタイルプラグインで追加する方法を示しています。
<extension id="xsltTransforms" point="com.rpc.core.xsltTransforms">
<xsltTransform id="com.rpc.core.vendor.model.PurchaseOrder.pdf">
<run class="com.rpc.core.reporting.DefaultTransformSourceProvider">
<parameter name="location" value="reports/PurchaseOrder.xsl" />
</run>
</xsltTransform>
</extension>
このような制御スタイルの利点は、実行されるアプリケーションに特定の機能を組み込むために必要となる設定変更およびメタ情報が、すべてカスタムプラグインの側に含まれるということです。コア プラグインや、新機能に対応するアプリケーションのメニューシステムに変更を加える必要はありません。すべては、Eclipse RCP フレームワークによって実行時に検出され、組み込まれます。
Eclipse サーバ
RPC Software は、Eclipse RPC のプラグイン構造を、CORE Business クライアントだけでなく、Tomcat ベースの CORE Business サーバでも活用しています。CORE Business サーバは、CORE Business Eclipse RCP ベースクライアントから入力されたデータを使用してレポーティング機能を提供します。CORE Business サーバの設計が進むに従って、Hibernate および Jasper Reports など、クライアントに存在するロジックおよびコンポーネントをいくつも再使用する必要があることが明らかになりました。再使用を可能にするソリューションで最もわかりやすいものは、サーバで必要となるクラスを、WAR ファイルに含まれる jar として再パッケージすることです。このソリューションの場合は、クラス構造が変わるたびに変更する必要のある、複雑なビルドスクリプトが必要となります。それよりも、Tomcat を「プラグイン対応」にする方が簡単だと考えられました。
Eclipse IDE は、ヘルプシステムのために Tomcat の組み込みのバージョンを使用しています。RPC Software は、これを基に、必要な機能を実現することができました。この時点で、RPC Software は servlet.jar ファイルを独自のプラグインに移行しました。RPC Software が作成したプラグインは、必要な場合に、このサーブレット API を使用することができます。また、既存の Tomcat プラグインは、標準 Java コンパイラではなく、Eclipse JDT コンパイラを使用するように変更しました。これにより、CORE Business は、JDK ではなく、JRE のみのバンドルで済むようになりました。最後に、JSP ページを含むプラグインが使用する classloader は、org.eclipse.core.runtime などの、必要なシステムプラグインを読み込むように変更しました。
package org.eclipse.help.internal.appserver;
public class PluginClassLoaderWrapper extends URLClassLoader {
...
/**
* This is a workaround for the jsp compiler that needs to know the
* classpath.
*/
public URL[] getURLs() {
Set urls = getPluginClasspath(_plugin);
return (URL[]) urls.toArray(new URL[urls.size()]);
}
private Set getPluginClasspath(String pluginId) {
// Collect set of plug-ins
Set plugins = new HashSet();
addPluginWithPrereqs(pluginId, plugins);
// Collect URLs for each plug-in
Set urls = new HashSet();
for (Iterator it = plugins.iterator(); it.hasNext();) {
String id = (String) it.next();
try {
Bundle b = Platform.getBundle(id);
if (b != null) {
// declared classpath
String headers = (String) b.getHeaders().get(Constants.BUNDLE_CLASSPATH);
ManifestElement[] paths =ManifestElement.parseHeader(Constants.BUNDLE_CLASSPATH, headers);
if (paths != null) {
for (int i = 0; i < paths.length; i++) {
String path = paths[i].getValue();
addBundlePath(urls, b, path);
}
} else {
// RPC custom code:
try { String bundleJarPath = b.getLocation();
if (bundleJarPath.equals(Constants.SYSTEM_BUNDLE_LOCATION)) {
SystemBundle systemBundle = (SystemBundle) b;
bundleJarPath = ((SystemBundleData) systemBundle.getBundleData()).getBundleFile().getBaseFile().toURL().getPath();
bundleJarPath =bundleJarPath.substring(bundleJarPath.lastIndexOf("plugins/"));
} else if(bundleJarPath.startsWith("initial@reference:file:")) {
bundleJarPath =b.getLocation().replaceFirst("initial@reference:file:", "");
} else {
bundleJarPath =b.getLocation().replaceFirst("update@", "");
}
if (bundleJarPath.endsWith("/")) {
bundleJarPath = bundleJarPath.substring(0, bundleJarPath.lastIndexOf("/"));
}
if (bundleJarPath.startsWith("plugins/") && bundleJarPath.endsWith(".jar")) {
URL installURL = Platform.getInstallLocation().getURL();
bundleJarPath = installURL.getPath() + bundleJarPath;
urls.add(new URL(installURL.getProtocol(), installURL.getHost(), bundleJarPath));
}
} catch (Exception ex) {
}
// RPC custom code:
}
// dev classpath
String[] devpaths =DevClassPathHelper.getDevClassPath(pluginId);
if (devpaths != null) {
for (int i = 0; i < devpaths.length; i++) {
addBundlePath(urls, b, devpaths[i]);
}
}
}
} catch (BundleException e) {
}
}
return urls;
}
// RPC custom code:
private void addBundlePath(Set urls, Bundle b, String path) {
URL url = b.getEntry(path);
if (url != null) {
try {
urls.add(FileLocator.toFileURL(url));
} catch (IOException ioe) {
}
}
}
// RPC custom code:
...
}
このようなソリューションには、従来の Java WAR 配置用セットアップに比べて多くの利点があります。まず、RPC Software は、クライアント層およびサーバ層の両方でまったく同じプラグインを使用することで、再使用性および保守性を向上させました。また、配置用セットアップの複雑性が軽減されました。war ファイルの場合、RPC Software は、各クライアントサイトに Tomcat をインストールし、配置に合わせて WAR を設定する必要がありました。サーバを、すぐに使用できる Eclipse RCP プラグインの集合として構築することで、各クライアントのインストール環境での設定およびテストは大幅に減少し、反対に信頼性は向上しました。
ブラウザの統合
Core Business アプリケーションでは、レポーティング機能の進化も注目を集めています。このアプリケーションでは、改良が進むにつれて、Apache FO、Jasper Reports、および標準的な Java 印刷 API など、多くのレポーティングオプションが顧客の要件に合わせて使用されるようになりました。レポートには、サーバベースのものと、クライアントでそのまま実行できるものとがありますが、顧客は、サーバベースのレポートも、ブラウザに切り替えることなくクライアントで表示できることを望んでいました。RPC Software は、この要望に応えるために、SWT の組み込みのブラウザコンポーネントを使用することができました。以下のようなわずかなコードによって、HTML ベースのレポートを、サーバから直接 Eclipse RPC ベースのクライアントに出力することができます。
final Browser browser = new Browser(shell, SWT.NONE);
browser.setUrl("http://eclipse.org");
クライアントには、サーバからのレポートを表示する機能だけでなく、クライアントの中で直接的にレポートの表示方法を選択できるドロップダウンコンボボックスなどの便利な機能も含まれています。動的な URL が生成され、その要求がサーバに送られます。
教訓
全体として RPC Software は、その開発要件において Eclipse RCP は非常に堅牢なフレームワークであると考えています。このフレームワークによって、RPC Software は、単一のコードベースを保持しながら、顧客のさまざまな要望に応えることができました。オープンソースであるために、不足している機能を必要に応じて強化することもできました。Java では、一般的に、Hibernate、Apache FO、および Jasper Reports などの API を使用することで、アプリケーションをより短期間で構築することが可能になります。RPC Software は、最初のアプリケーション開発に 30 か月が必要と考えていましたが、Eclipse RCP を使用したことで 14 か月で開発することができました。
RPC Software は、Eclipse を、Eclipse RCP 開発の IDE としても使用しています。PDE 開発環境では、RCP 開発を、一般的な Java 開発と同等なレベルで行うことができます。プラグイン機能サポートファイル向けのカスタムエディタが多数用意されているため、設定ファイルを手動で編集するのに比べて、入力が非常にしやすくなっています。また、RPC Software は、開発作業のスピードを上げるために、Jasper Report Generator および Eclipse TPTP プラグインなどの Eclipse IDE プラグインも活用しています。
今後の方向性
RPC Software は、今後も引き続きオープンソースソフトウェアを活用して製品を強化していく予定です。既に説明したように、リリース予定の CORE CRM 製品は、SugarCRM を使用することで、Web ベースの CRM アクセスをソリューションに追加します。また、CORE Business 製品で使用されているさまざまなレポーティング技術を、Eclipse BIRT 製品に移行することも検討されています。最後に、製品の次期バージョンに向けて、製品の更新およびベンダのカタログを、Eclipse Update サイト機能を使用して CORE Business クライアントのインストール環境に配布するための開発も行われています。
原文はこちらです:http://www.infoq.com/articles/rpc-eclipse
(このArticleは2007年1月29日にリリースされました)