package org.torrent.internal.io;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Logger;
import org.torrent.internal.util.Validator;
public class DigesterService implements Runnable {
private static final int BUFFER_SIZE = 4096;
private static final CheckRequest END_OF_REQUESTS = new CheckRequest();
private MessageDigest digest;
private BlockingQueue<CheckRequest> requests = new LinkedBlockingQueue<CheckRequest>();
private boolean terminated;
private static Logger log = Logger.getLogger(DigesterService.class
.getName());
public static class CheckRequest {
private final DataReader reader;
private final long dataStart;
private final long dataLength;
private final CheckCallback callback;
public CheckRequest(DataReader reader, long dataStart, long dataLength,
CheckCallback callback) {
Validator.nonNull(reader, callback);
Validator.isTrue(dataStart >= 0, "Negative start of data!");
Validator.isTrue(dataLength > 0, "Invalid length: " + dataLength);
this.reader = reader;
this.dataStart = dataStart;
this.dataLength = dataLength;
this.callback = callback;
}
private CheckRequest() {
reader = null;
dataStart = -1;
dataLength = -1;
callback = null;
}
public DataReader getReader() {
return reader;
}
public long getDataLength() {
return dataLength;
}
public long getDataStart() {
return dataStart;
}
public CheckCallback getCallback() {
return callback;
}
}
public interface CheckCallback {
void caughtException(IOException e);
void resultDigest(byte[] digest);
}
public DigesterService(String algorithm) throws NoSuchAlgorithmException {
digest = MessageDigest.getInstance(algorithm);
}
public void requestCheck(CheckRequest request) {
Validator.notNull(request, "Request is null!");
synchronized (requests) {
if (terminated || requests.contains(END_OF_REQUESTS)) {
throw new IllegalStateException("Service has been terminated!");
}
requests.add(request);
}
}
@Override
public void run() {
try {
synchronized (requests) {
if (terminated) {
throw new IllegalStateException(
"Service has been terminated!");
}
}
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
while (!Thread.interrupted()) {
CheckRequest request = requests.take();
if (request == END_OF_REQUESTS) {
break;
}
DataReader reader = request.getReader();
long remaining = request.getDataLength();
long position = request.getDataStart();
try {
while (remaining > 0) {
buffer.limit((int) Math.min(remaining, buffer
.capacity()));
reader.read(buffer, position);
buffer.flip();
position += buffer.limit();
remaining -= buffer.limit();
digest.update(buffer);
buffer.clear();
}
request.getCallback().resultDigest(digest.digest());
} catch (IOException e) {
request.getCallback().caughtException(e);
}
}
} catch (InterruptedException e) {
log.finest("Got interrupted: " + e);
} finally {
synchronized (requests) {
terminated = true;
requests.notifyAll();
}
}
}
public void terminate() {
synchronized (requests) {
if (terminated || requests.contains(END_OF_REQUESTS)) {
return;
}
requests.add(END_OF_REQUESTS);
}
}
public void awaitTermination(int timeout) {
synchronized (requests) {
if (terminated) {
return;
}
long endTime = System.currentTimeMillis() + timeout;
try {
while (!requests.isEmpty()
&& System.currentTimeMillis() > endTime) {
requests.wait(Math.max(0, endTime
- System.currentTimeMillis()));
}
} catch (InterruptedException e) {
log.finest(e.getLocalizedMessage());
}
}
}
}