Top > xmlrpc

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

** double [#l5f8e1e0]
XMLRPC の浮動小数点数型である double では、[[仕様:http://www.xmlrpc.com/spec]]をざっくり要約すると「無限大の表現はない。符号と数字とピリオドのみで空白は含まれない。値の範囲は実装依存。」ということになっています。

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

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

Java での代表的な実装である [[Apache WS XML-RPC v3.0:http://ws.apache.org/xmlrpc/]] では double の出力には [[Double.toString(double):http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/lang/Double.html#toString(double)]] を使っています。64bit の double なら Python と同様の精度 17 になります。面白いことに Python と Java では異なる表現を出力することがあります。個人的には Python が一風変わっていると思います。

Python 2.6, 3.0
 >>> 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

いずれにせよ XMLRPC のオリジナル仕様のままでは、浮動小数点数の精度落ちは避けられません。精度を確実に保持したいのであれば、base64 型を使って解釈をアプリケーション側で規定するか、XMLRPC 仕様を拡張して浮動小数点数の表現に hex 形式を使うかしなければなりません。

** dateTime.iso8601 [#b0cf6026]
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 の実装では、5.2.7 以降 TIMEZONE がつくようになりました。ちなみに XMLRPC datetime 型の timestamp メンバー変数は PHP 5.2.9 以前では誤った値になっているので、strtotime を使って scalar メンバー変数から変換することで正しい timestamp が得られます( [[PHP report:http://bugs.php.net/bug.php?id=44996]], [[XMLRPC-EPI report:https://sourceforge.net/tracker2/?func=detail&aid=1761368&group_id=23199&atid=377728]], [[XMLRPC-EPI patch:http://sourceforge.net/tracker2/index.php?func=detail&aid=2495315&group_id=23199&atid=377730]])。5.2.10 以降は修正されています。
 // output
 $a=date('c');
 xmlrpc_set_type($a,'datetime');

 // input
 $data=xmlrpc_decode($xml);
 $timestamp=strtotime($data->scalar);

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


** system.multiCall [#j92820ce]
XML-RPC の拡張仕様のひとつに system.multiCall というものがあります。仕様自体は本来は [[xmlrpc.com にあったはず:http://www.xmlrpc.com/discuss/msgReader$1208]]なのですが、長らく消失したままなので、現在は WebArchive などのサービスで探すしか手段がありません。Python の [[xmlrpclib:http://docs.python.org/library/xmlrpclib.html]] では、 system.multicall と小文字で実装されています。また [[PHP xmlrpc extension:http://www.php.net/xmlrpc]] の下位レイヤライブラリ [[xmlrpc-epi:http://xmlrpc-epi.sourceforge.net/]] においては system.multiCall と大文字で実装されているので注意が必要です。個人的には他のメソッド命名規則に合わせると大文字が自然だと思いますが、おそらく不幸な行き違いにより現在の状況に至っていると推測されます。

なお PHP 5.1.12 以前にはバグがあり、system.multiCall の呼び出しを行うと [[PHP がクラッシュします:http://bugs.php.net/bug.php?id=27446]]。必要であれば[[対応するパッチは入手できます:http://mp.i-revo.jp/user.php/kcvcrlkq/entry/346.html]]が、PHP のバージョンアップを優先的に検討してください。
なお PHP 5.2.12 以前にはバグがあり、system.multiCall の呼び出しを行うと [[PHP がクラッシュします:http://bugs.php.net/bug.php?id=27446]]。必要であれば[[対応するパッチは入手できます:http://mp.i-revo.jp/user.php/kcvcrlkq/entry/346.html]]が、PHP のバージョンアップを優先的に検討してください。

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

** UTF-8 [#aca75ed9]
XML-RPC はその名のとおり XML をベースにしているので、本来 [[XML 仕様にあるとおり:http://www.w3.org/TR/REC-xml/#charsets]] UTF-8 を扱うことができなければなりません。ところが、いくつかの誤った実装では XML 仕様に違反していて、UTF-8 でエンコードされた XML-RPC 通信を受け付けません。

[[Apache WS XML-RPC 2.0:http://ws.apache.org/xmlrpc/xmlrpc2/]] は、この問題を抱えています。標準では jar に同梱されている [[MinML:http://www.wilson.co.uk/xml/minml.htm]] という SAX パーサを使って XML を読み込みますが、同梱されているこのパーサは[[文字セット指定を無視するという致命的なバグがある:http://issues.apache.org/jira/browse/XMLRPC-91]]ことが知られています。[[MLでの発言:http://marc.info/?l=xmlrpc-user&m=103864494902626&w=2]]によると MinML の将来的なバージョンで対応する予定があるようですが、現時点では対応されていません(ちなみに現在の XML-RPC 仕様では文字列として ASCII 以外も許可されています)。対処法としては SAX パーサを標準ではないもの、たとえば [[xerces:http://xerces.apache.org/]] に切り替える方法があります。
 XmlRpc.setDriver("xerces");
この他にも、次のような名前で SAX パーサのクラスを切り替えられるようになっています。
| 名称    | クラス名 |h
| xerces  | org.apache.xerces.parsers.SAXParser |
| xp      | com.jclark.xml.sax.Driver |
| ibm1    | com.ibm.xml.parser.SAXDriver |
| ibm2    | com.ibm.xml.parsers.SAXParser |
| aelfred | com.microstar.xml.SAXDriver |
| oracle1 | oracle.xml.parser.XMLParser |
| oracle2 | oracle.xml.parser.v2.SAXParser |
| openxml | org.openxml.parser.XMLSAXParser |

現行の [[Apache WS XML-RPC 3.0:http://ws.apache.org/xmlrpc/]] では XML パーサ部は Java5 で標準化された JAXP つまり [[javax.xml.parsers.SAXParserFactory:http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/javax/xml/parsers/SAXParserFactory.html]] に基づいて実装されていて、デフォルトでは Sun の SAX パーサが使用されますので、この問題は起こらないでしょう。余談ですが JAXP でも必要であれば SAX パーサを切り替えることができます。

** HTTP Status code [#q1d4077e]
XMLRPC においては HTTP 通信路は、まさに通信路でしかありません。HTTP ステータスコードは常に 200 です。それ以外のステータスコードが返された場合は、XMLRPC としては「通信路が壊れた」状態です。XMLRPC において HTTP ステータスコードについて言及するのは、ちょうど HTTP において TCP について言及するのと同じことです。

    ホーム 一覧 検索 最終更新 バックアップ リンク元   ヘルプ   最終更新のRSS

© 2006-2008 Internet Revolution