レベル: 初級 Michael Wanderski (mwanders@us.ibm.com), Advisory Software Engineer, IBM Corp, IBM
2002年 12月 01日 この記事では、まずポートレットおよびWebサービスのプログラミングについて検討し、段階的な実例と共にこれら2つのテクノロジーを使用する方法を説明します。
ポータルおよびポートレット
ポータル・ベースのテクノロジーにより、簡単に使用できる統合インターフェースを用いてWebアプリケーションを前面に出すことができます。また、ポータルによって、必要な情報および表示の好みに基づいて、各ユーザーのアクセスを個人に合わせて調整することもできます。個別設定したアクセスの例としては、企業の株価情報の公開、購読者へのニュースの提供、および各部門の要求に合わせたアプリケーションの使用などがあります。
ポータルは、従来からMicrosoft Internet Explorer (IE) またはNetscape NavigatorといったWebブラウザー、あるいはユーザーに対して同時に複数のアプリケーションを表示するクライアントを用いて表示されます。ポータルを用いて、JavaアプレットやMacromedia Flashのようなリッチ・メディアを使用するクライアント・サイドの処理を行うことができます。したがって、ポータルWeb開発者は、コンテンツやアプリケーションをユーザーに提供するために使用できる広範なツール群が利用できます。ポータル・アプリケーション、つまりポートレット は、さまざまなアプリケーション・プログラム・インターフェースおよびポータル・ベースのサービスを通じて総合的なポータル・フレームワークへとシームレスに統合されます。IBM WebSphere Portal Serverの画面サンプルを図1 に示します。
図1. WebSphere Portal Serverのサンプル
この記事では、ポートレットおよびWebサービスのプログラミングをご紹介します。少なくとも、IBM WebSphere Portalプログラミングの経験をある程度積まれた方がこの記事を読まれることを想定しています。Webサービスの設計に関する基本的な知識があると有益です。まず、ポートレットおよびWebサービスのプログラミングについて検討してから、段階的な実例と共にこれら2つのテクノロジーを使用する方法を説明します。
ポートレットのプログラミング・サンプル
以下の単純なポートレット・サンプルを見ながら、ポートレットのAPIについて検討しましょう。このサンプルでは、当日の残り時間を時分単位で表示します。ポートレットAPI、およびポータル・サーバー内でのポートレット配置に関する知識がまったくない場合、WebSphere Portal InfoCenter (参考文献を参照) の資料を参照し、API、各タグ、おより関連タグ値についての詳細な説明をお読みください。
package com.ibm.wps.sample.time;
import java.io.*;
import java.util.*;
import org.apache.jetspeed.portlet.*;
import org.apache.jetspeed.portlets.*;
/** * RemainingTimePortlet.java
* Simple example to display the remaining hours and minutes in the day.
*/
public class RemainingTimePortlet extends AbstractPortlet {
/** * Main method called by the portal server to render the portlet
* display.
* @param request The portlet request object.
* @param response The portlet response object.
* @exception PortletException If an unexpected exception occurs.
* @exception IOException If an I/O exception occurs.
*/
public void service(PortletRequest request, PortletResponse response)
throws PortletException, IOException {
// Get the remaining hours and minutes.
Calendar calendar = Calendar.getInstance();
int hours = 24 - calendar.get(Calendar.HOUR_OF_DAY), minutes = 0;
if (calendar.get(Calendar.MINUTE) > 0) {
hours -= 1;
minutes = 60 - calendar.get(Calendar.MINUTE);
}
PrintWriter writer = response.getWriter();
writer.println("<p>The day will be over in " + hours + " hours and " + minutes + " minutes.</p>");
}
}
|
このポートレットは、当日の残り時間を時分単位で表示し、ポートレットが更新されると常にこの時間も更新されます。ここでは、基本クラス・インターフェースであるAbstractPortlet が拡張されて、新規ポートレット・アプリケーションが作成されることが示されています。サーブレットと同様、ポートレットには、ポートレット・ウィンドウ用のコンテンツを戻すためにオーバーライドしなければならないサービス・メソッドがあります。このサンプルでは、時間と分を判別し、応答PrintWriterへの参照を取得して、HTMLマークアップを出力ストリームに送り、図2 のように残り時間を表示します。
図2. 残り時間ポートレット
アプリケーションのインプリメンテーションを、標準的なオブジェクト指向の設計パターンのいずれかを用いて構成すると、優れた設計になります。アプリケーション・インターフェースのそのような設計パターンの1つとして、Model-View-Controller (MVC) 原理があります。MVC設計パターンの詳細な説明はここでは避けますが、簡単に言えば、このパターンではインターフェース設計にモジュール性、スケーラビリティー、および明瞭さを持たせることができます。(MVCに関する詳細は、参考文献を参照してください。)以下の図3 で示すとおり、このパターンは基本的な3つのコンポーネントに分割することができます。
-
モデル
- アプリケーションの状態およびデータ・モデルを保守するメイン・コンポーネント。
-
ビュー
- ユーザーに対するデータ・モデルのグラフィカル表現。
-
コントローラー
- 自身のモデルおよびビュー間の橋渡し。基盤データを操作する。
図3. Model-View-Controller (MVC)
先ほどのサンプルをもう一度以下に示します。今度はMVC設計を使用しています。(一部の構成、たとえばTimeBeanなどは、このサンプルでは余計だと思いますが、これらはより複雑な対話式インターフェースを作成する際に使用する基本設計パターンを示します。)
package com.ibm.wps.sample.time;
import java.io.*;
import java.util.*;
import com.ibm.wps.portlets.*;
import org.apache.jetspeed.portlet.*;
import org.apache.jetspeed.portlets.*;
/**
* RemainingTimeController.java
* Controller class for providing the right view for the portlet.
*/
public class RemainingTimeController extends AbstractPortletController {
/** The View for HTML devices (e.g. Internet Explorer, Netscape). */
protected String jspHTML = "/WEB-INF/time/html/";
/**
* Method for initializing the portlet.
* @param config The portlet configuration object.
* @exception UnavailableException If an unexpected error occurs.
*/
public void init(PortletConfig config)
throws UnavailableException {
super.init(config);
// Load the HTML View JSP name from the portlet configuration.
jspHTML += config.getInitParameter("view.HTML");
}
/**
* Method for rendering the portlet view.
* @param request The portlet request object.
* @param response The portlet response object.
* @exception PortletException If an unexpected exception occurs.
* @exception IOException If an I/O exception occurs.
*/
public void doView(PortletRequest request, PortletResponse response)
throws PortletException, IOException {
// Get the remaining hours and minutes.
Calendar calendar = Calendar.getInstance();
int hours = 24 - calendar.get(Calendar.HOUR_OF_DAY), minutes = 0;
if (calendar.get(Calendar.MINUTE) > 0) {
hours -= 1;
minutes = 60 - calendar.get(Calendar.MINUTE);
}
// Create a bean to pass the remaining time to the View JSP.
TimeBean timeBean = new TimeBean();
timeBean.setHours(hours);
timeBean.setMinutes(minutes);
// Insert bean into the request for the JSP to use.
request.setAttribute("timeBean", timeBean);
// Call the View JSP to render the display.
getPortletConfig().getContext().include(jspHTML, request, response);
}
}
|
package com.ibm.wps.sample.time;
import java.io.*;
/**
* TimeBean.java
* A bean used to pass data to the JSP View.
*/
public class TimeBean implements Serializable {
/** The remaining hours. */
private int hours = 0;
/** The remaining minutes. */
private int minutes = 0;
/**
* Set the remaining hours.
* @param hours The remaining hours.
*/
public void setHours(int hours) {
this.hours = hours;
}
/**
* Get the remaining hours.
* @return The remaining hours.
*/
public int getHours() {
return hours;
}
/**
* Set the remaining minutes.
* @param minutes The remaining minutes.
*/
public void setMinutes(int minutes) { this.minutes = minutes;
}
/**
* Get the remaining minutes.
* @return The remaining minutes.
*/
public int getMinutes() {
return minutes;
}
}
|
<!---------------------------------------------------------------------->
<!- DisplayRemainingTime.jsp -->
<!- A View JSP used to render the data for the remaining time portlet -->
<!---------------------------------------------------------------------->
<%@ page contentType="text/html" errorPage="" %>
<jsp:useBean id="timeBean"
class="com.ibm.wps.sample.time.TimeBean"
scope="request"/>
<p>The day will be over in <%= timeBean.getHours() %> and
<%= timeBean.getMinutes() %> minutes.</p>
|
まず、このサンプルの重要なコンポーネントについて説明します。クラスが、AbstractPortlet クラスを拡張しないという点に注目してください。ただし、ここでは代わりに、ポートレット・コントローラーの抽象を提供するAbstractPortletController クラスを拡張します。このオブジェクトは、AbstractPortlet クラスと同じセットアップ・メソッド(init、login、logout、およびdestroyメソッド) を定義します。
次に、クラスがサービス・メソッドを直接オーバーライドしなくなっている点に注目してください。その代わりに、特定のポートレット・モードに関連しているメソッドをオーバーライドしています。ポートレットには、以下の4つのモードのオペレーションがあります。
- Portlet.Mode.VIEW
- Portlet.Mode.EDIT
- Portlet.Mode.HELP
- Portlet.Mode.CONFIGURE
これらのモードのオペレーションは、それぞれの名前で指示されます。それぞれのモードごとに、AbstractPortletController クラスは、オーバーライド可能なレンダリング用のメソッドを提供します。これらのメソッドは、doView、doEdit、doHelp、およびdoConfigureです。
それでは、ポートレットが呼び出されてVIEWモードを表示する際に、何が起こるかを検証します。まず、現在時刻に対する残り時間が判別されます。次に、TimeBeanが作成され、表示に必要な時間と分の値が与えられます。その後TimeBeanは、ポートレットrequestオブジェクトのsetAttribute メソッドを使用して、現在の要求に付加されます。これにより、VIEW JavaServer Pages (JSP) コンポーネントが、上記のコードで示されているとおり、useBean JSPタグを使用して要求からBeanを取得できるようになります。最後に、ポートレット用にHTMLマークアップを生成するJSPコンポーネントが、表示を行うために呼び出されます。
ポートレットの配置ディスクリプターおよびポートレットのパッケージ化についての詳細は、参考文献の「WebSphere Portal InfoCenter」を参照してください。
Webサービスの概要
Webサービス という用語は、e-businessサービスが作成、公開、および発見されるフレームワークを示します。これらのサービスは、分散コンピューティング・トポロジーに配置され、動的に呼び出すことができます。図4 で示されるとおり、完全に配置されたWebサービス・アーキテクチャーには3つの基本的な参加者があります。
図4. Webサービスの参加者
サービス・プロバイダーは、タスクを実行するオペレーションのコレクションとして記述されているサービスをインプリメントし、そのサービスを発行して使用します。これらのサービスは、指定した基準に基づくサービス・レジストリーを通じて動的に発見され、最終的には、サービス・リクエスターによって起動されます。Webサービスの例としては、本などの商品の注文や、高度で複雑な数学演算用メソッドの提供などのサービスがあります。Webサービスの動的性質により、サービスの消費者およびプロバイダーが、リアルタイムで流動的かつ柔軟な環境で連動することができます。
Webサービス・アーキテクチャーのインプリメンテーションは、前述のサービス参加者の疎結合を許可するXML規格のスタックに基づいています。Webサービス・アーキテクチャーの基礎を形成する基本的なXMLテクノロジーは、以下のものです。
-
SOAP
- Simple Object Access Protocol (SOAP) は、非集中、あるいは分散環境において情報を交換するために用いられる軽量プロトコルです。
-
WSDL
- Webサービス記述言語 (WSDL) は、ネットワーク・ベースのサービスを、ドキュメント指向およびプロシージャー指向の両方のメッセージ用のオペレーションのセットとして記述するための方式です。
-
UDDI
- Universal Description, Discovery and Integration (UDDI) は、動的なサービスの統合を目的として、サービスの記述および発見を行うための、プラットフォームに依存しないレジストリーです。
これは、Webサービス・アーキテクチャーを構成する、さらに増加中の規格のほんの一例に過ぎません。詳細は、参考文献を参照してください。
幸運なことに、これらのXMLテクノロジーの詳細を完全に理解しなくとも、Webサービスが提供する機能の恩恵を得ることができます。IBM WebSphere Application Serverには、数多くのXML規格のインプリメンテーションが組み込まれています。これらには、使いやすい、クラス・レベルのプログラム・インターフェースが用意されています。またIBMは、IBM WebSphere Studio Application Developerといった上位クラスのツールへの統合向けのWeb Services Toolkit (WSTK) も提供しています。これにより、読者は独自のWebサービスを容易に作成できます。ダウンロードに関する情報は、参考文献を参照してください。
この記事では、Webサービス・アーキテクチャーのサービス・プロバイダーの側面についてのみ説明します。次のセクションでは、Application Serverにおいて使用できるSOAP APIを用いてサービスの作成および配置方法について説明します。一度配置したら、このサービスはサービス・リクエスターの呼び出しによって使用できます。それでは、そのようなサービス・リクエスターとしてポートレットを使用しながら実際にやってみましょう。
Webサービスのプログラミング・サンプル
なぜWebサービスの使用が賢明な設計目標であるか、という点に関しては、さまざまな理由があります。アプリケーションがサービス・ベンダーから一連の株価情報を取得する場合、Webサービス・フレームワークの動的性質を用いると、新規株価情報プロバイダーを見つけて使用できるため、有益です。内部プロジェクトの場合でも、Webサービスは有益です。たとえば、システム全体の他の多くのコンポーネントが使用する必要があるコンポーネントを配置する際に、各コンポーネントにさまざまな環境上の制約が課せられている場合、Webサービスのアプローチにより、すべての設計コンポーネントを統合するための、プラットフォームに依存しない方法が提供されます。
このサンプルではサービス・プロバイダーの役割にのみ焦点を当てることで、ポートレットおよびサービスを統合する方法を簡単に例証しています。(Webサービス・スタックの全側面を取り扱うことは、あまりにも大きなタスクであるため、この短い記事では記述しきれません。詳細は、参考文献のリンクを参照してください。)この記事のサンプルをそのまま使用して、指定した日付の残り時間の時分を示すサービスを作成します。このような単純なサービスにはこのサンプルは過剰ですが、これによりコードを複雑にせずに、サービスの作成、配置、および統合に関する重要な要因に焦点を当てることができます。
ここでは、サービスへの要求の着信を許可するApplication Serverを備えた新規Enterprise Archive (アプリケーション) を登録します。この目的を達成するために、Application Serverにパッケージ化されているSOAPツールキットを使用します。このサービスを設計するための最初のステップは、使用できるpublicオペレーションを決定することです。これらは以下のとおりです。
package com.ibm.wps.sample.time;
import java.util.*;
/**
* RemainingTimeService.java
* A service that provides the remaining hours and minutes of the date.
*/
public interface RemainingTimeService {
/**
* Get the remaining hours and minutes of the date.
* @param date The date to use.
* @return The remaining hours and minutes
*/
public TimeBean getRemainingTime(Date date);
/**
* Get the remaining hours of the date.
* @param date The date to use.
* @return The remaining hours.
*/
public Integer getRemainingHours(Date date);
/**
* Get the remaining minutes of the date.
* @param date The date to use.
* @return The remaining minutes.
*/
public Integer getRemainingMinutes(Date date);
}
|
次に、以下のJavaクラスで示されるとおり、このインターフェースをインプリメントするサービスを提供するオブジェクトを作成する必要があります。
package com.ibm.wps.sample.time;
import java.util.*;
/**
* RemainingTimeServiceServerImpl.java
* Provides an implementation for the RemainingTimeService service.
*/
public class RemainingTimeServiceServerImpl implements RemainingTimeService {
/**
* Get the remaining hours and minutes of the date.
* @param date The date to use.
* @return The remaining hours and minutes.
*/
public TimeBean getRemainingTime(Date date) {
Calendar calendar = Calendar.getInstance();
int hours = 24 - calendar.get(Calendar.HOUR_OF_DAY), minutes = 0;
if (calendar.get(Calendar.MINUTE) > 0) {
hours -= 1;
minutes = 60 - calendar.get(Calendar.MINUTE);
}
TimeBean timeBean = new TimeBean();
timeBean.setHours(hours);
timeBean.setMinutes(minutes);
return timeBean;
}
/**
* Get the remaining hours of the date.
* @param date The date to use. * @return The remaining hours.
*/
public Integer getRemainingHours(Date date) {
return new Integer(getRemainingTime(date).getHours());
}
/**
* Get the remaining minutes of the date.
* @param date The date to use.
* @return The remaining minutes.
*/
public Integer getRemainingMinutes(Date date) {
return new Integer(getRemainingTime(date).getMinutes());
}
}
|
これで、サーバーのインプリメンテーションが得られました。次に、このサービスをApplication Serverに配置するために、Enterprise Archive (EAR) ファイルを作成する必要があります。そして、SOAPツールキットをEARファイルにバンドルし、必要なSOAP配置ディスクリプターをすべて作成する必要があります。これらのコンポーネントをすべて適所に配置すると、Application Server内で実行するSOAPツールキットが、サーバー・サイドのサービスのインプリメンテーションへの全要求のルーティングを処理します。EARファイルの配置についての詳細は、参考文献の関連記事を参照してください。
EARファイルおよび関連リソース・アーカイブを作成するには、いくつかの方法があります。Application Developerには、自動的にEARファイルを生成するツールが用意されています。EARファイル構造を作成するには、ANT (apache.orgで入手できます) のようなオープン・ソースのツールを使用することもできます。このサンプルでは、EARファイルの中身を開いて、各リソースを記述します。EARファイルのパッケージ化については、読者の判断にお任せします。
作成するEARファイルには、以下のディレクトリーおよびファイル構造が含まれます。
RemainingTimeService.ear
.\RemainingTimeService.jar
.\soap.war
.\META-INF\application.xml
.\META-INF\ibm-application-bnd.xmi
.\META-INF\ibm-application-ext.xmi
|
RemainingTimeService.jarには、生成したすべてのクラス・ファイル (RemainingTimeService.class、TimeBean.class、RemainingTimeServiceServerImpl.class) が含まれます。soap.warは、すべてのSOAP関連リソース用のWeb Archive (WAR) ファイルであり、詳細は後ほど説明します。
配置ディスクリプター
META-INFディレクトリーに作成するEARファイルには、以下の3つの配置ディスクリプターがあります。
-
application.xml
- アプリケーションのメインの配置ディスクリプターです。ほとんどのツールは、入力に基づいてこのファイルを自動的に生成しますが、ANTまたはJARのような作成ツールを使用している場合は、ファイルの内容は以下のようになります。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application
PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application 1.2//EN"
"http://java.sun.com/j2ee/dtds/application_1_2.dtd">
<application id="Application_ID">
<display-name>RemainingTimeService</display-name>
<module id="WebModule_1">
<web>
<web-uri>soap.war</web-uri>
<context-root>/RemainingTimeService</context-root>
</web>
</module>
<security-role id="SecurityRole_1">
<role-name>SOAPRouterUsers</role-name>
</security-role>
</application>
|
次の2つの配置ディスクリプターは、Application Developerツールによって生成されます。ただし、それらの内容は次のとおりです。(詳細は、この記事では触れません。詳しくは、参考文献のWAS InfoCenterを参照してください。)ファイルを自動生成しない場合は、これらのサンプルをご使用のアプリケーション用に調整してください。
-
ibm-application-bnd.xmi
-
<applicationbnd:ApplicationBinding xmi:version="2.0"
xmlns:xmi="http://www.omg.org/XMI"
xmlns:applicationbnd="applicationbnd.xmi"
xmlns:application="application.xmi"
xmlns:common="common.xmi"
xmi:id="Application_ID_Bnd"
appName="RemainingTimeService">
<application href="META-INF/application.xml#Application_ID"/>
<authorizationTable xmi:id="AuthorizationTable_1">
<authorizations xmi:id="RoleAssignment_1">
<role href="META-INF/application.xml#SecurityRole_1"/>
<specialSubjects xmi:type="applicationbnd:Everyone"
xmi:id="Everyone_1"
name="Everyone"/>
</authorizations>
</authorizationTable>
<runAsMap xmi:id="RunAsMap_1"/>
</applicationbnd:ApplicationBinding>
|
-
ibm-application-ext.xmi
-
<applicationext:ApplicationExtension xmi:version="2.0"
xmlns:xmi="http://www.omg.org/XMI"
xmlns:applicationext="applicationext.xmi"
xmlns:application="application.xmi"
xmi:id="Application_ID_Ext">
<application href="META-INF/application.xml#Application_ID"/>
</applicationext:ApplicationExtension>
|
次に、soap.warファイルを作成して、EARファイル内のリソースとして追加してください。soap.warには以下のディレクトリーおよびファイル構造が含まれます。
soap.war
.\dds.xml
.\soap.xml
.\WEB-INF\web.xml
.\WEB-INF\ibm-web-bnd.xmi
.\WEB-INF\ibm-web-ext.xmi
|
SOAP配置ディスクリプターは、以下のサンプルに似たものとなります。標準Javaクラスによってインプリメントされるサービスを公開するメインの配置ディスクリプターは、以下のとおりです。
dds.xml
<root>
<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
id="urn:RemainingTimeService"
checkMustUnderstands="false">
<isd:provider type="java" scope="Application"
methods="getRemainingTime getRemainingHours getRemainingMinutes">
<isd:java class="com.ibm.wps.sample.time.RemainingTimeServiceServerImpl"
static="false"/>
</isd:provider>
<isd:faultListener>org.apache.soap.server.DOMFaultListener
</isd:faultListener>
<isd:mappings>
<isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:x="urn:RemainingTimeService"
qname="x:TimeBean"
javaType="com.ibm.wps.sample.time.TimeBean"
xml2JavaClassName=
"org.apache.soap.encoding.soapenc.BeanSerializer"
java2XMLClassName=
"org.apache.soap.encoding.soapenc.BeanSerializer"/>
</isd:mappings>
</isd:service>
</root>
|
サービス・タグのID属性であるservice-urnは、公開されたサービスが想定するURNを指定します(urn:TimeRemainingServiceなど)。また、サービス・タグのcheckMustUnderstands属性を "false" に設定して、要求内にMustUnderstandとマークされているSOAPヘッダーがある場合に、サーバーがFaultをスローしないように指示します。プロバイダーのタイプはApplicationと指定します (他に指定できる値はRequestおよびSessionです)。これにより、アプリケーションを作成するのは1度に限り、エンタープライズ・アプリケーションの存続期間中は再使用されることを指示します。プロバイダー・タグの最後の属性は、サービス・インプリメンテーションを前面に出す公開されたメソッドです。公開されたメソッドは静的属性ではないため、javaのstatic属性はfalseに設定されます。クラス属性は、このサービス・オブジェクトの完全修飾クラス名に設定されます。
ディスクリプターに提供する次の情報は、ユーザー定義のクラス・オブジェクト用のタイプ・マッピングです。この情報は、SOAPツールキットがオブジェクトを自動的にシリアライズしてSOAPドキュメント用のXMLとする際には重要です。このサンプルでは、TimeBean クラスが含まれるため、指定したURNおよびQNAMEを使用して、これをmap属性に登録します。また、標準のシリアライザーおよびデシリアライザー (それぞれ、xml2JavaClassNameおよびjava2XMLClassName属性) も使用します。これは、TimeBean クラスがJavaBean仕様にならってモデリングされており、このBeanSerializer クラスがその使用を目的としているためです。
生成ツールを使用しない場合は、個別に調整して、以下の追加ディスクリプターをWARファイルに直接組み込みます。これらの内容については、前述のディスクリプターと比べて、このサンプルではあまり重要でなく役に立たないため、説明は省きます。詳細は、参考文献を参照してください。
soap.xml
<!-- Apache SOAP Server Configuration File -->
<soapServer>
<!-- This ConfigManager looks for a dds.xml file in this -->
<!-- directory. The dds.xml file should contain a <root> element -->
<!-- which has deployment descriptor <isd:service> elements as -->
<!-- children. -->
<configManager value="com.ibm.soap.server.XMLDrivenConfigManager"/>
<serviceManager>
<option name="SOAPInterfaceEnabled" value="false"/>
</serviceManager>
</soapServer>
|
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app id="WebApp_1">
<display-name>Apache-SOAP</display-name>
<description>no description</description>
<servlet id="Servlet_1">
<servlet-name>rpcrouter</servlet-name>
<display-name>Apache-SOAP RPC Router</display-name>
<description>no description</description>
<servlet-class>org.apache.soap.server.http.RPCRouterServlet
</servlet-class>
<init-param id="InitParam_1">
<param-name>faultListener</param-name>
<param-value>org.apache.soap.server.DOMFaultListener</param-value>
</init-param>
</servlet>
<servlet id="Servlet_2">
<servlet-name>messagerouter</servlet-name>
<display-name>Apache-SOAP Message Router</display-name>
<servlet-class>org.apache.soap.server.http.MessageRouterServlet
</servlet-class>
<init-param id="InitParam_2">
<param-name>faultListener</param-name>
<param-value>org.apache.soap.server.DOMFaultListener</param-value>
</init-param>
</servlet>
<servlet-mapping id="ServletMapping_1">
<servlet-name>rpcrouter</servlet-name>
<url-pattern>/servlet/rpcrouter</url-pattern>
</servlet-mapping>
<servlet-mapping id="ServletMapping_2">
<servlet-name>messagerouter</servlet-name>
<url-pattern>/servlet/messagerouter</url-pattern>
</servlet-mapping>
<security-constraint id="SecurityConstraint_1">
<web-resource-collection id="WebResourceCollection_1">
<web-resource-name>/RemainingTimeServiceResource</web-resource-name>
<url-pattern>/RemainingTimeService/*</url-pattern>
<url-pattern>/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
<http-method>DELETE</http-method>
<http-method>PUT</http-method>
</web-resource-collection>
<auth-constraint id="AuthConstraint_1">
<description>SOAPRouterSecurityConstraints:+:</description>
<role-name>SOAPRouterUsers</role-name>
</auth-constraint>
<user-data-constraint id="UserDataConstraint_1">
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>
<security-role id="SecurityRole_1">
<role-name>SOAPRouterUsers</role-name>
</security-role>
</web-app>
|
ibm-web-bnd.xmi
<webappbnd:WebAppBinding xmi:version="2.0"
xmlns:xmi="http://www.omg.org/XMI"
xmlns:webappbnd="webappbnd.xmi"
xmlns:webapplication="webapplication.xmi"
xmi:id="WebApp_1_Bnd"
virtualHostName="default_host">
<webapp href="WEB-INF/web.xml#WebApp_1"/>
</webappbnd:WebAppBinding>
|
ibm-web-ext.xmi
<webappext:WebAppExtension xmi:version="2.0"
xmlns:xmi="http://www.omg.org/XMI"
xmlns:webappext="webappext.xmi"
xmlns:webapplication="webapplication.xmi"
xmi:id="WebApp_1_Ext">
<webApp href="WEB-INF/web.xml#WebApp_1"/>
</webappext:WebAppExtension>
|
ここまでで、アプリケーション・サーバーに配置できるSOAPサービスの作成方法について説明しました。このサービスと対話するためには、呼び出しアプリケーションがSOAPメッセージを使用して通信できる必要があります。これは、呼び出しアプリケーションにとって負担となる場合があるため、プログラム・インターフェースを使用して、SOAP通信およびツールキットの対話に関する詳細を隠蔽するサービスとの対話を行うと有益です。もう一度SOAPツールキットを使用して、このクライアント・サイドのAPIを提供します。
以下に示すとおり、RemainingTimeServiceインターフェースをインプリメントし、SOAPツールキットを使用してSOAPメッセージを介してサーバーのサービスと通信できるクライアント・オブジェクトも作成できます。
package com.ibm.wps.sample.time;
import java.net.*;
import java.util.*;
import org.apache.soap.*;
import org.apache.soap.rpc.*;
import org.apache.soap.util.xml.*;
import org.apache.soap.encoding.*;
import org.apache.soap.encoding.soapenc.*;
/**
* RemainingTimeServiceClientImpl.java
* Provides an implementation for the RemainingTimeService client.
*/
public class RemainingTimeServiceClientImpl implements RemainingTimeService {
/** The registry to specify user defined objects for serialization. */
private SOAPMappingRegistry smr = new SOAPMappingRegistry();
/** The URN to address the service. */
private String serviceURN = "urn:RemainingTimeService";
/** The URL to use to access the service. */
private URL soapRouter = null;
/**
* Construct a remaining time service client.
* @param host The host where the service resides.
* @exception MalformedURLException If an error occurs creating * the URL.
*/
public RemainingTimeServiceClientImpl(String host)
throws MalformedURLException {
super();
soapRouter = new URL("http://" + host + "/RemainingTimeService/servlet/rpcrouter");
// Populate the SOAP Mapping Registry with the user defined type.
mapSOAPClassTypes();
}
/**
* Get the remaining hours and minutes of the date.
* @param date The date to use.
* @return The remaining hours and minutes.
*/
public TimeBean getRemainingTime(Date date) {
Vector params = new Vector();
params.addElement(new Parameter("date", Date.class, date, null));
return (TimeBean)doCall("getRemainingTime", params);
}
/**
* Get the remaining hours of the date.
* @param date The date to use. * @return The remaining hours.
*/
public Integer getRemainingHours(Date date) {
Vector params = new Vector();
params.addElement(new Parameter("date", Date.class, date, null));
return (Integer)doCall("getRemainingHours", params);
}
/**
* Get the remaining minutes of the date.
* @param date The date to use.
* @return The remaining minutes.
*/
public Integer getRemainingMinutes(Date date) {
Vector params = new Vector();
params.addElement(new Parameter("date", Date.class, date, null));
return (Integer)doCall("getRemainingMinutes", params);
}
/** Make the actual call using the specified method and parameters. */
/** If any errors occur, then simply return null. */
private Object doCall(String method, Vector params) {
try {
// Create SOAP call using the specified parameters.
Call call = new Call();
call.setSOAPMappingRegistry(smr);
call.setTargetObjectURI(serviceURN);
call.setMethodName(method);
call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
call.setParams(params);
// Make the SOAP call and obtain the response.
Response resp = call.invoke(soapRouter, "");
// Check if there was an exception in the response.
if (resp.generatedFault()) {
return null;
}
// Return the result.
Parameter result = resp.getReturnValue();
return result == null ? null : result.getValue();
}
catch (SOAPException e) {
return null;
}
}
/** Map the new SOAP types so that they can be serialized. */
private void mapSOAPClassTypes() {
BeanSerializer beanSer = new BeanSerializer();
smr.mapTypes(Constants.NS_URI_SOAP_ENC,
new QName(serviceURN, "TimeBean"),
TimeBean.class,
beanSer,
beanSer);
}
}
|
クライアントはサーバーと同じインターフェースを拡張するため、インプリメンテーションも基盤となる通信の詳細も、呼び出しアプリケーションにとっては無関係です。このサンプルでは、クラス・タイプのマッピングが、サーバー・サイドの配置ディスクリプターと同じ方法で、および同じ理由から、最初にシリアライゼーション・レジストリー (SOAPMappingRegistry) に追加されます。このサンプルでは、サービスの動的発見には触れないため、サービスのホスト・ロケーションは既知のものとみなします。これはコンストラクターのパラメーターの一部として渡され、SOAP URLを作成するために使用されます。
クライアント・サイドのオブジェクトが構成された後、インターフェースの全メソッドを呼び出し用に使用できます。これらは、メソッド名およびサービス呼び出しのパラメーターをセットアップし、SOAPサーバーとの実際の通信の際にはdoCall メソッドに従います。たとえばgetRemainingTime メソッドでは、ベクトルが作成され、すべてのメソッド・パラメーター (この場合はDate オブジェクトのみ) が含まれます。パラメーター・ベクトルおよびメソッド名のストリング表記は、doCall メソッドに渡されます。
doCall メソッドで一度、SOAPCall オブジェクトが構成され、SOAPサーバーとの通信に必要なすべての関連情報が設定されます。これには、使用するシリアライゼーション・レジストリー、ターゲット・サービスURN (urn:RemainingTimeServiceなど)、メソッドと呼び出しパラメーター、およびSOAPエンコーディング・スタイルが含まれます。その後、指定されたホスト名を用いて構成されたSOAPルーターURLを使用して呼び出しが開始されます。最後に、戻された例外または生成された例外があればチェックし、SOAPサーバーから返された値を戻します。
サービスの抽象を提供する際に、クラス・レベルのインプリメンテーションの詳細が指定される場合には、オブジェクト指向のファクトリー設計パターンにならうとよいでしょう。たとえば、以下のファクトリー抽象を提供した場合、後ほどファクトリーが変更されて、現行の環境に応じてサーバー・サイドあるいはクライアント・サイドのいずれかのインプリメンテーションを構成して呼び出し側に戻すかが判別される場合があります。サーバー・サイドのサービスをローカルで使用できる場合には、すべてのSOAP通信の呼び出しのコストを負う必要はありません。これは、クライアント・サイドのサービスのAPIを戻すだけの単純なファクトリー・インプリメンテーションです。
package com.ibm.wps.sample.time;
import java.net.*;
/**
* RemainingTimeServiceFactory.java
* Factory for obtaining a reference to a remaining time service.
*/
public class RemainingTimeServiceFactory {
/**
* Obtain a remaining time service implementation.
* @param hostname The host where the service resides.
* @exception MalformedURLException If an error occurs creating the URL.
*/
public static RemainingTimeService getInstance(String hostname)
throws MalformedURLException {
return new RemainingTimeServiceClientImpl(hostname);
}
}
|
ポートレットとWebサービスの結合
ここでは、ポートレットを変更して、この新しいサービスを使用します。幸いにも、この作業の大部分は、SOAP通信の詳細を隠蔽するクライアント・サイドのサービスのAPIを作成することによって、すでに完了しています。ポートレットを変更してサービスを使用することは、以下のコード部分で示すように、このサービスAPIを使用するRemainingTimeControllerのdoView メソッドを変更することと同じように容易です。
public void doView(PortletRequest request, PortletResponse response)
throws PortletException, IOException {
RemainingTimeService service = null;
try {
service = RemainingTimeServiceFactory.getInstance("localhost");
}
catch (Exception e) {
throw new PortletException(e.getMessage());
}
// Insert bean into the request for the JSP to use.
request.setAttribute("timeBean", service.getRemainingTime(new Date()));
// Call the View JSP to render the display.
getPortletConfig().getContext().include(jspHTML, request, response);
}
}
|
結論
この記事では、ゼロからポートレットおよびWebサービスを作成するために、限定的ではありますが十分に実用的な例を用いて、くまなく実演しました。基本的な概念と、独自のポートレットおよびWebサービスの作成方法を説明したつもりです。この例ではサービス・プロバイダーの役割に焦点を当てましたが、Web Services Toolkitの他の側面をご自分で調べて、他のWebサービスの参加者をインプリメントしてみることをお勧めします。これらのアイデアをご自分の開発プロジェクトに取り込まれ、成功されることを期待しております。
参考文献
著者について  | |  | Michael Wanderskiは、Pervasive Computing Divisionに勤務するIBM Raleigh Labのアドバイザリー・ソフトウェア・エンジニアです。彼は現在、working on the WebSphere Everyplace Access製品の業務に関わっています。連絡先はmwanders@us.ibm.com です。 |
記事の評価
|