mirror of
https://github.com/anthonyraymond/joal.git
synced 2024-09-20 07:16:26 +08:00
style changes
- remove extraneous isDebugEnabled() checks; - rename some params for better readability; - decrease number of if-else blocks; - fix some typos.
This commit is contained in:
parent
a58ab74db5
commit
7fe610ce6a
|
@ -48,7 +48,7 @@ By default the web-ui is disabled, you can enable it with some more arguments:
|
||||||
- `--joal.ui.secret-token="SECRET_TOKEN"`: use your own secret token here (this is some kind of a password, choose a complicated one).
|
- `--joal.ui.secret-token="SECRET_TOKEN"`: use your own secret token here (this is some kind of a password, choose a complicated one).
|
||||||
|
|
||||||
Once joal is started head to: `http://localhost:port/SECRET_OBFUSCATION_PATH/ui/` (obviously, replace `SECRET_OBFUSCATION_PATH`) by the value you had chosen
|
Once joal is started head to: `http://localhost:port/SECRET_OBFUSCATION_PATH/ui/` (obviously, replace `SECRET_OBFUSCATION_PATH`) by the value you had chosen
|
||||||
The `joal.ui.path.prefix` might seems useless but it's actually **crucial** to set it as complex as possible to prevent peoples to know that joal is running on your server.
|
The `joal.ui.path.prefix` might seems useless but it's actually **crucial** to set it as complex as possible to prevent people to know that joal is running on your server.
|
||||||
|
|
||||||
If you want to use iframe you may also pass the `joal.iframe.enabled=true` argument. If you don't known what that is just ignore it.
|
If you want to use iframe you may also pass the `joal.iframe.enabled=true` argument. If you don't known what that is just ignore it.
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ Those projects are maintained by their individual authors, if you have any quest
|
||||||
|
|
||||||
# Thanks:
|
# Thanks:
|
||||||
This project use a modified version of the awesome [mpetazzoni/ttorrent](http://mpetazzoni.github.com/ttorrent/) library. Thanks to **mpetazzoni** for this.
|
This project use a modified version of the awesome [mpetazzoni/ttorrent](http://mpetazzoni.github.com/ttorrent/) library. Thanks to **mpetazzoni** for this.
|
||||||
Also this project has benefited from the help of several peoples, see [Thanks.md](THANKS.md)
|
Also this project has benefited from the help of several people, see [Thanks.md](THANKS.md)
|
||||||
|
|
||||||
## Supporters
|
## Supporters
|
||||||
[![Thanks for providing Jetbrain license](readme-assets/jetbrains.svg)](https://www.jetbrains.com/?from=joal)
|
[![Thanks for providing Jetbrain license](readme-assets/jetbrains.svg)](https://www.jetbrains.com/?from=joal)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package org.araymond.joal;
|
package org.araymond.joal;
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
|
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
|
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
|
||||||
|
@ -15,5 +14,4 @@ public class JackOfAllTradesApplication {
|
||||||
public static void main(final String[] args) {
|
public static void main(final String[] args) {
|
||||||
SpringApplication.run(JackOfAllTradesApplication.class, args);
|
SpringApplication.run(JackOfAllTradesApplication.class, args);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ public class CoreEventListener {
|
||||||
public CoreEventListener() {
|
public CoreEventListener() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Async
|
@Async
|
||||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
@EventListener
|
@EventListener
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package org.araymond.joal.core;
|
package org.araymond.joal.core;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import org.apache.http.config.SocketConfig;
|
import org.apache.http.config.SocketConfig;
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
import org.apache.http.impl.client.HttpClients;
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
@ -38,10 +37,7 @@ import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -168,17 +164,11 @@ public class SeedManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<AnnouncerFacade> getCurrentlySeedingAnnouncer() {
|
public List<AnnouncerFacade> getCurrentlySeedingAnnouncer() {
|
||||||
if (this.client == null) {
|
return this.client == null ? new ArrayList<>() : client.getCurrentlySeedingAnnouncer();
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
|
||||||
return client.getCurrentlySeedingAnnouncer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<InfoHash, Speed> getSpeedMap() {
|
public Map<InfoHash, Speed> getSpeedMap() {
|
||||||
if (this.bandwidthDispatcher == null) {
|
return this.bandwidthDispatcher == null ? new HashMap<>() : bandwidthDispatcher.getSpeedMap();
|
||||||
return Maps.newHashMap();
|
|
||||||
}
|
|
||||||
return bandwidthDispatcher.getSpeedMap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AppConfiguration getCurrentConfig() {
|
public AppConfiguration getCurrentConfig() {
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package org.araymond.joal.core.bandwith;
|
package org.araymond.joal.core.bandwith;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.collect.Sets;
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.araymond.joal.core.bandwith.weight.PeersAwareWeightCalculator;
|
import org.araymond.joal.core.bandwith.weight.PeersAwareWeightCalculator;
|
||||||
import org.araymond.joal.core.bandwith.weight.WeightHolder;
|
import org.araymond.joal.core.bandwith.weight.WeightHolder;
|
||||||
|
@ -10,10 +8,13 @@ import org.araymond.joal.core.torrent.torrent.InfoHash;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
|
import static java.util.Optional.ofNullable;
|
||||||
|
import static org.apache.commons.lang3.ObjectUtils.getIfNull;
|
||||||
import static org.slf4j.LoggerFactory.getLogger;
|
import static org.slf4j.LoggerFactory.getLogger;
|
||||||
|
|
||||||
public class BandwidthDispatcher implements BandwidthDispatcherFacade, Runnable {
|
public class BandwidthDispatcher implements BandwidthDispatcherFacade, Runnable {
|
||||||
|
@ -25,8 +26,8 @@ public class BandwidthDispatcher implements BandwidthDispatcherFacade, Runnable
|
||||||
private final Map<InfoHash, Speed> speedMap;
|
private final Map<InfoHash, Speed> speedMap;
|
||||||
private SpeedChangedListener speedChangedListener;
|
private SpeedChangedListener speedChangedListener;
|
||||||
private final int threadPauseInterval;
|
private final int threadPauseInterval;
|
||||||
private int threadLoopCounter = 0;
|
private int threadLoopCounter;
|
||||||
private volatile boolean stop = false;
|
private volatile boolean stop;
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
|
|
||||||
public BandwidthDispatcher(final int threadPauseInterval, final RandomSpeedProvider randomSpeedProvider) {
|
public BandwidthDispatcher(final int threadPauseInterval, final RandomSpeedProvider randomSpeedProvider) {
|
||||||
|
@ -35,7 +36,6 @@ public class BandwidthDispatcher implements BandwidthDispatcherFacade, Runnable
|
||||||
this.speedMap = new HashMap<>();
|
this.speedMap = new HashMap<>();
|
||||||
this.lock = new ReentrantReadWriteLock();
|
this.lock = new ReentrantReadWriteLock();
|
||||||
|
|
||||||
|
|
||||||
this.weightHolder = new WeightHolder<>(new PeersAwareWeightCalculator());
|
this.weightHolder = new WeightHolder<>(new PeersAwareWeightCalculator());
|
||||||
this.randomSpeedProvider = randomSpeedProvider;
|
this.randomSpeedProvider = randomSpeedProvider;
|
||||||
}
|
}
|
||||||
|
@ -44,19 +44,18 @@ public class BandwidthDispatcher implements BandwidthDispatcherFacade, Runnable
|
||||||
this.speedChangedListener = speedListener;
|
this.speedChangedListener = speedListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* This method does not benefit from the lock, because the value will never be accessed in a ambiguous way.
|
* This method does not benefit from the lock, because the value will never be accessed in a ambiguous way.
|
||||||
* And even if it happens, we returns 0 by default.
|
* And even if it happens, we return 0 by default.
|
||||||
*/
|
*/
|
||||||
public TorrentSeedStats getSeedStatForTorrent(final InfoHash infoHash) {
|
public TorrentSeedStats getSeedStatForTorrent(final InfoHash infoHash) {
|
||||||
final TorrentSeedStats torrentSeedStats = this.torrentsSeedStats.get(infoHash);
|
return getIfNull(this.torrentsSeedStats.get(infoHash), TorrentSeedStats::new);
|
||||||
return torrentSeedStats == null ? new TorrentSeedStats() : torrentSeedStats;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<InfoHash, Speed> getSpeedMap() {
|
public Map<InfoHash, Speed> getSpeedMap() {
|
||||||
try {
|
try {
|
||||||
this.lock.readLock().lock();
|
this.lock.readLock().lock();
|
||||||
return Maps.newHashMap(speedMap);
|
return new HashMap<>(speedMap);
|
||||||
} finally {
|
} finally {
|
||||||
this.lock.readLock().unlock();
|
this.lock.readLock().unlock();
|
||||||
}
|
}
|
||||||
|
@ -90,16 +89,17 @@ public class BandwidthDispatcher implements BandwidthDispatcherFacade, Runnable
|
||||||
this.threadLoopCounter = 0;
|
this.threadLoopCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method as to run as fast as possible to avoid blocking other ones. Because we wan't this loop
|
// This method as to run as fast as possible to avoid blocking other ones. Because we wasn't this loop
|
||||||
// to be scheduled as precise as we can. Locking to much will delay the Thread.sleep and cause stats
|
// to be scheduled as precise as we can. Locking too much will delay the Thread.sleep and cause stats
|
||||||
// to be undervalued
|
// to be undervalued
|
||||||
this.lock.readLock().lock();
|
this.lock.readLock().lock();
|
||||||
final Set<Map.Entry<InfoHash, TorrentSeedStats>> entrySet = Sets.newHashSet(this.torrentsSeedStats.entrySet());
|
final Set<Map.Entry<InfoHash, TorrentSeedStats>> entrySet = new HashSet<>(this.torrentsSeedStats.entrySet());
|
||||||
this.lock.readLock().unlock();
|
this.lock.readLock().unlock();
|
||||||
|
|
||||||
for (final Map.Entry<InfoHash, TorrentSeedStats> entry : entrySet) {
|
for (final Map.Entry<InfoHash, TorrentSeedStats> entry : entrySet) {
|
||||||
final Speed speed = this.speedMap.get(entry.getKey());
|
final long speedInBytesPerSecond = ofNullable(this.speedMap.get(entry.getKey()))
|
||||||
final long speedInBytesPerSecond = speed == null ? 0: speed.getBytesPerSeconds(); // avoid Map#getOrDefault as it will trigger a lot of Speed object instantiation for nothing.
|
.map(Speed::getBytesPerSeconds)
|
||||||
|
.orElse(0L);
|
||||||
// Divide by 1000 because of the thread pause interval being in milliseconds
|
// Divide by 1000 because of the thread pause interval being in milliseconds
|
||||||
// The multiplication HAS to be done before the division, otherwise we're going to have trailing zeroes
|
// The multiplication HAS to be done before the division, otherwise we're going to have trailing zeroes
|
||||||
entry.getValue().addUploaded((speedInBytesPerSecond * this.threadPauseInterval) / 1000);
|
entry.getValue().addUploaded((speedInBytesPerSecond * this.threadPauseInterval) / 1000);
|
||||||
|
@ -110,9 +110,7 @@ public class BandwidthDispatcher implements BandwidthDispatcherFacade, Runnable
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateTorrentPeers(final InfoHash infoHash, final int seeders, final int leechers) {
|
public void updateTorrentPeers(final InfoHash infoHash, final int seeders, final int leechers) {
|
||||||
if (logger.isDebugEnabled()) {
|
logger.debug("Updating Peers stats for {}", infoHash.humanReadableValue());
|
||||||
logger.debug("Updating Peers stats for {}", infoHash.humanReadableValue());
|
|
||||||
}
|
|
||||||
this.lock.writeLock().lock();
|
this.lock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
this.weightHolder.addOrUpdate(infoHash, new Peers(seeders, leechers));
|
this.weightHolder.addOrUpdate(infoHash, new Peers(seeders, leechers));
|
||||||
|
@ -123,9 +121,7 @@ public class BandwidthDispatcher implements BandwidthDispatcherFacade, Runnable
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerTorrent(final InfoHash infoHash) {
|
public void registerTorrent(final InfoHash infoHash) {
|
||||||
if (logger.isDebugEnabled()) {
|
logger.debug("{} has been added to bandwidth dispatcher.", infoHash.humanReadableValue());
|
||||||
logger.debug("{} has been added to bandwidth dispatcher.", infoHash.humanReadableValue());
|
|
||||||
}
|
|
||||||
this.lock.writeLock().lock();
|
this.lock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
this.torrentsSeedStats.put(infoHash, new TorrentSeedStats());
|
this.torrentsSeedStats.put(infoHash, new TorrentSeedStats());
|
||||||
|
@ -136,9 +132,7 @@ public class BandwidthDispatcher implements BandwidthDispatcherFacade, Runnable
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unregisterTorrent(final InfoHash infoHash) {
|
public void unregisterTorrent(final InfoHash infoHash) {
|
||||||
if (logger.isDebugEnabled()) {
|
logger.debug("{} has been removed from bandwidth dispatcher.", infoHash.humanReadableValue());
|
||||||
logger.debug("{} has been removed from bandwidth dispatcher.", infoHash.humanReadableValue());
|
|
||||||
}
|
|
||||||
this.lock.writeLock().lock();
|
this.lock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
this.weightHolder.remove(infoHash);
|
this.weightHolder.remove(infoHash);
|
||||||
|
@ -152,9 +146,7 @@ public class BandwidthDispatcher implements BandwidthDispatcherFacade, Runnable
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void refreshCurrentBandwidth() {
|
void refreshCurrentBandwidth() {
|
||||||
if (logger.isDebugEnabled()) {
|
logger.debug("Refreshing global bandwidth");
|
||||||
logger.debug("Refreshing global bandwidth");
|
|
||||||
}
|
|
||||||
this.lock.writeLock().lock();
|
this.lock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
this.randomSpeedProvider.refresh();
|
this.randomSpeedProvider.refresh();
|
||||||
|
@ -169,25 +161,25 @@ public class BandwidthDispatcher implements BandwidthDispatcherFacade, Runnable
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void recomputeSpeeds() {
|
void recomputeSpeeds() {
|
||||||
if (logger.isDebugEnabled()) {
|
logger.debug("Refreshing all torrents speeds");
|
||||||
logger.debug("Refreshing all torrents speeds");
|
|
||||||
}
|
|
||||||
for (final InfoHash infohash : this.torrentsSeedStats.keySet()) {
|
for (final InfoHash infohash : this.torrentsSeedStats.keySet()) {
|
||||||
final double percentOfSpeedAssigned = this.weightHolder.getTotalWeight() == 0.0
|
|
||||||
? 0.0
|
|
||||||
: this.weightHolder.getWeightFor(infohash) / this.weightHolder.getTotalWeight();
|
|
||||||
|
|
||||||
this.speedMap.compute(infohash, (hash, speed) -> {
|
this.speedMap.compute(infohash, (hash, speed) -> {
|
||||||
if (speed == null) {
|
if (speed == null) {
|
||||||
return new Speed(0);
|
return new Speed(0);
|
||||||
}
|
}
|
||||||
|
double percentOfSpeedAssigned = this.weightHolder.getTotalWeight() == 0.0
|
||||||
|
? 0.0
|
||||||
|
: this.weightHolder.getWeightFor(infohash) / this.weightHolder.getTotalWeight();
|
||||||
speed.setBytesPerSeconds((long) (this.randomSpeedProvider.getInBytesPerSeconds() * percentOfSpeedAssigned));
|
speed.setBytesPerSeconds((long) (this.randomSpeedProvider.getInBytesPerSeconds() * percentOfSpeedAssigned));
|
||||||
|
|
||||||
return speed;
|
return speed;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (speedChangedListener != null) {
|
if (speedChangedListener != null) {
|
||||||
this.speedChangedListener.speedsHasChanged(Maps.newHashMap(this.speedMap));
|
this.speedChangedListener.speedsHasChanged(new HashMap<>(this.speedMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
final StringBuilder sb = new StringBuilder("All torrents speeds has been refreshed:\n");
|
final StringBuilder sb = new StringBuilder("All torrents speeds has been refreshed:\n");
|
||||||
|
|
|
@ -3,5 +3,5 @@ package org.araymond.joal.core.bandwith;
|
||||||
import org.araymond.joal.core.torrent.torrent.InfoHash;
|
import org.araymond.joal.core.torrent.torrent.InfoHash;
|
||||||
|
|
||||||
public interface BandwidthDispatcherFacade {
|
public interface BandwidthDispatcherFacade {
|
||||||
TorrentSeedStats getSeedStatForTorrent(final InfoHash infoHash);
|
TorrentSeedStats getSeedStatForTorrent(InfoHash infoHash);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,14 +10,13 @@ public class RandomSpeedProvider {
|
||||||
|
|
||||||
public RandomSpeedProvider(final AppConfiguration appConfiguration) {
|
public RandomSpeedProvider(final AppConfiguration appConfiguration) {
|
||||||
this.appConfiguration = appConfiguration;
|
this.appConfiguration = appConfiguration;
|
||||||
|
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refresh() {
|
public void refresh() {
|
||||||
final Long minUploadRateInBytes = appConfiguration.getMinUploadRate() * 1000L;
|
final long minUploadRateInBytes = appConfiguration.getMinUploadRate() * 1000L;
|
||||||
final Long maxUploadRateInBytes = appConfiguration.getMaxUploadRate() * 1000L;
|
final long maxUploadRateInBytes = appConfiguration.getMaxUploadRate() * 1000L;
|
||||||
this.currentSpeed = (minUploadRateInBytes.equals(maxUploadRateInBytes))
|
this.currentSpeed = (minUploadRateInBytes == maxUploadRateInBytes)
|
||||||
? maxUploadRateInBytes
|
? maxUploadRateInBytes
|
||||||
: ThreadLocalRandom.current().nextLong(minUploadRateInBytes, maxUploadRateInBytes);
|
: ThreadLocalRandom.current().nextLong(minUploadRateInBytes, maxUploadRateInBytes);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,5 +5,5 @@ import org.araymond.joal.core.torrent.torrent.InfoHash;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public interface SpeedChangedListener {
|
public interface SpeedChangedListener {
|
||||||
void speedsHasChanged(final Map<InfoHash, Speed> speeds);
|
void speedsHasChanged(Map<InfoHash, Speed> speeds);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,15 +4,9 @@ import org.araymond.joal.core.bandwith.Peers;
|
||||||
|
|
||||||
public class PeersAwareWeightCalculator {
|
public class PeersAwareWeightCalculator {
|
||||||
public double calculate(final Peers peers) {
|
public double calculate(final Peers peers) {
|
||||||
if (peers.getSeeders() == 0) {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
final double leechersRatio = peers.getLeechersRatio();
|
final double leechersRatio = peers.getLeechersRatio();
|
||||||
if (leechersRatio == 0) {
|
return (peers.getSeeders() == 0 || leechersRatio == 0)
|
||||||
return 0.0;
|
? 0.0
|
||||||
}
|
: leechersRatio * 100 * leechersRatio * peers.getLeechers();
|
||||||
return leechersRatio * 100
|
|
||||||
* (peers.getSeeders() * leechersRatio)
|
|
||||||
* (((double) peers.getLeechers()) / peers.getSeeders());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ import java.util.Map;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
import static java.util.Optional.ofNullable;
|
||||||
|
|
||||||
public class WeightHolder<E> {
|
public class WeightHolder<E> {
|
||||||
|
|
||||||
private final Lock lock;
|
private final Lock lock;
|
||||||
|
@ -24,13 +26,9 @@ public class WeightHolder<E> {
|
||||||
final double weight = this.weightCalculator.calculate(peers);
|
final double weight = this.weightCalculator.calculate(peers);
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
final Weight previousWeight = this.weightMap.put(item, new Weight(weight));
|
ofNullable(this.weightMap.put(item, new Weight(weight))).ifPresentOrElse(
|
||||||
|
previousWeight -> this.totalWeight = this.totalWeight - previousWeight.getWeight() + weight,
|
||||||
if (previousWeight != null) {
|
() -> this.totalWeight += weight);
|
||||||
this.totalWeight = this.totalWeight - previousWeight.getWeight() + weight;
|
|
||||||
} else {
|
|
||||||
this.totalWeight += weight;
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
@ -39,28 +37,23 @@ public class WeightHolder<E> {
|
||||||
public void remove(final E item) {
|
public void remove(final E item) {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
final Weight weight = this.weightMap.remove(item);
|
ofNullable(this.weightMap.remove(item))
|
||||||
if (weight != null) {
|
.ifPresent(w -> this.totalWeight -= w.getWeight());
|
||||||
this.totalWeight -= weight.getWeight();
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* For performance reasons, this method does not benefit from the lock.
|
* For performance reasons, this method does not benefit from the lock.
|
||||||
* That's not a big deal because:
|
* That's not a big deal because:
|
||||||
* - if a value is not yet added it will return 0.0.
|
* - if a value is not yet added it will return 0.0.
|
||||||
* - if a value is still present il will returns the previous value.
|
* - if a value is still present it will return the previous value.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public double getWeightFor(final E item) {
|
public double getWeightFor(final E item) {
|
||||||
final Weight weight = this.weightMap.get(item);
|
return ofNullable(weightMap.get(item))
|
||||||
if (weight == null) {
|
.map(Weight::getWeight)
|
||||||
return 0.0;
|
.orElse(0.0);
|
||||||
}
|
|
||||||
return weight.getWeight();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getTotalWeight() {
|
public double getTotalWeight() {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static java.util.Optional.ofNullable;
|
||||||
import static org.araymond.joal.core.client.emulated.BitTorrentClientConfig.HttpHeader;
|
import static org.araymond.joal.core.client.emulated.BitTorrentClientConfig.HttpHeader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,10 +56,8 @@ public class BitTorrentClient {
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
Optional<String> getKey(final InfoHash infoHash, final RequestEvent event) {
|
Optional<String> getKey(final InfoHash infoHash, final RequestEvent event) {
|
||||||
if (keyGenerator == null) {
|
return ofNullable(keyGenerator)
|
||||||
return Optional.empty();
|
.map(keyGen -> keyGen.getKey(infoHash, event));
|
||||||
}
|
|
||||||
return Optional.of(keyGenerator.getKey(infoHash, event));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getQuery() {
|
public String getQuery() {
|
||||||
|
@ -83,12 +82,9 @@ public class BitTorrentClient {
|
||||||
.replaceAll("\\{port}", String.valueOf(connectionHandler.getPort()))
|
.replaceAll("\\{port}", String.valueOf(connectionHandler.getPort()))
|
||||||
.replaceAll("\\{numwant}", String.valueOf(this.getNumwant(event)));
|
.replaceAll("\\{numwant}", String.valueOf(this.getNumwant(event)));
|
||||||
|
|
||||||
final String peerId;
|
final String peerId = this.peerIdGenerator.getShouldUrlEncoded()
|
||||||
if (this.peerIdGenerator.getShouldUrlEncoded()) {
|
? urlEncoder.encode(this.getPeerId(torrentInfoHash, event))
|
||||||
peerId = urlEncoder.encode(this.getPeerId(torrentInfoHash, event));
|
: this.getPeerId(torrentInfoHash, event);
|
||||||
} else {
|
|
||||||
peerId = this.getPeerId(torrentInfoHash, event);
|
|
||||||
}
|
|
||||||
emulatedClientQuery = emulatedClientQuery.replaceAll("\\{peerid}", peerId);
|
emulatedClientQuery = emulatedClientQuery.replaceAll("\\{peerid}", peerId);
|
||||||
|
|
||||||
// set ip or ipv6 then remove placeholders that were left empty
|
// set ip or ipv6 then remove placeholders that were left empty
|
||||||
|
|
|
@ -49,10 +49,8 @@ public class BitTorrentClientConfig {
|
||||||
this.numwant = numwant;
|
this.numwant = numwant;
|
||||||
this.numwantOnStop = numwantOnStop;
|
this.numwantOnStop = numwantOnStop;
|
||||||
|
|
||||||
if (this.query.contains("{key}")) {
|
if (this.query.contains("{key}") && this.keyGenerator == null) {
|
||||||
if (this.keyGenerator == null) {
|
throw new TorrentClientConfigIntegrityException("Query string contains {key}, but no keyGenerator was found in .client file.");
|
||||||
throw new TorrentClientConfigIntegrityException("Query string contains {key}, but no keyGenerator was found in .client file.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,14 +64,13 @@ public class BitTorrentClientProvider implements Provider<BitTorrentClient> {
|
||||||
throw new FileNotFoundException(String.format("BitTorrent client configuration file '%s' not found.", clientConfigPath.toAbsolutePath()));
|
throw new FileNotFoundException(String.format("BitTorrent client configuration file '%s' not found.", clientConfigPath.toAbsolutePath()));
|
||||||
}
|
}
|
||||||
|
|
||||||
final BitTorrentClientConfig config;
|
|
||||||
try {
|
try {
|
||||||
config = objectMapper.readValue(clientConfigPath.toFile(), BitTorrentClientConfig.class);
|
BitTorrentClientConfig config = objectMapper.readValue(clientConfigPath.toFile(), BitTorrentClientConfig.class);
|
||||||
|
this.bitTorrentClient = config.createClient();
|
||||||
|
logger.debug("New client successfully generated.");
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
this.bitTorrentClient = config.createClient();
|
|
||||||
logger.debug("New client successfully generated.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class SemanticVersionFilenameComparator implements Comparator<String> {
|
static final class SemanticVersionFilenameComparator implements Comparator<String> {
|
||||||
|
|
|
@ -6,7 +6,6 @@ package org.araymond.joal.core.client.emulated;
|
||||||
public class TorrentClientConfigIntegrityException extends RuntimeException {
|
public class TorrentClientConfigIntegrityException extends RuntimeException {
|
||||||
private static final long serialVersionUID = -2441989395992766363L;
|
private static final long serialVersionUID = -2441989395992766363L;
|
||||||
|
|
||||||
|
|
||||||
public TorrentClientConfigIntegrityException(final String message) {
|
public TorrentClientConfigIntegrityException(final String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ public class UrlEncoder {
|
||||||
*/
|
*/
|
||||||
public String encode(final String toBeEncoded) {
|
public String encode(final String toBeEncoded) {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
for(final char ch: toBeEncoded.toCharArray()) {
|
for (final char ch : toBeEncoded.toCharArray()) {
|
||||||
sb.append(this.urlEncodeChar(ch));
|
sb.append(this.urlEncodeChar(ch));
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
|
|
|
@ -19,7 +19,6 @@ public class NeverRefreshKeyGenerator extends KeyGenerator {
|
||||||
@JsonProperty(value = "keyCase", required = true) final Casing keyCase
|
@JsonProperty(value = "keyCase", required = true) final Casing keyCase
|
||||||
) {
|
) {
|
||||||
super(algorithm, keyCase);
|
super(algorithm, keyCase);
|
||||||
|
|
||||||
this.key = generateKey();
|
this.key = generateKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ package org.araymond.joal.core.client.emulated.generator.key;
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.Sets;
|
|
||||||
import com.turn.ttorrent.common.protocol.TrackerMessage.AnnounceRequestMessage.RequestEvent;
|
import com.turn.ttorrent.common.protocol.TrackerMessage.AnnounceRequestMessage.RequestEvent;
|
||||||
import org.araymond.joal.core.client.emulated.generator.key.algorithm.KeyAlgorithm;
|
import org.araymond.joal.core.client.emulated.generator.key.algorithm.KeyAlgorithm;
|
||||||
import org.araymond.joal.core.client.emulated.utils.Casing;
|
import org.araymond.joal.core.client.emulated.utils.Casing;
|
||||||
|
@ -11,6 +10,7 @@ import org.araymond.joal.core.torrent.torrent.InfoHash;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ public class TorrentPersistentRefreshKeyGenerator extends KeyGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void evictOldEntries() {
|
private void evictOldEntries() {
|
||||||
Sets.newHashSet(this.keyPerTorrent.entrySet()).stream()
|
new HashSet<>(this.keyPerTorrent.entrySet()).stream()
|
||||||
.filter(this::shouldEvictEntry)
|
.filter(this::shouldEvictEntry)
|
||||||
.forEach(entry -> this.keyPerTorrent.remove(entry.getKey()));
|
.forEach(entry -> this.keyPerTorrent.remove(entry.getKey()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,5 +27,4 @@ public class NeverRefreshPeerIdGenerator extends PeerIdGenerator {
|
||||||
public String getPeerId(final InfoHash infoHash, final RequestEvent event) {
|
public String getPeerId(final InfoHash infoHash, final RequestEvent event) {
|
||||||
return peerId;
|
return peerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,13 @@ package org.araymond.joal.core.client.emulated.generator.peerid;
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.Sets;
|
|
||||||
import com.turn.ttorrent.common.protocol.TrackerMessage.AnnounceRequestMessage.RequestEvent;
|
import com.turn.ttorrent.common.protocol.TrackerMessage.AnnounceRequestMessage.RequestEvent;
|
||||||
import org.araymond.joal.core.client.emulated.generator.peerid.generation.PeerIdAlgorithm;
|
import org.araymond.joal.core.client.emulated.generator.peerid.generation.PeerIdAlgorithm;
|
||||||
import org.araymond.joal.core.torrent.torrent.InfoHash;
|
import org.araymond.joal.core.torrent.torrent.InfoHash;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ public class TorrentPersistentRefreshPeerIdGenerator extends PeerIdGenerator {
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void evictOldEntries() {
|
void evictOldEntries() {
|
||||||
Sets.newHashSet(this.peerIdPerTorrent.entrySet()).stream()
|
new HashSet<>(this.peerIdPerTorrent.entrySet()).stream()
|
||||||
.filter(this::shouldEvictEntry)
|
.filter(this::shouldEvictEntry)
|
||||||
.forEach(entry -> this.peerIdPerTorrent.remove(entry.getKey()));
|
.forEach(entry -> this.peerIdPerTorrent.remove(entry.getKey()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,13 +27,8 @@ public class TorrentVolatileRefreshPeerIdGenerator extends PeerIdGenerator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPeerId(final InfoHash infoHash, final RequestEvent event) {
|
public String getPeerId(final InfoHash infoHash, final RequestEvent event) {
|
||||||
final String peerId;
|
this.peerIdPerTorrent.computeIfAbsent(infoHash, k -> super.generatePeerId());
|
||||||
|
String peerId = this.peerIdPerTorrent.get(infoHash);
|
||||||
if (!this.peerIdPerTorrent.containsKey(infoHash)) {
|
|
||||||
this.peerIdPerTorrent.put(infoHash, super.generatePeerId());
|
|
||||||
}
|
|
||||||
|
|
||||||
peerId = this.peerIdPerTorrent.get(infoHash);
|
|
||||||
|
|
||||||
if (event == RequestEvent.STOPPED) {
|
if (event == RequestEvent.STOPPED) {
|
||||||
this.peerIdPerTorrent.remove(infoHash);
|
this.peerIdPerTorrent.remove(infoHash);
|
||||||
|
|
|
@ -11,5 +11,4 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
public interface PeerIdAlgorithm {
|
public interface PeerIdAlgorithm {
|
||||||
|
|
||||||
String generate();
|
String generate();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ public class RandomPoolWithChecksumPeerIdAlgorithm implements PeerIdAlgorithm {
|
||||||
// Using the current random to generate another random would be completely useless because if the SecureRandom appears to be predictable we will be able to predict the next int as well
|
// Using the current random to generate another random would be completely useless because if the SecureRandom appears to be predictable we will be able to predict the next int as well
|
||||||
int randNumber = new SecureRandom(createSecureRandomSeed()).nextInt();
|
int randNumber = new SecureRandom(createSecureRandomSeed()).nextInt();
|
||||||
randNumber = Math.abs(randNumber % 40);
|
randNumber = Math.abs(randNumber % 40);
|
||||||
return (randNumber + 10);
|
return randNumber + 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
|
|
@ -58,27 +58,26 @@ public class AppConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validate() {
|
private void validate() {
|
||||||
if (java.util.Objects.isNull(minUploadRate)) {
|
if (minUploadRate == null) {
|
||||||
throw new AppConfigurationIntegrityException("minUploadRate must not be null");
|
throw new AppConfigurationIntegrityException("minUploadRate must not be null");
|
||||||
}
|
} else if (minUploadRate < 0L) {
|
||||||
if (minUploadRate < 0L) {
|
|
||||||
throw new AppConfigurationIntegrityException("minUploadRate must be at least 0.");
|
throw new AppConfigurationIntegrityException("minUploadRate must be at least 0.");
|
||||||
}
|
}
|
||||||
if (java.util.Objects.isNull(maxUploadRate)) {
|
|
||||||
|
if (maxUploadRate == null) {
|
||||||
throw new AppConfigurationIntegrityException("maxUploadRate must not be null");
|
throw new AppConfigurationIntegrityException("maxUploadRate must not be null");
|
||||||
}
|
} else if (maxUploadRate < 0L) {
|
||||||
if (maxUploadRate < 0L) {
|
|
||||||
throw new AppConfigurationIntegrityException("maxUploadRate must greater or equal to 0.");
|
throw new AppConfigurationIntegrityException("maxUploadRate must greater or equal to 0.");
|
||||||
}
|
} else if (maxUploadRate < minUploadRate) {
|
||||||
if (maxUploadRate < minUploadRate) {
|
|
||||||
throw new AppConfigurationIntegrityException("maxUploadRate must be greater or equal to minUploadRate.");
|
throw new AppConfigurationIntegrityException("maxUploadRate must be greater or equal to minUploadRate.");
|
||||||
}
|
}
|
||||||
if (java.util.Objects.isNull(simultaneousSeed)) {
|
|
||||||
|
if (simultaneousSeed == null) {
|
||||||
throw new AppConfigurationIntegrityException("simultaneousSeed must not be null");
|
throw new AppConfigurationIntegrityException("simultaneousSeed must not be null");
|
||||||
}
|
} else if (simultaneousSeed < 1) {
|
||||||
if (simultaneousSeed < 1) {
|
|
||||||
throw new AppConfigurationIntegrityException("simultaneousSeed must be greater than 0.");
|
throw new AppConfigurationIntegrityException("simultaneousSeed must be greater than 0.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.isBlank(client)) {
|
if (StringUtils.isBlank(client)) {
|
||||||
throw new AppConfigurationIntegrityException("client is required, no file name given.");
|
throw new AppConfigurationIntegrityException("client is required, no file name given.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,9 +36,7 @@ public class JoalConfigProvider implements Provider<AppConfiguration> {
|
||||||
throw new FileNotFoundException(String.format("App configuration file '%s' not found.", joalConfPath));
|
throw new FileNotFoundException(String.format("App configuration file '%s' not found.", joalConfPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
logger.debug("App configuration file will be searched for in {}", joalConfPath.toAbsolutePath());
|
||||||
logger.debug("App configuration file will be searched for in {}", joalConfPath.toAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init() {
|
public void init() {
|
||||||
|
@ -58,15 +56,14 @@ public class JoalConfigProvider implements Provider<AppConfiguration> {
|
||||||
AppConfiguration loadConfiguration() {
|
AppConfiguration loadConfiguration() {
|
||||||
final AppConfiguration configuration;
|
final AppConfiguration configuration;
|
||||||
try {
|
try {
|
||||||
if (logger.isDebugEnabled()) {
|
logger.debug("Reading json configuration from '{}'.", joalConfPath.toAbsolutePath());
|
||||||
logger.debug("Reading json configuration from '{}'.", joalConfPath.toAbsolutePath());
|
|
||||||
}
|
|
||||||
configuration = objectMapper.readValue(joalConfPath.toFile(), AppConfiguration.class);
|
configuration = objectMapper.readValue(joalConfPath.toFile(), AppConfiguration.class);
|
||||||
logger.debug("Successfully red json configuration.");
|
logger.debug("Successfully red json configuration.");
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
logger.error("Failed to read configuration file", e);
|
logger.error("Failed to read configuration file", e);
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("App configuration has been successfully loaded.");
|
logger.info("App configuration has been successfully loaded.");
|
||||||
this.publisher.publishEvent(new ConfigHasBeenLoadedEvent(configuration));
|
this.publisher.publishEvent(new ConfigHasBeenLoadedEvent(configuration));
|
||||||
return configuration;
|
return configuration;
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
package org.araymond.joal.core.events.speed;
|
package org.araymond.joal.core.events.speed;
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import org.araymond.joal.core.bandwith.Speed;
|
import org.araymond.joal.core.bandwith.Speed;
|
||||||
import org.araymond.joal.core.torrent.torrent.InfoHash;
|
import org.araymond.joal.core.torrent.torrent.InfoHash;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class SeedingSpeedsHasChangedEvent {
|
public class SeedingSpeedsHasChangedEvent {
|
||||||
private final Map<InfoHash, Speed> speeds;
|
private final Map<InfoHash, Speed> speeds;
|
||||||
|
|
||||||
public SeedingSpeedsHasChangedEvent(final Map<InfoHash, Speed> speeds) {
|
public SeedingSpeedsHasChangedEvent(final Map<InfoHash, Speed> speeds) {
|
||||||
this.speeds = Maps.newHashMap(speeds);
|
this.speeds = new HashMap<>(speeds);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<InfoHash, Speed> getSpeeds() {
|
public Map<InfoHash, Speed> getSpeeds() {
|
||||||
|
|
|
@ -4,13 +4,15 @@ import com.google.common.base.Objects;
|
||||||
|
|
||||||
public class InfoHash {
|
public class InfoHash {
|
||||||
private final String infoHash;
|
private final String infoHash;
|
||||||
|
private final String humanReadable;
|
||||||
|
|
||||||
public InfoHash(final byte[] bytes) {
|
public InfoHash(final byte[] bytes) {
|
||||||
this.infoHash = new String(bytes, MockedTorrent.BYTE_ENCODING);
|
this.infoHash = new String(bytes, MockedTorrent.BYTE_ENCODING);
|
||||||
|
this.humanReadable = infoHash.replaceAll("\\p{C}", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String humanReadableValue() {
|
public String humanReadableValue() {
|
||||||
return infoHash.replaceAll("\\p{C}", "");
|
return humanReadable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String value() {
|
public String value() {
|
||||||
|
|
|
@ -7,8 +7,8 @@ import org.araymond.joal.core.torrent.torrent.MockedTorrent;
|
||||||
*/
|
*/
|
||||||
public interface TorrentFileChangeAware {
|
public interface TorrentFileChangeAware {
|
||||||
|
|
||||||
void onTorrentFileAdded(final MockedTorrent torrent);
|
void onTorrentFileAdded(MockedTorrent torrent);
|
||||||
|
|
||||||
void onTorrentFileRemoved(final MockedTorrent torrent);
|
void onTorrentFileRemoved(MockedTorrent torrent);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static java.util.Optional.ofNullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by raymo on 28/01/2017.
|
* Created by raymo on 28/01/2017.
|
||||||
*/
|
*/
|
||||||
|
@ -29,7 +31,6 @@ public class TorrentFileProvider extends FileAlterationListenerAdaptor {
|
||||||
private final TorrentFileWatcher watcher;
|
private final TorrentFileWatcher watcher;
|
||||||
private final Map<File, MockedTorrent> torrentFiles;
|
private final Map<File, MockedTorrent> torrentFiles;
|
||||||
private final Set<TorrentFileChangeAware> torrentFileChangeListener;
|
private final Set<TorrentFileChangeAware> torrentFileChangeListener;
|
||||||
private final Path torrentFolder;
|
|
||||||
private final Path archiveFolder;
|
private final Path archiveFolder;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
@ -55,7 +56,7 @@ public class TorrentFileProvider extends FileAlterationListenerAdaptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
public TorrentFileProvider(final SeedManager.JoalFoldersPath joalFoldersPath) throws FileNotFoundException {
|
public TorrentFileProvider(final SeedManager.JoalFoldersPath joalFoldersPath) throws FileNotFoundException {
|
||||||
this.torrentFolder = joalFoldersPath.getTorrentFilesPath();
|
Path torrentFolder = joalFoldersPath.getTorrentFilesPath();
|
||||||
if (!Files.exists(torrentFolder)) {
|
if (!Files.exists(torrentFolder)) {
|
||||||
logger.error("Folder " + torrentFolder.toAbsolutePath() + " does not exists.");
|
logger.error("Folder " + torrentFolder.toAbsolutePath() + " does not exists.");
|
||||||
throw new FileNotFoundException(String.format("Torrent folder '%s' not found.", torrentFolder.toAbsolutePath()));
|
throw new FileNotFoundException(String.format("Torrent folder '%s' not found.", torrentFolder.toAbsolutePath()));
|
||||||
|
@ -69,18 +70,11 @@ public class TorrentFileProvider extends FileAlterationListenerAdaptor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFileDelete(final File file) {
|
public void onFileDelete(final File file) {
|
||||||
if (!this.torrentFiles.containsKey(file)) {
|
ofNullable(this.torrentFiles.remove(file))
|
||||||
return;
|
.ifPresent(removedTorrent -> {
|
||||||
}
|
logger.info("Torrent file deleting detected, hot deleted file: {}", file.getAbsolutePath());
|
||||||
|
this.torrentFileChangeListener.forEach(listener -> listener.onTorrentFileRemoved(removedTorrent));
|
||||||
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.onTorrentFileRemoved(torrent));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -144,15 +138,12 @@ public class TorrentFileProvider extends FileAlterationListenerAdaptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void moveToArchiveFolder(final InfoHash infoHash) {
|
public void moveToArchiveFolder(final InfoHash infoHash) {
|
||||||
final Optional<File> first = this.torrentFiles.entrySet().stream()
|
this.torrentFiles.entrySet().stream()
|
||||||
.filter(entry -> entry.getValue().getTorrentInfoHash().equals(infoHash))
|
.filter(entry -> entry.getValue().getTorrentInfoHash().equals(infoHash))
|
||||||
.map(Map.Entry::getKey)
|
.map(Map.Entry::getKey)
|
||||||
.findFirst();
|
.findAny()
|
||||||
if (first.isPresent()) {
|
.ifPresentOrElse(this::moveToArchiveFolder,
|
||||||
this.moveToArchiveFolder(first.get());
|
() -> logger.warn("Cannot move torrent {} to archive folder. Torrent file seems not to be registered in TorrentFileProvider.", infoHash));
|
||||||
} else {
|
|
||||||
logger.warn("Cannot move torrent {} to archive folder. Torrent file seems not to be registered in TorrentFileProvider.", infoHash);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTorrentCount() {
|
public int getTorrentCount() {
|
||||||
|
|
|
@ -17,20 +17,16 @@ import org.araymond.joal.core.ttorrent.client.announcer.AnnouncerFacade;
|
||||||
import org.araymond.joal.core.ttorrent.client.announcer.AnnouncerFactory;
|
import org.araymond.joal.core.ttorrent.client.announcer.AnnouncerFactory;
|
||||||
import org.araymond.joal.core.ttorrent.client.announcer.request.AnnounceRequest;
|
import org.araymond.joal.core.ttorrent.client.announcer.request.AnnounceRequest;
|
||||||
import org.araymond.joal.core.ttorrent.client.announcer.request.AnnouncerExecutor;
|
import org.araymond.joal.core.ttorrent.client.announcer.request.AnnouncerExecutor;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.springframework.context.ApplicationEventPublisher;
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
|
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static org.slf4j.LoggerFactory.getLogger;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
public class Client implements TorrentFileChangeAware, ClientFacade {
|
public class Client implements TorrentFileChangeAware, ClientFacade {
|
||||||
private static final Logger logger = getLogger(Client.class);
|
|
||||||
|
|
||||||
private final AppConfiguration appConfiguration;
|
private final AppConfiguration appConfiguration;
|
||||||
private final TorrentFileProvider torrentFileProvider;
|
private final TorrentFileProvider torrentFileProvider;
|
||||||
private final ApplicationEventPublisher eventPublisher;
|
private final ApplicationEventPublisher eventPublisher;
|
||||||
|
@ -85,10 +81,10 @@ public class Client implements TorrentFileChangeAware, ClientFacade {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
for (int i = 0; i < this.appConfiguration.getSimultaneousSeed(); i++) {
|
for (int i = 0; i < this.appConfiguration.getSimultaneousSeed(); i++) {
|
||||||
try {
|
try {
|
||||||
this.lock.writeLock().lock();
|
this.lock.writeLock().lock();
|
||||||
|
|
||||||
this.addTorrent();
|
this.addTorrent();
|
||||||
} catch (final NoMoreTorrentsFileAvailableException ignored) {
|
} catch (final NoMoreTorrentsFileAvailableException ignored) {
|
||||||
break;
|
break;
|
||||||
|
@ -107,7 +103,7 @@ public class Client implements TorrentFileChangeAware, ClientFacade {
|
||||||
final MockedTorrent torrent = this.torrentFileProvider.getTorrentNotIn(
|
final MockedTorrent torrent = this.torrentFileProvider.getTorrentNotIn(
|
||||||
this.currentlySeedingAnnouncer.stream()
|
this.currentlySeedingAnnouncer.stream()
|
||||||
.map(Announcer::getTorrentInfoHash)
|
.map(Announcer::getTorrentInfoHash)
|
||||||
.collect(Collectors.toList())
|
.collect(toList())
|
||||||
);
|
);
|
||||||
final Announcer announcer = this.announcerFactory.create(torrent);
|
final Announcer announcer = this.announcerFactory.create(torrent);
|
||||||
this.currentlySeedingAnnouncer.add(announcer);
|
this.currentlySeedingAnnouncer.add(announcer);
|
||||||
|
|
|
@ -78,7 +78,7 @@ public class ConnectionHandler {
|
||||||
urlConnection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36");
|
urlConnection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36");
|
||||||
try (final BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), Charsets.UTF_8))) {
|
try (final BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), Charsets.UTF_8))) {
|
||||||
return InetAddress.getByName(in.readLine());
|
return InetAddress.getByName(in.readLine());
|
||||||
}finally {
|
} finally {
|
||||||
// Ensure all streams associated with http connection are closed
|
// Ensure all streams associated with http connection are closed
|
||||||
final InputStream err = ((HttpURLConnection) urlConnection).getErrorStream();
|
final InputStream err = ((HttpURLConnection) urlConnection).getErrorStream();
|
||||||
try { if (err != null) err.close(); }
|
try { if (err != null) err.close(); }
|
||||||
|
@ -126,7 +126,6 @@ public class ConnectionHandler {
|
||||||
for (int port = ConnectionHandler.PORT_RANGE_START; port <= ConnectionHandler.PORT_RANGE_END; port++) {
|
for (int port = ConnectionHandler.PORT_RANGE_START; port <= ConnectionHandler.PORT_RANGE_END; port++) {
|
||||||
final InetSocketAddress tryAddress = new InetSocketAddress(port);
|
final InetSocketAddress tryAddress = new InetSocketAddress(port);
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
channel = ServerSocketChannel.open();
|
channel = ServerSocketChannel.open();
|
||||||
channel.socket().bind(tryAddress);
|
channel.socket().bind(tryAddress);
|
||||||
|
|
|
@ -89,7 +89,6 @@ public class DelayQueue<T extends DelayQueue.InfoHashAble> {
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("NullableProblems")
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(final IntervalAware o) {
|
public int compareTo(final IntervalAware o) {
|
||||||
return this.releaseAt.compareTo(o.releaseAt);
|
return this.releaseAt.compareTo(o.releaseAt);
|
||||||
|
|
|
@ -42,7 +42,7 @@ public class Announcer implements AnnouncerFacade {
|
||||||
}
|
}
|
||||||
|
|
||||||
private TrackerClient buildTrackerClient(final MockedTorrent torrent, HttpClient httpClient) {
|
private TrackerClient buildTrackerClient(final MockedTorrent torrent, HttpClient httpClient) {
|
||||||
final List<URI> trackerURIs = torrent.getAnnounceList().stream() // Use a list to keep it ordered
|
final List<URI> trackerURIs = torrent.getAnnounceList().stream() // Use a list to keep it ordered
|
||||||
.sequential()
|
.sequential()
|
||||||
.flatMap(Collection::stream)
|
.flatMap(Collection::stream)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package org.araymond.joal.core.ttorrent.client.announcer.request;
|
package org.araymond.joal.core.ttorrent.client.announcer.request;
|
||||||
|
|
||||||
import com.google.common.collect.Sets;
|
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
import org.araymond.joal.core.torrent.torrent.InfoHash;
|
import org.araymond.joal.core.torrent.torrent.InfoHash;
|
||||||
import org.araymond.joal.core.ttorrent.client.announcer.Announcer;
|
import org.araymond.joal.core.ttorrent.client.announcer.Announcer;
|
||||||
|
@ -72,7 +71,7 @@ public class AnnouncerExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Announcer> denyAll() {
|
public List<Announcer> denyAll() {
|
||||||
final Set<InfoHash> infoHashes = Sets.newHashSet(this.currentlyRunning.keySet());
|
final Set<InfoHash> infoHashes = new HashSet<>(this.currentlyRunning.keySet());
|
||||||
final List<Announcer> announcersCanceled = new ArrayList<>();
|
final List<Announcer> announcersCanceled = new ArrayList<>();
|
||||||
|
|
||||||
for (final InfoHash infoHash: infoHashes) {
|
for (final InfoHash infoHash: infoHashes) {
|
||||||
|
|
|
@ -23,65 +23,49 @@ public class AnnounceEventPublisher implements AnnounceResponseHandlerChainEleme
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnnouncerWillAnnounce(final Announcer announcer, final RequestEvent event) {
|
public void onAnnouncerWillAnnounce(final Announcer announcer, final RequestEvent event) {
|
||||||
if(logger.isDebugEnabled()) {
|
logger.debug("Publish WillAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
|
||||||
logger.debug("Publish WillAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
|
|
||||||
}
|
|
||||||
this.eventPublisher.publishEvent(new WillAnnounceEvent(announcer, event));
|
this.eventPublisher.publishEvent(new WillAnnounceEvent(announcer, event));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnnounceStartSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
public void onAnnounceStartSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
||||||
if(logger.isDebugEnabled()) {
|
logger.debug("Publish SuccessfullyAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
|
||||||
logger.debug("Publish SuccessfullyAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
|
|
||||||
}
|
|
||||||
this.eventPublisher.publishEvent(new SuccessfullyAnnounceEvent(announcer, RequestEvent.STARTED));
|
this.eventPublisher.publishEvent(new SuccessfullyAnnounceEvent(announcer, RequestEvent.STARTED));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnnounceStartFails(final Announcer announcer, final Throwable throwable) {
|
public void onAnnounceStartFails(final Announcer announcer, final Throwable throwable) {
|
||||||
if(logger.isDebugEnabled()) {
|
logger.debug("Publish FailedToAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
|
||||||
logger.debug("Publish FailedToAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
|
|
||||||
}
|
|
||||||
this.eventPublisher.publishEvent(new FailedToAnnounceEvent(announcer, RequestEvent.STARTED, throwable.getMessage()));
|
this.eventPublisher.publishEvent(new FailedToAnnounceEvent(announcer, RequestEvent.STARTED, throwable.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnnounceRegularSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
public void onAnnounceRegularSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
||||||
if(logger.isDebugEnabled()) {
|
logger.debug("Publish SuccessfullyAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
|
||||||
logger.debug("Publish SuccessfullyAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
|
|
||||||
}
|
|
||||||
this.eventPublisher.publishEvent(new SuccessfullyAnnounceEvent(announcer, RequestEvent.NONE));
|
this.eventPublisher.publishEvent(new SuccessfullyAnnounceEvent(announcer, RequestEvent.NONE));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnnounceRegularFails(final Announcer announcer, final Throwable throwable) {
|
public void onAnnounceRegularFails(final Announcer announcer, final Throwable throwable) {
|
||||||
if(logger.isDebugEnabled()) {
|
logger.debug("Publish FailedToAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
|
||||||
logger.debug("Publish FailedToAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
|
|
||||||
}
|
|
||||||
this.eventPublisher.publishEvent(new FailedToAnnounceEvent(announcer, RequestEvent.NONE, throwable.getMessage()));
|
this.eventPublisher.publishEvent(new FailedToAnnounceEvent(announcer, RequestEvent.NONE, throwable.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnnounceStopSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
public void onAnnounceStopSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
||||||
if(logger.isDebugEnabled()) {
|
logger.debug("Publish SuccessfullyAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
|
||||||
logger.debug("Publish SuccessfullyAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
|
|
||||||
}
|
|
||||||
this.eventPublisher.publishEvent(new SuccessfullyAnnounceEvent(announcer, RequestEvent.STOPPED));
|
this.eventPublisher.publishEvent(new SuccessfullyAnnounceEvent(announcer, RequestEvent.STOPPED));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnnounceStopFails(final Announcer announcer, final Throwable throwable) {
|
public void onAnnounceStopFails(final Announcer announcer, final Throwable throwable) {
|
||||||
if(logger.isDebugEnabled()) {
|
logger.debug("Publish FailedToAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
|
||||||
logger.debug("Publish FailedToAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
|
|
||||||
}
|
|
||||||
this.eventPublisher.publishEvent(new FailedToAnnounceEvent(announcer, RequestEvent.STOPPED, throwable.getMessage()));
|
this.eventPublisher.publishEvent(new FailedToAnnounceEvent(announcer, RequestEvent.STOPPED, throwable.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTooManyAnnounceFailedInARaw(final Announcer announcer, final TooMuchAnnouncesFailedInARawException e) {
|
public void onTooManyAnnounceFailedInARaw(final Announcer announcer, final TooMuchAnnouncesFailedInARawException e) {
|
||||||
if(logger.isDebugEnabled()) {
|
logger.debug("Publish TooManyAnnouncesFailedEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
|
||||||
logger.debug("Publish TooManyAnnouncesFailedEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
|
|
||||||
}
|
|
||||||
this.eventPublisher.publishEvent(new TooManyAnnouncesFailedEvent(announcer));
|
this.eventPublisher.publishEvent(new TooManyAnnouncesFailedEvent(announcer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,33 +27,25 @@ public class AnnounceReEnqueuer implements AnnounceResponseHandlerChainElement {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnnounceStartSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
public void onAnnounceStartSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
||||||
if (logger.isDebugEnabled()) {
|
logger.debug("Enqueue torrent {} in regular queue.", announcer.getTorrentInfoHash().humanReadableValue());
|
||||||
logger.debug("Enqueue torrent {} in regular queue.", announcer.getTorrentInfoHash().humanReadableValue());
|
|
||||||
}
|
|
||||||
this.delayQueue.addOrReplace(AnnounceRequest.createRegular(announcer), result.getInterval(), ChronoUnit.SECONDS);
|
this.delayQueue.addOrReplace(AnnounceRequest.createRegular(announcer), result.getInterval(), ChronoUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnnounceStartFails(final Announcer announcer, final Throwable throwable) {
|
public void onAnnounceStartFails(final Announcer announcer, final Throwable throwable) {
|
||||||
if (logger.isDebugEnabled()) {
|
logger.debug("Enqueue torrent {} in start queue once again (because it failed).", announcer.getTorrentInfoHash().humanReadableValue());
|
||||||
logger.debug("Enqueue torrent {} in start queue once again (because it failed).", announcer.getTorrentInfoHash().humanReadableValue());
|
|
||||||
}
|
|
||||||
this.delayQueue.addOrReplace(AnnounceRequest.createStart(announcer), announcer.getLastKnownInterval(), ChronoUnit.SECONDS);
|
this.delayQueue.addOrReplace(AnnounceRequest.createStart(announcer), announcer.getLastKnownInterval(), ChronoUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnnounceRegularSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
public void onAnnounceRegularSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
||||||
if (logger.isDebugEnabled()) {
|
logger.debug("Enqueue torrent {} in regular queue.", announcer.getTorrentInfoHash().humanReadableValue());
|
||||||
logger.debug("Enqueue torrent {} in regular queue.", announcer.getTorrentInfoHash().humanReadableValue());
|
|
||||||
}
|
|
||||||
this.delayQueue.addOrReplace(AnnounceRequest.createRegular(announcer), result.getInterval(), ChronoUnit.SECONDS);
|
this.delayQueue.addOrReplace(AnnounceRequest.createRegular(announcer), result.getInterval(), ChronoUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnnounceRegularFails(final Announcer announcer, final Throwable throwable) {
|
public void onAnnounceRegularFails(final Announcer announcer, final Throwable throwable) {
|
||||||
if (logger.isDebugEnabled()) {
|
logger.debug("Enqueue torrent {} in regular queue once again (because it failed).", announcer.getTorrentInfoHash().humanReadableValue());
|
||||||
logger.debug("Enqueue torrent {} in regular queue once again (because it failed).", announcer.getTorrentInfoHash().humanReadableValue());
|
|
||||||
}
|
|
||||||
this.delayQueue.addOrReplace(AnnounceRequest.createRegular(announcer), announcer.getLastKnownInterval(), ChronoUnit.SECONDS);
|
this.delayQueue.addOrReplace(AnnounceRequest.createRegular(announcer), announcer.getLastKnownInterval(), ChronoUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,9 +55,7 @@ public class AnnounceReEnqueuer implements AnnounceResponseHandlerChainElement {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnnounceStopFails(final Announcer announcer, final Throwable throwable) {
|
public void onAnnounceStopFails(final Announcer announcer, final Throwable throwable) {
|
||||||
if (logger.isDebugEnabled()) {
|
logger.debug("Enqueue torrent {} in stop queue once again (because it failed).", announcer.getTorrentInfoHash().humanReadableValue());
|
||||||
logger.debug("Enqueue torrent {} in stop queue once again (because it failed).", announcer.getTorrentInfoHash().humanReadableValue());
|
|
||||||
}
|
|
||||||
this.delayQueue.addOrReplace(AnnounceRequest.createStop(announcer), 0, ChronoUnit.SECONDS);
|
this.delayQueue.addOrReplace(AnnounceRequest.createStop(announcer), 0, ChronoUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ import org.araymond.joal.core.ttorrent.client.announcer.exceptions.TooMuchAnnoun
|
||||||
import org.araymond.joal.core.ttorrent.client.announcer.request.SuccessAnnounceResponse;
|
import org.araymond.joal.core.ttorrent.client.announcer.request.SuccessAnnounceResponse;
|
||||||
|
|
||||||
public interface AnnounceResponseCallback {
|
public interface AnnounceResponseCallback {
|
||||||
void onAnnounceWillAnnounce(final RequestEvent event, final Announcer announcer);
|
void onAnnounceWillAnnounce(RequestEvent event, Announcer announcer);
|
||||||
void onAnnounceSuccess(final RequestEvent event, final Announcer announcer, final SuccessAnnounceResponse result);
|
void onAnnounceSuccess(RequestEvent event, Announcer announcer, SuccessAnnounceResponse result);
|
||||||
void onAnnounceFailure(final RequestEvent event, final Announcer announcer, final Throwable throwable);
|
void onAnnounceFailure(RequestEvent event, Announcer announcer, Throwable throwable);
|
||||||
void onTooManyAnnounceFailedInARaw(final RequestEvent event, final Announcer announcer, final TooMuchAnnouncesFailedInARawException e);
|
void onTooManyAnnounceFailedInARaw(RequestEvent event, Announcer announcer, TooMuchAnnouncesFailedInARawException e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,12 @@ import org.araymond.joal.core.ttorrent.client.announcer.exceptions.TooMuchAnnoun
|
||||||
import org.araymond.joal.core.ttorrent.client.announcer.request.SuccessAnnounceResponse;
|
import org.araymond.joal.core.ttorrent.client.announcer.request.SuccessAnnounceResponse;
|
||||||
|
|
||||||
public interface AnnounceResponseHandlerChainElement {
|
public interface AnnounceResponseHandlerChainElement {
|
||||||
void onAnnouncerWillAnnounce(final Announcer announcer, RequestEvent event);
|
void onAnnouncerWillAnnounce(Announcer announcer, RequestEvent event);
|
||||||
void onAnnounceStartSuccess(final Announcer announcer, final SuccessAnnounceResponse result);
|
void onAnnounceStartSuccess(Announcer announcer, SuccessAnnounceResponse result);
|
||||||
void onAnnounceStartFails(final Announcer announcer, final Throwable throwable);
|
void onAnnounceStartFails(Announcer announcer, Throwable throwable);
|
||||||
void onAnnounceRegularSuccess(final Announcer announcer, final SuccessAnnounceResponse result);
|
void onAnnounceRegularSuccess(Announcer announcer, SuccessAnnounceResponse result);
|
||||||
void onAnnounceRegularFails(final Announcer announcer, final Throwable throwable);
|
void onAnnounceRegularFails(Announcer announcer, Throwable throwable);
|
||||||
void onAnnounceStopSuccess(final Announcer announcer, final SuccessAnnounceResponse result);
|
void onAnnounceStopSuccess(Announcer announcer, SuccessAnnounceResponse result);
|
||||||
void onAnnounceStopFails(final Announcer announcer, final Throwable throwable);
|
void onAnnounceStopFails(Announcer announcer, Throwable throwable);
|
||||||
void onTooManyAnnounceFailedInARaw(final Announcer announcer, final TooMuchAnnouncesFailedInARawException e);
|
void onTooManyAnnounceFailedInARaw(Announcer announcer, TooMuchAnnouncesFailedInARawException e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,7 @@ public class BandwidthDispatcherNotifier implements AnnounceResponseHandlerChain
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnnounceStartSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
public void onAnnounceStartSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
||||||
if(logger.isDebugEnabled()) {
|
logger.debug("Register {} in bandwidth dispatcher and update stats.", announcer.getTorrentInfoHash().humanReadableValue());
|
||||||
logger.debug("Register {} in bandwidth dispatcher and update stats.", announcer.getTorrentInfoHash().humanReadableValue());
|
|
||||||
}
|
|
||||||
final InfoHash infoHash = announcer.getTorrentInfoHash();
|
final InfoHash infoHash = announcer.getTorrentInfoHash();
|
||||||
this.bandwidthDispatcher.registerTorrent(infoHash);
|
this.bandwidthDispatcher.registerTorrent(infoHash);
|
||||||
this.bandwidthDispatcher.updateTorrentPeers(infoHash, result.getSeeders(), result.getLeechers());
|
this.bandwidthDispatcher.updateTorrentPeers(infoHash, result.getSeeders(), result.getLeechers());
|
||||||
|
@ -38,9 +36,7 @@ public class BandwidthDispatcherNotifier implements AnnounceResponseHandlerChain
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnnounceRegularSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
public void onAnnounceRegularSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
||||||
if(logger.isDebugEnabled()) {
|
logger.debug("Update {} stats in bandwidth dispatcher.", announcer.getTorrentInfoHash().humanReadableValue());
|
||||||
logger.debug("Update {} stats in bandwidth dispatcher.", announcer.getTorrentInfoHash().humanReadableValue());
|
|
||||||
}
|
|
||||||
final InfoHash infoHash = announcer.getTorrentInfoHash();
|
final InfoHash infoHash = announcer.getTorrentInfoHash();
|
||||||
this.bandwidthDispatcher.updateTorrentPeers(infoHash, result.getSeeders(), result.getLeechers());
|
this.bandwidthDispatcher.updateTorrentPeers(infoHash, result.getSeeders(), result.getLeechers());
|
||||||
}
|
}
|
||||||
|
@ -51,9 +47,7 @@ public class BandwidthDispatcherNotifier implements AnnounceResponseHandlerChain
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnnounceStopSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
public void onAnnounceStopSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
||||||
if(logger.isDebugEnabled()) {
|
logger.debug("Unregister {} from bandwidth dispatcher.", announcer.getTorrentInfoHash().humanReadableValue());
|
||||||
logger.debug("Unregister {} from bandwidth dispatcher.", announcer.getTorrentInfoHash().humanReadableValue());
|
|
||||||
}
|
|
||||||
this.bandwidthDispatcher.unregisterTorrent(announcer.getTorrentInfoHash());
|
this.bandwidthDispatcher.unregisterTorrent(announcer.getTorrentInfoHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,9 +57,7 @@ public class BandwidthDispatcherNotifier implements AnnounceResponseHandlerChain
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTooManyAnnounceFailedInARaw(final Announcer announcer, final TooMuchAnnouncesFailedInARawException e) {
|
public void onTooManyAnnounceFailedInARaw(final Announcer announcer, final TooMuchAnnouncesFailedInARawException e) {
|
||||||
if(logger.isDebugEnabled()) {
|
logger.debug("Unregister {} from bandwidth dispatcher.", announcer.getTorrentInfoHash().humanReadableValue());
|
||||||
logger.debug("Unregister {} from bandwidth dispatcher.", announcer.getTorrentInfoHash().humanReadableValue());
|
|
||||||
}
|
|
||||||
this.bandwidthDispatcher.unregisterTorrent(announcer.getTorrentInfoHash());
|
this.bandwidthDispatcher.unregisterTorrent(announcer.getTorrentInfoHash());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,9 +45,7 @@ public class ClientNotifier implements AnnounceResponseHandlerChainElement {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnnounceStopSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
public void onAnnounceStopSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
|
||||||
if(logger.isDebugEnabled()) {
|
logger.debug("Notify client that a torrent has stopped.");
|
||||||
logger.debug("Notify client that a torrent has stopped.");
|
|
||||||
}
|
|
||||||
this.client.onTorrentHasStopped(announcer);
|
this.client.onTorrentHasStopped(announcer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,9 +55,7 @@ public class ClientNotifier implements AnnounceResponseHandlerChainElement {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTooManyAnnounceFailedInARaw(final Announcer announcer, final TooMuchAnnouncesFailedInARawException e) {
|
public void onTooManyAnnounceFailedInARaw(final Announcer announcer, final TooMuchAnnouncesFailedInARawException e) {
|
||||||
if(logger.isDebugEnabled()) {
|
logger.debug("Notify client that a torrent has failed too many times.");
|
||||||
logger.debug("Notify client that a torrent has failed too many times.");
|
|
||||||
}
|
|
||||||
this.client.onTooManyFailedInARaw(announcer);
|
this.client.onTooManyFailedInARaw(announcer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,8 +73,7 @@ public class TrackerClient {
|
||||||
final AnnounceResponseMessage announceResponseMessage = (AnnounceResponseMessage) responseMessage;
|
final AnnounceResponseMessage announceResponseMessage = (AnnounceResponseMessage) responseMessage;
|
||||||
|
|
||||||
final int interval = announceResponseMessage.getInterval();
|
final int interval = announceResponseMessage.getInterval();
|
||||||
// Subtract one to seeders since we are one of them.
|
final int seeders = announceResponseMessage.getComplete() == 0 ? 0 : announceResponseMessage.getComplete() - 1; // Subtract one to seeders since we are one of them
|
||||||
final int seeders = announceResponseMessage.getComplete() == 0 ? 0 : announceResponseMessage.getComplete() - 1;
|
|
||||||
final int leechers = announceResponseMessage.getIncomplete();
|
final int leechers = announceResponseMessage.getIncomplete();
|
||||||
return new SuccessAnnounceResponse(interval, seeders, leechers);
|
return new SuccessAnnounceResponse(interval, seeders, leechers);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package org.araymond.joal.core.ttorrent.client.announcer.tracker;
|
package org.araymond.joal.core.ttorrent.client.announcer.tracker;
|
||||||
|
|
||||||
import com.google.common.collect.Iterators;
|
import com.google.common.collect.Iterators;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
@ -9,8 +8,6 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class TrackerClientUriProvider {
|
public class TrackerClientUriProvider {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(TrackerClientUriProvider.class);
|
|
||||||
|
|
||||||
private final Iterator<URI> addressIterator;
|
private final Iterator<URI> addressIterator;
|
||||||
private URI currentURI = null;
|
private URI currentURI = null;
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ public class TrackerResponseHandler implements ResponseHandler<TrackerMessage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
final int contentLength = entity.getContentLength() < 1 ? 1024 : (int) entity.getContentLength();
|
final int contentLength = entity.getContentLength() < 1 ? 1024 : (int) entity.getContentLength();
|
||||||
try(final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(contentLength)) {
|
try (final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(contentLength)) {
|
||||||
if (response.getStatusLine().getStatusCode() >= 300) {
|
if (response.getStatusLine().getStatusCode() >= 300) {
|
||||||
logger.warn("Tracker response is an error.");
|
logger.warn("Tracker response is an error.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class JacksonConfig {
|
||||||
/**
|
/**
|
||||||
* Forces the timezone to be added to every LocalDateTime
|
* Forces the timezone to be added to every LocalDateTime
|
||||||
*/
|
*/
|
||||||
public static final class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
|
public static final class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
|
||||||
@Override
|
@Override
|
||||||
public void serialize(final LocalDateTime value, final JsonGenerator gen, final SerializerProvider serializers) throws IOException {
|
public void serialize(final LocalDateTime value, final JsonGenerator gen, final SerializerProvider serializers) throws IOException {
|
||||||
final ZonedDateTime zonedDateTime = value.atZone(ZoneId.systemDefault());
|
final ZonedDateTime zonedDateTime = value.atZone(ZoneId.systemDefault());
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
package org.araymond.joal.web.config;
|
package org.araymond.joal.web.config;
|
||||||
|
|
||||||
import org.araymond.joal.web.annotations.ConditionalOnWebUi;
|
import org.araymond.joal.web.annotations.ConditionalOnWebUi;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.*;
|
||||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
|
||||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
|
|
||||||
@ConditionalOnWebUi
|
@ConditionalOnWebUi
|
||||||
// Do not use @EnableWebMvc as it will remove all the default springboot config.
|
// Do not use @EnableWebMvc as it will remove all the default springboot config.
|
||||||
|
|
|
@ -20,9 +20,9 @@ public class JoalMessageSendingTemplate {
|
||||||
this.messageSendingOperations = messageSendingOperations;
|
this.messageSendingOperations = messageSendingOperations;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void convertAndSend(final String s, final MessagePayload payload) throws MessagingException {
|
public void convertAndSend(final String destination, final MessagePayload payload) throws MessagingException {
|
||||||
final StompMessage stompMessage = StompMessage.wrap(payload);
|
final StompMessage stompMessage = StompMessage.wrap(payload);
|
||||||
messageSendingOperations.convertAndSend(s, stompMessage);
|
messageSendingOperations.convertAndSend(destination, stompMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package org.araymond.joal.core.events.speed;
|
package org.araymond.joal.core.events.speed;
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import org.araymond.joal.core.bandwith.Speed;
|
import org.araymond.joal.core.bandwith.Speed;
|
||||||
import org.araymond.joal.core.torrent.torrent.InfoHash;
|
import org.araymond.joal.core.torrent.torrent.InfoHash;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -13,7 +13,7 @@ public class SeedingSpeedsHasChangedEventTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldBuild() {
|
public void shouldBuild() {
|
||||||
final Map<InfoHash, Speed> speeds = Maps.newHashMap();
|
final Map<InfoHash, Speed> speeds = new HashMap<>();
|
||||||
final SeedingSpeedsHasChangedEvent event = new SeedingSpeedsHasChangedEvent(speeds);
|
final SeedingSpeedsHasChangedEvent event = new SeedingSpeedsHasChangedEvent(speeds);
|
||||||
|
|
||||||
assertThat(event.getSpeeds()).isEqualTo(speeds);
|
assertThat(event.getSpeeds()).isEqualTo(speeds);
|
||||||
|
|
Loading…
Reference in a new issue