SOAP Webサービス再活用のススメ

Pocket

ジクソーパズル

(Petr Kratochvil / CC0 Public Domain)

こんにちは、初投稿の吉田です。よろしくお願いします。

Java SE 8のサポート公式アップデートの終了通知があったので、そろそろ既存システムのリプレースが話題になってくる頃ですね。新しい仕組みを再構築するのか、システム資産を延命するのか、といった検討を始めている方も多いと思います。

今回は、「SOAP Webサービスを今風に再構築する」という判断をする前に、タイトルにあるとおり現在の運用で既存資産の価値を高められないかを探ってみたいと思います。
過去につまずいた経験なども記載しているため、少し長いですがお付き合いください。

「SOAP Webサービス」のおさらい

この投稿に興味を持っていただいた方は「SOAPって何?」という事は無いかもしれませんが、簡単におさらいです。

SOAP Webサービスの状況

SOAPは、システム機能をプロセスやOSを超えてサービスとして提供できるように構成されたプロトコルで、少し前はよく利用されていました。
"Webサービス" = "SOAPを利用したWeb API" というくらい一般的でした。
最近、"Webサービス"という言葉は、Webアプリなど広い意味で使われることが多くなったので、本投稿では"SOAP Webサービス"と記載します。

これまでJava SEにはSOAP Webサービスを提供するためのパッケージ(エンジン)である、Java API for XML Web Services(JAX-WS)が含まれていましたが、Java SE 9から非推奨となりました。
おそらくJava SE 10(18.3)からは削除されるのでしょう。
JAX-WSの実装を推進しているオープンソースプロジェクトが停止するわけではありませんが、敢えてSOAP Webサービスを新規構築しよう、という手段はとり難くなるでしょう。

SOAPエンジンの役割

SOAP Webサービスを構築する場合、通常はSOAPエンジンのフレームワークを利用し、システム構築の作業負荷を軽減します。

SOAPエンジンの役割

SOAPエンジンの役割

SOAPでは、インターフェース部分をSOAPエンジンに任せることで、開発者がアプリケーションの構築に注力でき、インターフェースの信頼性や自由度が高まる、というメリットを得られます。
反面、複雑なインターフェースになったり、SOAPエンジンに依存したソースコードになってしまう、といったデメリットがあります。

SOAP Webサービスは駆逐されていくのか

このような煩雑さを解消して簡易に利用したいという要望や、JavaScriptの活性化から、近年ではREST+JSONの組み合わせでWeb APIを提供するケースが主流になっています。
しかし、現役で稼動しているエンタープライズ環境のSOAP Webサービス提供側システム(プロバイダ)は、大量のデータを抱えていることが多く、接続している利用側システム(リクエスタ)も多いため、再構築は大きな決断となります。
プロバイダ側の構築手法が確立されていることが多いので、これを一から組みなおすことは難しいと考えられます。
そこで、これからリクエスタの追加構築やリプレースをおこなう際に、リクエスタ側の煩雑さを解消できないか検討してみようと思います。

あらためてSOAPの課題ってなんだろう?

プロジェクトの規模や状況に拠って変わりますが、私の経験からSOAPを利用したシステムを構築した際に、「これは大変だった」という点を挙げてみます。

1.スタブソースの管理がたいへん

SOAPエンジンが提供するクライアントツールでは、サービスの定義情報(WSDL:Web Services Description Language)を利用してリクエスタ向けにスタブソースを生成する支援機能があります。
しかし、このような生成ソースは個々にSOAP Webサービスの通信機能を提供するため、メンテナンスする機能が多くなります。

Apache Axis2でWSDLからスタブを作成して確認

例として、Apache Axis2でWSDLファイルからスタブを作成するツール(wsdl2java)を実行してみましょう。
用意したサンプルのWSDLはコチラ。

前述のWSDLを抜粋すると、

3項目程度の要素を持つpersonという型を定義し、

personを検索して返す read という機能を定義したとします。
試しにWSDLファイルを、SOAPエンジンであるApache Axis2(1.7.7)のwsdl2javaツールにかけてJavaソースを生成してみましょう。

