JSON の Jackson が勝手に Stream を close してしまう問題

問題

JSON ライブラリ JacksonObjectMapper を使ったシリアライズ・デシリアライズで、Stream や Reader/Writer を対象にしたメソッドを使うと、そのStream, Reader/Writer が自動的に close されてしまうので、再び読み書きしようとした際に IOException になってしまう。
(Jackson 2.7 時点)

解決策

これは、デフォルトで読み書き後に close されるようになっているためで、この自動 close を無効にすれば解決する。

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);    // read 時の自動 close を無効に
objectMapper.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE);       // write 時の自動 close を無効に

ObjectMapper に与える JsonFactory に対して指定してもいいので、別解としてこんな書き方も。

MessagePackFactory factory = new MessagePackFactory();
factory.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);    // read 時の自動 close を無効に
factory.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE);       // write 時の自動 close を無効に
ObjectMapper objectMapper = new ObjectMapper(factory);

こんな時に問題になる

ObjectMapper#readValue や ObjectMapper#writeValue を使って、複数のオブジェクトを順番にストリームに書き出し/から読み込みする場合、例えばこんなコードで IOException が出る。

ObjectMapper objectMapper = new ObjectMapper();
for (Object msg : msgs) {
   objectMapper.writeValue(out_stream, msg);    // ※ 2 個目の msg を write する時に IOException になる
}
ObjectMapper objectMapper = new ObjectMapper();
while(true) {
   Map<String, Object> msg = objectMapper.readValue(in_stream, HashMap.class);    // ※ 2周目に read する時に IOException になる
   callback(msg);
}

経緯

Jackson と MessagePack と組み合わせて、ネットワーク経由で届く MessagePack エンコードなメッセージを順次処理して〜みたいなことをしようとしてぶつかった。