From f76ee64d2cd93a0439c6306e1fcf6230c633590c Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 5 Jan 2014 13:25:26 +0100 Subject: [PATCH] Add possibility to restart/research failed downloads. --- .../java/net/pterodactylus/xdcc/main/Main.java | 8 ++- .../pterodactylus/xdcc/ui/stdin/CommandReader.java | 8 ++- .../xdcc/ui/stdin/CommandReaderFactory.java | 32 ++++++++++ .../xdcc/ui/stdin/DownloadFailure.java | 59 ++++++++++++++++++ .../xdcc/ui/stdin/DownloadFailures.java | 61 +++++++++++++++++++ .../xdcc/ui/stdin/FailedDownloadsCommand.java | 68 +++++++++++++++++++++ .../xdcc/ui/stdin/NetworkAdapter.java | 11 ++-- .../xdcc/ui/stdin/ResearchCommand.java | 65 ++++++++++++++++++++ .../xdcc/ui/stdin/RestartCommand.java | 69 ++++++++++++++++++++++ .../net/pterodactylus/xdcc/ui/stdin/State.java | 25 ++++++-- .../xdcc/ui/stdin/DownloadFailuresTest.java | 47 +++++++++++++++ 11 files changed, 438 insertions(+), 15 deletions(-) create mode 100644 src/main/java/net/pterodactylus/xdcc/ui/stdin/CommandReaderFactory.java create mode 100644 src/main/java/net/pterodactylus/xdcc/ui/stdin/DownloadFailure.java create mode 100644 src/main/java/net/pterodactylus/xdcc/ui/stdin/DownloadFailures.java create mode 100644 src/main/java/net/pterodactylus/xdcc/ui/stdin/FailedDownloadsCommand.java create mode 100644 src/main/java/net/pterodactylus/xdcc/ui/stdin/ResearchCommand.java create mode 100644 src/main/java/net/pterodactylus/xdcc/ui/stdin/RestartCommand.java create mode 100644 src/test/java/net/pterodactylus/xdcc/ui/stdin/DownloadFailuresTest.java diff --git a/src/main/java/net/pterodactylus/xdcc/main/Main.java b/src/main/java/net/pterodactylus/xdcc/main/Main.java index 58d6b01..6d6d849 100644 --- a/src/main/java/net/pterodactylus/xdcc/main/Main.java +++ b/src/main/java/net/pterodactylus/xdcc/main/Main.java @@ -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(); diff --git a/src/main/java/net/pterodactylus/xdcc/ui/stdin/CommandReader.java b/src/main/java/net/pterodactylus/xdcc/ui/stdin/CommandReader.java index 381c519..83c9802 100644 --- a/src/main/java/net/pterodactylus/xdcc/ui/stdin/CommandReader.java +++ b/src/main/java/net/pterodactylus/xdcc/ui/stdin/CommandReader.java @@ -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 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 index 0000000..62cd6df --- /dev/null +++ b/src/main/java/net/pterodactylus/xdcc/ui/stdin/CommandReaderFactory.java @@ -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 . + */ + +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 David ‘Bombe’ Roden + */ +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 index 0000000..2177067 --- /dev/null +++ b/src/main/java/net/pterodactylus/xdcc/ui/stdin/DownloadFailure.java @@ -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 . + */ + +package net.pterodactylus.xdcc.ui.stdin; + +import net.pterodactylus.xdcc.data.Download; + +/** + * TODO + * + * @author David ‘Bombe’ Roden + */ +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 index 0000000..da4ee40 --- /dev/null +++ b/src/main/java/net/pterodactylus/xdcc/ui/stdin/DownloadFailures.java @@ -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 . + */ + +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 David ‘Bombe’ Roden + */ +public class DownloadFailures implements Iterable { + + private final Map 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 iterator() { + synchronized (downloadFailures) { + return downloadFailures.entrySet().stream().map(new Function, DownloadFailure>() { + @Override + public DownloadFailure apply(Entry 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 index 0000000..929c2f3 --- /dev/null +++ b/src/main/java/net/pterodactylus/xdcc/ui/stdin/FailedDownloadsCommand.java @@ -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 . + */ + +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 David ‘Bombe’ Roden + */ +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 getAliases() { + return emptySet(); + } + + @Override + public State execute(State state, List parameters, Writer outputWriter) throws IOException { + int downloadIndex = 0; + List 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); + } + +} diff --git a/src/main/java/net/pterodactylus/xdcc/ui/stdin/NetworkAdapter.java b/src/main/java/net/pterodactylus/xdcc/ui/stdin/NetworkAdapter.java index a45a341..bfbaee5 100644 --- a/src/main/java/net/pterodactylus/xdcc/ui/stdin/NetworkAdapter.java +++ b/src/main/java/net/pterodactylus/xdcc/ui/stdin/NetworkAdapter.java @@ -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 index 0000000..5f9e57a --- /dev/null +++ b/src/main/java/net/pterodactylus/xdcc/ui/stdin/ResearchCommand.java @@ -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 . + */ + +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 David ‘Bombe’ Roden + */ +public class ResearchCommand extends SearchCommand { + + public ResearchCommand(Core core) { + super(core); + } + + @Override + public String getName() { + return "research"; + } + + @Override + public Collection getAliases() { + return asList("rs"); + } + + @Override + public State execute(State state, List parameters, Writer outputWriter) throws IOException { + Integer index = Ints.tryParse(parameters.get(0)); + List 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.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 index 0000000..84a7d2e --- /dev/null +++ b/src/main/java/net/pterodactylus/xdcc/ui/stdin/RestartCommand.java @@ -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 . + */ + +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 David ‘Bombe’ Roden + */ +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 getAliases() { + return asList("sa"); + } + + @Override + public State execute(State state, List parameters, Writer outputWriter) throws IOException { + Integer index = Ints.tryParse(parameters.get(0)); + List 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; + } + +} diff --git a/src/main/java/net/pterodactylus/xdcc/ui/stdin/State.java b/src/main/java/net/pterodactylus/xdcc/ui/stdin/State.java index e292d09..28cecd9 100644 --- a/src/main/java/net/pterodactylus/xdcc/ui/stdin/State.java +++ b/src/main/java/net/pterodactylus/xdcc/ui/stdin/State.java @@ -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 lastDownloads; + private final List lastFailedDownloads; + /** Creates a new empty state. */ public State() { - this(Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList()); + this(Lists.newArrayList(), Lists.newArrayList(), Lists.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 lastConnections, List lastResults, List lastDownloads) { + State(List lastConnections, List lastResults, List lastDownloads, List lastFailedDownloads) { this.lastConnections = lastConnections; this.lastResults = lastResults; this.lastDownloads = lastDownloads; + this.lastFailedDownloads = lastFailedDownloads; } // @@ -92,6 +97,10 @@ public class State { return lastDownloads; } + public List getLastFailedDownloads() { + return lastFailedDownloads; + } + // // MUTATORS // @@ -105,7 +114,7 @@ public class State { * @return The new state */ public State setLastConnections(List 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 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 lastDownloads) { - return new State(lastConnections, lastResults, lastDownloads); + return new State(lastConnections, lastResults, lastDownloads, lastFailedDownloads); + } + + public State setLastFailedDownloads(List 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 index 0000000..9f55e4e --- /dev/null +++ b/src/test/java/net/pterodactylus/xdcc/ui/stdin/DownloadFailuresTest.java @@ -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 . + */ + +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 David ‘Bombe’ Roden + */ +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()); + } + +} -- 2.7.4