package ecologylab.concurrent;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Observable;
import ecologylab.generic.Continuation;
public class BatchDownload extends Observable implements Continuation<Object>
{
/**
* Where the Downloadables are placed after download.
* The key can be arbitrary and is not used during download. It IS
* respected and will be maintained across downloads for later
* identification.
*/
Hashtable finalDownloadables;
/**
* The number of total downloads that SHOULD be performed.
*/
int totalNumDownloads;
/**
* The number of downloads that have COMPLETED.
*/
int numberDownloadsSuccessful = 0;
/**
* The number of downloads that failed and will NOT be attempted again.
*/
int numberDownloadsFailed = 0;
DownloadMonitor downloadMonitor;
String batchName = "Batch Downloader";
/**
* Number of threads to use for downloading these Downloadables
*/
int numDownloadThreads;
/**
* HashMap of downloadables keyed with an arbitrary index.
*/
HashMap downloadables;
/**
* An inverted index of downloadables used to map the delivered Downloadable object
* to its key when building the finalDownloadables Hashtable.
*/
HashMap invertedIndexDownloadables;
boolean firstRun = true;
/**
* @param batchName The name that identifies this batch of downloads. Used
* to name the DownloadMonitor.
* @param numDownloadThreads The number of threads to use for simultaneous downloads.
*/
public BatchDownload(String batchName, int numDownloadThreads)
{
this.batchName = batchName;
this.numDownloadThreads = numDownloadThreads;
downloadMonitor = new DownloadMonitor(batchName, numDownloadThreads);
}
/**
* Set the hash map of downloadables that will be downloaded on start.
*
* @param downloadables A HashMap of identifier keys mapped to Downloadables for download
*/
public void setDownloads(HashMap downloadables)
{
//free previous resources (if any), but don't kill the download monitor.
freeResources(false);
numberDownloadsFailed = 0;
numberDownloadsSuccessful = 0;
this.downloadables = new HashMap(downloadables);
totalNumDownloads = downloadables.size();
finalDownloadables = new Hashtable(totalNumDownloads);
invertedIndexDownloadables = new HashMap(totalNumDownloads);
// build the downloadables inverted index for fast lookup based on
//the Downloadable object.
Iterator it = downloadables.keySet().iterator();
while (it.hasNext())
{
Object key = it.next();
Object value = downloadables.get(key);
invertedIndexDownloadables.put(value, key);
}
}
/**
* Begin downloading each page and retrieving it's content.
*/
public void startDownloads()
{
Iterator urlIterator = downloadables.keySet().iterator();
while (urlIterator.hasNext())
{
Downloadable downloadable = (Downloadable) downloadables
.get(urlIterator.next());
downloadMonitor.download(downloadable, this);
}
/*
if (!firstRun)
{
System.out.println("starting downloads manually!");
downloadMonitor.performDownloads();
System.out.println("finished blocking at performDownloads()");
}
firstRun = false;
*/
}
@Override
public void callback(Object o)
{
if (o == null)
{
System.err.println("Null download delivery");
notifyObserversIfFinished();
numberDownloadsFailed++;
return;
}
numberDownloadsSuccessful++;
Downloadable downloadable = (Downloadable) o;
Object originalKey = invertedIndexDownloadables.get(downloadable);
finalDownloadables.put(originalKey, downloadable);
notifyObserversIfFinished();
}
public boolean isDone()
{
return ((numberDownloadsFailed + numberDownloadsSuccessful) == totalNumDownloads);
}
protected void notifyObserversIfFinished()
{
if (isDone())
{
setChanged();
notifyObservers();
}
}
public Hashtable getDownloads()
{
return finalDownloadables;
}
/**
* Free any resources associated with instantiating and using this object.
* This is only necessary to attempt to minimize how much time elapses
* before this object is garbage collected after use and nullification.
*
* @param freeDownloadMonitor Whether or not to stop and clear the download monitor.
*/
protected void freeResources(boolean freeDownloadMonitor)
{
if (finalDownloadables != null)
{
finalDownloadables.clear();
finalDownloadables = null;
}
if (downloadables != null)
{
downloadables.clear();
downloadables = null;
}
if (invertedIndexDownloadables != null)
{
invertedIndexDownloadables.clear();
invertedIndexDownloadables = null;
}
if (freeDownloadMonitor && downloadMonitor != null)
{
downloadMonitor.stop();
downloadMonitor = null;
}
}
public void freeResources()
{
freeResources(true);
}
}