MASATOの開発日記


前の開発日記 次の開発日記 一覧

2003/09/06

最近、コーディングって時間がかかる作業だなーと思います。。 1時間かけてもあまりたいしたことはできませんし、ちょっとしたトラブルであっというまに数時間吹っ飛んでしまいます。。 今はほとんど日曜プログラマなので色々作るのは難しいですなぁ・・・。

java.nioパッケージの未来

Java2 1.4から追加されたパッケージに、java.nioというものがあります。 このパッケージは、Sunのサイトによると、 java.ioパッケージの入出力を補足するものとあります。 しかし、パッケージの内容を良く見てみると、補足するどころか置き換えるほどの能力があります。 但し、今のところjava.nioパッケージ単独でチャネル(入力ストリームのようなもの)を生成できないようです。 よって、今のところは本当にjava.ioパッケージの入出力を補足するものだと思います。 しかし、今後、java.nioに欠けているAPIが幾つか追加されれば、java.nioこそがJavaの標準IOパッケージになるでしょう。

java.nioパッケージは、java.ioパッケージに比べて以下のような優れた点があります。

もしかしてJava製作元はjava.ioを作り直したかったのかなーなんて邪推してみたりして。

ファイルをロックしてDOMで読み書きする(1/2)

環境Java2 1.4 Standard Edition

1つのXMLファイルを、ロックしつつDOMで読み書きする方法についてのお話です。 ロックするためには、ファイルをNew I/O APIのチャネルに関連付ける必要があります。 しかし、Java 1.4のXMLパーサーは、ストリームに結び付けられたXMLファイルを読み込むことはできますが、 チャネルに関連付けられたXMLファイルを読み込むことができません。 よって、以下の処理を行う必要があります。

さて、問題となるのがチャネルをストリームに結びつける方法です。 java.nio.channels.Channels#newInputStreamやjava.nio.channels.Channels#newOutputStreamを使えば 結び付けられることは結び付けられます。 しかし、以下の3つの仕様が組み合わさって致命的な問題が引き起こされます。

致命的な問題とは、javax.xml.transform.Transformer#transformによりXMLパーサーがXMLファイルを読み込むと、 ロックが解除されてしまうことです。これではロックしつつ読み書きすることなんてできません。

これを解決するため、 閉じても、結び付けられたチャネルは閉じられないようなストリームを使うことにしました。 そのようなストリームは、以下のChannelsExtensionクラスを用いて作成します。

import java.io.*;
import java.nio.*;
import java.nio.channels.*;

public class ChannelsExtension {
  public ChannelsExtension() {
    super();
  }
  public static InputStream newUncloseInputStream(ReadableByteChannel ch) {
    class ChannelInputStream extends InputStream {
      private ReadableByteChannel channel;
      public ChannelInputStream(ReadableByteChannel channel) {
        super();
        this.channel = channel;
      }

      public int read() throws IOException {
        if (channel == null)return -1;
        byte[] buf = new byte[1];
        ByteBuffer byteBuf = ByteBuffer.wrap(buf);
        int count = channel.read(byteBuf);
        if (count <= 0)return -1;
        return buf[0];
      }

      public int read(byte[] arg0, int arg1, int arg2) throws IOException {
        if (channel == null)return -1;
        ByteBuffer byteBuf = ByteBuffer.wrap(arg0, arg1, arg2);
        return channel.read(byteBuf);
      }

      public void close() throws IOException {
        super.close();
        if (channel != null) {
          channel = null;
        }
      }
    }
    return new ChannelInputStream(ch);
  }

  public static OutputStream newUncloseOutputStream(WritableByteChannel ch) {
    class ChannelOutputStream extends OutputStream {
      private WritableByteChannel channel;

      public ChannelOutputStream(WritableByteChannel channel) {
        super();
        this.channel = channel;
      }

      public void write(int arg0) throws IOException {
        if (channel == null) return;
        byte[] buf = new byte[] {(byte)arg0};
        ByteBuffer byteBuf = ByteBuffer.wrap(buf);
        channel.write(byteBuf);
      }

      public void write(byte[] arg0, int arg1, int arg2) throws IOException {
        if (channel == null) return;
        ByteBuffer byteBuf = ByteBuffer.wrap(arg0, arg1, arg2);
        channel.write(byteBuf);
      }

      public void close() throws IOException {
        super.close();
        if (channel != null) {
          channel = null;
        }
      }
    }
    return new ChannelOutputStream(ch);
  }
}

ChannelsExtension#newUncloseOutputStream及びChannelsExtension#newUncloseInputStreamで 目的のストリームを作成することができます。 これを使って次はどうするのかというのは・・・次回説明します。

前の開発日記 次の開発日記 一覧