package uc.files.transfer; import helpers.GH; import helpers.PreferenceChangedAdapter; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import logger.LoggerFactory; import org.apache.log4j.Logger; import uc.DCClient; import uc.PI; import uc.protocols.Compression.CompressionWrapper; import uc.protocols.client.ClientProtocol; public class Download extends AbstractFileTransfer { private static final int DRAINTIME = 20; private static Semaphore downloads = new Semaphore(Integer.MAX_VALUE); private static volatile int maxSpeed; private static void update() { maxSpeed = PI.getInt(PI.downloadLimit); if (maxSpeed <= 0) { maxSpeed = Integer.MAX_VALUE /DRAINTIME ; } } static { new PreferenceChangedAdapter(PI.get(),PI.downloadLimit) { @Override public void preferenceChanged(String preference,String oldValue, String newValue) { update(); } }; update(); DCClient.getScheduler().scheduleAtFixedRate(new Runnable() { private int i = 0; private int overDue = 0; public void run() { if (++i % DRAINTIME == 0 ) { downloads.drainPermits(); } downloads.release((maxSpeed+overDue)/10); overDue = (maxSpeed+overDue)%10; } },1,100,TimeUnit.MILLISECONDS); } private static final Logger logger = LoggerFactory.make(); private volatile long bytesDownloaded; /** * the interval where we are writing to */ private final AbstractWritableFileInterval wfc; private volatile ReadableByteChannel source; private volatile ByteChannel socketChannel; //channel representing the internet connection public Download(FileTransferInformation fti,ClientProtocol cp, AbstractWritableFileInterval wfc) throws IOException { super(fti,cp); this.wfc = wfc; } public void cancel() { logger.debug("Cancelling download"); GH.close(socketChannel,source); } public AbstractFileInterval getFileInterval() { return wfc; } @Override public boolean isUpload() { return false; } /** * starts the upload.. */ @Override public void transferData(ByteChannel src) throws IOException { logger.debug("in Download.transferData()"); this.socketChannel = src; CompressionWrapper cw = getCompression().wrapIncoming(src); this.compressor = cw; source = cw.getRbc(); //add compression WritableByteChannel target = wfc.getWriteChannel(); ByteBuffer bb = ByteBuffer.allocate(BUFFER_SIZE); bb.clear(); try { notifyObservers(TransferChange.STARTED); logger.debug("start transferring"); int written; int toAcquire = 0; while ((source.isOpen() && source.read(bb) >= 0) || bb.position() != 0) { //second is for writing to bb.flip(); if ((written = target.write(bb)) < 0) { //if no bytes can be written... break break; } bytesDownloaded += written; toAcquire += written; downloads.acquireUninterruptibly(toAcquire / 1024); toAcquire %= 1024 ; bb.compact(); //break if no more bytes are left.. if (wfc.getRelativeCurrentPos() == wfc.length()) { break; } } } finally { // logger.info("end download: "+wfc.getCurrentPosition() + " "+wfc.length()); GH.close(target); //source is not closed.. notifyObservers(TransferChange.FINISHED); logger.debug("end transferData()"); } } @Override public long getBytesTransferred() { return bytesDownloaded; } }