/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * @author Scott Ferguson */ package com.caucho.vfs; import java.io.IOException; import java.io.InterruptedIOException; import com.caucho.inject.Module; import com.caucho.util.L10N; /** * Stream using with JNI. */ @Module public class JniStream extends StreamImpl { private static final L10N L = new L10N(JniStream.class); private final static int INTERRUPT_EXN = -2; private final static int DISCONNECT_EXN = -3; public final static int TIMEOUT_EXN = -4; private static NullPath NULL_PATH; private final JniSocketImpl _socket; private IOException _readException; private long _totalReadBytes; private long _totalWriteBytes; /** * Create a new JniStream based on the java.io.* stream. */ public JniStream(JniSocketImpl socket) { _socket = socket; if (NULL_PATH == null) NULL_PATH = new NullPath("jni-stream"); setPath(NULL_PATH); } public void init() { _readException = null; } @Override public boolean canRead() { return ! _socket.isClosed(); } @Override public boolean isClosed() { return _socket.isClosed(); } @Override public int read(byte []buf, int offset, int length) throws IOException { if (buf == null) throw new NullPointerException(); else if (offset < 0 || buf.length < offset + length) throw new ArrayIndexOutOfBoundsException(); else if (_readException != null) throw _readException; int result = _socket.read(buf, offset, length, -1); if (result > 0) { _totalReadBytes += result; return result; } else if (result < -1) { _readException = exception(result); throw _readException; } else if (length == 0 && result == 0) return 0; else return -1; } @Override public int readTimeout(byte []buf, int offset, int length, long timeout) throws IOException { if (buf == null) throw new NullPointerException(); else if (offset < 0 || buf.length < offset + length) throw new ArrayIndexOutOfBoundsException(); int result = _socket.read(buf, offset, length, timeout); if (result > 0) { _totalReadBytes += result; return result; } else if (result == TIMEOUT_EXN) { return ReadStream.READ_TIMEOUT; } else if (result < -1) { throw exception(result); } else return -1; } @Override public int getAvailable() throws IOException { return isEof() ? -1 : 1; } // XXX: needs update @Override public boolean isEof() throws IOException { return _socket.isEof(); } @Override public boolean canWrite() { return ! _socket.isClosed(); } @Override public void write(byte []buf, int offset, int length, boolean isEnd) throws IOException { if (length <= 0) return; else if (buf == null) throw new NullPointerException(); else if (offset < 0 || buf.length < offset + length) throw new ArrayIndexOutOfBoundsException(); int result = _socket.write(buf, offset, length, isEnd); if (result < -1) { // server/1l21: -1 with exception is necessary to catch client disconnect throw exception(result); } else if (result == -1) { throw new ClientDisconnectException(L.l("unexpected end of file in write")); } _totalWriteBytes += result; } public void flush() throws IOException { } public long getTotalReadBytes() { return _totalReadBytes; } public long getTotalWriteBytes() { return _totalWriteBytes; } IOException exception(int result) throws IOException { switch (result) { case INTERRUPT_EXN: return new InterruptedIOException("interrupted i/o"); case DISCONNECT_EXN: return new ClientDisconnectException("connection reset by peer"); case TIMEOUT_EXN: return new SocketTimeoutException("client timeout"); default: return new ClientDisconnectException("unknown exception=" + result); } } /** * Closes the stream. */ @Override public void close() throws IOException { _socket.close(); } /* public void finalize() throws IOException { close(); } */ @Override public String toString() { return getClass().getSimpleName() + "[" + _socket + "]"; } }