AxisでSocket Read Timeout

以下の環境でWebService通信をするプログラムを作成したところ、AxisでSocket Read Timeouのフォルトが返ってきてしまうという事象が発生。

  • クライアントOS : Windows XP
  • クライアント : スマートクライアント(C#)
  • サーバOS : Linux
  • サーバ : Tomcat 5.5 + Axis 1.3

ローカルのTomcat(Windows XP)ではエラーにならなかったのにぃ。

で、tcpmonをかませて見ると、どうも本当にAxisが待っているようだ。クライアント側のContent-Lengthとかあってないんじゃないかと疑って、指折り数えてみるとやっぱり正しい。また、tcpmonでリクエストを再送すると、さくっと正常終了する。
うーん、OSとかが原因なのかもしれないと思いつつ、本当に読めないのかをプログラムで確認することにした。
やり方としては、フィルターをかませて、HttpServletRequestをラップし、そいつが全てのPOSTデータを読み切ってから、Axisから読み出せるようにしてやればいいだろう。

フィルタの処理部分はこんな感じ。

public void doFilter(ServletRequest request,
        ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        
    if (request instanceof HttpServletRequest) {
        HttpServletRequest httpreq =
                (HttpServletRequest)request;
        if ("post".equalsIgnoreCase(
                httpreq.getMethod())) {
            request = new PrereadRequestWrapper(httpreq);
            ((PrereadRequestWrapper)request).readFully();
        }
    }
    chain.doFilter(request, response);
}

で、実際にリクエストをラップしているのはこんな感じ。

package hoge.hoge.foo;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * POSTデータを先読みするリクエストラッパー
 * @author noryksj
 */
public class PrereadRequestWrapper
        extends HttpServletRequestWrapper {
    private static final Log LOG =
        LogFactory.getLog(PrereadRequestWrapper.class);
    private byte postData;
    
    public PrereadRequestWrapper(
            HttpServletRequest parent) {
        super(parent);
    }
    
    protected void readFully() throws IOException {
        int length = getContentLength();
        if (length > 0) {
            ByteArrayOutputStream out =
                new ByteArrayOutputStream(length);
            InputStream in = super.getInputStream();
            byte buff = new byte[512];
            int remain = length;
            while (remain > 0) {
                int readLen = in.read(buff);
                if (readLen <= 0) {
                    break;
                }
                out.write(buff, 0, readLen);
                remain -= readLen;
            }
            out.close();
            postData = out.toByteArray();
            if (LOG.isDebugEnabled()) {
                LOG.debug("Content-Length=" + length +
                    ",ReadRemain=" + remain);
            }
        }
    }

    public ServletInputStream getInputStream()
            throws IOException {
        if (postData != null) {
            return    new ServletInputStreamAdapter(
                    new ByteArrayInputStream(postData));
        }
        return    super.getInputStream();
    }

    private static class ServletInputStreamAdapter
            extends ServletInputStream {
        private InputStream origin;
        
        ServletInputStreamAdapter(InputStream origin) {
            this.origin = origin;
        }
        
        public int readLine(byte b, int off, int len)
                throws IOException {
            throw    new UnsupportedOperationException(
                          "readLine not supported.");
        }
        public int read(byte b, int off, int len)
                throws IOException {
            return    origin.read(b, off, len);
        }
        public int read(byte[] b) throws IOException {
            return    origin.read(b);
        }
        public void mark(int readlimit) {
            origin.mark(readlimit);
        }
        public long skip(long n) throws IOException {
            return    origin.skip(n);
        }
        public void reset() throws IOException {
            origin.reset();
        }
        public int read() throws IOException {
            return    origin.read();
        }
        public boolean markSupported() {
            return    origin.markSupported();
        }
        public void close() throws IOException {
            origin.close();
        }
        public int available() throws IOException {
            return    origin.available();
        }
    }
    
}

ちょっと怪しい感じのコードだが、まあ、いいだろう。
さて、これでどうなるかなーと、実行させてみる。すると...あらら、動いちゃいました。するってーと、問題はOSでもないってことかな。多分、問題はAxisにあるような気がするけど、はてさて...