このようなシンプルなWSDLでも、生成されたPersonStub.javaは約200KByte(空行含め5000行弱)ほどのサイズになりました。
PersonStub.javaには、Personクラスの定義や、実際に通信をおこなうスタブ処理が記載されます。

生成ソースの肥大化

横並びに各インターフェースでこのような生成ファイルを作成していくと、徐々に負担が大きくなっていきます。

  • 生成ソースは設計書が無く、開発チームが処理を把握しておくことが難しい
  • 通信前後のログ出力など、生成ソースに手を加えたいが、修正箇所が多い
  • インターフェース変更やライブラリ更新の際に、ソースの再生成と再加工が必要

結果、契約上のグレーゾーンが生まれたり、自動生成ソースをメンテナンスするチームがいない、といったトラブルになることがあります。

2.SOAPエンジンの相性問題

相互運用性の問題

リクエスタは前述の生成された処理からSOAPエンジンのライブラリを用いますが、プロバイダ側と異なった製品を用いた際に、このライブラリが相性問題を起こすことがあります。
この問題はWeb Services Interoperability(WS-I)という団体が設立されるほど根深い問題でした。

SOAPエンジンの相性問題

SOAPエンジンの相性問題

WS-Iによる基本プロファイルの策定によって、相互通信できるケースは増えましたが、複数バージョンのプロファイルが制定されたことによる混乱も生まれました。
また、複雑なデータ通信をおこなった際に相性問題が発覚し、開発フェーズの終盤で問題となるリスクは未だに存在します。
SOAPエンジンのバージョンを更新した際など、意外なタイミングで相性問題が発生することもあります。

ライブラリの競合

他に、珍しいケースですが、同一プロセス内にSOAPエンジンのライブラリが混在することで、利用しているライブラリの競合が発生することもあります。

SOAPエンジンの競合

SOAPエンジンの競合

このような状況では、正常に動作する場合と異常な動作になる場合があるため、原因の究明が難しくなります。

そもそもリクエスタ側にSOAPエンジンの機能は必要?

SOAPエンジンが提供する支援機能を用いることで、プロジェクトのリスクと運用の負荷が上がる場合がある点を挙げました。
それなら思い切って、SOAPエンジンから提供されるリクエスタ向けの機能は利用せず、SOAP通信機能を作成することを検討してみてはいかがでしょうか。
SOAP自体はHTTPのPOSTメソッドですので、送受信するデータを整理できれば可能だと考えられます。

WSDLの構成

ここで一度、WSDLの構成を確認してみましょう。

WSDL構成イメージ

WSDL構成イメージ

WSDL構成イメージのtypes要素は、先程の抜粋例でpersonが定義された箇所です。送受信パラメータとなるリクエストデータとレスポンスデータ自体も定義されます。
これらの型定義にはXML Schemaが利用されています。
message要素では、リクエストとレスポンスで用いられるデータ型を指定し、通信に利用するmessageをoperation要素で定義します。

先程のpersonを検索するreadサービスのリクエストメッセージはこのような書式になります。

対するレスポンスメッセージ(例:2件の回答があったと仮定)は、このような書式になります。

WSDLのtypes要素で定義されたreadRequestとreadResponseでデータ連携していますね(ここでは省いていますが、エラーの場合はレスポンス内容は変わります)。

SOAPエンジンのサポートなしで通信できるか実験

リクエスト文字列をSOAPプロバイダに送信し、SOAPでの通信が成立するか試してみます。
「SoapUI」といったツールを用いるとWSDLから簡単にテスト用のSOAPプロバイダを作成することができて便利です。
利用した環境は
・Java SE 1.8.0
・SoapUI 5.4.0
です。

以下の処理を作成してみました。
※例は授受メッセージのサイズがStringに格納しても問題ないケースを想定しています。
[リクエスト処理]

