Add possibility to restart/research failed downloads.
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sun, 5 Jan 2014 12:25:26 +0000 (13:25 +0100)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sun, 5 Jan 2014 12:25:26 +0000 (13:25 +0100)
src/main/java/net/pterodactylus/xdcc/main/Main.java
src/main/java/net/pterodactylus/xdcc/ui/stdin/CommandReader.java
src/main/java/net/pterodactylus/xdcc/ui/stdin/CommandReaderFactory.java [new file with mode: 0644]
src/main/java/net/pterodactylus/xdcc/ui/stdin/DownloadFailure.java [new file with mode: 0644]
src/main/java/net/pterodactylus/xdcc/ui/stdin/DownloadFailures.java [new file with mode: 0644]
src/main/java/net/pterodactylus/xdcc/ui/stdin/FailedDownloadsCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/xdcc/ui/stdin/NetworkAdapter.java
src/main/java/net/pterodactylus/xdcc/ui/stdin/ResearchCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/xdcc/ui/stdin/RestartCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/xdcc/ui/stdin/State.java
src/test/java/net/pterodactylus/xdcc/ui/stdin/DownloadFailuresTest.java [new file with mode: 0644]

index 58d6b01..6d6d849 100644 (file)
@@ -34,6 +34,8 @@ import net.pterodactylus.xdcc.data.Network;
 import net.pterodactylus.xdcc.data.Network.NetworkBuilder;
 import net.pterodactylus.xdcc.data.Network.ServerBuilder;
 import net.pterodactylus.xdcc.ui.stdin.CommandReader;
+import net.pterodactylus.xdcc.ui.stdin.DownloadFailure;
+import net.pterodactylus.xdcc.ui.stdin.DownloadFailures;
 import net.pterodactylus.xdcc.ui.stdin.NetworkAdapter;
 
 import com.google.common.eventbus.AsyncEventBus;
@@ -95,11 +97,13 @@ public class Main {
                        }
                }
 
-               CommandReader commandReader = new CommandReader(core, new InputStreamReader(System.in, "UTF-8"), new OutputStreamWriter(System.out, "UTF-8"));
+               DownloadFailures downloadFailures = new DownloadFailures();
+
+               CommandReader commandReader = new CommandReader(core, new InputStreamReader(System.in, "UTF-8"), new OutputStreamWriter(System.out, "UTF-8"), downloadFailures);
                commandReader.start();
                eventBus.register(commandReader);
 
-               NetworkAdapter networkAcceptor = new NetworkAdapter(eventBus, core, configuration.getTelnetPort());
+               NetworkAdapter networkAcceptor = new NetworkAdapter(eventBus, (reader, writer) -> new CommandReader(core, reader, writer, downloadFailures), configuration.getTelnetPort());
                networkAcceptor.start();
 
                core.start();
