この章では、SIPサーブレット・アプリケーション開発について説明します。内容は次のとおりです。
SIP Servlet APIは、JCP (Java Community Process)のJSR289として標準化されています。
注意: このドキュメントでは、「SIP Servlet」という用語でAPIを表し、このAPIを使って作成されたアプリケーションを「SIPサーブレット」と称しています。 |
Javaサーブレットはサーバー側アプリケーションの開発に使用され、HTTPサーブレットはサーブレットのサブクラスでWebアプリケーションの作成に使用されます。SIPサーブレットは、SIP固有の機能が追加された汎用サーブレットAIPとして定義されます。
SIPサーブレットはHTTPサーブレットとよく似ているので、HTTPサーブレットの開発者はこのプログラミング・モデルにすぐに慣れるでしょう。HTTPサーブレットで定義されているサービス・レベルとSIPサーブレットで定義されているサービス・レベルはよく似ているので、HTTPとSIPの両方をサポートするアプリケーションを設計するのは簡単です。例1-1は単純なSIPサーブレットの例です。
例1-1 SimpleSIPServlet.java
package oracle.example.simple; import java.io.IOException; import javax.servlet.*; import javax.servlet.sip.*; public class SimpleSIPServlet extends SipServlet { protected void doMessage(SipServletRequest req) throws ServletException, IOException { SipServletResponse res = req.createResponse(200); res.send(); } }
上の例は、SIP MESSAGEリクエストに対して200 OKレスポンスを返す単純なSIPサーブレットを示しています。このリストからわかるように、SIP ServletとHTTP Servletには次のような多くの共通点があります。
サーブレットはAPIが提供するベース・クラスを継承する必要があります。HTTPサーブレットはHttpServletを継承し、SIPサーブレットはSipServletを継承する必要があります。
メソッドdoXxxをオーバーライドして実装する必要があります。HTTPサーブレットにはGET/POSTメソッドに対応するdoGet/doPostメソッドがあります。同様に、SIPサーブレットにはメソッド名(上の例ではMESSAGEメソッド)に対応するdoXxxメソッドがあります。アプリケーション開発者は必要なメソッドをオーバーライドして実装します。
SIP Servletのライフサイクルおよび管理メソッド(init、destroy)はHTTP Servletとまったく同じです。セッションおよび属性の操作も同じです。
上の例では表示されませんが、web.xmlに対応するSIPサーブレットのsip.xmlという名前のデプロイメント記述子があります。アプリケーション開発者とサービス・マネージャは、複数のSIPサーブレットを使用してアプリケーションを構成するためにこのファイルを編集できます。
ただし、SIPサーブレットとHTTPサーブレットの間には相違点もいくつかあります。重要な違いの1つにプロトコルによるものがあります。次の項では、こうした相違点とSIPサーブレットの特徴について説明します。
このセクションでは、SIPサーブレットとHTTPサーブレットの違いについて説明します。
例1-1のdoMessageメソッドには引数が1つしかありませんでした。HTTPでは、トランザクションは1対のリクエストとレスポンスから成っているので、doXxxメソッドの引数でリクエスト(HttpServletRequest)とレスポンス(HttpServletResponse)を指定します。アプリケーションはリクエストを実行するために、そこからパラメータなどの情報を取り出し、結果をレスポンスの本体で返します。
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
SIPでは、1つのリクエストに対して複数のレスポンスを返すこともできます。
上の図はINVITEリクエストへのレスポンス例を示しています。この例では、サーバーが1つのINVITEリクエストに対して3つのレスポンス(100、180、200)を返しています。SIPサーブレットでこのようなシーケンスを実装するには、doXxxメソッドでリクエストのみを指定し、アプリケーションがオーバーライドしたメソッドで必要なレスポンスを生成して返します。
現在、SIP Servletでは以下のdoXxxメソッドが定義されています。
protected void doInvite(SipServletRequest req); protected void doAck(SipServletRequest req); protected void doOptions(SipServletRequest req); protected void doBye(SipServletRequest req); protected void doCancel(SipServletRequest req); protected void doSubscribe(SipServletRequest req); protected void doNotify(SipServletRequest req); protected void doMessage(SipServletRequest req); protected void doInfo(SipServletRequest req); protected void doPrack(SipServletRequest req);
SIPの重要な特徴の1つとして、クライアントとサーバーが固定されないという点があります。HTTPの場合、Webブラウザは常にHTTPリクエストを送信し、HTTPレスポンスを受信します。HTTPリクエストを受信し、HTTPレスポンスを送信することはありません。しかし、SIPでは各端末がクライアントとサーバーの両方の機能を持っている必要があります。
図1-3では、両方のSIP電話が相手に電話をかけたり通話を切ったりできる必要があります。
上の例は、呼出しまたは切断を行う端末がクライアントとして動作していることを示しています。SIPでは、クライアントとサーバーの役割を1回のダイアログの中で逆転できます。このクライアント機能をUAC (User Agent Client)、サーバー機能をUAS (User Agent Server)、端末をUA (User Agent)と呼びます。SIPサーブレットではレスポンスを受信するためのメソッドも定義されています。
protected void doProvisionalResponse(SipServletResponse res); protected void doSuccessResponse(SipServletResponse res); protected void doRedirectResponse(SipServletResponse res); protected void doErrorResponse(SipServletResponse res);
これらのdoXxxレスポンス・メソッドはリクエストのメソッド名ではありません。次のようにレスポンスのタイプによって名前が付けられています。
doProvisionalResponse - 暫定レスポンス(1xxレスポンス)の受信時に呼び出されるメソッド
doSuccessResponse - 成功レスポンスの受信時に呼び出されるメソッド
doRedirectResponse - リダイレクト・レスポンスの受信時に呼び出されるメソッド
doErrorResponse - エラー・レスポンス(4xx、5xx、6xxレスポンス)の受信時に呼び出されるメソッド
レスポンスを受信するためのメソッドが存在するということは、SIP Servletではリクエストとレスポンスがそれぞれ別々のスレッドでアプリケーションに送られることを意味しています。アプリケーションはSIPメッセージの関連を明示的に管理する必要があります。リクエストとレスポンスがそれぞれ独立していると、処理のプロセスはいくらか複雑になりますが、より柔軟なプロセスを記述することが可能になります。
SIP Servletを使用すると、アプリケーションでリクエストを明示的に作成することもできます。これらの機能を使用することで、SIPサーブレットはサーバー(UAS)としてリクエストを待つだけでなく、クライアント(UAC)としてリクエストを送信することができます。
HTTPプロトコルと異なるもう1つの機能に「フォーク」があります。フォークとは、1つのリクエストを複数のサーバーに同時に(またはシーケンシャルに)プロキシするプロセスのことで、1つの電話番号に複数の端末(オペレータ)が関連付けられている場合(コール・センターなど)に使用されます。
SIP Servletには、プロキシ機能を持つアプリケーションのためにSIPリクエストをプロキシするユーティリティが備わっています。
図1-5に示すように、SIPメッセージの構造はHTTPと同じです。
HTTPは本質的にHTMLファイルと画像を転送するためのプロトコルです。転送するコンテンツはメッセージ・ボディに格納されます。HTTP Servletでは、大量のコンテンツを送受信できるようにするストリーム操作ベースのAPIが定義されています。
ServletOutputStream getOutputStream() PrintWriter getWriter() int getBufferSize() void setBufferSize(int size) void resetBuffer() void flushBuffer()
それに対してSIPでは、メッセージ本文に少量のコンテンツしか格納されません。なぜなら、SIPはリアル・タイムのコミュニケーションを想定しているからです。したがって、上記のメソッドは互換性を維持するためだけに存在し、機能は無効になっています。
SIPにおいてボディに格納されるコンテンツとしては以下のものがあります。
SDP (Session Description Protocol) - 端末間で使用されるマルチ・メディア・セッションを定義するためのプロトコル。このプロトコルはRFC2373で定義されています。
プレゼンス情報 - CPIMで定義されているプレゼンス情報を記述するメッセージ。
IMメッセージ - IM (インスタント・メッセージ)ボディ。ユーザー入力メッセージはメッセージ・ボディに格納されます。
メッセージ本文は小さいので、これをストリーミングで処理するとオーバーヘッドが増加します。SIPサーブレットはAPIを再定義して、次のようにメッセージをメモリー上で操作するようにしています。
次の項では、SIPサーブレット・コンテナとしてOracle WebLogic Server SIP Containerが提供する主な機能について説明します。
アプリケーション管理 - サーブレット・コンテキストによるアプリケーション管理、サーブレットのライフサイクル管理、デプロイメント記述子によるアプリケーション初期化などの機能について説明します。
SIPメッセージング - 着信したSIPメッセージを解析して適切なSIPサーブレットに渡す、SIPサーブレットによって作成されたメッセージを適切なUASに送信する、SIPヘッダー・フィールドを自動的に設定するといった機能について説明します。
ユーティリティ機能 - セッション、ファクトリ、プロキシなど、SIPサーブレットで使用できる機能について説明します。
HTTPサーブレット・コンテナと同様、SIPサーブレット・コンテナもサーブレット・コンテキストによってアプリケーションを管理します(図1-6を参照)。サーブレット・コンテキスト(アプリケーション)は通常はWAR形式で保管され、各アプリケーション・サーバーにデプロイされます。
注意: アプリケーション・サーバーにデプロイする方法は、製品によって異なります。詳細は、お使いのアプリケーション・サーバーのドキュメントを参照してください |
コンバージドSIP/Webアプリケーションのサーブレット・コンテキストには、SIPサーブレット、HTTPサーブレット、およびJSPを含めることができます。
Oracle WebLogic Server SIP Containerでは、プラットフォームとしてお使いのアプリケーション・サーバーと同じ方法でアプリケーションをデプロイできます。ただし、SIPサーブレットを含むアプリケーションをデプロイする場合は、SIPサーブレットで定義されたSIP固有のデプロイメント記述子(sip.xml)が必要です。一般的なコンバージドSIP/Webアプリケーションのファイル構造を表1-1に示します。
表1-1 アプリケーションのファイル構造の例
ファイル |
説明 |
WEB-INF/ |
コンバージドSIP/Webアプリケーションの構成ファイルと実行ファイルをこのディレクトリに配置します。このディレクトリ内のファイルをWeb上で直接参照することはできません(サーブレットではできます)。 |
WEB-INF/web.xml |
WebアプリケーションのJava EE標準構成ファイル。 |
WEB-INF/sip.xml |
SIPアプリケーションのSIP Servlet定義の構成ファイル。 |
WEB-INF/classes/ |
コンパイル済みのクラス・ファイルをこのディレクトリに保存します。HTTPサーブレットとSIPサーブレットの両方を保存することができます。 |
WEB-INF/lib/ |
JARファイルとしてアーカイブされたクラス・ファイルをこのディレクトリに保存します。HTTPサーブレットとSIPサーブレットの両方を保存することができます。 |
*.jsp, *.jpg |
JSPなどのWebアプリケーションを構成するファイルをJava EEと同じ方法でデプロイできます。 |
sip.xmlファイルで指定する情報はweb.xmlのものと似ていますが、<servlet-mapping>設定はHTTPサーブレットとは異なります。HTTPでは、URLのファイル名部分に関連するサーブレットを指定します。しかし、SIPにはファイル名という概念がありません。SIPリクエストのヘッダー・フィールドまたはURIを使ってフィルタ条件を設定します。例1-2では、「register」というSIPサーブレットがすべてのREGISTERメソッドに割り当てられています。
例1-2 sip.xmlのフィルタ条件の例
<servlet-mapping> <servlet-name>registrar</servlet-name> <pattern> <equal> <var>request.method</var> <value>REGISTER</value> </equal> </pattern> </servlet-mapping>
デプロイされた後、サーブレット・コンテキストのライフサイクルはサーブレット・コンテナによって保守されます。サーブレット・コンテキストの起動/停止は通常はサーバーの起動/停止時に行われますが、システム管理者がサーブレット・コンテキストの起動、停止、再ロードを明示的に行うこともできます。
SIPサーブレット・コンテナが提供するSIPメッセージング機能は以下のタイプに分類されます。
受信したSIPメッセージを解析します。
解析したメッセージを適切なSIPサーブレットに渡します。
SIPサーブレット生成のメッセージを適切なUAに送信します。
レスポンス(「100 Trying」など)を自動的に生成します。
SIPヘッダー・フィールドを自動的に管理します。
SIPサーブレットが処理するすべてのSIPメッセージは、SipServletRequestオブジェクトまたはSipServletResponseオブジェクトとして表現されます。受信したメッセージはパーサーによって解析されてから、これらのオブジェクトのいずれかに変換され、SIPサーブレット・コンテナに送られます。
SIPサーブレット・コンテナは以下の3種類のSIPメッセージを受信します。いずれであるかにより、ターゲット・サーブレットが決められます。
最初のSIPリクエスト - SIPサーブレット・コンテナは、どのSIPセッションにも属していないリクエストを受け取ると、前述したsip.xmlファイル内のフィルタ条件を使ってターゲットのSIPサーブレットを決定します。コンテナは初期リクエストを受け取ったときに新しいSIPセッションを作成するので、その時点以降に受け取ったSIPリクエストは後続のリクエストと見なされます。
注意: フィルタ処理は慎重に行ってください。Oracle WebLogic Server SIP Containerでは、受信したSIPメッセージが複数のSIPサーブレットに合致する場合に、いずれか1つのSIPサーブレットに渡されます。リクエスト・パラメータなどの追加の基準を使用して、サーブレットにリクエストを転送できます。 |
後続のSIPリクエスト - SIPサーブレット・コンテナは、いずれかのSIPセッションに属するリクエストを受け取ると、そのリクエストをそのセッションに関連付けられたSIPサーブレットに渡します。リクエストがセッションに属するかどうかは、ダイアログIDを使って判別されます。
SIPサーブレットはメッセージを処理するたびに、コンテナが呼出しIDをロックします。SIPサーブレットは、後続のリクエストを受け取るときに同じ呼出しIDの以前のリクエストを処理している場合、SIPサーブレット・コンテナは後続のリクエストをキューイングします。キューイングされたメッセージは、サーブレットが最初のメッセージの処理を終了し、SIPサーブレット・コンテナに制御を返した後にのみ処理されます。
この同時実行性制御は、1つのコンテナとクラスタリングされた環境の両方で保証されています。アプリケーション開発者は、どのような特定のコールIDに対しても1つのメッセージだけが所定の時間に処理されることを理解して、アプリケーションのコードを作成できます。
受信したレスポンスがSIPサーブレットがプロキシしたリクエストに対するものであれば、そのSIPセッションは特定されているため、レスポンスは自動的に同じサーブレットに渡されます。SIPサーブレットがそれ自身のリクエストを送信する場合は、最初にSIPセッションでレスポンスを受け取るサーブレットを指定する必要があります。たとえば、リクエストを送信するSIPサーブレットでレスポンスも受信する場合は、SIPセッションで次のハンドラ設定を指定する必要があります。
SipServletRequest req = getSipFactory().createRequest(appSession, ...); req.getSession().setHandler(getServletName());
SIPで「セッション」というと、通常はRTP/RTSPによるリアル・タイム・セッションを意味します。それに対してHTTPサーブレットでは、「セッション」が複数のHTTPトランザクションを関連付ける方法を意味します。このドキュメントでは、セッション関連の用語を次のように定義します。
表1-2 セッション関連の用語
リアル・タイム・セッション |
RTP/RTSPによって確立されたリアル・タイム・セッション。 |
HTTPセッション |
HTTP Servletによって定義されたセッション。複数のHTTPトランザクションを関連付ける方法。 |
SIPセッション |
HTTPセッションと同じ概念をSIPで実装する方法。SIP (RFC3261)には似通った概念として「ダイアログ」がありますが、ライフサイクルと生成の条件が異なるため、このドキュメントではこれを別の用語として扱います。 |
アプリケーション・セッション |
複数のプロトコルおよびダイアログを使って複数のHTTPセッションおよびSIPセッションを関連付けるアプリケーションのための方法。「APセッション」ともいいます。 |
Oracle WebLogic Server SIP Containerは、次に示すレスポンス・プロセスと再送信プロセスを自動的に実行します。
「100 Trying」の送信 - Oracle WebLogic Server SIP ContainerはINVITEリクエストを受信すると、自動的に「100 Trying」を作成して送信します。
CANCELへのレスポンス - Oracle WebLogic Server SIP ContainerはCANCELリクエストを受信すると、そのリクエストが有効なら次のプロセスを実行します。
CANCELリクエストに対して200レスポンスを送信します。
取消し対象のINVITEリクエストに対して487レスポンスを送信します。
SIPサーブレット上でdoCancelメソッドを呼び出します。これにより、アプリケーションはdoCancelメソッド内でプロセスを中止することができ、明示的にレスポンスを返す必要がなくなります。
INVITEへのエラー・レスポンスに対してACKを送信 - SIPサーブレットによって送信されたINVITEに対して4xx、5xxまたは6xxレスポンスが返されたとき、Oracle WebLogic Server SIP Containerは自動的にACKを作成して送信します。なぜなら、ACKはSIPシーケンスでのみ必要であり、SIPサーブレットはこれを必要としないからです。
SIPサーブレットがINVITEへの4xx、5xx、または6xxレスポンスを送信した場合は、そのレスポンスに対するACKを決して受信することはありません。
UDP使用時の再送信プロセス - SIPでは、UDPなどの信頼性の低いトランスポートが使われている場合に、送信されたメッセージが再送信されることが規定されています。Oracle WebLogic Server SIP Containerは、この仕様に従って再送信プロセスを自動的に実行します。
多くの場合、HTTPサーブレットではアプリケーションがヘッダー・フィールドを明示的に設定したり調べたりする必要はありません。HTTPサーブレット・コンテナがContent-LengthやContent-Typeなどのフィールドを自動的に管理するからです。SIPサーブレットにも同じヘッダー管理機能があります。
ただし、SIPではメッセージ配信に関する重要な情報を入れるフィールドがいくつかあるので、これらのヘッダーをアプリケーションでは変更できません。SIPサーブレットで変更できないヘッダーは「システム・ヘッダー」と呼ばれます。表1-3はシステム・ヘッダーの一覧です。
表1-3 システム・ヘッダー
ヘッダー名 |
説明 |
Call-ID |
複数のSIPメッセージをCallとして関連付けるためのID情報が含まれます。 |
From、To |
SIPリクエストの送信者と受信者に関する情報(SIP、URIなど)が含まれます。サーブレット・コンテナによってタグ・パラメータが与えられます。 |
CSeq |
シーケンス番号とメソッド名が含まれます。 |
Via |
SIPメッセージが通過したサーバーのリストが含まれます。これはリクエストへのレスポンスを送信するためにパスを追跡したい場合に使用します。 |
Record-Route、Route |
プロキシ・サーバーが後続のリクエストを仲介するときに使われます。 |
Contact |
端末間の直接的なコミュニケーションに使われるネットワーク情報(IPアドレスやポート番号など)が含まれます。REGISTERメッセージ、3xx、または485レスポンスについては、これはシステム・ヘッダーと見なされないので、SIPサーブレットがこの情報を直接編集することができます。 |
SIP Servletでは、SIPサーブレットで使用できる次のユーティリティが定義されています。
SIPセッション、アプリケーション・セッション
SIPファクトリ
プロキシ
前述したように、SIPサーブレットは「SIPセッション」を提供し、その概念はHTTPセッションと同じです。HTTPでは、Cookieなどの情報を使って複数のトランザクションが関連付けられます。SIPでは、この関連付けがヘッダー情報(Call-IDおよびFromとToのタグ・パラメータ)を使って行われます。サーブレット・コンテナがSIPセッションを保守し管理します。同じダイアログ内のメッセージは同じSIPセッションを参照できます。また、ダイアログを作成しないメソッド(MESSAGEなど)の場合、同じヘッダー情報を持つメッセージをセッションとして管理できます。
SIPサーブレットには「アプリケーション・セッション」という概念がありますが、これはHTTPサーブレットにはないものです。アプリケーション・セッションとは、複数のSIPセッションおよびHTTPセッションを関連付けて管理するためのオブジェクトです。これはB2BUAなどのアプリケーションに適しています。
SIPファクトリ(SipFactory)は、アプリケーションの実行に必要なSIP Servlet固有のオブジェクトを作成するためのファクトリ・クラスです。次のオブジェクトを生成することができます。
表1-4 SipFactoryで生成されるオブジェクト
クラス名 |
説明 |
URI、SipURI、Address |
文字列からSIP URIなどのアドレス情報を生成できます。 |
SipApplicationSession |
新しいアプリケーション・セッションを作成します。SIPサーブレットが新しいSIPシグナル・プロセスを開始するときに呼び出されます。 |
SipServletRequest |
SIPサーブレットがUACとして振る舞ってリクエストを作成するときに使われます。このようなリクエストはProxy.proxyToと一緒に送信することができません。SipServletRequestと共に送信する必要があります。 |
SipFactoryは、次のコードに示すように、サーブレット・コンテキスト属性のデフォルト名の下にあります。
ServletContext context = getServletContext(); SipFactory factory = (SipFactory) context.getAttribute("javax.servlet.sip.SipFactory");
プロキシはリクエストをプロキシするためにSIPサーブレットによって使用されるユーティリティです。SIPでは、プロキシ処理にフォークなどの独自のシーケンスがあります。Proxyによるプロキシ処理では以下の設定を指定することができます。
再帰的ルーティング(recurse) - プロキシ処理の宛先が3xxレスポンスを返した場合、リクエストは指定のターゲットにプロキシされます。
Record-Routeの設定 - 指定されたリクエストでRecord-Routeヘッダーを設定します。
パラレル/シーケンシャル(parallel) - フォークをパラレルに実行するかシーケンシャルに実行するかを決定します。
ステートフル - プロキシ処理がトランザクション・ステートフルかどうかを決定します。ステートフル・プロキシ・モードはJSR289で非推奨になったため、このパラメータは関係ありません。
スーパーバイジング・モード - プロキシ処理の状態が変化した場合(レスポンス受領)、アプリケーションはこれを報告します。