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;
}
}
- 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();
/** The writer to write the results to. */
private final Writer writer;
+ private final DownloadFailures downloadFailures;
/**
* Creates a new command reader.
* @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();
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();
}
@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) {
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+/*
+ * 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());
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
/** 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;
}
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() {
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
package net.pterodactylus.xdcc.ui.stdin;
+import java.util.ArrayList;
import java.util.List;
import net.pterodactylus.irc.Connection;
/** 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<>());
}
/**
* @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;
}
//
return lastDownloads;
}
+ public List<Download> getLastFailedDownloads() {
+ return lastFailedDownloads;
+ }
+
//
// MUTATORS
//
* @return The new state
*/
public State setLastConnections(List<Connection> lastConnections) {
- return new State(lastConnections, lastResults, lastDownloads);
+ return new State(lastConnections, lastResults, lastDownloads, lastFailedDownloads);
}
/**
* @return The new state
*/
public State setLastResults(List<Result> lastResults) {
- return new State(lastConnections, lastResults, lastDownloads);
+ return new State(lastConnections, lastResults, lastDownloads, lastFailedDownloads);
}
/**
* @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);
}
}
--- /dev/null
+/*
+ * 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());
+ }
+
+}