package bitNom; import org.ccnx.ccn.impl.support.Log; import org.ccnx.ccn.protocol.ContentName; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; public class Download implements Runnable{ public static final int chunkSize = 524288; public static final int maxActiveChunks = 10; private boolean _finished; public int _amountDone; private int _fileSize; private int _nChunks; private String _inName; private String _outName; private List<ChunkDownload> chunkLoaders; private ArrayBlockingQueue<ChunkDownload> _activeChunks; public List<String> _peers; private ArrayBlockingQueue<Boolean> _waitToken; private int _waiters; public float percentDone() { return 100 * _amountDone/_fileSize; } public boolean finished() { return _finished; } // The regular expression in which chunk numbers must appear in filename // Under this implementation, a file chunk is named, for example, file.txt.001. public static final String chunkEncoding = ".*\\.[0-9]+\\..*"; public static final String chunkNumberEncoding = "\\.[0-9]+\\."; /* =========================================================================================== * * Static functions for handling chunk related things. * Might want to move these to a different class. * * =========================================================================================== */ // Return true if the filename indicates we have a chunk of a file. public static final boolean isChunk(String s){ return s.matches(chunkEncoding); } public static final boolean isChunk(ContentName n){ return isChunk(n.toString()); } // Extract the chunk number from the name of public static final int getChunkNumber(String s){ String copy = s; int retNum = -1; // Get the region that isn't the number String complement[] = copy.split(chunkNumberEncoding); // Remove that region from the string. int j = complement.length; for (int i = 0; i < j; i++) { copy = copy.replace(complement[i], ""); } copy = copy.substring(1, copy.length() - 1); retNum = Integer.parseInt(copy); return retNum; } public static final int getChunkNumber(ContentName n){ return getChunkNumber(n.toString()); } public static final String getChunkName(String s){ String name = s; return name.replaceFirst("\\.[0-9]+\\.", ""); } public static final String getChunkName(ContentName n){ return getChunkName(n.toString()); } public static final File getChunkFilename(File f){ return new File(getChunkName(f.toString())); } /* =========================================================================================== * * The actual Download class methods * * =========================================================================================== */ Download(String inName, String outName, List<String> recentPeers, int nChunks) { _inName = inName; _outName = outName; _peers = recentPeers; _nChunks = nChunks; _activeChunks = new ArrayBlockingQueue<ChunkDownload>(maxActiveChunks); // Right now, this is just an estimate of the file size. _fileSize = nChunks * chunkSize; // Helping variables to allow a thread to blocking wait on this download. _waitToken = new ArrayBlockingQueue<Boolean>(1); _waitToken.add(true); _waiters = 0; } private void concatenateChunks(){ try { RandomAccessFile output; output = new RandomAccessFile(Globals.ourHome + _outName, "rwd"); FileChannel outputChannel = output.getChannel(); int i = 0; // Open every chunk file and transfer the data to the output file // For now, we leave the extra chunks on disk, but now is where // we would delete them. try { for (; i < _nChunks; i++) { byte [] buffer = new byte[Download.chunkSize]; ByteBuffer buf = ByteBuffer.wrap(buffer); FileInputStream curChunk = new FileInputStream(Globals.ourHome + _outName + "." + i +"."); FileChannel inputChannel = curChunk.getChannel(); int w, l; l = inputChannel.read(buf); buf.position(0); w = outputChannel.write(buf); outputChannel.truncate(_fileSize - (Download.chunkSize - l)); if (Globals.dbDL) Log.info("Chunk: Read " + l + " bytes, wrote "+ w + " bytes."); } } catch (FileNotFoundException e) { Log.warning("Missing chunk {0} of file {1}.", i, e.getMessage()); } catch (IOException e) { Log.warning("Error concatenating file {0}", _outName); } }catch (IOException e) { System.out.println("I don't get it" + e.getMessage()); //Log.warning("Missing chunk {0} of file {1}.", i, _outName); } } // Wait on this download to finish. Implemented by trying to push something to the // blocking queue. Since the pusher waits until the queue has space, we only empty // the queue if the download is finished. Call this from a separate thread only. public void waitForMe(){ _waiters++; try { _waitToken.put(true); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void cleanUp() { // "Notify" the waiters that we're done. try { while (_waiters > 0 && _waitToken.take()){ _waiters--; } _finished = true; } catch (InterruptedException e) { Log.severe("Download waiting indefinitely."); } } public void printStatus(){ Log.info("Download: {0}\n\t {1}% complete!", _outName, percentDone()); } // Attempt to start a ChunkDownloader. If the number of active downloaders is met, // the ChunkDownloader will block. public synchronized void addToActive(ChunkDownload c) throws InterruptedException { _activeChunks.put(c); } public void run() { // Create a chunk downloader for every chunk chunkLoaders = Collections.synchronizedList(new ArrayList<ChunkDownload>()); for (int i = 0; i < _nChunks; i++) { String chunkExtension = "." + i + "."; // Number of segments in the current chunk //int nSegments = (int) ((i == _nChunks - 1) ? (_fileSize % chunkSize / Globals.segSize) : chunkSize / Globals.segSize); int nSegments = 1; ChunkDownload temp = new ChunkDownload(_inName + chunkExtension, _outName + chunkExtension, _peers, nSegments, this); chunkLoaders.add(temp); (new Thread(temp)).start(); } // Wait for all of them to finish. for (int i = 0; i < _nChunks; i++) { chunkLoaders.get(i).waitForMe(); _amountDone += chunkSize; } // Piece all the chunks together concatenateChunks(); // Clean up, wake up waiting threads, etc. cleanUp(); } }