ものがたり(旧)

atsushieno.hatenablog.com に続く

脱コミュニケーション

Miguelには、1ヶ月の50%くらいはWCF/WCS hackingをやってほしい、と言われているのだけど、現実にはXmlSerializerとWebServiceまわりでほとんど費やしているのが現状だ。が本当に放置しているとdead projectになってしまうので、今週はこっちに手を付けることにした。

WCFで一番基本的なクラスはMessageなのだけど、こいつもめんどくさい。基本的にMessageは一度WriteMessage()で使い切ったら終わりである。何度も使うには、CreateBufferedCopy()で返ってくるMessageBufferを使う必要がある。MessageBufferにはCreateMessage()と別にCreateNavigator()というXPathNavigatorを返しちゃったりする迷惑な奴がある。これが単なるXPathNavigatorなら良いのだが、SeekableXPathNavigatorなんてものを返しちゃったりする。

MessageもMessageBufferもabstractなので、実装はCreateMessage()でどう作られたかによって変わるのだけど、基本的にMessageBufferは内部でIXPathNavigableをもっている(実装はSystem.ServiceModel.dllの中にあるMono.Xml.XPath.DTMXPathDocument2というクラス)。これはXPathDocumentの実装とあまり変わらないが、SeekableXPathNavigatorを返す。これはApache XalanのDTMのように、ノードをintの"position"で指示することができる。これによって、内部的にはかなりのメモリ消費量が抑えられるというわけだ(従来は至る所でClone()が必要だった)。

このMessageBufferがCreateMessage()で返すMessageはトリッキーな構造になっている。まずIXPathNavigableからXPathNavigatorを生成し、そいつからReadSubtree()を呼び出すとXmlReaderが返ってくるので、コレをCreateMessage()のXmlReader引数と同じように扱ってMessageを返す。ReadSubtree()の実装(System.Xml.dll)にはXPathNavigatorReaderというクラスが使われている。

ちなみに、IXPathNavigableの生成には、DTMXPathDocumentWriter2というクラスを使って、Message.WriteMessage()に食わせた後に出来上がるDTMXPathDocument2を使っているのだけど、コレと同じ事がnew XmlDocument().CreateNavigator().AppendChild()で返されるXmlWriterでも出来るのだけど、XmlDocument.CreateNavigator()で返されるのはSeekableXPathNavigatorではないという点が(些細ながら)問題になる。

一部の例外を除いて、個人的にはうまいこと楽して実装していると思っているのだけど、これだけ役者が揃っていると、Messageが期待通りの出力を見せてくれないとき(どこかにバグがあるとき)、一体どこに原因があるのかは明白ではないのが困りものだ。今日もSystem.Xml.dllのクラスのしょうもないバグを見つけるのに数時間かかってしまった。多分誰もこれがWCF hackingに関係していると思わないだろうな…