mirror of
https://github.com/anthonyraymond/joal.git
synced 2024-09-20 15:26:25 +08:00
Added config to tell how much torrent have to be seed simultaneously. Start seeding another torrent when one stop.
This commit is contained in:
parent
4588658156
commit
05a85073e9
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
"minUploadRate": 180,
|
||||
"maxUploadRate": 190,
|
||||
"seedFor": 1200,
|
||||
"waitBetweenSeed": 240,
|
||||
"simultaneousSeed": 2,
|
||||
"client": "azureus-5.7.5.0.client"
|
||||
}
|
||||
|
|
|
@ -12,22 +12,19 @@ public class AppConfiguration {
|
|||
|
||||
private final int minUploadRate;
|
||||
private final int maxUploadRate;
|
||||
private final int seedFor;
|
||||
private final int waitBetweenSeed;
|
||||
private final int simultaneousSeed;
|
||||
private final String client;
|
||||
|
||||
@JsonCreator
|
||||
AppConfiguration(
|
||||
@JsonProperty(value = "minUploadRate", required = true) final int minUploadRate,
|
||||
@JsonProperty(value = "maxUploadRate", required = true) final int maxUploadRate,
|
||||
@JsonProperty(value = "seedFor", required = true) final int seedFor,
|
||||
@JsonProperty(value = "waitBetweenSeed", required = true) final int waitBetweenSeed,
|
||||
@JsonProperty(value = "simultaneousSeed", required = true) final int simultaneousSeed,
|
||||
@JsonProperty(value = "client", required = true) final String client
|
||||
) {
|
||||
this.minUploadRate = minUploadRate;
|
||||
this.maxUploadRate = maxUploadRate;
|
||||
this.seedFor = seedFor;
|
||||
this.waitBetweenSeed = waitBetweenSeed;
|
||||
this.simultaneousSeed = simultaneousSeed;
|
||||
this.client = client;
|
||||
|
||||
validate();
|
||||
|
@ -41,12 +38,8 @@ public class AppConfiguration {
|
|||
return minUploadRate;
|
||||
}
|
||||
|
||||
public int getSeedFor() {
|
||||
return seedFor;
|
||||
}
|
||||
|
||||
public int getWaitBetweenSeed() {
|
||||
return waitBetweenSeed;
|
||||
public int getSimultaneousSeed() {
|
||||
return simultaneousSeed;
|
||||
}
|
||||
|
||||
@JsonProperty("client")
|
||||
|
@ -64,11 +57,8 @@ public class AppConfiguration {
|
|||
if (maxUploadRate <= minUploadRate) {
|
||||
throw new AppConfigurationIntegrityException("maxUploadRate must be strictly greater than minUploadRate.");
|
||||
}
|
||||
if (seedFor < 1) {
|
||||
throw new AppConfigurationIntegrityException("seedFor must be greater than 1.");
|
||||
}
|
||||
if (waitBetweenSeed < 1) {
|
||||
throw new AppConfigurationIntegrityException("waitBetweenSeed must be greater than 1.");
|
||||
if (simultaneousSeed < 1) {
|
||||
throw new AppConfigurationIntegrityException("simultaneousSeed must be greater than 0.");
|
||||
}
|
||||
if (StringUtils.isBlank(client)) {
|
||||
throw new AppConfigurationIntegrityException("client is required, no file name given.");
|
||||
|
@ -82,13 +72,12 @@ public class AppConfiguration {
|
|||
final AppConfiguration that = (AppConfiguration) o;
|
||||
return minUploadRate == that.minUploadRate &&
|
||||
maxUploadRate == that.maxUploadRate &&
|
||||
seedFor == that.seedFor &&
|
||||
waitBetweenSeed == that.waitBetweenSeed &&
|
||||
simultaneousSeed == that.simultaneousSeed &&
|
||||
Objects.equal(client, that.client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(minUploadRate, maxUploadRate, seedFor, waitBetweenSeed, client);
|
||||
return Objects.hashCode(minUploadRate, maxUploadRate, simultaneousSeed, client);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package org.araymond.joal.core.torrent.watcher;
|
||||
|
||||
import org.araymond.joal.core.ttorent.client.MockedTorrent;
|
||||
|
||||
/**
|
||||
* Created by raymo on 23/05/2017.
|
||||
*/
|
||||
public interface TorrentFileChangeAware {
|
||||
|
||||
void onTorrentAdded(final MockedTorrent torrent);
|
||||
|
||||
void onTorrentRemoved(final MockedTorrent torrent);
|
||||
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package org.araymond.joal.core.torrent.watcher;
|
||||
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.araymond.joal.core.events.TorrentFileAddedForSeed;
|
||||
|
@ -22,10 +22,7 @@ import java.nio.file.Files;
|
|||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Created by raymo on 28/01/2017.
|
||||
|
@ -37,8 +34,8 @@ public class TorrentFileProvider extends FileAlterationListenerAdaptor {
|
|||
private final TorrentFileWatcher watcher;
|
||||
private final Map<File, MockedTorrent> torrentFiles;
|
||||
private final Path archiveFolder;
|
||||
private final Random rand;
|
||||
private final ApplicationEventPublisher publisher;
|
||||
private final Set<TorrentFileChangeAware> torrentFileChangeListener;
|
||||
|
||||
private boolean isInitOver = false;
|
||||
|
||||
|
@ -77,15 +74,24 @@ public class TorrentFileProvider extends FileAlterationListenerAdaptor {
|
|||
|
||||
this.torrentFiles = Collections.synchronizedMap(new HashMap<File, MockedTorrent>());
|
||||
|
||||
this.rand = new Random();
|
||||
|
||||
this.watcher = new TorrentFileWatcher(this, torrentFolder);
|
||||
torrentFileChangeListener = new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFileDelete(final File file) {
|
||||
if (!this.torrentFiles.containsKey(file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final MockedTorrent torrent = this.torrentFiles.get(file);
|
||||
if (torrent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("Torrent file deleting detected, hot deleted file: {}", file.getAbsolutePath());
|
||||
this.torrentFiles.remove(file);
|
||||
this.torrentFileChangeListener.forEach(listener -> listener.onTorrentRemoved(torrent));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,6 +102,7 @@ public class TorrentFileProvider extends FileAlterationListenerAdaptor {
|
|||
this.torrentFiles.put(file, torrent);
|
||||
if (this.isInitOver) {
|
||||
this.publisher.publishEvent(new TorrentFileAddedForSeed(torrent));
|
||||
this.torrentFileChangeListener.forEach(listener -> listener.onTorrentAdded(torrent));
|
||||
}
|
||||
} catch (final IOException | NoSuchAlgorithmException e) {
|
||||
logger.warn("File '{}' not added to torrent list, because failed to read file '", file.getAbsolutePath(), e);
|
||||
|
@ -109,19 +116,27 @@ public class TorrentFileProvider extends FileAlterationListenerAdaptor {
|
|||
this.onFileCreate(file);
|
||||
}
|
||||
|
||||
public MockedTorrent getRandomTorrentFile() throws NoMoreTorrentsFileAvailableException {
|
||||
if (this.torrentFiles.isEmpty()) {
|
||||
logger.error("There is no more .torrent file available.");
|
||||
throw new NoMoreTorrentsFileAvailableException("No more torrent file available.");
|
||||
}
|
||||
final int indexToPick = this.rand.nextInt(this.torrentFiles.size());
|
||||
return Iterators.get(this.torrentFiles.values().iterator(), indexToPick);
|
||||
public void registerListener(final TorrentFileChangeAware listener) {
|
||||
this.torrentFileChangeListener.add(listener);
|
||||
}
|
||||
|
||||
public void moveToArchiveFolder(final File torrentFile) {
|
||||
public void unRegisterListener(final TorrentFileChangeAware listener) {
|
||||
this.torrentFileChangeListener.remove(listener);
|
||||
}
|
||||
|
||||
public MockedTorrent getTorrentNotIn(final List<MockedTorrent> unwantedTorrents) throws NoMoreTorrentsFileAvailableException {
|
||||
Preconditions.checkNotNull(unwantedTorrents, "List of unwantedTorrents cannot be null.");
|
||||
return this.torrentFiles.values().stream()
|
||||
.filter(torrent -> !unwantedTorrents.contains(torrent))
|
||||
.findAny()
|
||||
.orElseThrow(() -> new NoMoreTorrentsFileAvailableException("No more torrent file available."));
|
||||
}
|
||||
|
||||
void moveToArchiveFolder(final File torrentFile) {
|
||||
if (!torrentFile.exists()) {
|
||||
return;
|
||||
}
|
||||
this.onFileDelete(torrentFile);
|
||||
|
||||
try {
|
||||
Files.deleteIfExists(archiveFolder.resolve(torrentFile.getName()));
|
||||
|
@ -132,17 +147,8 @@ public class TorrentFileProvider extends FileAlterationListenerAdaptor {
|
|||
}
|
||||
}
|
||||
|
||||
public void moveToArchiveFolder(final MockedTorrent torrentFile) {
|
||||
final File fileToArchive = this.torrentFiles.entrySet().stream()
|
||||
.filter(entry -> entry.getValue().getHexInfoHash().equalsIgnoreCase(torrentFile.getHexInfoHash()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> {
|
||||
final IllegalStateException e = new IllegalStateException("Failed to find which torrent file to archive.");
|
||||
logger.warn("Failed to find which torrent file to archive.", e);
|
||||
return e;
|
||||
})
|
||||
.getKey();
|
||||
this.moveToArchiveFolder(fileToArchive);
|
||||
public void moveToArchiveFolder(final MockedTorrent torrent) {
|
||||
this.moveToArchiveFolder(torrent.getPath().toFile());
|
||||
}
|
||||
|
||||
public int getTorrentCount() {
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.araymond.joal.core.events.NoMoreLeechers;
|
|||
import org.araymond.joal.core.events.NoMoreTorrentsFileAvailable;
|
||||
import org.araymond.joal.core.events.announce.AnnounceRequestingEvent;
|
||||
import org.araymond.joal.core.exception.NoMoreTorrentsFileAvailableException;
|
||||
import org.araymond.joal.core.torrent.watcher.TorrentFileChangeAware;
|
||||
import org.araymond.joal.core.torrent.watcher.TorrentFileProvider;
|
||||
import org.araymond.joal.core.ttorent.client.announce.Announcer;
|
||||
import org.araymond.joal.core.ttorent.client.announce.AnnouncerEventListener;
|
||||
|
@ -20,11 +21,12 @@ import org.springframework.context.ApplicationEventPublisher;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Created by raymo on 14/05/2017.
|
||||
*/
|
||||
public class Client implements AnnouncerEventListener {
|
||||
public class Client implements AnnouncerEventListener, TorrentFileChangeAware {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Client.class);
|
||||
|
||||
private final JoalConfigProvider configProvider;
|
||||
|
@ -52,10 +54,11 @@ public class Client implements AnnouncerEventListener {
|
|||
}
|
||||
|
||||
public void share() {
|
||||
this.torrentFileProvider.registerListener(this);
|
||||
this.setState(ClientState.STARTING);
|
||||
this.bandwidthDispatcher.start();
|
||||
// TODO : number of torrent to seed should be in config
|
||||
final int numberOfTorrentToSeed = 1;
|
||||
|
||||
final int numberOfTorrentToSeed = configProvider.get().getSimultaneousSeed();
|
||||
for (int i = 0; i < numberOfTorrentToSeed; ++i) {
|
||||
try {
|
||||
addSeedingTorrent();
|
||||
|
@ -67,23 +70,32 @@ public class Client implements AnnouncerEventListener {
|
|||
this.setState(ClientState.STARTED);
|
||||
}
|
||||
|
||||
private Announcer addSeedingTorrent() throws NoMoreTorrentsFileAvailableException {
|
||||
//TODO : replace getRandom by something else to ensure we are not seeding the same torrent twice
|
||||
final MockedTorrent torrent = torrentFileProvider.getRandomTorrentFile();
|
||||
private void addSeedingTorrent() throws NoMoreTorrentsFileAvailableException {
|
||||
final List<MockedTorrent> unwantedTorrent = this.announcers.stream()
|
||||
.map(Announcer::getSeedingTorrent)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
final MockedTorrent torrent = torrentFileProvider.getTorrentNotIn(unwantedTorrent);
|
||||
|
||||
this.addSeedingTorrent(torrent);
|
||||
}
|
||||
|
||||
private void addSeedingTorrent(final MockedTorrent torrent) {
|
||||
final Announcer announcer = new Announcer(torrent, this.self, this.bitTorrentClient, publisher);
|
||||
announcer.registerEventListener(this);
|
||||
logger.debug("Added announcer for Torrent {}", torrent.getName());
|
||||
this.announcers.add(announcer);
|
||||
|
||||
logger.debug("Added announcer for Torrent {}", torrent.getName());
|
||||
announcer.start();
|
||||
return announcer;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
this.setState(ClientState.STOPPING);
|
||||
this.torrentFileProvider.unRegisterListener(this);
|
||||
this.bandwidthDispatcher.stop();
|
||||
|
||||
// We need to work with a copy of the list, because when stop, an event is launched that remove the announcer from the list.
|
||||
// And it result in a concurrent modification exception.
|
||||
this.bandwidthDispatcher.stop();
|
||||
Lists.newArrayList(this.announcers).forEach(Announcer::stop);
|
||||
this.setState(ClientState.STOPPED);
|
||||
}
|
||||
|
@ -94,7 +106,25 @@ public class Client implements AnnouncerEventListener {
|
|||
}
|
||||
}
|
||||
|
||||
//TODO : add event handler to catch TorrentFileAdded / TorrentFileRemoved, to be able to stop when removed, and add if new torrent is available and this.announcers < numberOfTorrentToSeed
|
||||
@Override
|
||||
public void onTorrentAdded(final MockedTorrent torrent) {
|
||||
if (this.currentState != ClientState.STARTED) {
|
||||
return;
|
||||
}
|
||||
if (this.announcers.size() >= configProvider.get().getSimultaneousSeed()) {
|
||||
return;
|
||||
}
|
||||
addSeedingTorrent(torrent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTorrentRemoved(final MockedTorrent torrent) {
|
||||
for (final Announcer announcer : this.announcers) {
|
||||
if (announcer.isForTorrent(torrent)) {
|
||||
announcer.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnnounceRequesting(final RequestEvent event, final TorrentWithStats torrent) {
|
||||
|
|
|
@ -54,6 +54,10 @@ public class MockedTorrent extends Torrent {
|
|||
return new MockedTorrent(data, true, torrent.toPath());
|
||||
}
|
||||
|
||||
public Path getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
// TODO : consider a better way to handle equals and hashcode
|
||||
|
|
|
@ -122,6 +122,11 @@ public class Announcer implements Runnable, AnnounceResponseListener {
|
|||
|
||||
@Override
|
||||
public void handleDiscoveredPeers(final TorrentWithStats torrent, final List<Peer> peers) {
|
||||
logger.info(
|
||||
"Peers discovery for torrent {}: {} leechers",
|
||||
torrent.getTorrent().getName(),
|
||||
peers == null ? 0 : peers.size()
|
||||
);
|
||||
if (peers == null || peers.isEmpty()) {
|
||||
this.eventListeners.forEach(listener -> listener.onNoMoreLeecherForTorrent(this, torrent));
|
||||
}
|
||||
|
@ -222,10 +227,11 @@ public class Announcer implements Runnable, AnnounceResponseListener {
|
|||
|
||||
while (!this.stop) {
|
||||
try {
|
||||
this.getCurrentTrackerClient().announce(event, false);
|
||||
for (final AnnouncerEventListener listener : this.eventListeners) {
|
||||
listener.onAnnounceRequesting(event, this.torrent);
|
||||
}
|
||||
this.getCurrentTrackerClient().announce(event, false);
|
||||
|
||||
this.promoteCurrentTrackerClient();
|
||||
event = AnnounceRequestMessage.RequestEvent.NONE;
|
||||
} catch (final AnnounceException ae) {
|
||||
|
@ -257,10 +263,10 @@ public class Announcer implements Runnable, AnnounceResponseListener {
|
|||
event = AnnounceRequestMessage.RequestEvent.STOPPED;
|
||||
|
||||
try {
|
||||
this.getCurrentTrackerClient().announce(event, true);
|
||||
for (final AnnouncerEventListener listener : this.eventListeners) {
|
||||
listener.onAnnounceRequesting(event, this.torrent);
|
||||
}
|
||||
this.getCurrentTrackerClient().announce(event, true);
|
||||
} catch (final AnnounceException ae) {
|
||||
logger.warn(ae.getMessage());
|
||||
}
|
||||
|
@ -308,6 +314,14 @@ public class Announcer implements Runnable, AnnounceResponseListener {
|
|||
.get(this.currentClient);
|
||||
}
|
||||
|
||||
public MockedTorrent getSeedingTorrent() {
|
||||
return this.torrent.getTorrent();
|
||||
}
|
||||
|
||||
public boolean isForTorrent(final MockedTorrent torrent) {
|
||||
return this.torrent.getTorrent().equals(torrent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Promote the current tracker client to the top of its tier.
|
||||
* <p>
|
||||
|
|
|
@ -19,62 +19,54 @@ public class AppConfigurationSerializationTest {
|
|||
|
||||
@Test
|
||||
public void shouldFailToDeserializeIfMinUploadRateIsNotDefined() throws IOException {
|
||||
assertThatThrownBy(() -> mapper.readValue("{\"maxUploadRate\":190,\"seedFor\":1200,\"waitBetweenSeed\":1200,\"client\":\"azureus.client\"}", AppConfiguration.class))
|
||||
assertThatThrownBy(() -> mapper.readValue("{\"maxUploadRate\":190,\"simultaneousSeed\":2,\"client\":\"azureus.client\"}", AppConfiguration.class))
|
||||
.isInstanceOf(JsonMappingException.class)
|
||||
.hasMessageContaining("Missing required creator property 'minUploadRate'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFailToDeserializeIfMaxUploadRateIsNotDefined() throws IOException {
|
||||
assertThatThrownBy(() -> mapper.readValue("{\"minUploadRate\":180,\"seedFor\":1200,\"waitBetweenSeed\":1200,\"client\":\"azureus.client\"}", AppConfiguration.class))
|
||||
assertThatThrownBy(() -> mapper.readValue("{\"minUploadRate\":180,\"simultaneousSeed\":2,\"client\":\"azureus.client\"}", AppConfiguration.class))
|
||||
.isInstanceOf(JsonMappingException.class)
|
||||
.hasMessageContaining("Missing required creator property 'maxUploadRate'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFailToDeserializeIfSeedForIsNotDefined() throws IOException {
|
||||
assertThatThrownBy(() -> mapper.readValue("{\"minUploadRate\":180,\"maxUploadRate\":190,\"waitBetweenSeed\":1200,\"client\":\"azureus.client\"}", AppConfiguration.class))
|
||||
public void shouldFailToDeserializeIfSimultaneousSeedIsNotDefined() throws IOException {
|
||||
assertThatThrownBy(() -> mapper.readValue("{\"minUploadRate\":180,\"maxUploadRate\":190,\"client\":\"azureus.client\"}", AppConfiguration.class))
|
||||
.isInstanceOf(JsonMappingException.class)
|
||||
.hasMessageContaining("Missing required creator property 'seedFor'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFailToDeserializeIfWaitBetweenSeedIsNotDefined() throws IOException {
|
||||
assertThatThrownBy(() -> mapper.readValue("{\"minUploadRate\":180,\"maxUploadRate\":190,\"seedFor\":1200,\"client\":\"azureus.client\"}", AppConfiguration.class))
|
||||
.isInstanceOf(JsonMappingException.class)
|
||||
.hasMessageContaining("Missing required creator property 'waitBetweenSeed'");
|
||||
.hasMessageContaining("Missing required creator property 'simultaneousSeed'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFailToDeserializeIfClientIsNotDefined() throws IOException {
|
||||
assertThatThrownBy(() -> mapper.readValue("{\"minUploadRate\":180,\"maxUploadRate\":190,\"seedFor\":1200,\"waitBetweenSeed\":1200}", AppConfiguration.class))
|
||||
assertThatThrownBy(() -> mapper.readValue("{\"minUploadRate\":180,\"maxUploadRate\":190,\"simultaneousSeed\":2}", AppConfiguration.class))
|
||||
.isInstanceOf(JsonMappingException.class)
|
||||
.hasMessageContaining("Missing required creator property 'client'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSerialize() throws JsonProcessingException {
|
||||
final AppConfiguration config = new AppConfiguration(180, 190, 1200, 1200, "azureus.client");
|
||||
assertThat(mapper.writeValueAsString(config)).isEqualTo("{\"minUploadRate\":180,\"maxUploadRate\":190,\"seedFor\":1200,\"waitBetweenSeed\":1200,\"client\":\"azureus.client\"}");
|
||||
final AppConfiguration config = new AppConfiguration(180, 190, 2, "azureus.client");
|
||||
assertThat(mapper.writeValueAsString(config)).isEqualTo("{\"minUploadRate\":180,\"maxUploadRate\":190,\"simultaneousSeed\":2,\"client\":\"azureus.client\"}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDeserialize() throws IOException {
|
||||
final AppConfiguration config = mapper.readValue(
|
||||
"{\"minUploadRate\":180,\"maxUploadRate\":190,\"seedFor\":1200,\"waitBetweenSeed\":1200,\"client\":\"azureus.client\"}",
|
||||
"{\"minUploadRate\":180,\"maxUploadRate\":190,\"simultaneousSeed\":2,\"client\":\"azureus.client\"}",
|
||||
AppConfiguration.class
|
||||
);
|
||||
assertThat(config.getMinUploadRate()).isEqualTo(180);
|
||||
assertThat(config.getMaxUploadRate()).isEqualTo(190);
|
||||
assertThat(config.getSeedFor()).isEqualTo(1200);
|
||||
assertThat(config.getWaitBetweenSeed()).isEqualTo(1200);
|
||||
assertThat(config.getSimultaneousSeed()).isEqualTo(2);
|
||||
assertThat(config.getClientFileName()).isEqualTo("azureus.client");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSerializeAndDeserialize() throws IOException {
|
||||
final AppConfiguration config = mapper.readValue(
|
||||
"{\"minUploadRate\":180,\"maxUploadRate\":190,\"seedFor\":1200,\"waitBetweenSeed\":1200,\"client\":\"azureus.client\"}",
|
||||
"{\"minUploadRate\":180,\"maxUploadRate\":190,\"simultaneousSeed\":2,\"client\":\"azureus.client\"}",
|
||||
AppConfiguration.class
|
||||
);
|
||||
|
||||
|
|
|
@ -12,110 +12,95 @@ public class AppConfigurationTest {
|
|||
|
||||
@Test
|
||||
public void ShouldNotBuildIfMinUploadRateIsLessThanZero() {
|
||||
assertThatThrownBy(() -> new AppConfiguration(-1, 190, 1200, 1200, "azureus.client"))
|
||||
assertThatThrownBy(() -> new AppConfiguration(-1, 190, 2, "azureus.client"))
|
||||
.isInstanceOf(AppConfigurationIntegrityException.class)
|
||||
.hasMessageContaining("minUploadRate must be at least 0.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateIfMinUploadRateEqualsZero() {
|
||||
final AppConfiguration config = new AppConfiguration(0, 190, 1200, 1200, "azureus.client");
|
||||
final AppConfiguration config = new AppConfiguration(0, 190, 2, "azureus.client");
|
||||
|
||||
assertThat(config.getMinUploadRate()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ShouldNotBuildIfMaxUploadRateIsLessThanOne() {
|
||||
assertThatThrownBy(() -> new AppConfiguration(180, -1, 1200, 1200, "azureus.client"))
|
||||
assertThatThrownBy(() -> new AppConfiguration(180, -1, 2, "azureus.client"))
|
||||
.isInstanceOf(AppConfigurationIntegrityException.class)
|
||||
.hasMessageContaining("maxUploadRate must greater than 0.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateIfMinUploadRateEqualsOne() {
|
||||
final AppConfiguration config = new AppConfiguration(0, 1, 1200, 1200, "azureus.client");
|
||||
final AppConfiguration config = new AppConfiguration(0, 1, 2, "azureus.client");
|
||||
|
||||
assertThat(config.getMaxUploadRate()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ShouldNotBuildIfMaxRateIsLesserThanMinRate() {
|
||||
assertThatThrownBy(() -> new AppConfiguration(180, 150, 1200, 1200, "azureus.client"))
|
||||
assertThatThrownBy(() -> new AppConfiguration(180, 150, 2, "azureus.client"))
|
||||
.isInstanceOf(AppConfigurationIntegrityException.class)
|
||||
.hasMessageContaining("maxUploadRate must be strictly greater than minUploadRate.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ShouldNotBuildIfMaxRateEqualsThanMinRate() {
|
||||
assertThatThrownBy(() -> new AppConfiguration(180, 180, 1200, 1200, "azureus.client"))
|
||||
assertThatThrownBy(() -> new AppConfiguration(180, 180, 2, "azureus.client"))
|
||||
.isInstanceOf(AppConfigurationIntegrityException.class)
|
||||
.hasMessageContaining("maxUploadRate must be strictly greater than minUploadRate.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ShouldNotBuildIfSeedForIsLessThanOne() {
|
||||
assertThatThrownBy(() -> new AppConfiguration(180, 190, 0, 1200, "azureus.client"))
|
||||
public void ShouldNotBuildIfSimultaneousSeedIsLessThanOne() {
|
||||
assertThatThrownBy(() -> new AppConfiguration(180, 190, 0, "azureus.client"))
|
||||
.isInstanceOf(AppConfigurationIntegrityException.class)
|
||||
.hasMessageContaining("seedFor must be greater than 1.");
|
||||
.hasMessageContaining("simultaneousSeed must be greater than 0.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateIfSeedForIsOne() {
|
||||
final AppConfiguration config = new AppConfiguration(180, 190, 1, 1200, "azureus.client");
|
||||
public void shouldCreateIfSimultaneousSeedIsOne() {
|
||||
final AppConfiguration config = new AppConfiguration(180, 190, 1, "azureus.client");
|
||||
|
||||
assertThat(config.getSeedFor()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ShouldNotBuildIfWaitBetweenSeedIsLessThanOne() {
|
||||
assertThatThrownBy(() -> new AppConfiguration(180, 190, 1200, 0, "azureus.client"))
|
||||
.isInstanceOf(AppConfigurationIntegrityException.class)
|
||||
.hasMessageContaining("waitBetweenSeed must be greater than 1.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateIfWaitBetweenSeedIsOne() {
|
||||
final AppConfiguration config = new AppConfiguration(180, 190, 1200, 1, "azureus.client");
|
||||
|
||||
assertThat(config.getWaitBetweenSeed()).isEqualTo(1);
|
||||
assertThat(config.getSimultaneousSeed()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ShouldNotBuildIfClientIsNull() {
|
||||
assertThatThrownBy(() -> new AppConfiguration(180, 190, 1200, 1200, null))
|
||||
assertThatThrownBy(() -> new AppConfiguration(180, 190, 2, null))
|
||||
.isInstanceOf(AppConfigurationIntegrityException.class)
|
||||
.hasMessageContaining("client is required, no file name given.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ShouldNotBuildIfClientIsEmpty() {
|
||||
assertThatThrownBy(() -> new AppConfiguration(180, 190, 1200, 1200, " "))
|
||||
assertThatThrownBy(() -> new AppConfiguration(180, 190, 2, " "))
|
||||
.isInstanceOf(AppConfigurationIntegrityException.class)
|
||||
.hasMessageContaining("client is required, no file name given.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBuild() {
|
||||
final AppConfiguration config = new AppConfiguration(180, 190, 1250, 1200, "azureus.client");
|
||||
final AppConfiguration config = new AppConfiguration(180, 190, 2, "azureus.client");
|
||||
|
||||
assertThat(config.getMinUploadRate()).isEqualTo(180);
|
||||
assertThat(config.getMaxUploadRate()).isEqualTo(190);
|
||||
assertThat(config.getSeedFor()).isEqualTo(1250);
|
||||
assertThat(config.getWaitBetweenSeed()).isEqualTo(1200);
|
||||
assertThat(config.getSimultaneousSeed()).isEqualTo(2);
|
||||
assertThat(config.getClientFileName()).isEqualTo("azureus.client");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeEqualsByProperties() {
|
||||
final AppConfiguration config = new AppConfiguration(180, 190, 1200, 1200, "azureus.client");
|
||||
final AppConfiguration config2 = new AppConfiguration(180, 190, 1200, 1200, "azureus.client");
|
||||
final AppConfiguration config = new AppConfiguration(180, 190, 2, "azureus.client");
|
||||
final AppConfiguration config2 = new AppConfiguration(180, 190, 2, "azureus.client");
|
||||
assertThat(config).isEqualTo(config2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHaveSameHashCodeWithSameProperties() {
|
||||
final AppConfiguration config = new AppConfiguration(180, 190, 1200, 1200, "azureus.client");
|
||||
final AppConfiguration config2 = new AppConfiguration(180, 190, 1200, 1200, "azureus.client");
|
||||
final AppConfiguration config = new AppConfiguration(180, 190, 2, "azureus.client");
|
||||
final AppConfiguration config2 = new AppConfiguration(180, 190, 2, "azureus.client");
|
||||
assertThat(config.hashCode()).isEqualTo(config2.hashCode());
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,7 @@ public class JoalConfigProviderTest {
|
|||
public static final AppConfiguration defaultConfig = new AppConfiguration(
|
||||
180,
|
||||
190,
|
||||
840,
|
||||
600,
|
||||
2,
|
||||
"azureus-5.7.5.0.client"
|
||||
);
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import java.nio.file.FileVisitOption;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -65,7 +66,7 @@ public class TorrentFileProviderDITest {
|
|||
@Test
|
||||
public void shouldInjectConfigProvider() throws NoMoreTorrentsFileAvailableException {
|
||||
assertThat(torrentFileProvider.getTorrentCount()).isEqualTo(1);
|
||||
assertThat(torrentFileProvider.getRandomTorrentFile().getName()).isEqualTo("ubuntu-17.04-desktop-amd64.iso");
|
||||
assertThat(torrentFileProvider.getTorrentNotIn(new ArrayList<>()).getName()).isEqualTo("ubuntu-17.04-desktop-amd64.iso");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package org.araymond.joal.core.torrent.watcher;
|
||||
|
||||
import org.araymond.joal.core.exception.NoMoreTorrentsFileAvailableException;
|
||||
import org.araymond.joal.core.ttorent.client.MockedTorrent;
|
||||
import org.araymond.joal.core.utils.TorrentFileCreator;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -15,11 +18,14 @@ import java.nio.file.FileVisitOption;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import static java.nio.file.Files.exists;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* Created by raymo on 13/03/2017.
|
||||
|
@ -66,7 +72,7 @@ public class TorrentFileProviderTest {
|
|||
public void shouldFailIfFolderDoesNotContainsTorrentFiles() throws IOException {
|
||||
final TorrentFileProvider provider = new TorrentFileProvider(resourcePath.toString(), Mockito.mock(ApplicationEventPublisher.class));
|
||||
|
||||
assertThatThrownBy(provider::getRandomTorrentFile)
|
||||
assertThatThrownBy(() -> provider.getTorrentNotIn(new ArrayList<>()))
|
||||
.isInstanceOf(NoMoreTorrentsFileAvailableException.class)
|
||||
.hasMessageContaining("No more torrent file available.");
|
||||
}
|
||||
|
@ -126,7 +132,7 @@ public class TorrentFileProviderTest {
|
|||
assertThat(provider.getTorrentCount()).isEqualTo(1);
|
||||
|
||||
assertThat(archivedTorrentPath.resolve("ubuntu.torrent")).doesNotExist();
|
||||
provider.moveToArchiveFolder(provider.getRandomTorrentFile());
|
||||
provider.moveToArchiveFolder(provider.getTorrentNotIn(new ArrayList<>()));
|
||||
assertThat(torrentsPath.resolve("ubuntu.torrent")).doesNotExist();
|
||||
|
||||
assertThat(archivedTorrentPath.resolve("ubuntu.torrent")).exists();
|
||||
|
@ -147,5 +153,122 @@ public class TorrentFileProviderTest {
|
|||
assertThat(archivedTorrentPath.resolve("ubuntu.torrent")).exists();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotFailIfFileIsNotPresentWhenArchiving() throws IOException {
|
||||
final Path torrentFile = TorrentFileCreator.create(torrentsPath.resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
|
||||
|
||||
final TorrentFileProvider provider = new TorrentFileProvider(resourcePath.toString(), Mockito.mock(ApplicationEventPublisher.class));
|
||||
|
||||
try {
|
||||
provider.moveToArchiveFolder(torrentFile.resolve("dd.torrent").toFile());
|
||||
} catch (final Throwable throwable) {
|
||||
fail("should not fail if file were not present.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCallOnFileDeleteBeforeDeletingFileWhenArchiving() throws IOException {
|
||||
final Path torrentFile = TorrentFileCreator.create(torrentsPath.resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
|
||||
|
||||
final TorrentFileProvider provider = Mockito.spy(new TorrentFileProvider(resourcePath.toString(), Mockito.mock(ApplicationEventPublisher.class)));
|
||||
Mockito.doAnswer(invocation -> {
|
||||
assertThat(torrentFile.toFile()).exists();
|
||||
return null;
|
||||
}).when(provider).onFileDelete(torrentFile.toFile());
|
||||
|
||||
provider.onFileCreate(torrentFile.toFile());
|
||||
provider.moveToArchiveFolder(torrentFile.toFile());
|
||||
Mockito.verify(provider, Mockito.times(1)).moveToArchiveFolder(torrentFile.toFile());
|
||||
assertThat(torrentFile.toFile()).doesNotExist();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotifyListenerOnFileAdded() throws IOException {
|
||||
final Path torrentFile = TorrentFileCreator.create(torrentsPath.resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
|
||||
|
||||
final TorrentFileProvider provider = new TorrentFileProvider(resourcePath.toString(), Mockito.mock(ApplicationEventPublisher.class));
|
||||
final CountDownLatch createLock = new CountDownLatch(1);
|
||||
final CountDownLatch deleteLock = new CountDownLatch(1);
|
||||
provider.registerListener(new CountDownLatchListener(createLock, deleteLock));
|
||||
|
||||
provider.postConstruct();
|
||||
provider.onFileCreate(torrentFile.toFile());
|
||||
|
||||
assertThat(createLock.getCount()).isEqualTo(0);
|
||||
assertThat(deleteLock.getCount()).isEqualTo(1);
|
||||
provider.preDestroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotNotifyFileAddedBeforeInitIsOver() throws IOException {
|
||||
final Path torrentFile = TorrentFileCreator.create(torrentsPath.resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
|
||||
|
||||
final TorrentFileProvider provider = new TorrentFileProvider(resourcePath.toString(), Mockito.mock(ApplicationEventPublisher.class));
|
||||
final CountDownLatch createLock = new CountDownLatch(1);
|
||||
final CountDownLatch deleteLock = new CountDownLatch(1);
|
||||
provider.registerListener(new CountDownLatchListener(createLock, deleteLock));
|
||||
|
||||
provider.onFileCreate(torrentFile.toFile());
|
||||
|
||||
assertThat(createLock.getCount()).isEqualTo(1);
|
||||
assertThat(deleteLock.getCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotifyListenerOnFileRemoved() throws IOException {
|
||||
final Path torrentFile = TorrentFileCreator.create(torrentsPath.resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
|
||||
|
||||
final TorrentFileProvider provider = new TorrentFileProvider(resourcePath.toString(), Mockito.mock(ApplicationEventPublisher.class));
|
||||
provider.onFileCreate(torrentFile.toFile());
|
||||
|
||||
final CountDownLatch createLock = new CountDownLatch(1);
|
||||
final CountDownLatch deleteLock = new CountDownLatch(1);
|
||||
provider.registerListener(new CountDownLatchListener(createLock, deleteLock));
|
||||
|
||||
provider.onFileDelete(torrentFile.toFile());
|
||||
|
||||
assertThat(createLock.getCount()).isEqualTo(1);
|
||||
assertThat(deleteLock.getCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotifyListenerOnFileChanged() throws IOException {
|
||||
final Path torrentFile = TorrentFileCreator.create(torrentsPath.resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
|
||||
|
||||
final TorrentFileProvider provider = new TorrentFileProvider(resourcePath.toString(), Mockito.mock(ApplicationEventPublisher.class));
|
||||
final CountDownLatch createLock = new CountDownLatch(1);
|
||||
final CountDownLatch deleteLock = new CountDownLatch(1);
|
||||
provider.registerListener(new CountDownLatchListener(createLock, deleteLock));
|
||||
|
||||
provider.postConstruct();
|
||||
provider.onFileChange(torrentFile.toFile());
|
||||
|
||||
assertThat(createLock.getCount()).isEqualTo(0);
|
||||
assertThat(deleteLock.getCount()).isEqualTo(0);
|
||||
provider.preDestroy();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static final class CountDownLatchListener implements TorrentFileChangeAware {
|
||||
|
||||
private final CountDownLatch createLock;
|
||||
private final CountDownLatch deleteLock;
|
||||
|
||||
private CountDownLatchListener(final CountDownLatch createLock, final CountDownLatch deleteLock) {
|
||||
this.createLock = createLock;
|
||||
this.deleteLock = deleteLock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTorrentAdded(final MockedTorrent torrent) {
|
||||
createLock.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTorrentRemoved(final MockedTorrent torrent) {
|
||||
deleteLock.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
"minUploadRate": 180,
|
||||
"maxUploadRate": 190,
|
||||
"seedFor": 840,
|
||||
"waitBetweenSeed": 600,
|
||||
"simultaneousSeed": 2,
|
||||
"client": "azureus-5.7.5.0.client"
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue