/*
* 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;
}
// XXX: needs update
public int getAvailable() throws IOException
{
return 0;
}
@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();
}
*/
}