index 381c519..83c9802 100644 (file)
@@ -62,6 +62,7 @@ public class CommandReader extends AbstractExecutionThreadService {
 
        /** The writer to write the results to. */
        private final Writer writer;
+       private final DownloadFailures downloadFailures;
 
        /**
         * Creates a new command reader.
@@ -73,9 +74,10 @@ public class CommandReader extends AbstractExecutionThreadService {
         * @param writer
         *              The write to write results to
         */
-       public CommandReader(Core core, Reader reader, Writer writer) {
+       public CommandReader(Core core, Reader reader, Writer writer, DownloadFailures downloadFailures) {
                this.reader = new BufferedReader(reader);
                this.writer = writer;
+               this.downloadFailures = downloadFailures;
 
                /* initialize commands. */
                ImmutableList.Builder<Command> commandBuilder = ImmutableList.builder();
@@ -86,6 +88,9 @@ public class CommandReader extends AbstractExecutionThreadService {
                commandBuilder.add(new ListConnectionsCommand(core));
                commandBuilder.add(new AbortDownloadCommand(core));
                commandBuilder.add(new DisconnectCommand(core));
+               commandBuilder.add(new FailedDownloadsCommand(downloadFailures));
+               commandBuilder.add(new RestartCommand(core, downloadFailures));
+               commandBuilder.add(new ResearchCommand(core));
                commands = commandBuilder.build();
        }
 
@@ -165,6 +170,7 @@ public class CommandReader extends AbstractExecutionThreadService {
        @Subscribe
        public void downloadFailed(DownloadFailed downloadFailed) {
                Download download = downloadFailed.download();
+               downloadFailures.addFailedDownload(download, System.currentTimeMillis());
                try {
                        writeLine(red(String.format("Download of %s (from %s, %s) has failed at %.1f%% and %s/s.", download.filename(), download.bot().name(), download.bot().network().name(), download.dccReceiver().progress() * 100.0 / download.dccReceiver().size(), f(download.dccReceiver().overallRate()))));
                } catch (IOException ioe1) {
diff --git a/src/main/java/net/pterodactylus/xdcc/ui/stdin/CommandReaderFactory.java b/src/main/java/net/pterodactylus/xdcc/ui/stdin/CommandReaderFactory.java
new file mode 100644 (file)
index 0000000..62cd6df
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * XdccDownloader - CommandReaderFactory.java - Copyright © 2013 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.xdcc.ui.stdin;
+
+import java.io.Reader;
+import java.io.Writer;
+
+/**
+ * Factory that can create {@link CommandReader}s, given a {@link Reader} and a {@link Writer}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public interface CommandReaderFactory {
+
+       public CommandReader create(Reader reader, Writer writer);
+
+}
diff --git a/src/main/java/net/pterodactylus/xdcc/ui/stdin/DownloadFailure.java b/src/main/java/net/pterodactylus/xdcc/ui/stdin/DownloadFailure.java
new file mode 100644 (file)
index 0000000..2177067
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * XdccDownloader - DownloadFailure.java - Copyright © 2013 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.xdcc.ui.stdin;
+
+import net.pterodactylus.xdcc.data.Download;
+
+/**
+ * TODO
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DownloadFailure {
+
+       private final Download download;
+       private final long failureTime;
+
+       public DownloadFailure(Download download, long failureTime) {
+               this.download = download;
+               this.failureTime = failureTime;
+       }
+
+       public Download getDownload() {
+               return download;
+       }
+
+       public long getFailureTime() {
+               return failureTime;
+       }
+
+       @Override
+       public int hashCode() {
+               return (int) (download.hashCode() ^ (failureTime & 0xffffffff) ^ (failureTime >> 32));
+       }
+
+       @Override
+       public boolean equals(Object object) {
+               if (!(object instanceof DownloadFailure)) {
+                       return false;
+               }
+               DownloadFailure downloadFailure = (DownloadFailure) object;
+               return downloadFailure.getDownload().equals(getDownload()) && (downloadFailure.getFailureTime() == getFailureTime());
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/xdcc/ui/stdin/DownloadFailures.java b/src/main/java/net/pterodactylus/xdcc/ui/stdin/DownloadFailures.java
new file mode 100644 (file)
index 0000000..da4ee40
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * XdccDownloader - DownloadFailures.java - Copyright © 2013 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.xdcc.ui.stdin;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.Function;
+
+import net.pterodactylus.xdcc.data.Download;
+
+/**
+ * TODO
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DownloadFailures implements Iterable<DownloadFailure> {
+
+       private final Map<Download, Long> downloadFailures = new HashMap<>();
+
+       public void addFailedDownload(Download download, long failureTime) {
+               synchronized (downloadFailures) {
+                       downloadFailures.put(download, failureTime);
+               }
+       }
+
+       public void removeFailedDownload(Download download) {
+               synchronized (downloadFailures) {
+                       downloadFailures.remove(download);
+               }
+       }
+
+       @Override
+       public Iterator<DownloadFailure> iterator() {
+               synchronized (downloadFailures) {
+                       return downloadFailures.entrySet().stream().map(new Function<Entry<Download, Long>, DownloadFailure>() {
+                               @Override
+                               public DownloadFailure apply(Entry<Download, Long> downloadEntry) {
+                                       return new DownloadFailure(downloadEntry.getKey(), downloadEntry.getValue());
+                               }
+                       }).iterator();
+               }
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/xdcc/ui/stdin/FailedDownloadsCommand.java b/src/main/java/net/pterodactylus/xdcc/ui/stdin/FailedDownloadsCommand.java
new file mode 100644 (file)
index 0000000..929c2f3
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * XdccDownloader - FailedDownloadsCommand.java - Copyright © 2013 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.xdcc.ui.stdin;
+
+import static java.lang.String.format;
+import static java.util.Collections.emptySet;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import net.pterodactylus.xdcc.data.Download;
+
+/**
+ * TODO
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class FailedDownloadsCommand implements Command {
+
+       private final DownloadFailures downloadFailures;
+
+       public FailedDownloadsCommand(DownloadFailures downloadFailures) {
+               this.downloadFailures = downloadFailures;
+       }
+
+       @Override
+       public String getName() {
+               return "failed";
+       }
+
+       @Override
+       public Collection<String> getAliases() {
+               return emptySet();
+       }
+
+       @Override
+       public State execute(State state, List<String> parameters, Writer outputWriter) throws IOException {
+               int downloadIndex = 0;
+               List<Download> failedDownloads = new ArrayList<>();
+               for (DownloadFailure downloadFailure : downloadFailures) {
+                       Download download = downloadFailure.getDownload();
+                       failedDownloads.add(download);
+                       outputWriter.write(format("[%d] %s from %s\n", downloadIndex, download.filename(), download.bot().name()));
+                       downloadIndex++;
+               }
+               outputWriter.write("End of failed downloads.\n");
+               return state.setLastFailedDownloads(failedDownloads);
+       }
+
+}
index a45a341..bfbaee5 100644 (file)
@@ -42,19 +42,18 @@ public class NetworkAdapter extends AbstractExecutionThreadService {
        /** The event bus. */
        private final EventBus eventBus;
 
-       /** The core being controlled. */
-       private final Core core;
+       private final CommandReaderFactory commandReaderFactory;
        private final int port;
 
        /**
         * Creates a new network acceptor.
         *
         * @param eventBus
-        * @param core
+        * @param commandReaderFactory
         */
-       public NetworkAdapter(EventBus eventBus, Core core, int port) {
+       public NetworkAdapter(EventBus eventBus, CommandReaderFactory commandReaderFactory, int port) {
                this.eventBus = eventBus;
-               this.core = core;
+               this.commandReaderFactory = commandReaderFactory;
                this.port = port;
        }
 
@@ -69,7 +68,7 @@ public class NetworkAdapter extends AbstractExecutionThreadService {
                                OutputStream socketOutputStream = clientSocket.getOutputStream();
                                final InputStreamReader socketInputStreamReader = new InputStreamReader(socketInputStream, "UTF-8");
                                final OutputStreamWriter socketOutputStreamWriter = new OutputStreamWriter(socketOutputStream, "UTF-8");
-                               final CommandReader commandReader = new CommandReader(core, socketInputStreamReader, socketOutputStreamWriter);
+                               final CommandReader commandReader = commandReaderFactory.create(socketInputStreamReader, socketOutputStreamWriter);
                                eventBus.register(commandReader);
                                commandReader.addListener(new Listener() {
 
diff --git a/src/main/java/net/pterodactylus/xdcc/ui/stdin/ResearchCommand.java b/src/main/java/net/pterodactylus/xdcc/ui/stdin/ResearchCommand.java
new file mode 100644 (file)
index 0000000..5f9e57a
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * XdccDownloader - ResearchCommand.java - Copyright © 2013 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.xdcc.ui.stdin;
+
+import static java.util.Arrays.asList;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import net.pterodactylus.xdcc.core.Core;
+import net.pterodactylus.xdcc.data.Download;
+
+import com.google.common.primitives.Ints;
+
+/**
+ * TODO
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class ResearchCommand extends SearchCommand {
+
+       public ResearchCommand(Core core) {
+               super(core);
+       }
+
+       @Override
+       public String getName() {
+               return "research";
+       }
+
+       @Override
+       public Collection<String> getAliases() {
+               return asList("rs");
+       }
+
+       @Override
+       public State execute(State state, List<String> parameters, Writer outputWriter) throws IOException {
+               Integer index = Ints.tryParse(parameters.get(0));
+               List<Download> lastFailedDownloads = state.getLastFailedDownloads();
+               if ((index != null) && (index < lastFailedDownloads.size())) {
+                       Download failedDownload = lastFailedDownloads.get(index);
+                       return super.execute(state, asList(failedDownload.pack().name()), outputWriter);
+               }
+               return super.execute(state, Collections.<String>emptyList(), outputWriter);
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/xdcc/ui/stdin/RestartCommand.java b/src/main/java/net/pterodactylus/xdcc/ui/stdin/RestartCommand.java
new file mode 100644 (file)
index 0000000..84a7d2e
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * XdccDownloader - RestartCommand.java - Copyright © 2013 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.xdcc.ui.stdin;
+
+import static java.util.Arrays.asList;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.List;
+
+import net.pterodactylus.xdcc.core.Core;
+import net.pterodactylus.xdcc.data.Download;
+
+import com.google.common.primitives.Ints;
+
+/**
+ * TODO
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class RestartCommand implements Command {
+
+       private final Core core;
+       private final DownloadFailures downloadFailures;
+
+       public RestartCommand(Core core, DownloadFailures downloadFailures) {
+               this.core = core;
+               this.downloadFailures = downloadFailures;
+       }
+
+       @Override
+       public String getName() {
+               return "restart";
+       }
+
+       @Override
+       public Collection<String> getAliases() {
+               return asList("sa");
+       }
+
+       @Override
+       public State execute(State state, List<String> parameters, Writer outputWriter) throws IOException {
+               Integer index = Ints.tryParse(parameters.get(0));
+               List<Download> lastFailedDownloads = state.getLastFailedDownloads();
+               if ((index != null) && (index < lastFailedDownloads.size())) {
+                       Download failedDownload = lastFailedDownloads.get(index);
+                       core.fetch(failedDownload.bot(), failedDownload.pack());
+                       downloadFailures.removeFailedDownload(failedDownload);
+               }
+               return state;
+       }
+
+}
index e292d09..28cecd9 100644 (file)
@@ -17,6 +17,7 @@
 
 package net.pterodactylus.xdcc.ui.stdin;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import net.pterodactylus.irc.Connection;
@@ -40,9 +41,11 @@ public class State {
        /** The last downloads displayed. */
        private final List<Download> lastDownloads;
 
+       private final List<Download> lastFailedDownloads;
+
        /** Creates a new empty state. */
        public State() {
-               this(Lists.<Connection>newArrayList(), Lists.<Result>newArrayList(), Lists.<Download>newArrayList());
+               this(Lists.<Connection>newArrayList(), Lists.<Result>newArrayList(), Lists.<Download>newArrayList(), new ArrayList<>());
        }
 
        /**
@@ -53,12 +56,14 @@ public class State {
         * @param lastResults
         *              The last results
         * @param lastDownloads
-        *              The last downloads
+        * @param lastFailedDownloads
+        *              The last failed downloads shown
         */
-       State(List<Connection> lastConnections, List<Result> lastResults, List<Download> lastDownloads) {
+       State(List<Connection> lastConnections, List<Result> lastResults, List<Download> lastDownloads, List<Download> lastFailedDownloads) {
                this.lastConnections = lastConnections;
                this.lastResults = lastResults;
                this.lastDownloads = lastDownloads;
+               this.lastFailedDownloads = lastFailedDownloads;
        }
 
        //
@@ -92,6 +97,10 @@ public class State {
                return lastDownloads;
        }
 
+       public List<Download> getLastFailedDownloads() {
+               return lastFailedDownloads;
+       }
+
        //
        // MUTATORS
        //
@@ -105,7 +114,7 @@ public class State {
         * @return The new state
         */
        public State setLastConnections(List<Connection> lastConnections) {
-               return new State(lastConnections, lastResults, lastDownloads);
+               return new State(lastConnections, lastResults, lastDownloads, lastFailedDownloads);
        }
 
        /**
@@ -117,7 +126,7 @@ public class State {
         * @return The new state
         */
        public State setLastResults(List<Result> lastResults) {
-               return new State(lastConnections, lastResults, lastDownloads);
+               return new State(lastConnections, lastResults, lastDownloads, lastFailedDownloads);
        }
 
        /**
@@ -129,7 +138,11 @@ public class State {
         * @return The new state
         */
        public State setLastDownloads(List<Download> lastDownloads) {
-               return new State(lastConnections, lastResults, lastDownloads);
+               return new State(lastConnections, lastResults, lastDownloads, lastFailedDownloads);
+       }
+
+       public State setLastFailedDownloads(List<Download> lastFailedDownloads) {
+               return new State(lastConnections, lastResults, lastDownloads, lastFailedDownloads);
        }
 
 }
diff --git a/src/test/java/net/pterodactylus/xdcc/ui/stdin/DownloadFailuresTest.java b/src/test/java/net/pterodactylus/xdcc/ui/stdin/DownloadFailuresTest.java
new file mode 100644 (file)
index 0000000..9f55e4e
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * XdccDownloader - DownloadFailuresTest.java - Copyright © 2013 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.xdcc.ui.stdin;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.emptyIterable;
+import static org.mockito.Mockito.mock;
+
+import net.pterodactylus.xdcc.data.Download;
+
+import org.junit.Test;
+
+/**
+ * TODO
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DownloadFailuresTest {
+
+       @Test
+       public void canRemoveDownloadFailures() {
+               long failureTime = System.currentTimeMillis();
+               DownloadFailures downloadFailures = new DownloadFailures();
+               Download download = mock(Download.class);
+               downloadFailures.addFailedDownload(download, failureTime);
+               assertThat(downloadFailures, contains(new DownloadFailure(download, failureTime)));
+               downloadFailures.removeFailedDownload(download);
+               assertThat(downloadFailures, emptyIterable());
+       }
+
+}