(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 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はコチラ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
<?xml version="1.0" encoding="UTF-8"?> <wsdl:definitions targetNamespace="http://test.crmstyle.com/person/" xmlns:impl="http://test.crmstyle.com/person/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsd1="http://test.crmstyle.com/person/xsd" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"> <wsdl:types> <schema elementFormDefault="qualified" targetNamespace="http://test.crmstyle.com/person/xsd" xmlns="http://www.w3.org/2001/XMLSchema"> <complexType name="select"> <attribute name="value" type="xsd:int" use="required"/> <attribute name="text" type="xsd:string" use="optional"/> </complexType> <complexType name="selectList"> <sequence> <element maxOccurs="unbounded" minOccurs="0" name="select" type="xsd1:select"/> </sequence> </complexType> <complexType name="complexData"> <choice> <element name="textData" nillable="true" type="xsd:string"/> <element name="selectData" type="xsd1:selectList"/> </choice> <attribute name="op" type="xsd:string" use="optional"/> </complexType> <complexType name="person"> <all> <element maxOccurs="1" minOccurs="0" name="mailaddress" type="xsd:string"/> <element maxOccurs="1" minOccurs="0" name="name" type="xsd:string"/> <element maxOccurs="1" minOccurs="0" name="birthday" nillable="true" type="xsd:date"/> </all> </complexType> <complexType name="searchCondition"> <all> <element name="column" type="xsd:string"/> <element name="condition" type="xsd1:complexData"/> </all> </complexType> <element name="fault"> <complexType> <sequence> <element name="errorCode" type="xsd:int"/> <element name="errorMessage" type="xsd:string"/> </sequence> </complexType> </element> <element name="readRequest"> <complexType> <all> <element name="searchCondition" type="xsd1:searchCondition"/> <element minOccurs="0" name="limit" type="xsd:int"/> </all> </complexType> </element> <element name="readResponse"> <complexType> <sequence> <element name="count" type="xsd:int"/> <element maxOccurs="100" minOccurs="0" name="person" type="xsd1:person"/> </sequence> </complexType> </element> </schema> </wsdl:types> <wsdl:message name="read"> <wsdl:part name="requestParameter" element="xsd1:readRequest"> </wsdl:part> </wsdl:message> <wsdl:message name="readResponse"> <wsdl:part name="readReturn" element="xsd1:readResponse"> </wsdl:part> </wsdl:message> <wsdl:message name="faultMessage"> <wsdl:part name="faultMessage" element="xsd1:fault"> </wsdl:part> </wsdl:message> <wsdl:portType name="personPortType"> <wsdl:operation name="read" parameterOrder="requestParameter"> <wsdl:input message="impl:read"></wsdl:input> <wsdl:output message="impl:readResponse"></wsdl:output> <wsdl:fault name="faultMessage" message="impl:faultMessage"></wsdl:fault> </wsdl:operation> </wsdl:portType> <wsdl:binding name="personBinding" type="impl:personPortType"> <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="read"> <wsdlsoap:operation soapAction="read"/> <wsdl:input> <wsdlsoap:body use="literal"/> </wsdl:input> <wsdl:output> <wsdlsoap:body use="literal"/> </wsdl:output> <wsdl:fault name="faultMessage"> <wsdlsoap:fault name="faultMessage" use="literal"/> </wsdl:fault> </wsdl:operation> </wsdl:binding> <wsdl:service name="person"> <wsdl:port name="personPort" binding="impl:personBinding"> <wsdlsoap:address location="https://xxx/xxx/"/> </wsdl:port> </wsdl:service> </wsdl:definitions> |
前述のWSDLを抜粋すると、
1 2 3 4 5 6 7 |
<complexType name="person"> <all> <element maxOccurs="1" minOccurs="0" name="mailaddress" type="xsd:string"/> <element maxOccurs="1" minOccurs="0" name="name" type="xsd:string"/> <element maxOccurs="1" minOccurs="0" name="birthday" nillable="true" type="xsd:date"/> </all> </complexType> |
3項目程度の要素を持つpersonという型を定義し、
1 2 3 4 5 |
<wsdl:operation name="read" parameterOrder="requestParameter"> <wsdl:input message="impl:read"></wsdl:input> <wsdl:output message="impl:readResponse"></wsdl:output> <wsdl:fault name="faultMessage" message="impl:faultMessage"></wsdl:fault> </wsdl:operation> |
personを検索して返す read という機能を定義したとします。
試しにWSDLファイルを、SOAPエンジンであるApache Axis2(1.7.7)のwsdl2javaツールにかけてJavaソースを生成してみましょう。
1 2 3 4 5 |
filename size ----------------------------------- FaultMessage.java 957 PersonCallbackHandler.java 1,653 PersonStub.java 207,006 |
このようなシンプルなWSDLでも、生成されたPersonStub.javaは約200KByte(空行含め5000行弱)ほどのサイズになりました。
PersonStub.javaには、Personクラスの定義や、実際に通信をおこなうスタブ処理が記載されます。
生成ソースの肥大化
横並びに各インターフェースでこのような生成ファイルを作成していくと、徐々に負担が大きくなっていきます。
- 生成ソースは設計書が無く、開発チームが処理を把握しておくことが難しい
- 通信前後のログ出力など、生成ソースに手を加えたいが、修正箇所が多い
- インターフェース変更やライブラリ更新の際に、ソースの再生成と再加工が必要
結果、契約上のグレーゾーンが生まれたり、自動生成ソースをメンテナンスするチームがいない、といったトラブルになることがあります。
2.SOAPエンジンの相性問題
相互運用性の問題
リクエスタは前述の生成された処理からSOAPエンジンのライブラリを用いますが、プロバイダ側と異なった製品を用いた際に、このライブラリが相性問題を起こすことがあります。
この問題はWeb Services Interoperability(WS-I)という団体が設立されるほど根深い問題でした。
WS-Iによる基本プロファイルの策定によって、相互通信できるケースは増えましたが、複数バージョンのプロファイルが制定されたことによる混乱も生まれました。
また、複雑なデータ通信をおこなった際に相性問題が発覚し、開発フェーズの終盤で問題となるリスクは未だに存在します。
SOAPエンジンのバージョンを更新した際など、意外なタイミングで相性問題が発生することもあります。
ライブラリの競合
他に、珍しいケースですが、同一プロセス内にSOAPエンジンのライブラリが混在することで、利用しているライブラリの競合が発生することもあります。
このような状況では、正常に動作する場合と異常な動作になる場合があるため、原因の究明が難しくなります。
そもそもリクエスタ側にSOAPエンジンの機能は必要?
SOAPエンジンが提供する支援機能を用いることで、プロジェクトのリスクと運用の負荷が上がる場合がある点を挙げました。
それなら思い切って、SOAPエンジンから提供されるリクエスタ向けの機能は利用せず、SOAP通信機能を作成することを検討してみてはいかがでしょうか。
SOAP自体はHTTPのPOSTメソッドですので、送受信するデータを整理できれば可能だと考えられます。
WSDLの構成
ここで一度、WSDLの構成を確認してみましょう。
WSDL構成イメージのtypes要素は、先程の抜粋例でpersonが定義された箇所です。送受信パラメータとなるリクエストデータとレスポンスデータ自体も定義されます。
これらの型定義にはXML Schemaが利用されています。
message要素では、リクエストとレスポンスで用いられるデータ型を指定し、通信に利用するmessageをoperation要素で定義します。
先程のpersonを検索するreadサービスのリクエストメッセージはこのような書式になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://test.crmstyle.com/person/xsd"> <soapenv:Header/> <soapenv:Body> <xsd:readRequest> <xsd:searchCondition> <xsd:column>name</xsd:column> <xsd:condition> <xsd:textData>テスト太郎</xsd:textData> </xsd:condition> </xsd:searchCondition> <xsd:limit>100</xsd:limit> </xsd:readRequest> </soapenv:Body> </soapenv:Envelope> |
対するレスポンスメッセージ(例:2件の回答があったと仮定)は、このような書式になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://test.crmstyle.com/person/xsd"> <soapenv:Header/> <soapenv:Body> <xsd:readResponse> <xsd:count>2</xsd:count> <xsd:person> <xsd:mailaddress>test_taro@***.***</xsd:mailaddress> <xsd:name>テスト太郎</xsd:name> <xsd:birthday>2000-01-01</xsd:birthday> </xsd:person> <xsd:person> <xsd:mailaddress>test.taro@***.***</xsd:mailaddress> <xsd:name>テスト太郎</xsd:name> <xsd:birthday>2000-01-02</xsd:birthday> </xsd:person> </xsd:readResponse> </soapenv:Body> </soapenv:Envelope> |
WSDLのtypes要素で定義されたreadRequestとreadResponseでデータ連携していますね(ここでは省いていますが、エラーの場合はレスポンス内容は変わります)。
SOAPエンジンのサポートなしで通信できるか実験
リクエスト文字列をSOAPプロバイダに送信し、SOAPでの通信が成立するか試してみます。
「SoapUI」といったツールを用いるとWSDLから簡単にテスト用のSOAPプロバイダを作成することができて便利です。
利用した環境は
・Java SE 1.8.0
・SoapUI 5.4.0
です。
以下の処理を作成してみました。
※例は授受メッセージのサイズがStringに格納しても問題ないケースを想定しています。
[リクエスト処理]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
package soapsample; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; /** * SOAPリクエスタ<br> * <p> * 与えられたStringでSOAPリクエストを送信します。<br> */ public class SoapRequestor { /** * SOAPリクエスト送信処理 * @param endPointUrl エンドポイントURL * @param actionName アクション名 * @param requestMessage 送信XML内容 * @param encode 入出力データのエンコード指定文字列 * @return 受信XML内容 * @throws IOException */ public String postSoapMessage( String endPointUrl, String actionName, String requestMessage, String encode, int timeoutSeconds ) throws IOException{ System.out.println("■postSoapMessage Start"); String responseString = ""; URL url = new URL(endPointUrl); // エンドポイントのURLを作成 HttpURLConnection connection = null; // HTTPコネクション BufferedWriter requestWriter = null; // 入力データ書込 try { // コネクションを作成 connection = (HttpURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setDoInput(true); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-type", "text/xml; charset=utf-8"); connection.setRequestProperty("SoapAction", actionName); connection.setConnectTimeout(timeoutSeconds * 1000); //設定はミリ秒のため1000倍 connection.setReadTimeout(timeoutSeconds * 1000); //設定はミリ秒のため1000倍 // メッセージを送信する requestWriter = new BufferedWriter(new OutputStreamWriter( connection.getOutputStream(), StandardCharsets.UTF_8)); requestWriter.write(requestMessage); requestWriter.flush(); int lastHttpStatus = connection.getResponseCode(); // サーバ処理を呼び出し、結果コードを取得 // 受信結果がOKの場合 if (lastHttpStatus == HttpURLConnection.HTTP_OK) { // 標準ストリームを受信する try(InputStreamReader isReaderRes = new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8); BufferedReader responseReader = new BufferedReader(isReaderRes)) { // 結果を取得する responseString = readStream(connection.getInputStream(), StandardCharsets.UTF_8); } }else{ // エラーストリームを受信する InputStream errorStream = connection.getErrorStream(); if(errorStream != null){ try(InputStreamReader isReaderRes = new InputStreamReader(errorStream, StandardCharsets.UTF_8); BufferedReader responseReader = new BufferedReader(isReaderRes)) { // 結果を取得する responseString = readStream(errorStream, StandardCharsets.UTF_8); } } // その他のエラーの場合 // 例外を発生させる(例外はメールで通知される場合があるためメッセージにリクエスト内容は含めない) throw new IOException("Http Response Error" + ": Code=" + Integer.toString(lastHttpStatus) + ": ResponseMessage=" + connection.getResponseMessage() + ": ResponseXML=" + responseString); } } finally { //BufferedWriterを解放 if (requestWriter != null) { try{requestWriter.close();} catch(Exception ex){System.out.println(ex);} } //コネクションを解放 if (connection != null) { try{connection.disconnect();} catch(Exception ex){System.out.println(ex);} } } System.out.println("■postSoapMessage End"); return responseString; } /** * 文字列読込み * <p> * InputStreamから文字列を取得する。<br> * 改行コードが変更されるのを避けるため、BufferedReaderは利用していない。 * @param stream * @param encode * @return 取得文字列 */ private String readStream(InputStream stream, Charset encode) throws IOException{ StringBuilder strb = new StringBuilder(""); try(InputStreamReader reader = new InputStreamReader(stream, encode)) { char[] buf = new char[1024]; //1024文字ずつ取得する int numRead; while (0 <= (numRead = reader.read(buf))) { strb.append(buf, 0, numRead); } } // 結果を返す return strb.toString(); } } |
このサンプルソースでのポイントは以下です。
- 今回はHttpURLConnectionを利用する
- [52行目]:HTTPメソッドとして"POST"を指定する
- [53行目]:HTTPヘッダに"Content-type"として"text/xml"を指定する(SOAP1.1の場合)
- [54行目]:HTTPヘッダに"SoapAction"を設定する
[メイン処理]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
package soapsample; import java.io.IOException; public class SoapSample { public static void main(String[] args) { SoapRequestor sp = new SoapRequestor(); String endPointUrl = "http://localhost:8088/mockpersonBinding"; String requestMessage = "<soapenv:Envelope " + " xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" " + " xmlns:xsd=\"http://test.crmstyle.com/person/xsd\"> " + " <soapenv:Header/> " + " <soapenv:Body> " + " <xsd:readRequest> " + " <xsd:searchCondition> " + " <xsd:column>name</xsd:column> " + " <xsd:condition> " + " <xsd:textData>テスト太郎</xsd:textData> " + " </xsd:condition> " + " </xsd:searchCondition> " + " <xsd:limit>100</xsd:limit> " + " </xsd:readRequest> " + " </soapenv:Body> " + "</soapenv:Envelope> "; try { String responceMessage = sp.postSoapMessage( endPointUrl, "read", requestMessage, "UTF-8", 60); System.out.println("Responce:"); System.out.println(responceMessage); } catch (IOException e) { e.printStackTrace(); } } } |
実験の結果
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ファイルができました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema targetNamespace="http://test.crmstyle.com/person/xsd" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsd1="http://test.crmstyle.com/person/xsd" elementFormDefault="qualified" attributeFormDefault="qualified"> <complexType name="select"> <attribute name="value" type="xsd:int" use="required"/> <attribute name="text" type="xsd:string" use="optional"/> </complexType> <complexType name="selectList"> <sequence> <element maxOccurs="unbounded" minOccurs="0" name="select" type="xsd1:select"/> </sequence> </complexType> <complexType name="complexData"> <choice> <element name="textData" nillable="true" type="xsd:string"/> <element name="selectData" type="xsd1:selectList"/> </choice> <attribute name="op" type="xsd:string" use="optional"/> </complexType> <complexType name="person"> <all> <element maxOccurs="1" minOccurs="0" name="mailaddress" type="xsd:string"/> <element maxOccurs="1" minOccurs="0" name="name" type="xsd:string"/> <element maxOccurs="1" minOccurs="0" name="birthday" nillable="true" type="xsd:date"/> </all> </complexType> <complexType name="searchCondition"> <all> <element name="column" type="xsd:string"/> <element name="condition" type="xsd1:complexData"/> </all> </complexType> <element name="fault"> <complexType> <sequence> <element name="errorCode" type="xsd:int"/> <element name="errorMessage" type="xsd:string"/> </sequence> </complexType> </element> <element name="readRequest"> <complexType> <all> <element name="searchCondition" type="xsd1:searchCondition"/> <element minOccurs="0" name="limit" type="xsd:int"/> </all> </complexType> </element> <element name="readResponse"> <complexType> <sequence> <element name="count" type="xsd:int"/> <element maxOccurs="100" minOccurs="0" name="person" type="xsd1:person"/> </sequence> </complexType> </element> </xsd:schema> |
XSDファイルの活用例
Java SEにはxjcというツールが付属していて、XMLにシリアライズ/デシリアライズできるクラスソースを生成することができます。
1 2 3 4 5 6 7 8 9 10 11 |
filename size ----------------------------------- ComplexData.java 3,585 Fault.java 2,507 ObjectFactory.java 3,894 Person.java 3,618 ReadRequest.java 2,730 ReadResponse.java 2,849 SearchCondition.java 2,626 Select.java 2,295 SelectList.java 2,324 |
サイズとしては3KBytes前後のファイルが作成されます。
概要だけ記載すると、生成されたクラスはインスタンスに値を詰めてシリアライズすれば、Java Architecture for XML Binding(JAXB)という仕組みによってXML文書を得ることができます。反対にXML文書からデシリアライズし、Javaクラスをインスタンス化することもできます。
作成されたソースを確認すると、今回は、birthday項目にxsd:dateという日付型を利用したため、Personクラスの外側でJAXBのクラスを参照する必要がでてしまったので、文字列型(xsd:string)に修正したほうが扱い易い場合もあるでしょう。
1 2 3 4 5 6 |
public JAXBElement<XMLGregorianCalendar> getBirthday() { return birthday; } public void setBirthday(JAXBElement<XMLGregorianCalendar> value) { this.birthday = value; } |
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)以降を見据え、新規構築するリクエスタはできるだけ身軽にしておくべき
- 働き方改革も考えて省力化を考えないとね
- 初回ブログ投稿で頑張りすぎた感が残った