Author: toad Date: 2008-04-12 12:53:36 +0000 (Sat, 12 Apr 2008) New Revision: 19225
Modified: trunk/freenet/src/freenet/clients/http/QueueToadlet.java Log: Persist completion notifications across restarts, until the user deletes them. Modified: trunk/freenet/src/freenet/clients/http/QueueToadlet.java =================================================================== --- trunk/freenet/src/freenet/clients/http/QueueToadlet.java 2008-04-12 12:21:54 UTC (rev 19224) +++ trunk/freenet/src/freenet/clients/http/QueueToadlet.java 2008-04-12 12:53:36 UTC (rev 19225) @@ -3,14 +3,24 @@ * http://www.gnu.org/ for further details of the GPL. */ package freenet.clients.http; +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.EOFException; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URI; import java.text.NumberFormat; import java.util.Collections; import java.util.Comparator; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -41,6 +51,7 @@ import freenet.support.api.HTTPRequest; import freenet.support.api.HTTPUploadedFile; import freenet.support.io.BucketTools; +import freenet.support.io.Closer; import freenet.support.io.FileBucket; public class QueueToadlet extends Toadlet implements RequestCompletionCallback { @@ -74,6 +85,7 @@ this.fcp = fcp; if(fcp == null) throw new NullPointerException(); fcp.setCompletionCallback(this); + loadCompletedIdentifiers(); } public void handlePost(URI uri, HTTPRequest request, ToadletContext ctx) throws ToadletContextClosedException, IOException, RedirectException { @@ -1055,12 +1067,133 @@ return "GET, POST"; } + /** + * List of completed request identifiers which the user hasn't acknowledged yet. + */ + private final HashSet completedRequestIdentifiers = new HashSet(); + public void notifyFailure(ClientRequest req) { // FIXME do something??? } public void notifySuccess(ClientRequest req) { - // FIXME persist across restarts. + synchronized(completedRequestIdentifiers) { + completedRequestIdentifiers.add(req.getIdentifier()); + } + registerAlert(req); + saveCompletedIdentifiersOffThread(); + } + + private void saveCompletedIdentifiersOffThread() { + core.getExecutor().execute(new Runnable() { + public void run() { + saveCompletedIdentifiers(); + } + }, "Save completed identifiers"); + } + + private void loadCompletedIdentifiers() { + File completedIdentifiersList = new File(core.node.getNodeDir(), "completed.list"); + File completedIdentifiersListNew = new File(core.node.getNodeDir(), "completed.list.bak"); + if(!readCompletedIdentifiers(completedIdentifiersList)) { + readCompletedIdentifiers(completedIdentifiersListNew); + } + String[] identifiers; + synchronized(completedRequestIdentifiers) { + identifiers = (String[]) completedRequestIdentifiers.toArray(new String[completedRequestIdentifiers.size()]); + } + for(int i=0;i<identifiers.length;i++) { + ClientRequest req = fcp.getGlobalClient().getRequest(identifiers[i]); + if(req == null) continue; + registerAlert(req); + } + } + + private boolean readCompletedIdentifiers(File file) { + FileInputStream fis = null; + try { + fis = new FileInputStream(file); + BufferedInputStream bis = new BufferedInputStream(fis); + InputStreamReader isr = new InputStreamReader(bis, "UTF-8"); + BufferedReader br = new BufferedReader(isr); + synchronized(completedRequestIdentifiers) { + completedRequestIdentifiers.clear(); + while(true) { + String identifier = br.readLine(); + completedRequestIdentifiers.add(identifier); + } + } + } catch (EOFException e) { + // Normal + return true; + } catch (FileNotFoundException e) { + // Normal + return false; + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } catch (IOException e) { + Logger.error(this, "Could not read completed identifiers list from "+file); + return false; + } finally { + Closer.close(fis); + } + } + + private void saveCompletedIdentifiers() { + FileOutputStream fos = null; + BufferedWriter bw = null; + File completedIdentifiersList = new File(core.node.getNodeDir(), "completed.list"); + File completedIdentifiersListNew = new File(core.node.getNodeDir(), "completed.list.bak"); + File temp; + try { + temp = File.createTempFile("completed.list", ".tmp", core.node.getNodeDir()); + temp.deleteOnExit(); + fos = new FileOutputStream(temp); + OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8"); + bw = new BufferedWriter(osw); + String[] identifiers; + synchronized(completedRequestIdentifiers) { + identifiers = (String[]) completedRequestIdentifiers.toArray(new String[completedRequestIdentifiers.size()]); + } + for(int i=0;i<identifiers.length;i++) + bw.write(identifiers[i]+'\n'); + } catch (FileNotFoundException e) { + Logger.error(this, "Unable to save completed requests list (can't find node directory?!!?): "+e, e); + return; + } catch (IOException e) { + Logger.error(this, "Unable to save completed requests list: "+e, e); + return; + } finally { + if(bw != null) { + try { + bw.close(); + } catch (IOException e) { + try { + fos.close(); + } catch (IOException e1) { + // Ignore + } + } + } else { + try { + fos.close(); + } catch (IOException e1) { + // Ignore + } + } + } + completedIdentifiersListNew.delete(); + temp.renameTo(completedIdentifiersListNew); + if(!completedIdentifiersListNew.renameTo(completedIdentifiersList)) { + completedIdentifiersList.delete(); + if(!completedIdentifiersListNew.renameTo(completedIdentifiersList)) { + Logger.error(this, "Unable to store completed identifiers list because unable to rename "+completedIdentifiersListNew+" to "+completedIdentifiersList); + } + } + } + + private void registerAlert(ClientRequest req) { + final String identifier = req.getIdentifier(); if(req instanceof ClientGet) { FreenetURI uri = ((ClientGet)req).getURI(); long size = ((ClientGet)req).getDataSize(); @@ -1070,7 +1203,14 @@ L10n.addL10nSubstitution(text, "QueueToadlet.downloadSucceeded", new String[] { "link", "/link", "origlink", "/origlink", "filename", "size" }, new String[] { "<a href=\"/queue/"+uri.toACIIString()+"\">", "</a>", "<a href=\"/"+uri.toACIIString()+"\">", "</a>", name, SizeUtil.formatSize(size) } ); - core.alerts.register(new SimpleHTMLUserAlert(true, title, text, UserAlert.MINOR)); + core.alerts.register(new SimpleHTMLUserAlert(true, title, text, UserAlert.MINOR) { + public void onDismiss() { + synchronized(completedRequestIdentifiers) { + completedRequestIdentifiers.remove(identifier); + } + saveCompletedIdentifiersOffThread(); + } + }); } else if(req instanceof ClientPut) { FreenetURI uri = ((ClientPut)req).getFinalURI(); long size = ((ClientPut)req).getDataSize(); @@ -1080,7 +1220,14 @@ L10n.addL10nSubstitution(text, "QueueToadlet.uploadSucceeded", new String[] { "link", "/link", "filename", "size" }, new String[] { "<a href=\"/"+uri.toACIIString()+"\">", "</a>", name, SizeUtil.formatSize(size) } ); - core.alerts.register(new SimpleHTMLUserAlert(true, title, text, UserAlert.MINOR)); + core.alerts.register(new SimpleHTMLUserAlert(true, title, text, UserAlert.MINOR) { + public void onDismiss() { + synchronized(completedRequestIdentifiers) { + completedRequestIdentifiers.remove(identifier); + } + saveCompletedIdentifiersOffThread(); + } + }); } else if(req instanceof ClientPutDir) { FreenetURI uri = ((ClientPutDir)req).getFinalURI(); long size = ((ClientPutDir)req).getTotalDataSize(); @@ -1091,10 +1238,17 @@ L10n.addL10nSubstitution(text, "QueueToadlet.siteUploadSucceeded", new String[] { "link", "/link", "origlink", "/origlink", "filename", "size", "files" }, new String[] { "<a href=\"/"+uri.toACIIString()+"\">", "</a>", name, SizeUtil.formatSize(size), Integer.toString(files) } ); - core.alerts.register(new SimpleHTMLUserAlert(true, title, text, UserAlert.MINOR)); + core.alerts.register(new SimpleHTMLUserAlert(true, title, text, UserAlert.MINOR) { + public void onDismiss() { + synchronized(completedRequestIdentifiers) { + completedRequestIdentifiers.remove(identifier); + } + saveCompletedIdentifiersOffThread(); + } + }); } } - + String l10n(String key, String pattern, String value) { return L10n.getString("QueueToadlet."+key, pattern, value); }
