package bitNom; import org.ccnx.ccn.io.CCNFileInputStream; import java.io.IOException; import java.nio.*; import org.ccnx.ccn.CCNHandle; import org.ccnx.ccn.config.ConfigurationException; import org.ccnx.ccn.io.CCNInputStream; import org.ccnx.ccn.protocol.ContentName; import org.ccnx.ccn.protocol.MalformedContentNameStringException; import org.ccnx.ccn.utils.CommonParameters; //TODO: // Decide which peer to download from first. i.e. which order to query the peers in. // The Downloader should provide methods to: //- Given a list of known locations of a file and a single segment number, // attempt to download that segment from any one of the peers. public class SegDownloader implements Runnable { public String dlPath; private Dstatus status; private ChunkDownload parent; private int seg; public Dstatus status(){ return status; } public int seg() { return seg; } SegDownloader(ChunkDownload par, String ccnPath, int segNum){ parent = par; dlPath = ccnPath; status = Dstatus.NONE; seg = segNum; } public void run(){ parent.addActive(this); while (status != Dstatus.FINISHED) { String oldPath = dlPath; download(); synchronized (parent) { // If the downloading failed, notify the Download to give us a new path. if (status == Dstatus.FAILED) { parent.addStopped(this); } // If the downloading succeeded, notify the Download we're done else if (status == Dstatus.FINISHED) { parent.finishSegment(seg); parent.removeActive(this); parent.addStopped(this); } } synchronized (this){ if (status == Dstatus.FAILED) try { // Wait for our download path to update. // This is in a while loop in case the Download gives us a new download // before we even start waiting, or if we get woken up by // something besides the Download somehow. while (oldPath == dlPath) { wait(); } } catch (InterruptedException e) { // TODO Auto-generated catch block //e.printStackTrace(); } } } } // Try to download from the current peer. // This section is mostly copied and pasted from ccngetfile. // Except the file output stream was changed into a file channel // in order to facilitate skipping by some byte offset. private void download(){ status = Dstatus.DOWNLOADING; try { int readsize = 1024; // make an argument for testing... // If we get one file name, put as the specific name given. // If we get more than one, put underneath the first as parent. // Ideally want to use newVersion to get latest version. Start // with random version. ContentName argName = ContentName.fromURI(dlPath); CCNHandle handle = CCNHandle.open(); long starttime = System.currentTimeMillis(); CCNInputStream input; if (CommonParameters.unversioned) input = new CCNInputStream(argName, handle); else input = new CCNFileInputStream(argName, handle); // Set the stream timeout to be pretty high. It takes a while // for the other end to generate the segment. input.setTimeout(100000); // Buffer to read from the ccn stream byte [] buffer = new byte[readsize]; ByteBuffer buf = ByteBuffer.wrap(buffer); int readcount = 0; int readtotal = 0; long readtimes = 0; // Seek the stream to the point where our segment starts // This gets done in both the uploader and downloader. // The uploader does it to serve the segment it needs to, // and the downloader does it to automatically make the stream // request the correct segment. A seek attempts to not get // more bytes than is necessary from the stream. long position = (Globals.segSize * seg); input.seek(position); // While we can still read bytes from the ccn stream* // *The method auto-blocks. It *should* only fail when we reach the end. while ((readcount = input.read(buffer)) != -1 && readtotal < Globals.segSize){ readtotal += readcount; parent.channel.write(buf, position + readtimes * readsize); buf.position(0); // Update the percent we have downloaded. parent.percentDone += 1 + readcount/(Globals.segSize * parent.nSeg()); readtimes++; } // Truncate the file if we're the last segment, since we wrote past the actual end of the file. if (seg == parent.nSeg() - 1) { parent.channel.truncate(((parent.nSeg() - 1) * Globals.segSize) + readtotal); } if (Globals.dbDL || Globals.dbSD){ System.out.println("Segment took: "+(System.currentTimeMillis() - starttime)+"ms"); System.out.println("Retrieved Segment " + seg + " of " + dlPath + " got " + readtotal + " bytes."); } status = Dstatus.FINISHED; // Exceptions mostly untouched from ccngetfile, except instead of exiting, we tell the // Download that we failed. } catch (ConfigurationException e) { System.out.println("Configuration exception in ccngetfile: " + e.getMessage()); synchronized(parent){ status = Dstatus.FAILED; //parent.bstopped.add(this); } } catch (MalformedContentNameStringException e) { System.out.println("Malformed name: " + dlPath + " " + e.getMessage()); synchronized(parent){ status = Dstatus.FAILED; //parent.bstopped.add(this); } } catch (IOException e) { System.out.println("Cannot write file or read content. " + e.getMessage()); synchronized(parent){ status = Dstatus.FAILED; //parent.bstopped.add(this); } } } }