Package freenet.io.xfer

Source Code of freenet.io.xfer.PartiallyReceivedBulk

/* This code is part of Freenet. It is distributed under the GNU General
* Public License, version 2 (or at your option any later version). See
* http://www.gnu.org/ for further details of the GPL. */
package freenet.io.xfer;

import java.io.IOException;
import java.util.Arrays;

import freenet.io.comm.MessageCore;
import freenet.io.comm.RetrievalException;
import freenet.support.BitArray;
import freenet.support.LogThresholdCallback;
import freenet.support.Logger;
import freenet.support.Logger.LogLevel;
import freenet.support.api.RandomAccessBuffer;

/**
* Equivalent of PartiallyReceivedBlock, for large(ish) file transfers.
* As presently implemented, we keep a bitmap in RAM of blocks received, so it should be adequate
* for fairly large files (128kB for a 1GB file e.g.). We can compress this structure later on if
* need be.
* @author toad
*/
public class PartiallyReceivedBulk {
 
  /** The size of the data being received. Does *not* have to be a multiple of blockSize. */
  final long size;
  /** The size of the blocks sent as packets. */
  final int blockSize;
  private final RandomAccessBuffer raf;
  /** Which blocks have been received and written? */
  private final BitArray blocksReceived;
  final int blocks;
  private BulkTransmitter[] transmitters;
  final MessageCore usm;
  /** The one and only BulkReceiver */
  BulkReceiver recv;
  private int blocksReceivedCount;
  // Abort status
  boolean _aborted;
  int _abortReason;
  String _abortDescription;

        private static volatile boolean logMINOR;
  static {
    Logger.registerLogThresholdCallback(new LogThresholdCallback(){
      @Override
      public void shouldUpdate(){
        logMINOR = Logger.shouldLog(LogLevel.MINOR, this);
      }
    });
  }
 
  /**
   * Construct a PartiallyReceivedBulk.
   * @param size Size of the file, does not have to be a multiple of blockSize.
   * @param blockSize Block size.
   * @param raf Where to store the data.
   * @param initialState If true, assume all blocks have been received. If false, assume no blocks have
   * been received.
   */
  public PartiallyReceivedBulk(MessageCore usm, long size, int blockSize, RandomAccessBuffer raf, boolean initialState) {
    this.size = size;
    this.blockSize = blockSize;
    this.raf = raf;
    this.usm = usm;
    long blocks = (size + blockSize - 1) / blockSize;
    if(blocks > Integer.MAX_VALUE)
      throw new IllegalArgumentException("Too big");
    this.blocks = (int)blocks;
    blocksReceived = new BitArray(this.blocks);
    if(initialState) {
      blocksReceived.setAllOnes();
      blocksReceivedCount = this.blocks;
    }
    assert(raf.size() >= size);
  }

  /**
   * Clone the blocksReceived BitArray. Used by BulkTransmitter to find what blocks are available on
   * creation. BulkTransmitter will have already taken the lock and will keep it over the add() also.
   * @return A copy of blocksReceived.
   */
  synchronized BitArray cloneBlocksReceived() {
    return new BitArray(blocksReceived);
  }
 
  /**
   * Add a BulkTransmitter to the list of BulkTransmitters. When a block comes in, we will tell each
   * BulkTransmitter about it.
   * @param bt The BulkTransmitter to register.
   */
  synchronized void add(BulkTransmitter bt) {
    if(transmitters == null)
      transmitters = new BulkTransmitter[] { bt };
    else {
      transmitters = Arrays.copyOf(transmitters, transmitters.length+1);
      transmitters[transmitters.length-1] = bt;
    }
  }
 
  /**
   * Called when a block has been received. Will copy the data from the provided buffer and store it.
   * @param blockNum The block number.
   * @param data The byte array from which to read the data.
   * @param offset The start of the data in the buffer.
   */
  void received(int blockNum, byte[] data, int offset, int length) {
    if(blockNum > blocks) {
      Logger.error(this, "Received block "+blockNum+" of "+blocks+" !");
      return;
    }
    if(logMINOR)
      Logger.minor(this, "Received block "+blockNum);
    BulkTransmitter[] notifyBTs;
    long fileOffset = (long)blockNum * (long)blockSize;
    int bs = (int) Math.min(blockSize, size - fileOffset);
    if(length < bs) {
      String err = "Data too short! Should be "+bs+" actually "+length;
      Logger.error(this, err+" for "+this);
      abort(RetrievalException.PREMATURE_EOF, err);
      return;
    }
    synchronized(this) {
      if(blocksReceived.bitAt(blockNum)) return; // ignore
      blocksReceived.setBit(blockNum, true); // assume the rest of the function succeeds
      blocksReceivedCount++;
      notifyBTs = transmitters;
    }
    try {
      raf.pwrite(fileOffset, data, offset, bs);
    } catch (Throwable t) {
      Logger.error(this, "Failed to store received block "+blockNum+" on "+this+" : "+t, t);
      abort(RetrievalException.IO_ERROR, t.toString());
    }
    if(notifyBTs == null) return;
    for(BulkTransmitter notifyBT: notifyBTs) {
      // Not a generic callback, so no catch{} guard
      notifyBT.blockReceived(blockNum);
    }
  }

  public void abort(int errCode, String why) {
    if(logMINOR)
      Logger.normal(this, "Aborting "+this+": "+errCode+" : "+why+" first missing is "+blocksReceived.firstZero(0), new Exception("debug"));
    BulkTransmitter[] notifyBTs;
    BulkReceiver notifyBR;
    synchronized(this) {
      _aborted = true;
      _abortReason = errCode;
      _abortDescription = why;
      notifyBTs = transmitters;
      notifyBR = recv;
    }
    if(notifyBTs != null) {
      for(BulkTransmitter notifyBT: notifyBTs) {
        notifyBT.onAborted();
      }
    }
    if(notifyBR != null)
      notifyBR.onAborted();
    raf.close();
  }

  public synchronized boolean isAborted() {
    return _aborted;
  }

  public boolean hasWholeFile() {
    return blocksReceivedCount >= blocks;
  }

  public byte[] getBlockData(int blockNum) {
    long fileOffset = (long)blockNum * (long)blockSize;
    int bs = (int) Math.min(blockSize, size - fileOffset);
    byte[] data = new byte[bs];
    try {
      raf.pread(fileOffset, data, 0, bs);
    } catch (IOException e) {
      Logger.error(this, "Failed to read stored block "+blockNum+" on "+this+" : "+e, e);
      abort(RetrievalException.IO_ERROR, e.toString());
      return null;
    }
    return data;
  }

  public synchronized void remove(BulkTransmitter remove) {
    boolean found = false;
    for(BulkTransmitter t: transmitters) {
      if(t == remove) found = true;
    }
    if(!found) return;
    BulkTransmitter[] newTrans = new BulkTransmitter[transmitters.length-1];
    int j = 0;
    for(BulkTransmitter t: transmitters) {
      if(t == remove) continue;
      newTrans[j++] = t;
    }
    transmitters = newTrans;
  }
 
  public int getAbortReason() {
    return _abortReason;
  }
 
  public String getAbortDescription() {
    return _abortDescription;
  }
}
TOP

Related Classes of freenet.io.xfer.PartiallyReceivedBulk

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.