XMLRPC 使用上の注意点

システム間結合をする際 XML-RPC という便利な仕様があります。この仕様はよくできていて、仕様が軽量である上に、よほどのことがなければトラブルは起こらないでしょう。以下は少ないトラブルのうち、実際に経験した内容です。

double

XMLRPC の浮動少数型である double では、仕様をざっくり要約すると「無限大の表現はない。符号と数字とピリオドのみで空白は含まれない。値の範囲は実装依存。」ということになっています。

Python の xmlrpclib では、XMLRPC の浮動少数型である double の出力は repr() で生成するので、精度は 17 になります(浮動少数)。また repr() は 2.23e-05 といった XMLRPC 仕様に適合しない文字列表現を出力することがありますが、実用上は問題ありません。余談になりますが str() でも浮動少数を文字列に変換した場合の精度は 12 で、また%演算子を使ったフォーマットを使うとそれ相応の精度に切り詰められます。decimal を使うと高精度が扱えますが、システム間結合を目的としている XMLRPC では通信先での受け入れができなければ意味がないので、これが登場することは稀でしょう。

PHP の xmlrpc extension では XMLRPC の浮動少数の出力は zval から取得(Z_DVAL_P を使用)し、それを ap_php_snprintf の %.*G でフォーマットしますので php.ini の precision に依存します。つまりデフォルトでは精度は 14 になります。こちらも NAN, INF, -INF や 2.23E-5 といった文字列を出力することがありますが、実用上は問題ないでしょう。面白いことに Python, PHP 間で指数表現形式は異なります。

Java での代表的な実装である Apache XML-RPC では double の出力には Double.toString(double) を使っています。64bit の double なら Python と同様の精度 17 になります。面白いことに Python と Java では異なる表現を出力することがあります。個人的には Python が一風変わっていると思います。

Python 2.6

>>> repr(0.000000023)
'2.3000000000000001e-08'

Java 1.6.0_10

class hoge {
 public static void main(String[] argv){
  System.out.println(0.000000023);
 }
}
> java hoge
2.3E-8

dateTime.iso8601

ISO 8601 の仕様は 2 回改定が行われていて、改定のたびに表現形式が増えています。XML-RPC でのシステム間結合時に全てのフォーマットに対応するのは現実的ではありません。秒単位までの精度表現が使われて、いくつかの限定されたフォーマットにのみ対応しているのが実情です。

世の中で一般的に採用される傾向にあるのが、2009-01-07T12:45:32+09:00 といったフォーマットです。困ったことに XML-RPC で例として挙げられている形式はこれとは異なり、19980717T14:08:55 という形式になっています。この問題点は TIMEZONE が与えられないことで、XML-RPC 仕様では、それぞれのサーバがどの TIMEZONE を使うかを明示的に宣言するとされています。うっかり油断していると、システム間での TIMEZONE の違いに起因して正しく時刻を伝えられないかもしれないので、注意が必要です。個人的には TIMEZONE つきで通信してしまうのがいいと思っています。

PHP xmlrpc extension の実装では、残念ながら出力に TIMEZONE はつきません(仕様)。入力時は TIMEZONE 指定があったとしても無視されてしまいます(バグ)。PHP には strtotime という便利な関数がありますが、これは活用されていません。

$a=date('c');
xmlrpc_set_type($a,'datetime');

Python xmlrpclib の実装でも出力時に TIMEZONE はつかない(仕様)上に、入力受け取り時も time.strptime(data, "%Y%m%dT%H:%M:%S") としているので失敗します(バグ)。

system.multiCall

XML-RPC の拡張仕様のひとつに system.multiCall というものがあります。仕様自体は本来は xmlrpc.com にあったはずなのですが、長らく消失したままなので、現在は WebArchive などのサービスで探すしか手段がありません。Python の xmlrpclib では、 system.multicall と小文字で実装されています。また PHP xmlrpc extension の下位レイヤライブラリ xmlrpc-epi においては system.multiCall となぜか大文字で実装されているうえに、PHP extension 側が対応していないため、呼び出しを行うと PHP がクラッシュします。実に不憫です。必要であれば対応するパッチは入手できます。個人的には他のメソッド命名規則に合わせると大文字が自然だと思いますが、おそらく不幸な行き違いにより現在の状況に至っていると推測されます。

追い討ちをかけるようですが system.multiCall でリクエストをシリアライズすると、通常シリアルにしか実行されないので、むしろパラレル化を行ったほうがシステム結合において全体のレスポンスタイム短縮化の恩恵が大きいことが多いです。この拡張仕様を積極的に使う理由は特にないかもしれません。

UTF-8

XML-RPC はその名のとおり XML をベースにしているので、本来 XML 仕様にあるとおり UTF-8 を扱うことができなければなりません。ところが、いくつかの誤った実装では XML 仕様に違反していて、UTF-8 でエンコードされた XML-RPC 通信を受け付けません。

Apache WS XML-RPC 2.0 は、この問題を抱えています。標準では jar に同梱されている MinML という SAX パーサが使用されますが、同梱されているこのパーサは文字セット指定を無視するという致命的なバグがあることが知られています。MLでの発言によると MinML の将来的なバージョンで対応する予定があるようですが、現時点では対応されていません(ちなみに現在の XML-RPC 仕様では文字列として ASCII 以外も許可されています)。対処法としてはパーサを xerses に切り替える方法があります。

XmlRpc.setDriver("xerces");

この他にも、次のような名前でパーサのクラスを切り替えられるようになっています。

名称クラス名
xercesorg.apache.xerces.parsers.SAXParser
xpcom.jclark.xml.sax.Driver
ibm1com.ibm.xml.parser.SAXDriver
ibm2com.ibm.xml.parsers.SAXParser
aelfredcom.microstar.xml.SAXDriver
oracle1oracle.xml.parser.XMLParser
oracle2oracle.xml.parser.v2.SAXParser
openxmlorg.openxml.parser.XMLSAXParser

現行の Apache WS XML-RPC 3.0 では XML パーサ部は Java5 で標準化された JAXP に基づいて実装されていてデフォルトでは Sun のパーサが使用されますので、この問題は起こらないでしょう。余談ですが JAXP でも必要であればパーサを切り替えることができます。


    Front page List of pages Search Recent changes Backup Referer   Help   RSS of recent changes

© 2006-2008 Internet Revolution