このサンプルソースでのポイントは以下です。

  • 今回はHttpURLConnectionを利用する
  • [52行目]:HTTPメソッドとして"POST"を指定する
  • [53行目]:HTTPヘッダに"Content-type"として"text/xml"を指定する(SOAP1.1の場合)
  • [54行目]:HTTPヘッダに"SoapAction"を設定する

[メイン処理]

実験の結果

mainを起動し、用意したリクエストを送ると、SoapUIに設定しておいたダミー回答が返されました。
SoapUIのモックはダミーで回答するだけなので、メッセージの内容まではチェックしていませんが、疎通が取れることは確認できました。

あとはSOAP特有のEnvelope、Header、Bodyタグを編集する機能と、エラーが発生しSOAP Faultが返された場合の振る舞いを整理すれば、XMLでの会話が可能になりますね。

でもXML文書をプログラムから作成するのは面倒

XMLが単純に要素のみが列挙された構成であれば、DOMでパースしてMapに格納するとシンプルになります。
複雑な構成の場合、XMLを作成することになりますが、XMLに変換(シリアライズ)可能なプレーンなクラスを利用することが多いでしょう。
その一例として、XSD(XML Schema)ファイルから、そのクラスを生成してみましょう。

XSDファイルの作成

まずは、WSDLからXSDファイルを抜き出してみます。

  • types要素からschema要素を抜き出す
  • xsd要素を追加しschema などのXML名前空間を設定する
  • ファイル拡張子を".xsd"にして保存する

このような手順でXSDファイルができました。

XSDファイルの活用例

Java SEにはxjcというツールが付属していて、XMLにシリアライズ/デシリアライズできるクラスソースを生成することができます。

xjcの実行イメージ

xjcの実行イメージ



サイズとしては3KBytes前後のファイルが作成されます。

概要だけ記載すると、生成されたクラスはインスタンスに値を詰めてシリアライズすれば、Java Architecture for XML Binding(JAXB)という仕組みによってXML文書を得ることができます。反対にXML文書からデシリアライズし、Javaクラスをインスタンス化することもできます。
作成されたソースを確認すると、今回は、birthday項目にxsd:dateという日付型を利用したため、Personクラスの外側でJAXBのクラスを参照する必要がでてしまったので、文字列型(xsd:string)に修正したほうが扱い易い場合もあるでしょう。

JAXBについてもJava SE 9から非推奨となっているため、Java SE 10(18.3)以降は個別で入手することになりそうです。

XML文書で連携できると見えてくるもの

SOAPエンジンが提供する重厚なライブラリを使わなくてもSOAP Webサービスを利用できる、と感じていただけたのではないかと思います。

オンライン処理以外での活用

XML文書でデータ連携できると、オンライン処理以外でも活用しやすくなるという効果も生まれます。
レコード数がそれほど多くない軽量なバッチ処理や、ある程度データを蓄えて連携(ディレード処理)を繰り返す場合、オンライン向けに用意された機能を流用することがあります。
そのような場合に、XMLでの連携が可能ならば、活用の用途が広がる可能性があります。

ETL(Extract/Transform/Load)ツールの活用

XML連携の活用例を挙げると、ETL(Extract/Transform/Load)ツールを利用し、バッチ処理の製造と運用のコストを抑えられる可能性があります。
もちろん、ETLツールの多くはSOAPコネクタを提供していますが、SOAPの相性問題により、実際に通信できないケースが多くあります。
このような場合に、カスタマイズコネクタを作成可能なETLツールを利用すれば、インプット/アウトプットをXMLとして、ETLツールで他のシステムと連携できます。
ETLツールの活用については、またの機会に試してみたいと思います。

今回のまとめ

  • リリース直前にSOAPの相性問題が発覚したら、工程のリカバリーがとてもしんどい
  • 工夫次第でSOAPエンジンのライブラリを使わなくても、SOAP Webサービスは利用できる
  • Java SE 10(18.3)以降を見据え、新規構築するリクエスタはできるだけ身軽にしておくべき
  • 働き方改革も考えて省力化を考えないとね
  • 初回ブログ投稿で頑張りすぎた感が残った
Pocket

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です