Lotus Connections を使用した教育産業向けのよりスマートなコラボレーション: パート1: Lotus Connections と RESTful Web アプリケーションの統合

Lotus Connections プロフィールを使用して著者別にパブリケーションをトラッキング

本記事では、IBM Lotus Connections を REST ベースの Web サービスと統合し、製品を教育産業独自に即して拡張する方法を説明しています。

Vladislav Ponomarev, Software Engineer, IBM

Photo of Vladislav PonomarevVladislav Ponomarev is a Web technology solutions team leader at IBM Russian Software and Technology Lab. His major areas of expertise include Java EE applications, virtualization, and cloud computing.



Carl Osipov, Software Architect, IBM

Photo of Carl OsipovCarl Osipov is an experienced software architect with the Strategy and Technology organization in IBM Software Group. His skills are in the area of distributed computing, speech application development, and computational natural language understanding. He has published and presented on topics including service-oriented architecture and conversational dialog management to peers in the industry and academia. His current focus is on business analytics and optimization.



2010年 12月 24日

要約: XML および JSON API をサポートする RESTful Web アプリケーションを使用して、IBM Lotus Connections の機能を拡張しましょう。Lotus Connections のプロフィール・ユーザー・インターフェースは、Dojo Toolkit に基づくカスタム・ウィジェットです。この Web アプリケーションによって、大学教授が自らのパブリケーションをソーシャル・ネットワーク・プロフィール・ページで共有できます。アプリケーションのさらなるカスタマイズにより、教授のプロフィールから他の情報 (研究助成金や教育コースなど) を共有できます。

このシリーズの記事では、高等教育機関 (単科大学や総合大学) を対象としたコラボレーションのユース・ケースを説明し、IBM Lotus Connections を他の IBM 製品と同様、拡張やカスタマイズにより、教育産業特有のコラボレーション・シナリオを強化する技術的なガイドを提供します。また、このシリーズでは、IBM 製品のビジネス分析および最適化の機能に焦点を当て、Lotus Connections ユーザーが潜在的なコラボレーターを見つけて共同作業を進める上で、よりスマートな意思決定を行えるようにします。教育産業の IT (情報技術) リーダーやストラテジストなど、組織におけるコラボレーション・ソリューションに携わる人々にとって、このシリーズの内容が価値のあるものになることを望んでいます。

シリーズ最初の記事では、IBM Lotus Connections を REST ベースの Web サービスと統合し、製品を教育産業独自に即して拡張する方法を説明します。後半の記事では、拡張された Lotus Connections 機能を使用して、主任研究員がスキルに基づくコラボレーション・グループの識別や研究員のパブリケーションのコンテンツ分析を行うプロセスを最適化し、コラボレーション・プロフィールおよび他のトピックを強化します。

はじめに

IBM Lotus Connections® 2.5 (Lotus Connections) のプロフィール・アプリケーションは、カスタム・ウィジェットを使用して拡張できます。ウィジェットのユーザー・インターフェース (UI) と コードがプロフィールにバンドルされている場合、カスタマイズは比較的シンプルなプロセスです。しかし、カスタム・プロフィール・ウィジェットが Lotus Connections の外部で管理されているデータを表示および操作する場合は、外部 API を持つ個別のアプリケーションと Lotus Connections の統合が必要になることがあります。

この記事の最初のパートでは、Web アプリケーションの機能と要件の概要について説明します。次に、使用されているテクノロジーに進み、アプリケーションのアーキテクチャーを説明します。最後に、インプリメントについて説明し、アプリケーションで使用されているセキュリティー・メカニズムについてもカバーします。

この記事の 2 番目のパートはクライアント・サイドの、iWidget インプリメンテーションを取り上げます。ユーザーに提供されるウィジェット機能の概要を説明し、ウィジェット UI の構成方法と Dojo Toolkit を用いた構築方法を明らかにします。

最後に、コンポーネント (プロフィール、Web アプリケーション、およびウィジェット) を相互に統合する方法を示します。統合するために必要な Lotus Connections 構成ファイルの例も用意されています。


RESTful Web アプリケーション

プロフィールのユーザー (教育機関や研究機関の教授など) が、作成したパブリケーションのリストを維持するために役立つ Web アプリケーションについて説明します。このアプリケーションは機能を HTTP ベースの REST インターフェースを通じて公開し、JavaScript を使用するブラウザーや他の潜在的なサード・パーティー・ソフトウェアからアプリケーションへの通信を容易にします。アプリケーションは、次の要件を満たします。

  1. REST インターフェースの要件は次のとおりです。
  • XML でフォーマットされた要求メッセージを受け取り、JSON 形式および XML 形式でレンダリングされた応答をサポートする。
  • パブリケーションの作成、取得、更新、削除 (CRUD) の各オペレーションを提供する。
  1. 各パブリケーションに対し、次のデータを保持する必要があります。
  • 自動生成 ID
  • パブリケーションの所有者
  • タイトル
  • 公開された年
  • 著者
  • ジャーナル
  • パブリッシャー
  1. 認証されていないユーザーには、パブリケーションを変更するオペレーションを禁止する必要があります。
  2. 認証されたユーザーには、自分が所有しているパブリケーションのみ変更を許可する必要があります。
  3. アプリケーションは、コードを再コンパイルすることなく、(パブリケーションに加え) 新しい REST エンティティーをサポートするために、容易に拡張可能であることが必要です。たとえば、各プロフィール・ユーザーの助成金のリストを維持するアプリケーションであれば、コードを変更せずに残し、アプリケーションのアセンブリー、デプロイメント、およびデータベース・スキーマの変更だけに開発作業を限定しなければなりません。

