package hep.io.root.daemon.xrootd;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* Read from an open file.
* @author tonyj
*/
class ReadOperation extends Operation<Integer> {
private OpenFile file;
ReadOperation(OpenFile file, long fileOffset, byte[] buffer, int bufOffset, int size) {
super("read", new ReadMessage(file, fileOffset, size), new ReadCallback(ByteBuffer.wrap(buffer, bufOffset, size)));
this.file = file;
}
ReadOperation(OpenFile file, long fileOffset, ByteBuffer buffer) {
super("read", new ReadMessage(file, fileOffset, buffer.remaining()), new ReadCallback(buffer));
this.file = file;
}
ReadOperation(OpenFile file, FileChannel fileChannel, long fileOffset, int size) {
super("read", new ReadMessage(file, fileOffset, size), new FileReadCallback(fileChannel, fileOffset, size));
this.file = file;
}
@Override
Operation getPrerequisite() {
return new OpenOperation(file);
}
@Override
Destination getDestination() {
return file.getDestination();
}
@Override
Multiplexor getMultiplexor() {
return file.getMultiplexor();
}
private static class ReadMessage extends Message {
private OpenFile file;
private long fileOffset;
private int size;
ReadMessage(OpenFile file, long fileOffset, int size) {
super(XrootdProtocol.kXR_read);
this.file = file;
this.fileOffset = fileOffset;
this.size = size;
}
@Override
void writeExtra(ByteBuffer out) throws IOException {
// Note, we do things this way because the file handle may have changed
// since we were created, as a result of a redirect.
out.putInt(file.getHandle());
out.putLong(fileOffset);
out.putInt(size);
}
}
private static class ReadCallback extends Callback<Integer> {
private ByteBuffer buffer;
private int initialPosition;
ReadCallback(ByteBuffer buffer) {
this.initialPosition = buffer.position();
this.buffer = buffer;
}
public Integer responseReady(Response response) throws IOException {
response.readData(buffer);
int result = buffer.position()-initialPosition;
return result == 0 ? -1 : result;
}
@Override
public void clear() {
buffer.position(initialPosition);
}
}
private static class FileReadCallback extends Callback<Integer> {
private FileChannel fileChannel;
private long bufOffset;
private long readLength;
private int bufLength;
FileReadCallback(FileChannel fileChannel, long bufOffset, int bufLength) {
this.bufOffset = bufOffset;
this.readLength = 0;
this.bufLength = bufLength;
this.fileChannel = fileChannel;
}
public Integer responseReady(Response response) throws IOException {
// FIXME: If this fails how do we know if it is a socket problem or a file problem?
int ll = 0;
while (ll<response.getLength()) {
long l = fileChannel.transferFrom(response.getSocketChannel(), bufOffset+readLength+ll, response.getLength()-ll);
if (l<=0) throw new EOFException();
ll += l;
}
readLength += ll;
return readLength == 0 ? -1 : (int) readLength;
}
@Override
public void clear() {
readLength = 0;
}
}
}