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にあるような気がするけど、はてさて...