使用されているテクノロジー

IBM Lotus Connections 2.5 のインストール済み環境には IBM WebSphere® Application Server (WebSphere) 6.1 のインスタンスが含まれているため、アプリケーションの実行には後者を使用します。 WebSphere 6.1 は、Java™ Servlet 2.4 (JSR 154) および Enterprise JavaBeans™ (EJB) 2.1 (JSR 153) を含む J2EE 仕様のセットをインプリメントしています。

このアプリケーションは、JSR 154 準拠のサーブレットをサービス・エンドポイントとして使用します。要件のところで示したように、XML メッセージをサーブレットに受け入れるために、doGet(), doPost(), doPut(), および doDelete() の各メソッドをインプリメントする必要があります。

WebSphere 6.1 JRE ではアノテーション対応の JAXB (XML シリアライゼーションおよびデシリアライゼーションの標準) を利用できないため、Java 6 JAXB ライブラリーを使用してサーブレットでの XML サポートを有効にしました。JSON のサポートについては、今回のインプリメンテーションは WebSphere Feature Pack for Web 2.0 の一部として提供されている JSON4J ライブラリーに基づいています。永続性をサポートするために、オブジェクト - リレーショナル・データベース・マッピングのプログラミング・モデルを有効にする JSR 153 エンティティー Beans を使用します (詳細については、「Appendix B: EJB pros and cons」を参照してください)。

アプリケーションのアーキテクチャーの概要

アプリケーションのアーキテクチャーを 図 1 に示します。

図 1. アプリケーションのアーキテクチャー
アプリケーションのアーキテクチャー

図 の左側に示されている UI によって、パブリケーションへの CRUD オペレーションを実行するための HTML グラフィカル・コントロールがユーザーに提供されます。Web ブラウザー内でのユーザー・アクションによってオペレーションがトリガーされるたびに、アプリケーションの JavaScript が REST インターフェースを通じて HTTP 要求をアプリケーションのサーバー・サイドの部分 (図 中央) に渡します。UI をプロフィール・ページにレンダリングできるようにするために、HTML マークアップおよび JavaScript は Lotus Connections 互換の iWidget 形式でパッケージ化されています。

図 の中央の部分は、サーブレットをホスティングする JEE コンテナー、エンティティー Bean (EJB)、および補助コンポーネントを表しています。ブラウザーから受信した HTTP 要求はセキュリティー・フィルターを通過し、REST サーブレットの適切なメソッド (doPost() メソッドなど) に渡されます。次に、サーブレットはファサード・オブジェクトを通じて適切な CRUD オペレーションをエンティティー Bean コンポーネント上に呼び出します。ファサードは、エンティティー Bean によって提供されるきめ細かいインターフェースからクライアント・コードを隠し、クライアントが軽量 POJO (Plain Old Java Objects) を扱えるようにします。最後に、サーブレットが応答を適切なフォーマットにレンダリングし、要求の処理が完了します。

図 1 の右端の部分は、データベース・サーバーにデプロイされたデータベース・テーブルを示しています。JEE コンテナーとデータベースは論理的に分離されたように表示されていますが、手法的に同じ場所に存在することがあります。それでは、図 2 に示されたデータベースの構造を見ていきましょう。

図 2. データベースの構造
データベースの構造

PUBLICATION データベース・テーブル (詳細は 図 2 参照) は、Lotus Connections の PEOPLEDB データベースの一部として作成されます。プロフィールとアプリケーションで共通のデータベースを保持する利点の 1 つは、外部キーの制約 ( 図 2 参照) を PUBLICATION テーブルの OWNERID 列に課し、EMPLOYEE テーブルの 1 次キー列を参照させるようにできることです。

それでは、POJO オブジェクトの階層から始めて、POJO と EJB の関係を見ていきましょう ( 図 3 参照)。

図 3. ドメイン・オブジェクトの階層
ドメイン・オブジェクトの階層

Tルート・クラス DomainObject は、1 つの ID を持つドメイン・オブジェクトおよび個別のアクセサー・メソッドを表します。下位クラスの OwnedDomainObject (図 の左下) は、所有者 ID フィールドを追加します。Publication オブジェクトは OwnedDomainObject を継承し、パブリケーション固有のフィールドを追加します。これらのクラスは JAXB アノテーション付きであり、XML との間のオブジェクトのシリアライゼーションおよびデシリアライゼーションを可能にします。

図 4 のエンティティー Bean を参照してください。

図 4. エンティティー Bean の階層
エンティティー Bean の階層

