/*
* Dijjer - A Peer to Peer HTTP Cache
* Copyright (C) 2004,2005 Change.Tv, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package freenet.io.xfer;
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import freenet.support.Buffer;
import freenet.support.LogThresholdCallback;
import freenet.support.Logger;
import freenet.support.Logger.LogLevel;
/**
* @author ian
*
* To change the template for this generated type comment go to Window - Preferences - Java - Code Generation - Code and
* Comments
*/
public class PartiallyReceivedBlock {
private static volatile boolean logMINOR;
private static volatile boolean logDEBUG;
static {
Logger.registerLogThresholdCallback(new LogThresholdCallback(){
@Override
public void shouldUpdate(){
logMINOR = Logger.shouldLog(LogLevel.MINOR, this);
logDEBUG = Logger.shouldLog(LogLevel.DEBUG, this);
}
});
}
byte[] _data;
boolean[] _received;
int _receivedCount;
public final int _packets, _packetSize;
boolean _aborted;
boolean _abortedLocally;
int _abortReason;
String _abortDescription;
ArrayList<PacketReceivedListener> _packetReceivedListeners = new ArrayList<PacketReceivedListener>();
public PartiallyReceivedBlock(int packets, int packetSize, byte[] data) {
if (data.length != packets * packetSize) {
throw new RuntimeException("Length of data ("+data.length+") doesn't match packet number and size");
}
_data = data;
_received = new boolean[packets];
for (int x=0; x<_received.length; x++) {
_received[x] = true;
}
_receivedCount = packets;
_packets = packets;
_packetSize = packetSize;
}
public PartiallyReceivedBlock(int packets, int packetSize) {
_data = new byte[packets * packetSize];
_received = new boolean[packets];
_packets = packets;
_packetSize = packetSize;
}
public synchronized Deque<Integer> addListener(PacketReceivedListener listener) throws AbortedException {
if (_aborted) {
throw new AbortedException("Adding listener to aborted PRB");
}
_packetReceivedListeners.add(listener);
Deque<Integer> ret = new LinkedList<Integer>();
for (int x = 0; x < _packets; x++) {
if (_received[x]) {
ret.addLast(x);
}
}
return ret;
}
public synchronized boolean isReceived(int packetNo) throws AbortedException {
if (_aborted) {
throw new AbortedException("PRB is aborted");
}
return _received[packetNo];
}
public synchronized int getNumPackets() throws AbortedException {
if (_aborted) {
throw new AbortedException("PRB is aborted");
}
return _packets;
}
public synchronized int getPacketSize() throws AbortedException {
if (_aborted) {
throw new AbortedException("PRB is aborted");
}
return _packetSize;
}
public void addPacket(int position, Buffer packet) throws AbortedException {
PacketReceivedListener[] prls;
synchronized(this) {
if (_aborted) {
throw new AbortedException("PRB is aborted");
}
if (packet.getLength() != _packetSize) {
throw new RuntimeException("New packet size "+packet.getLength()+" but expecting packet of size "+_packetSize);
}
if (_received[position])
return;
_receivedCount++;
packet.copyTo(_data, position * _packetSize);
_received[position] = true;
// FIXME keep it as as an array
prls = _packetReceivedListeners.toArray(new PacketReceivedListener[_packetReceivedListeners.size()]);
}
for (PacketReceivedListener prl: prls) {
prl.packetReceived(position);
}
}
public synchronized boolean allReceivedAndNotAborted() {
return _receivedCount == _packets && !_aborted;
}
public synchronized boolean allReceived() throws AbortedException {
if(_receivedCount == _packets) {
if(logDEBUG) Logger.debug(this, "Received "+_receivedCount+" of "+_packets+" on "+this);
return true;
}
if (_aborted) {
throw new AbortedException("PRB is aborted: "+_abortReason+" : "+_abortDescription+" received "+_receivedCount+" of "+_packets+" on "+this);
}
return false;
}
public synchronized byte[] getBlock() throws AbortedException {
if(allReceived()) return _data;
throw new RuntimeException("Tried to get block before all packets received");
}
public synchronized Buffer getPacket(int x) throws AbortedException {
if (_aborted) {
throw new AbortedException("PRB is aborted");
}
if (!_received[x]) {
throw new IllegalStateException("that packet is not received");
}
return new Buffer(_data, x * _packetSize, _packetSize);
}
public synchronized void removeListener(PacketReceivedListener listener) {
_packetReceivedListeners.remove(listener);
}
/**
* Abort the transfer.
* @param reason
* @param description
* @param cancelledLocally If true, the transfer was deliberately aborted by *this node*,
* not as the result of a timeout.
* @return Null if the PRB is aborted now. The data if it is not.
*/
public byte[] abort(int reason, String description, boolean cancelledLocally) {
PacketReceivedListener[] listeners;
synchronized(this) {
if(_aborted) {
if(logMINOR) Logger.minor(this, "Already aborted "+this+" : reason="+_abortReason+" description="+_abortDescription);
return null;
}
if(_receivedCount == _packets) {
if(logMINOR) Logger.minor(this, "Already received");
return _data;
}
Logger.normal(this, "Aborting PRB: "+reason+" : "+description+" on "+this, new Exception("debug"));
_aborted = true;
_abortedLocally = cancelledLocally;
_abortReason = reason;
_abortDescription = description;
listeners = _packetReceivedListeners.toArray(new PacketReceivedListener[_packetReceivedListeners.size()]);
_packetReceivedListeners.clear();
}
for (PacketReceivedListener prl : listeners) {
prl.receiveAborted(reason, description);
}
return null;
}
public synchronized boolean isAborted() {
return _aborted;
}
public synchronized int getAbortReason() {
return _abortReason;
}
public synchronized String getAbortDescription() {
return _abortDescription;
}
public static interface PacketReceivedListener {
public void packetReceived(int packetNo);
public void receiveAborted(int reason, String description);
}
public boolean abortedLocally() {
return _abortedLocally;
}
}