各 POJO は、対応するエンティティー Bean を持っています ( 図 4 の左側を参照)。さらに、Bean インターフェース継承ツリーは、POJO のツリーと類似しています。つまり、DomainObject は対応する DomainLocal インターフェースと DomainLocalHome インターフェースを持ち、OwnedDomainObject も同様です ( 図 4 の右側を参照)。 Bean インターフェースは、Java 5 Generics 機能を使用して、次のように構築されます。

  • Bean を POJO に変換するために使用される toPojo() ファクトリー・メソッドを持つクライアント・コードを提供します。
  • POJO を引数として受け取る update() メソッドおよび create() メソッドを定義します。

専用のファサード・オブジェクトはエンティティー Bean をクライアント・コードから隠し、CRUD オペレーションをエンティティー Bean に実行します。エンティティーが Publication エンティティーと同様に作成されていれば (つまり、同じ継承ルールに従う場合)、ファサード ( 図 1 参照) は registerDomainObject() メソッドを通じて登録することにより、任意のエンティティーをサポートできます。

アプリケーションの開始時に、REST サーブレットは web.xml ファイルから取得した構成パラメーターを使用し、アダプター・オブジェクトを作成して構成します。このため、アプリケーション・アセンブラー (EJB 2.1 仕様の第 3.1.2 章で定義された JEE 役割) によって、アプリケーションがコードを再コンパイルせずに新しいエンティティーを扱えるようなります。REST サービス・オブジェクトには、HTTP 要求を特定のエンティティー・タイプにマッピングする役割があります。

データ・アクセス・レイヤー

Publication POJO は、前のセクションの 図 3 に従ってインプリメントされます。クラス・フィールドが @XmlElement のようにアノテーションを付けられることにより、XML との間で JAXB を使用したオブジェクトのシリアライズおよびデシリアライズが可能になることに注意してください。

エンティティー Bean は、ネットワーク越しに Bean にアクセスされるのを防ぐために、ローカル・インターフェースのみ公開します。リスト 1 に、エンティティーのローカル・インターフェースの定義を示します。

リスト 1. ローカル・インターフェース
public interface DomainLocal<D extends DomainObject>
{
 String getId();
 void setId(String newId);
 
 void update(D domainObject);
 D toPojo();
}

public interface OwnedDomainLocal<D extends OwnedDomainObject> extends DomainLocal<D>
{
 String getOwnerId();
 void setOwnerId(String ownerId);
}

public interface PublicationLocal extends OwnedDomainLocal<Publication>, EJBLocalObject
{
 public java.lang.String getTitle();
 public void setTitle(java.lang.String newTitle);

 public java.lang.String getJournal();
 public void setJournal(java.lang.String newJournal);

 public java.lang.String getPublisher();
 public void setPublisher(java.lang.String newPublisher);

 public java.lang.Integer getYearPublished();
 public void setYearPublished(java.lang.Integer newYear);

 public java.lang.String getInternetLink();
 public void setInternetLink(java.lang.String newLink);

 public void setAuthors(String authors);
 public String getAuthors();
}

階層のルートである DomainLocal インターフェースによって、エンティティーが自分自身を POJO に変換するファクトリー・メソッドを持たなければならないことが規定されています。また、POJO インスタンスを受け取る update() メソッドが、きめ細かな mutator メソッド ( setXXX() メソッド) へのファサードとして機能します。1回のオペレーションですべてのフィールドを設定できるように、このメソッドを使用してエンティティーの状態を変更するとよいでしょう。

ローカル・ホーム・インターフェースは、同じ階層を反映しています。これらのインターフェースによって、エンティティーを作成および検索するメソッドが定義されます。

リスト 2. ローカル・ホーム・インターフェース
public interface DomainLocalHome<D extends DomainObject, L extends DomainLocal<D>>
{
 Collection<L> findAll()
 throws FinderException;
}

public interface OwnedDomainLocalHome
 <D extends OwnedDomainObject, L extends OwnedDomainLocal<D>>
 extends DomainLocalHome<D, L>
{
 Collection<L> findByOwner(String ownerId)
 throws FinderException;
}

public interface PublicationLocalHome
 extends OwnedDomainLocalHome<Publication, PublicationLocal>, EJBLocalHome
{
 public PublicationLocal create(Publication pub) // Return type is local interface!
 throws CreateException; 

 public PublicationLocal findByPrimaryKey(String primaryKey)
 throws FinderException;
}

Java 言語の Generics 機能がコード全体で広範囲に使用されているため、クライアント内でクラスをキャストする必要性が最小限になります。しかし、Generics 機能を使用することには、いくつかの問題点があります。

EJB 仕様により、create() メソッドおよび findByPrimaryKey() メソッドの戻り値の型がエンティティーのインターフェースに制限されます リスト 2 に示すコード・スニペットでコメントが付けられた行を参照してください)。インプリメンテーション・オブジェクトは JEE コンテナーによってランタイムに生成されるため、ジェネリック情報が利用できないときは、findByOwner()で行ったように、 create() を階層の上方に移動してジェネリックにすることができません。

Java Generics の消去機能は、具体的な POJO クラス情報の型を隠します。このため、ローカル・インターフェースの update() メソッドおよび toPojo() メソッドのインプリメンテーションは、共通の祖先 (つまり、DomainObject) に基づきます。

リスト 3 に、メソッドのインプリメンテーションを示します。

リスト 3. Publication Bean のジェネリック・メソッドのインプリメンテーション
public abstract class PublicationBean implements javax.ejb.EntityBean
{
 public String ejbCreate(Publication pub)
 throws javax.ejb.CreateException
 {
 setId(pub.getId());
 setAuthors(pub.getAuthors());
 setInternetLink(pub.getInternetLink());
 setJournal(pub.getJournal());
 setOwnerId(pub.getOwnerId());
 setPublisher(pub.getPublisher());
 setTitle(pub.getTitle());
 setYearPublished(pub.getYearPublished());
 
 return null;
 }

 public void ejbPostCreate(Publication pub)
 throws javax.ejb.CreateException {
 }

 public void update(DomainObject domainObject)
 {
 Publication pub = (Publication) domainObject;
 
 setAuthors(pub.getAuthors());
 setInternetLink(pub.getInternetLink());
 setJournal(pub.getJournal());
 setOwnerId(pub.getOwnerId());
 setPublisher(pub.getPublisher());
 setTitle(pub.getTitle());
 setYearPublished(pub.getYearPublished());
 }
 
 public DomainObject toPojo()
 {
 Publication pub = new Publication();
 pub.setId(getId());
 pub.setAuthors(getAuthors());
 pub.setInternetLink(getInternetLink());
 pub.setJournal(getJournal());
 pub.setOwnerId(getOwnerId());
 pub.setPublisher(getPublisher());
 pub.setTitle(getTitle());
 pub.setYearPublished(getYearPublished());
 
 return pub;
 }
...
}

これで EJB がインプリメントできたので、ファサード・オブジェクトが必要になります。ファサード・オブジェクトは、最初に、JNDI (Java Naming and Directory Interface) 検索コードを隠し、次に、個別の POJO クラスの観点から、クライアントが REST エンティティー (Publication など) を扱えるようにします。ファサードは、自分自身のインターフェースに CRUD メソッド群を持つ必要があります ( リスト 4 参照)。

リスト 4. ファサード・インターフェースのメソッド
public <D extends DomainObject> Collection<D> find(Class<D> domainClass)
public <D extends DomainObject> D find(Class<D> domainClass, String id)
public <D extends DomainObject> void create(D domainObject)
public <D extends DomainObject> void remove(Class<D> domainClass, String id)
public <D extends DomainObject> void update(D domainObject)
public <LH extends DomainLocalHome<D, ?>, D extends DomainObject>
void registerDomainObject(
 Class<LH> localHomeClass,
 Class<D> domainClass,
 String jndiName,
    LocalInterfaceCaller<?, LH, D> caller)

リスト内の最後のメソッドの目的は、ファサード・オブジェクトに新しい REST エンティティーを認識させることです。

エンティティーの登録の例

Publication REST エンティティーが、ファサード内でどのように登録されるのかを以下に示します。

service.registerDomainObject(
PublicationLocalHome.class, Publication.class, "ejb/Publication", new PublicationCaller());

ファサード・オブジェクトによって、クライアント・コードは具体的な POJO クラス (Publication など) と基本クラス (DomainObject または OwnedDomainObject)の両方を扱えるようになります。ただし、もう1つの障害があります。ファサードは、ベース・インターフェースではなく、具体的なホーム・インターフェース ( PublicationLocalHome など) に属する create() メソッドおよび findByPrimaryKey() メソッドを呼び出さなければなりません。これを回避するには、registerDomainObject() メソッドを通じて新規 REST エンティティーを登録するときに、クライアント・コードで、前述のホーム・インターフェースへの呼び出しをカプセル化する追加オブジェクトを渡す必要があります ( リスト 5参照)。

リスト 5. 呼び出し元のインプリメンテーションのサンプル
public class PublicationCaller implements 
    CrudService.LocalInterfaceCaller<PublicationLocal, PublicationLocalHome, Publication>
{ 
    public void callCreate(PublicationLocalHome localHome, 
        Publication domainObject) throws CreateException { 
        localHome.create(domainObject); 
    } 

public PublicationLocal callFindByPrimaryKey( PublicationLocalHome localHome, String id) 
    throws FinderException { 
        return localHome.findByPrimaryKey(id); 
    }
}

LocalInterfaceCaller の Generic 型のパラメーターは、create() メソッドおよび findByPrimaryKey() メソッドを提供する具体的なローカル・インターフェースおよびローカル・ホーム・インターフェースで置換されます。

EJB に関連するコードを隠すことに加え、(ドメイン・オブジェクトの階層とともに) ファサード・オブジェクトには別の利点もあります。つまり、EJB 3 への移行を簡素化することができます。POJO は JPA アノテーションが付けられて (Java Persistence API)、エンティティー Bean になり、ファサード・インプリメンテーションは、次のバージョンの EJB 仕様を反映するように変更されます。クライアント・コードは影響を受けないことに注意してください。

図 5. ファサード・オブジェクト・クラスの図
ファサード・オブジェクト・クラスの図

ファサード・オブジェクトのインプリメンテーションについては、サンプルのソース・コードを参照してください。サンプルへのリンクは ダウンロード にあります。

セキュリティー・メカニズム

セキュリティーのインプリメンテーションを理解するために、どのような脅威に対しアプリケーションを保護するのかをまとめましょう。

  1. 変更オペレーション (POST、PUT、および DELETE という HTTP verbs で表されます) は、認証されたユーザーにのみ許可する必要があります。
  2. 認証されたユーザーには、ユーザー自身が所有しているパブリケーションの変更のみ許可する必要があります。

Lotus Connections で使用されている認証メカニズムは、各 HTTP 要求の Cookie とともに運ばれる LTPA (Lightweight Third-Party Authentication) トークンに基づいています。アプリケーション UI はプロフィールに統合されているため、REST サーブレットに送られる要求には同じトークンが存在するものと期待できます。WebSphere Application Server はトークンに基づいてユーザー ID を検出するため、アプリケーションは HttpServletRequest クラスで定義された getRemoteUser() メソッドを呼び出すことによって認証をチェックします。ユーザーが認証されていると、このメソッドはユーザーのログイン名を返します。それ以外の場合、この呼び出しは null を返します。

セキュリティー・メカニズムをインプリメントするために、サーブレットの前でフィルターを使用して各要求をインターセプトします (図 6 参照)。フィルターは、上記に示した方法で、ユーザーが認証されているかどうかを判断します。ユーザーが認証されておらず、HTTP メソッドが GET 以外の場合は、要求の処理が終了し、HTTP ステータス 401 (無許可) が返されます。HTTP メソッドが GET の場合は、制御フローがサーブレットへと転送されます。最後に、認証されたユーザーが変更オペレーションを実行しようとすると、フィルターはユーザーのログイン名を取り出し、データベースに照会してユーザーの Lotus Connections ID を取得し、それをセッション・オブジェクト内にキャッシュします。ユーザーが一度認証されると、フィルターはそれ以降の各呼び出しに対して何も行わないため、ID の取得は 1 回だけとなります。

図 6. 認証フィルター
Diagram of the authentication filter

変更オペレーションの HTTP 要求がサーブレットに到達すると、ユーザー ID がセッション・オブジェクトにキャッシュされるため、この ID とターゲット REST エンティティー (この場合は Publication) の所有者 ID を比較できます。この許可チェックをカプセル化するために、上記のファサード・オブジェクト上にアダプターが開発されました。アダプターはファサードへの参照を保持します。後者はアダプターで定義された CRUD メソッドを反映するパブリック・メソッドで宣言されますが、ユーザー ID を追加の引数として使用します。REST エンティティーの所有者 ID がユーザー ID と比較され、一致しない場合は、許可の例外がスローされます。

XML と JSON のサポート

JAXB ライブラリーがクラス・パスにあり、POJO オブジェクトにアノテーションが付けられている限り、XML はアプリケーション内でサポートされ、すぐに使用可能です。そのプロセスは容易で、ドメイン・クラスを JAXBContext オブジェクト内で登録し、そのファクトリー・メソッドの createMarshaller() および createUnmarshaller() を使用して、XML シリアライゼーションおよびデシリアライゼーション可能なドメイン・オブジェクトを取得することと同様です。

JSON のサポートを有効にするために、WebSphere Feature Pack for Web 2.0 の一部として提供されている JSON4J ライブラリーが使用されます。JSON 形式での出力は、dojo.data.ItemFileReadStore コンポーネントの要件に適合するように調整されます。つまり、返されるオブジェクトは、identifier、label、 および items の各属性を指定します。

サーブレットは XML メッセージを受け取り、HTTP 要求の Accept ヘッダーまたは as 要求パラメーターに応じて、XML 形式または JSON 形式のどちらかで応答をレンダリングします。要求パラメーターの方が、ヘッダーよりも優先されます。このロジックは、オブジェクト・シリアライゼーションを担う XML または JSON レンダラーを作成するサーブレットの専用ファクトリー・メソッドとしてカプセル化されています。下の 図 7 を参照してください。

図 7. Renderer クラスの図
Renderer クラスの図

REST サーブレットのインプリメンテーション

REST サーブレットのライフサイクルは init() メソッドから始まります。このメソッドは、web.xml で定義されたパラメーターに基づいてサーブレットの初期化を処理します。

初期化パラメーターの CrudConfigurators には、CrudServiceConfigurator インターフェースをインプリメントするクラスのコンマ区切りリストが含まれています。これらのオブジェクトは、引数のないコンストラクターを持っていなければなりません。インスタンス化のあと、CRUD ファサード・インスタンスを受け取る各オブジェクトの configure() メソッドが呼び出されます。各コンフィギュレーターは、REST エンティティーのセットをファサード内に登録します。

同様に、restConfigurators パラメーターには、RestServiceConfiguration インプリメンテーションのコンマ区切りリストが含まれています。処理は crudConfigurators の場合と同じです。RestService オブジェクトは、基本的に要求パスと特定のドメイン・クラス間の対応を確立する役割を持っています。たとえば、DELETE /rest/publication/123 要求は、Publication エンティティーに対して実行する必要があります。各コンフィギュレーターは、REST エンティティーの固有のセットを RestService オブジェクト内に登録します。

この構成メカニズムと前に説明したファサード・オブジェクトにより、コードを再コンパイルせずに、Publication の設計アプローチに従う任意のエンティティーのサポートを追加することが容易になります。新規の各エンティティーに対し、アプリケーション・アセンブラーは、新規コンフィギュレーター・オブジェクトをデプロイメント・ディスクリプター内のサーブレット初期化パラメーターに追加し、これらをドメイン・クラスとともに Web アプリケーションにバンドルする必要があります。

web.xml の構成が完了すると、サーブレットは HTTP 要求を処理する準備が整います。下の 表 1 に、サーブレットによって提供される REST API をまとめます。簡潔にするために、URL の <host>:<port> の部分が省略されています。

表 1. サーブレットの REST API
HTTP メソッドURIパラメーター
GET/tonkawaWeb/rest/publication
[?ownerId=<ownerId>]
[&as={xml|json}]
POST/tonkawaWeb/rest/publication[?as={xml|json}]
DELETE, PUT/tonkawaWeb/rest/publication/<id>[?as={xml|json}]

処理が成功すると、GET 要求の応答ボディにはパブリケーションのリストが含められ、POST の応答には 1 つのパブリケーション (データベースに保存されるパブリケーション) が含められます。PUT または DELETE の応答には、確認メッセージだけが含められます。例外が発生した場合、応答ボディには、HTTP ステータス・コードとオプションのエラー・メッセージが適切なレンダリング形式で含められます。

リスト 6 に、HTTP 要求の例を示します。

リスト 6. HTTP POST 要求のサンプル
POST /tonkawaWeb/rest/publication
Accept: application/json

<?xml version="1.0" encoding="UTF-8"?>
<publication>
 <id></id>
 <title>Publication title</title>
 <internetLink>http://mysite.com</internetLink>
 <authors>John Doe</authors>
 <journal>My Journal</journal>
 <publisher>Publisher and Co</publisher>
 <yearPublished>2010</yearPublished>
 <ownerId>6d14080a-e26e-40d3-8588-9b7b4011e65d</ownerId>
</publication>

要求の処理を 図 8 のシーケンス図に示します。コンポーネントが相互に接続されています。

図 8. HTTP POST の処理

クリックして大きなイメージを見る

図 8. HTTP POST の処理


iWidget

アプリケーションのユーザー・インターフェースは、プロフィール UI に含まれている iWidget 1.0 コンポーネントによってインプリメントされます。ユーザー・インターフェースは、Lotus Connections 2.5 とともに提供される Dojo Toolkit JavaScript ライブラリー・バージョン 1.2.3 に基づいています。出版ウィジェットには、以下の機能があります。

  • Lotus Connections ユーザー用の匿名アクセス用プロフィール・ページに、パブリケーションのリストを表示します。
  • クライアント・サイドの検証ルールを実施しながら、パブリケーションを作成または更新します。
  • パブリケーションを削除します。

iWidget の編集ボタンは、ユーザーがユーザー自身のプロフィールを表示しているときにのみ利用できます。

UI の構造

iWidget UI は、3 ペインの Dojo スタック・コンテナーによって構成されています。最初のペインには、パブリケーションのリストが読み取り専用形式でユーザーに表示されます ( 図 9a を参照)。

図 9a. パブリケーションのリスト

ユーザーが自分自身のプロフィールを表示すると、個々のパブリケーションを選択して編集可能な次のペインに切り替えることができます(図 9b を参照)。

図 9b. パブリケーションの詳細

このペインには、「 Add 」、「 Edit 」、および「 Remove 」の各ボタンがあります。「 Add 」ボタンまたは「 Edit」ボタンをクリックすると、編集フォーム・ペインが表示されます。このフォームには、タイトル、リンク、著者、ジャーナル、パブリッシャー、年、および選択されたパブリケーションの各フィールドがあります (図 9c)。

図 9c. パブリケーションの編集

フォームの入力コントロールには、基本的な検証ルール ( 後述の 「 入力の検証 」 セクションを参照 ) が適用されます。

インプリメンテーション

仕様によると、iWidget は、コールバック・ハンドラーの定義がある JavaScript クラス (この場合は DwPublication クラス) と HTML マークアップを含む XML ファイルで構成されます。

図 10 に、UI コンポーネントどうしの対話、および UI コンポーネントとサーバーとの対話がどのように行われるかを示します。

図 10. iWidget コンポーネントの対話

パブリケーション iWidget を最初にロードするとき、その onLoad() コールバック・ハンドラーが実行され ( 図 10 のステップ 1 )、さらに HTML マークアップが Dojo によって解析されるため、ウィジェットがブラウザーにレンダリングされます。次に、Dojo データ・ストア・オブジェクト (dojo.data.ItemFileWriteStore) のインスタンスを生成し、サーバー上でパブリケーションのリストを照会するようインスタンスに指示することにより、DwPublication クラスがパブリケーションのリストを取り出します (2)。 個別の DOM ノードを構築するコールバック関数にデータ・ストアが渡され、読み取り専用リストが表示されます (2.1)。 ユーザーが「Edit publications 」リンクをクリックすると、DwPublication クラスが新規 DataGrid オブジェクトを作成し、グリッドがある編集パネルを表示します (3)。 グリッドは、ステップ (2) で作成されたデータ・ストアからのコンテンツによって生成されます。

編集パネルには、「Create」「Edit および 「Remove」の各ボタンがあります。最初の 2 つのボタンのいずれかをクリックすると、データを入力する編集フォームがユーザーに表示されます。フォームを送信すると、制御フローが転送されて DwPublication に戻され、パブリケーションを作成または更新する HTTP POST または PUT 要求が送信されます。

Remove」ボタンをクリックした場合は、DwPublication によって、データベースからパブリケーションを削除する HTTP DELETE 要求が送信されます。

この iWidget インプリメンテーションにより、正しい更新動作が確実に実行され、Lotus Connections によって有効になった機能が最大限に活用されます。プロフィールの Web ページが完全にロードまたは再ロードされ、iWidget が最大化された状態になると、Dojo によって追加された HTML ボディの onLoad() イベント・ハンドラーによって、Dojo ウィジェットを作成するためにページ上の HTML マークアップが自動的に解析されます。一方、iWidget がコンテキスト・メニューを通じて更新されると (または、最小化された iWidget がページのロード後に最大化されると)、iWidget の DOM ツリーが破棄され、iWidget 定義の一部として指定された XML ファイルに基づいて再構築されます。この場合は解析が行われず、手動で呼び出す必要があります。また、更新イベントまたは最大化イベントの前に作成された Dojo ウィジェットは、メモリー・リークを防ぐために破棄される必要があります。

図 11 に、これらのフローの違いを示します。DwPublication クラスの onLoad() コールバック・ハンドラーおよび onUnload() コールバック・ハンドラーは、古いウィジェットをクリーンアップし、HTML マークアップを再び解析する必要がある場合に呼び出されます。

図 11. iWidget の (再)ロードの動作
iWidget の (再)ロードの動作

ローカライズのサポート

UI の完全なローカライズを有効にするために、特定ノードの innerHTML プロパティーを、リソース・バンドルから取り出したローカライズ済みストリングに設定するよう設計された、いくつかのカスタム Dojo ウィジェットがインプリメントされています。最初に、label プロパティーをサポートするミックスイン (mix-in) を開発します ( リスト 7 参照)。

リスト 7. 'label' プロパティーをともなうミックスイン
dojo.provide("com.ibm.tonkawa.i18nInnerHtmlMixin");

dojo.declare(
 "com.ibm.tonkawa.i18nInnerHtmlMixin",
 null,
 {
 label: {},
 _defaultLabel: "$unset$",

 buildRendering: function()
 {
 this.inherited(arguments);
 this.containerNode.innerHTML = this.label ? this.label : this._defaultLabel;
 }
 }
);

ミックスインを配置した状態で、上位階層のリスト内で i18nInnerHtmlMixin を指定することにより、新たに宣言された Dojo ウィジェットに label プロパティーを追加できます ( リスト 8 のコード・スニペットを参照 )。buildRendering() メソッドのデフォルトのインプリメンテーションをオーバーライドして、DOM ノードを必要としているものの中に label プロパティーの値を入力することができます。

リスト 8. LocalizedLabel ウィジェット
dojo.provide("com.ibm.tonkawa.LocalizedLabel");

dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.require("com.ibm.tonkawa.i18nInnerHtmlMixin");

dojo.declare(
 "com.ibm.tonkawa.LocalizedLabel",
 [dijit._Widget, dijit._Templated, com.ibm.tonkawa.i18nInnerHtmlMixin],
 {
 templateString: "<label for=\"${forLabel}\" dojoAttachPoint=\"containerNode\"></label>",
 forLabel: ""
 }
);

最後に、リスト 9 に示されるように、ローカライズされた Dojo ウィジェットがHTML マークアップで使用されます。

リスト 9. HTML マークアップでのローカライズ済みウィジェットの宣言
<script>
dojo.requireLocalization("com.ibm.tonkawa", "Publication", null, "en,ru,ROOT"); myBundle = dojo.i18n.getLocalization("com.ibm.tonkawa", "Publication"); </script> ... <div forLabel="publicationYearPublished" dojoType="com.ibm.tonkawa.LocalizedLabel" label="myBundle.labelYearPublished"/>

この場合は、リソース・バンドルから labelYearPublished リソース・ストリングが取り出され、カスタム・ウィジェットの label プロパティー値として設定されます。

プロフィール と Dojo

特定の Dojo コントロールをプロフィールで使えるようにするために、インプリメンテーションの工夫が行われています。 まず、id 属性が定義された <div> ブロック内にコンテナーを配置する必要があります。次に、dojox.grid.DataGridを正しく表示するために、マークアップの解析の前に、CSS ファイルのコレクションをロードしなければなりません。これは、<link type=”text/css”> ノードを構築してページ・ヘッダーに追加する JavaScript コードによって行われます。

入力の検証

アプリケーションのほとんどすべての Dojo ウィジェット ( diji.form.SimpleTextarea を除く) は、ユーザー入力サポートの検証機能を受け入れます。したがって、Publication 編集フォームをインプリメントするために、これらのウィジェットが選択されました。また、テキスト領域のギャップを埋めるために追加のウィジェット com.ibm.tonkawa.ValidationTextarea, が開発され、HTML textarea 要素が検証のサポートによって強化されます。簡潔に言うと、dijit.form.ValidationTextBox ウィジェットと dijit.form.SimpleTextarea ウィジェット、およびオーバーライドされたonBlur() コールバック・ハンドラーと onFocus() コールバック・ハンドラーの組み合わせによって、検証が実行されます。

サーバーに送信する XML メッセージを作成するとき、より小さい (<) またはより大きい (>) などの特定の文字が、XSS 攻撃によるインジェクションを防ぐためにエスケープされます。同様に、これらの文字はHTML ページにレンダリングされるときも、エスケープされます。


付録 A: プロフィールとの統合

カスタム iWidget をプロフィール・アプリケーションに追加するすべてのプロセスは、IBM Lotus Connections インフォメーション・センター に記載されています。プロフィールの停止、ウィジェット構成ファイルのチェックアウト、iWidget 定義および配置の指示による構成ファイルの拡張、構成ファイルのチェックインなどを行います。リスト 10 に、widgets-config.xml ファイルの例を示します。

リスト 10. プロフィールのウィジェット定義および配置設定
<widgetDef
 defId="dwPublication"
 bundleRefId="tonkawaBundle"
 url="{contextRoot}/../tonkawaWeb/widget/dwPublication.xml?version={version}"/>
…
<widgetInstance uiLocation="col2" defIdRef="dwPublication"/>

オプションとして、カスタム・リソース・バンドルを登録し、ローカライズ用ストリングを iWidget のタイトルやメニュー項目に提供できます。バンドルは、プロパティー・ファイルが含まれるディレクトリーを zip したもので、その定義が Lotus Connections の構成ファイル (LotusConnections-config.xml) に追加されます。例については、リスト 11 を参照してください。

リスト 11. リソース・バンドルの定義
<resources>
 <!-- Bundle is located in the ext-i18n-resources/tonkawa.zip file -->
 <localZipFile file="tonkawa.zip">
 <bundle bid="tonkawaBundle" name="tonkawa.widgets.resources"/>
 </localZipFile>
…
</resources>

最後に、プロフィール・アプリケーションが開始します。


付録 B: EJB の長所と短所

この記事で説明したアプリケーションの場合は、EJB を使用することで次の利点を得られます。

  • デプロイメント環境からの独立。EJB プログラミング・モデルにより、開発者は基礎となるミドルウェアから分離されます。
  • EJB コンポーネントの再利用性。プログラミング・モデルの仕様によって JEE 準拠のコンテナーに必要な API が規定されており、コンポーネント・コードの変更を最小限に抑えながら、最も適したミドルウェア固有の要件を選択できます。
  • JEE プラットフォームとの統合。JDBC (Java Database Connectivity)、JMS (Java Message Service)、JCA (J2EE Connector architecture) など、関連する成熟したテクノロジーにより、EJB コードで仕様に沿った明確な方法で活用できるサービスが提供されます。
  • 分散トランザクションのサポート。
  • 明快なスケーラビリティー。

EJB 2.1 の複雑さはよく知られています。たとえば、複数インターフェース (ローカル、リモート、ホーム)、JNDI 検索コード、および JEE コンテナー特有のパラメーターに基づく XML デプロイメント・ディスクリプターは、開発者の負担になります。この記事で取り上げたアプリケーションでは、上記で強調した利点が、複雑さによる欠点を上回っています。また、次世代の EJB 仕様である EJB 3.0 によって、複雑さの問題の多くが解決されます。新しい仕様のサポートは、WebSphere 6.1 上に EJB 3.0 Feature Pack をインストールすることで有効にできます。このフィーチャー・パックをどの Lotus Connections デプロイメント環境にもインストールすることは受け入れられない可能性があるため、この記事では、永続レイヤーのインプリメンテーションとして EJB 2.1 を説明しました。しかし、「データ・アクセス・レイヤー」セクションで説明した特定のテクニックを使用すると、EJB 3 への移行を簡素化できます。


まとめ

この記事では、カスタム REST Web アプリケーションを作成し、これを IBM Lotus Connections プロフィールに統合するためのアプローチを説明しました。アプリケーションによってプロフィールが拡張され、特定ユーザーのパブリケーション・リストを維持する機能が得られます。アプリケーションは Lotus Connections のルック・アンド・フィールに調和したユーザー・フレンドリーな UI を持ち、Dojo Toolkit コンポーネント上に構築されています。データの整合性を維持し、破損を防止するために、セキュリティー制約が実施されています。さらに、アプリケーションは拡張可能で、コードを再コンパイルせずに新しいエンティティーをサポートできます。


ダウンロード

内容ファイル名サイズ
Sample implementation codesource.zip840KB

参考文献

学ぶために

製品や技術を入手するために

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Lotus
ArticleID=603293
ArticleTitle=Lotus Connections を使用した教育産業向けのよりスマートなコラボレーション: パート1: Lotus Connections と RESTful Web アプリケーションの統合
publish-date=12242010