Switch back to tomcat and fix some warnings

This commit is contained in:
anthonyraymond 2018-06-30 20:49:53 +02:00
parent b5c78dd0d7
commit 78df5e9859
19 changed files with 895 additions and 874 deletions

484
pom.xml
View file

@ -1,242 +1,242 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.araymond.joal</groupId>
<artifactId>jack-of-all-trades</artifactId>
<version>2.1.3</version>
<packaging>jar</packaging>
<name>jack-of-all-trades</name>
<description>A Java command line RatioMaster</description>
<scm>
<connection>scm:git:https://github.com/anthonyraymond/joal.git</connection>
<developerConnection>scm:git:git@github.com:anthonyraymond/joal.git</developerConnection>
<url>https://github.com/anthonyraymond/joal</url>
<tag>HEAD</tag>
</scm>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<!-- Project -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!-- COMPILE -->
<commons-io.version>2.6</commons-io.version>
<commons-lang3.version>3.7</commons-lang3.version>
<commons-codec.version>1.11</commons-codec.version>
<fluent-hc.version>4.5.5</fluent-hc.version>
<generex.version>1.0.2</generex.version>
<google.guava.version>25.0-jre</google.guava.version>
<ttorrent-core.version>1.5</ttorrent-core.version>
<!-- TESTS -->
<org-assertj.version>3.9.1</org-assertj.version>
<!-- Plugins -->
<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
<jacoco.plugin.version>0.8.1</jacoco.plugin.version>
<coveralls.plugin.version>4.3.0</coveralls.plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
<exclusion>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-messaging</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<dependency>
<groupId>com.turn</groupId>
<artifactId>ttorrent-core</artifactId>
<version>${ttorrent-core.version}</version>
</dependency>
<dependency>
<groupId>com.github.mifmif</groupId>
<artifactId>generex</artifactId>
<version>${generex.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${google.guava.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>fluent-hc</artifactId>
<version>${fluent-hc.version}</version>
</dependency>
<!-- TEST -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<!-- Version inherited from spring-boot-starter-test -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<!-- Version inherited from spring-boot-starter-test -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${org-assertj.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<pluginRepositories>
<pluginRepository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>bintray-cy6ergn0m-maven</id>
<name>bintray-plugins</name>
<url>http://dl.bintray.com/cy6ergn0m/maven</url>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.plugin.version}</version>
<configuration>
<excludes>
<exclude>org/araymond/joal/JackOfAllTradesApplication.class</exclude>
<exclude>org/araymond/joal/ApplicationReadyListener.class</exclude>
<exclude>org/araymond/joal/ApplicationClosingListener.class</exclude>
<exclude>org/araymond/joal/web/config/BeanConfig.class</exclude>
<exclude>org/araymond/joal/web/messages/</exclude>
<exclude>org/araymond/joal/web/services/corelistener/</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.eluder.coveralls</groupId>
<artifactId>coveralls-maven-plugin</artifactId>
<version>${coveralls.plugin.version}</version>
</plugin>
</plugins>
</build>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.araymond.joal</groupId>
<artifactId>jack-of-all-trades</artifactId>
<version>2.1.3</version>
<packaging>jar</packaging>
<name>jack-of-all-trades</name>
<description>A Java command line RatioMaster</description>
<scm>
<connection>scm:git:https://github.com/anthonyraymond/joal.git</connection>
<developerConnection>scm:git:git@github.com:anthonyraymond/joal.git</developerConnection>
<url>https://github.com/anthonyraymond/joal</url>
<tag>HEAD</tag>
</scm>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<!-- Project -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!-- COMPILE -->
<commons-io.version>2.6</commons-io.version>
<commons-lang3.version>3.7</commons-lang3.version>
<commons-codec.version>1.11</commons-codec.version>
<fluent-hc.version>4.5.5</fluent-hc.version>
<generex.version>1.0.2</generex.version>
<google.guava.version>25.1-jre</google.guava.version>
<ttorrent-core.version>1.5</ttorrent-core.version>
<!-- TESTS -->
<org-assertj.version>3.9.1</org-assertj.version>
<!-- Plugins -->
<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
<jacoco.plugin.version>0.8.1</jacoco.plugin.version>
<coveralls.plugin.version>4.3.0</coveralls.plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!--<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>-->
<exclusion>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-messaging</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<dependency>
<groupId>com.turn</groupId>
<artifactId>ttorrent-core</artifactId>
<version>${ttorrent-core.version}</version>
</dependency>
<dependency>
<groupId>com.github.mifmif</groupId>
<artifactId>generex</artifactId>
<version>${generex.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${google.guava.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>fluent-hc</artifactId>
<version>${fluent-hc.version}</version>
</dependency>
<!-- TEST -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<!-- Version inherited from spring-boot-starter-test -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<!-- Version inherited from spring-boot-starter-test -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${org-assertj.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<pluginRepositories>
<pluginRepository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>bintray-cy6ergn0m-maven</id>
<name>bintray-plugins</name>
<url>http://dl.bintray.com/cy6ergn0m/maven</url>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.plugin.version}</version>
<configuration>
<excludes>
<exclude>org/araymond/joal/JackOfAllTradesApplication.class</exclude>
<exclude>org/araymond/joal/ApplicationReadyListener.class</exclude>
<exclude>org/araymond/joal/ApplicationClosingListener.class</exclude>
<exclude>org/araymond/joal/web/config/BeanConfig.class</exclude>
<exclude>org/araymond/joal/web/messages/</exclude>
<exclude>org/araymond/joal/web/services/corelistener/</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.eluder.coveralls</groupId>
<artifactId>coveralls-maven-plugin</artifactId>
<version>${coveralls.plugin.version}</version>
</plugin>
</plugins>
</build>
</project>

View file

@ -28,7 +28,7 @@ public class CoreEventListener {
@Async
@Order(Ordered.HIGHEST_PRECEDENCE)
@EventListener
void handleTorrentFileAddedForSeed(final TorrentFileAddedEvent event) throws IOException {
public void handleTorrentFileAddedForSeed(final TorrentFileAddedEvent event) throws IOException {
logger.debug("Event TorrentFileAddedEvent caught.");
}
@ -44,7 +44,7 @@ public class CoreEventListener {
@Async
@Order(Ordered.HIGHEST_PRECEDENCE)
@EventListener
void handleSeedSessionHasEnded(final GlobalSeedStoppedEvent event) {
public void handleSeedSessionHasEnded(final GlobalSeedStoppedEvent event) {
logger.debug("Event GlobalSeedStoppedEvent caught.");
// TODO : log that the seed session is over
}

View file

@ -5,6 +5,7 @@ import com.google.common.collect.Maps;
import org.araymond.joal.core.bandwith.BandwidthDispatcher;
import org.araymond.joal.core.bandwith.RandomSpeedProvider;
import org.araymond.joal.core.bandwith.Speed;
import org.araymond.joal.core.bandwith.SpeedChangedListener;
import org.araymond.joal.core.client.emulated.BitTorrentClient;
import org.araymond.joal.core.client.emulated.BitTorrentClientProvider;
import org.araymond.joal.core.config.AppConfiguration;
@ -93,9 +94,7 @@ public class SeedManager {
final RandomSpeedProvider randomSpeedProvider = new RandomSpeedProvider(appConfiguration);
this.bandwidthDispatcher = new BandwidthDispatcher(5000, randomSpeedProvider);
this.bandwidthDispatcher.setSpeedListener((speeds -> {
this.publisher.publishEvent(new SeedingSpeedsHasChangedEvent(speeds));
}));
this.bandwidthDispatcher.setSpeedListener(new SeedManagerSpeedChangeListener(this.publisher));
this.bandwidthDispatcher.start();
final AnnounceDataAccessor announceDataAccessor = new AnnounceDataAccessor(bitTorrentClient, bandwidthDispatcher, this.connectionHandler);
@ -235,4 +234,17 @@ public class SeedManager {
}
}
private static final class SeedManagerSpeedChangeListener implements SpeedChangedListener {
private final ApplicationEventPublisher publisher;
private SeedManagerSpeedChangeListener(final ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@Override
public void speedsHasChanged(final Map<InfoHash, Speed> speeds) {
this.publisher.publishEvent(new SeedingSpeedsHasChangedEvent(speeds));
}
}
}

View file

@ -1,215 +1,217 @@
package org.araymond.joal.core.bandwith;
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.araymond.joal.core.bandwith.weight.PeersAwareWeightCalculator;
import org.araymond.joal.core.bandwith.weight.WeightHolder;
import org.araymond.joal.core.torrent.torrent.InfoHash;
import org.slf4j.Logger;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import static org.slf4j.LoggerFactory.getLogger;
public class BandwidthDispatcher implements BandwidthDispatcherFacade, Runnable {
private static final Logger logger = getLogger(BandwidthDispatcher.class);
private final ReentrantReadWriteLock lock;
private final WeightHolder<InfoHash> weightHolder;
private final RandomSpeedProvider randomSpeedProvider;
private final Map<InfoHash, TorrentSeedStats> torrentsSeedStats;
private final Map<InfoHash, Speed> speedMap;
private SpeedChangedListener speedChangedListener;
private final int threadPauseInterval;
private int threadLoopCounter = 0;
private volatile boolean stop = false;
private Thread thread;
public BandwidthDispatcher(final int threadPauseInterval, final RandomSpeedProvider randomSpeedProvider) {
this.threadPauseInterval = threadPauseInterval;
this.torrentsSeedStats = new HashMap<>();
this.speedMap = new HashMap<>();
this.lock = new ReentrantReadWriteLock();
this.weightHolder = new WeightHolder<>(new PeersAwareWeightCalculator());
this.randomSpeedProvider = randomSpeedProvider;
}
public void setSpeedListener(final SpeedChangedListener speedListener) {
this.speedChangedListener = speedListener;
}
/*
* 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.
*/
public TorrentSeedStats getSeedStatForTorrent(final InfoHash infoHash) {
return this.torrentsSeedStats.getOrDefault(infoHash, new TorrentSeedStats());
}
public Map<InfoHash, Speed> getSpeedMap() {
try {
this.lock.readLock().lock();
return speedMap;
} finally {
this.lock.readLock().unlock();
}
}
public void start() {
this.stop = false;
this.thread = new Thread(this);
this.thread.setName("bandwidth-dispatcher");
this.thread.start();
}
public void stop() {
this.stop = true;
this.thread.interrupt();
try {
this.thread.join();
} catch (final InterruptedException ignored) {
}
}
@Override
public void run() {
try {
while (!this.stop) {
Thread.sleep(this.threadPauseInterval);
++this.threadLoopCounter;
// refresh bandwidth every 1200000 milliseconds (20 minutes)
if (this.threadLoopCounter == 1200000 / this.threadPauseInterval) {
this.refreshCurrentBandwidth();
this.threadLoopCounter = 0;
}
// This method as to run as fast as possible to avoid blocking other ones. Because we wan't this loop
// to be scheduled as precise as we can. Locking to much will delay the Thread.sleep and cause stats
// to be undervalued
this.lock.readLock().lock();
final Set<Map.Entry<InfoHash, TorrentSeedStats>> entrySet = Sets.newHashSet(this.torrentsSeedStats.entrySet());
this.lock.readLock().unlock();
for (final Map.Entry<InfoHash, TorrentSeedStats> entry : entrySet) {
final long speedInBytesPerSecond = this.speedMap.getOrDefault(entry.getKey(), new Speed(0)).getBytesPerSeconds();
// 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
entry.getValue().addUploaded((speedInBytesPerSecond * this.threadPauseInterval) / 1000);
}
}
} catch (final InterruptedException ignore) {
}
}
public void updateTorrentPeers(final InfoHash infoHash, final int seeders, final int leechers) {
if (logger.isDebugEnabled()) {
logger.debug("Updating Peers stats for {}", infoHash.humanReadableValue());
}
this.lock.writeLock().lock();
try {
this.weightHolder.addOrUpdate(infoHash, new Peers(seeders, leechers));
this.recomputeSpeeds();
} finally {
this.lock.writeLock().unlock();
}
}
public void registerTorrent(final InfoHash infoHash) {
if (logger.isDebugEnabled()) {
logger.debug("{} has been added to bandwidth dispatcher.", infoHash.humanReadableValue());
}
this.lock.writeLock().lock();
try {
this.torrentsSeedStats.put(infoHash, new TorrentSeedStats());
this.speedMap.put(infoHash, new Speed(0));
} finally {
this.lock.writeLock().unlock();
}
}
public void unregisterTorrent(final InfoHash infoHash) {
if (logger.isDebugEnabled()) {
logger.debug("{} has been removed from bandwidth dispatcher.", infoHash.humanReadableValue());
}
this.lock.writeLock().lock();
try {
this.weightHolder.remove(infoHash);
this.torrentsSeedStats.remove(infoHash);
this.speedMap.remove(infoHash);
this.recomputeSpeeds();
} finally {
this.lock.writeLock().unlock();
}
}
@VisibleForTesting
void refreshCurrentBandwidth() {
if (logger.isDebugEnabled()) {
logger.debug("Refreshing global bandwidth");
}
this.lock.writeLock().lock();
try {
this.randomSpeedProvider.refresh();
this.recomputeSpeeds();
if (logger.isDebugEnabled()) {
logger.debug("Global bandwidth refreshed, new value is {}", FileUtils.byteCountToDisplaySize(this.randomSpeedProvider.getInBytesPerSeconds()));
}
} finally {
this.lock.writeLock().unlock();
}
}
@VisibleForTesting
void recomputeSpeeds() {
if (logger.isDebugEnabled()) {
logger.debug("Refreshing all torrents speeds");
}
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) -> {
if (speed == null) {
return new Speed(0);
}
speed.setBytesPerSeconds((long) (this.randomSpeedProvider.getInBytesPerSeconds() * percentOfSpeedAssigned));
return speed;
});
}
if (speedChangedListener != null) {
this.speedChangedListener.speedsHasChanged(Maps.newHashMap(this.speedMap));
}
try {
if (logger.isDebugEnabled()) {
final StringBuilder sb = new StringBuilder("All torrents speeds has been refreshed:\n");
final double totalWeight = this.weightHolder.getTotalWeight();
this.speedMap.forEach((infoHash, speed) -> {
final String humanReadableSpeed = FileUtils.byteCountToDisplaySize(speed.getBytesPerSeconds());
final double torrentWeight = this.weightHolder.getWeightFor(infoHash);
final double weightInPercent = torrentWeight > 0.0
? totalWeight / torrentWeight * 100
: 0;
sb.append(" ")
.append(infoHash.humanReadableValue())
.append(":")
.append("\n ").append("current speed: ").append(humanReadableSpeed).append("/s")
.append("\n ").append("overall upload: ").append(FileUtils.byteCountToDisplaySize(this.torrentsSeedStats.get(infoHash).getUploaded()))
.append("\n ").append("weight: ").append(weightInPercent).append("% (").append(torrentWeight).append(" out of ").append(totalWeight).append(")")
.append("\n");
});
sb.setLength(sb.length() - 1); // remove last \n
logger.debug(sb.toString());
}
} catch (final Exception e) {
logger.debug("Error while printing debug message for speed.", e);
}
}
}
package org.araymond.joal.core.bandwith;
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.araymond.joal.core.bandwith.weight.PeersAwareWeightCalculator;
import org.araymond.joal.core.bandwith.weight.WeightHolder;
import org.araymond.joal.core.torrent.torrent.InfoHash;
import org.slf4j.Logger;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import static org.slf4j.LoggerFactory.getLogger;
public class BandwidthDispatcher implements BandwidthDispatcherFacade, Runnable {
private static final Logger logger = getLogger(BandwidthDispatcher.class);
private final ReentrantReadWriteLock lock;
private final WeightHolder<InfoHash> weightHolder;
private final RandomSpeedProvider randomSpeedProvider;
private final Map<InfoHash, TorrentSeedStats> torrentsSeedStats;
private final Map<InfoHash, Speed> speedMap;
private SpeedChangedListener speedChangedListener;
private final int threadPauseInterval;
private int threadLoopCounter = 0;
private volatile boolean stop = false;
private Thread thread;
public BandwidthDispatcher(final int threadPauseInterval, final RandomSpeedProvider randomSpeedProvider) {
this.threadPauseInterval = threadPauseInterval;
this.torrentsSeedStats = new HashMap<>();
this.speedMap = new HashMap<>();
this.lock = new ReentrantReadWriteLock();
this.weightHolder = new WeightHolder<>(new PeersAwareWeightCalculator());
this.randomSpeedProvider = randomSpeedProvider;
}
public void setSpeedListener(final SpeedChangedListener speedListener) {
this.speedChangedListener = speedListener;
}
/*
* 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.
*/
public TorrentSeedStats getSeedStatForTorrent(final InfoHash infoHash) {
final TorrentSeedStats torrentSeedStats = this.torrentsSeedStats.get(infoHash);
return torrentSeedStats == null ? new TorrentSeedStats() : torrentSeedStats;
}
public Map<InfoHash, Speed> getSpeedMap() {
try {
this.lock.readLock().lock();
return Maps.newHashMap(speedMap);
} finally {
this.lock.readLock().unlock();
}
}
public void start() {
this.stop = false;
this.thread = new Thread(this);
this.thread.setName("bandwidth-dispatcher");
this.thread.start();
}
public void stop() {
this.stop = true;
this.thread.interrupt();
try {
this.thread.join();
} catch (final InterruptedException ignored) {
}
}
@Override
public void run() {
try {
while (!this.stop) {
Thread.sleep(this.threadPauseInterval);
++this.threadLoopCounter;
// refresh bandwidth every 1200000 milliseconds (20 minutes)
if (this.threadLoopCounter == 1200000 / this.threadPauseInterval) {
this.refreshCurrentBandwidth();
this.threadLoopCounter = 0;
}
// This method as to run as fast as possible to avoid blocking other ones. Because we wan't this loop
// to be scheduled as precise as we can. Locking to much will delay the Thread.sleep and cause stats
// to be undervalued
this.lock.readLock().lock();
final Set<Map.Entry<InfoHash, TorrentSeedStats>> entrySet = Sets.newHashSet(this.torrentsSeedStats.entrySet());
this.lock.readLock().unlock();
for (final Map.Entry<InfoHash, TorrentSeedStats> entry : entrySet) {
final Speed speed = 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.
// 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
entry.getValue().addUploaded((speedInBytesPerSecond * this.threadPauseInterval) / 1000);
}
}
} catch (final InterruptedException ignore) {
}
}
public void updateTorrentPeers(final InfoHash infoHash, final int seeders, final int leechers) {
if (logger.isDebugEnabled()) {
logger.debug("Updating Peers stats for {}", infoHash.humanReadableValue());
}
this.lock.writeLock().lock();
try {
this.weightHolder.addOrUpdate(infoHash, new Peers(seeders, leechers));
this.recomputeSpeeds();
} finally {
this.lock.writeLock().unlock();
}
}
public void registerTorrent(final InfoHash infoHash) {
if (logger.isDebugEnabled()) {
logger.debug("{} has been added to bandwidth dispatcher.", infoHash.humanReadableValue());
}
this.lock.writeLock().lock();
try {
this.torrentsSeedStats.put(infoHash, new TorrentSeedStats());
this.speedMap.put(infoHash, new Speed(0));
} finally {
this.lock.writeLock().unlock();
}
}
public void unregisterTorrent(final InfoHash infoHash) {
if (logger.isDebugEnabled()) {
logger.debug("{} has been removed from bandwidth dispatcher.", infoHash.humanReadableValue());
}
this.lock.writeLock().lock();
try {
this.weightHolder.remove(infoHash);
this.torrentsSeedStats.remove(infoHash);
this.speedMap.remove(infoHash);
this.recomputeSpeeds();
} finally {
this.lock.writeLock().unlock();
}
}
@VisibleForTesting
void refreshCurrentBandwidth() {
if (logger.isDebugEnabled()) {
logger.debug("Refreshing global bandwidth");
}
this.lock.writeLock().lock();
try {
this.randomSpeedProvider.refresh();
this.recomputeSpeeds();
if (logger.isDebugEnabled()) {
logger.debug("Global bandwidth refreshed, new value is {}", FileUtils.byteCountToDisplaySize(this.randomSpeedProvider.getInBytesPerSeconds()));
}
} finally {
this.lock.writeLock().unlock();
}
}
@VisibleForTesting
void recomputeSpeeds() {
if (logger.isDebugEnabled()) {
logger.debug("Refreshing all torrents speeds");
}
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) -> {
if (speed == null) {
return new Speed(0);
}
speed.setBytesPerSeconds((long) (this.randomSpeedProvider.getInBytesPerSeconds() * percentOfSpeedAssigned));
return speed;
});
}
if (speedChangedListener != null) {
this.speedChangedListener.speedsHasChanged(Maps.newHashMap(this.speedMap));
}
try {
if (logger.isDebugEnabled()) {
final StringBuilder sb = new StringBuilder("All torrents speeds has been refreshed:\n");
final double totalWeight = this.weightHolder.getTotalWeight();
this.speedMap.forEach((infoHash, speed) -> {
final String humanReadableSpeed = FileUtils.byteCountToDisplaySize(speed.getBytesPerSeconds());
final double torrentWeight = this.weightHolder.getWeightFor(infoHash);
final double weightInPercent = torrentWeight > 0.0
? totalWeight / torrentWeight * 100
: 0;
sb.append(" ")
.append(infoHash.humanReadableValue())
.append(":")
.append("\n ").append("current speed: ").append(humanReadableSpeed).append("/s")
.append("\n ").append("overall upload: ").append(FileUtils.byteCountToDisplaySize(this.torrentsSeedStats.get(infoHash).getUploaded()))
.append("\n ").append("weight: ").append(weightInPercent).append("% (").append(torrentWeight).append(" out of ").append(totalWeight).append(")")
.append("\n");
});
sb.setLength(sb.length() - 1); // remove last \n
logger.debug(sb.toString());
}
} catch (final Exception e) {
logger.debug("Error while printing debug message for speed.", e);
}
}
}

View file

@ -132,7 +132,7 @@ public class BitTorrentClient {
public List<Map.Entry<String, String>> createRequestHeaders() {
final List<Map.Entry<String, String>> headers = new ArrayList<>(this.headers.size() + 1);
this.headers.stream().forEachOrdered(header -> {
for (final Map.Entry<String, String> header : this.headers) {
final String name = header.getKey();
final String value = header.getValue()
.replaceAll("\\{java}", System.getProperty("java.version"))
@ -146,7 +146,7 @@ public class BitTorrentClient {
}
headers.add(new AbstractMap.SimpleImmutableEntry<>(name, value));
});
}
return headers;
}

View file

@ -14,7 +14,6 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

View file

@ -2,6 +2,7 @@ package org.araymond.joal.core.client.emulated.generator.peerid.generation;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import com.google.common.base.Objects;
import org.apache.commons.lang3.StringUtils;
import org.araymond.joal.core.client.emulated.TorrentClientConfigIntegrityException;
@ -60,7 +61,7 @@ public class RandomPoolWithChecksumPeerIdAlgorithm implements PeerIdAlgorithm {
@VisibleForTesting
byte[] createSecureRandomSeed() {
return Instant.now().toString().getBytes();
return Instant.now().toString().getBytes(Charsets.UTF_8);
}
private Integer getRandomIntBetween10And50() {

View file

@ -62,7 +62,7 @@ public class TorrentFileProvider extends FileAlterationListenerAdaptor {
}
this.archiveFolder = joalFoldersPath.getTorrentArchivedPath();
this.torrentFiles = Collections.synchronizedMap(new HashMap<File, MockedTorrent>());
this.torrentFiles = Collections.synchronizedMap(new HashMap<>());
this.watcher = new TorrentFileWatcher(this, torrentFolder);
this.torrentFileChangeListener = new HashSet<>();
}

View file

@ -1,224 +1,224 @@
package org.araymond.joal.core.ttorrent.client;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.turn.ttorrent.common.protocol.TrackerMessage.AnnounceRequestMessage.RequestEvent;
import org.araymond.joal.core.config.AppConfiguration;
import org.araymond.joal.core.events.torrent.files.TorrentFileAddedEvent;
import org.araymond.joal.core.events.torrent.files.TorrentFileDeletedEvent;
import org.araymond.joal.core.exception.NoMoreTorrentsFileAvailableException;
import org.araymond.joal.core.torrent.torrent.InfoHash;
import org.araymond.joal.core.torrent.torrent.MockedTorrent;
import org.araymond.joal.core.torrent.watcher.TorrentFileChangeAware;
import org.araymond.joal.core.torrent.watcher.TorrentFileProvider;
import org.araymond.joal.core.ttorrent.client.announcer.Announcer;
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.request.AnnounceRequest;
import org.araymond.joal.core.ttorrent.client.announcer.request.AnnouncerExecutor;
import org.slf4j.Logger;
import org.springframework.context.ApplicationEventPublisher;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;
import static org.slf4j.LoggerFactory.getLogger;
public class Client implements TorrentFileChangeAware, ClientFacade {
private static final Logger logger = getLogger(Client.class);
private final AppConfiguration appConfiguration;
private final TorrentFileProvider torrentFileProvider;
private final ApplicationEventPublisher eventPublisher;
private AnnouncerExecutor announcerExecutor;
private final List<Announcer> currentlySeedingAnnouncer;
private final DelayQueue<AnnounceRequest> delayQueue;
private final AnnouncerFactory announcerFactory;
private final ReentrantReadWriteLock lock;
private Thread thread;
private volatile boolean stop = true;
Client(final AppConfiguration appConfiguration, final TorrentFileProvider torrentFileProvider, final AnnouncerExecutor announcerExecutor, final DelayQueue<AnnounceRequest> delayQueue, final AnnouncerFactory announcerFactory, final ApplicationEventPublisher eventPublisher) {
Preconditions.checkNotNull(appConfiguration, "AppConfiguration must not be null");
Preconditions.checkNotNull(torrentFileProvider, "TorrentFileProvider must not be null");
Preconditions.checkNotNull(delayQueue, "DelayQueue must not be null");
Preconditions.checkNotNull(announcerFactory, "AnnouncerFactory must not be null");
this.eventPublisher = eventPublisher;
this.appConfiguration = appConfiguration;
this.torrentFileProvider = torrentFileProvider;
this.announcerExecutor = announcerExecutor;
this.delayQueue = delayQueue;
this.announcerFactory = announcerFactory;
this.currentlySeedingAnnouncer = new ArrayList<>();
this.lock = new ReentrantReadWriteLock();
}
@VisibleForTesting
void setAnnouncerExecutor(final AnnouncerExecutor announcerExecutor) {
this.announcerExecutor = announcerExecutor;
}
@Override
public void start() {
this.stop = false;
this.thread = new Thread(() -> {
while (!this.stop) {
final List<AnnounceRequest> availables = this.delayQueue.getAvailables();
availables.forEach(req -> {
this.announcerExecutor.execute(req);
try {
this.lock.writeLock().lock();
this.currentlySeedingAnnouncer.removeIf(an -> an.equals(req.getAnnouncer())); // remove the last recorded event
this.currentlySeedingAnnouncer.add(req.getAnnouncer());
} finally {
this.lock.writeLock().unlock();
}
});
try {
Thread.sleep(1000);
} catch (final InterruptedException ignored) {
}
}
});
for (int i = 0; i < this.appConfiguration.getSimultaneousSeed(); i++) {
try {
this.lock.writeLock().lock();
this.addTorrent();
} catch (final NoMoreTorrentsFileAvailableException ignored) {
break;
} finally {
this.lock.writeLock().unlock();
}
}
this.thread.setName("client-orchestrator-thread");
this.thread.start();
this.torrentFileProvider.registerListener(this);
}
private void addTorrent() throws NoMoreTorrentsFileAvailableException {
final MockedTorrent torrent = this.torrentFileProvider.getTorrentNotIn(
this.currentlySeedingAnnouncer.stream()
.map(Announcer::getTorrentInfoHash)
.collect(Collectors.toList())
);
final Announcer announcer = this.announcerFactory.create(torrent);
this.currentlySeedingAnnouncer.add(announcer);
this.delayQueue.addOrReplace(AnnounceRequest.createStart(announcer), 0, ChronoUnit.SECONDS);
}
@Override
public void stop() {
try {
this.lock.writeLock().lock();
this.stop = true;
this.torrentFileProvider.unRegisterListener(this);
this.thread.interrupt();
try {
this.thread.join();
} catch (final InterruptedException ignored) {
}
this.delayQueue.drainAll().stream()
.filter(req -> req.getEvent() != RequestEvent.STARTED)
.map(AnnounceRequest::getAnnouncer)
.map(AnnounceRequest::createStop)
.forEach(this.announcerExecutor::execute);
this.announcerExecutor.awaitForRunningTasks();
} finally {
this.lock.writeLock().unlock();
}
}
public void onTooManyFailedInARaw(final Announcer announcer) {
if (this.stop) {
this.currentlySeedingAnnouncer.remove(announcer);
return;
}
try {
this.lock.writeLock().lock();
this.currentlySeedingAnnouncer.remove(announcer); // Remove from announcers list asap, otherwise the deletion will trigger a announce stop event.
this.torrentFileProvider.moveToArchiveFolder(announcer.getTorrentInfoHash());
this.addTorrent();
} catch (final NoMoreTorrentsFileAvailableException ignored) {
} finally {
this.lock.writeLock().unlock();
}
}
public void onNoMorePeers(final InfoHash infoHash) {
if (!this.appConfiguration.shouldKeepTorrentWithZeroLeechers()) {
this.torrentFileProvider.moveToArchiveFolder(infoHash);
}
}
public void onTorrentHasStopped(final Announcer stoppedAnnouncer) {
if (this.stop) {
this.currentlySeedingAnnouncer.remove(stoppedAnnouncer);
return;
}
try {
this.lock.writeLock().lock();
this.addTorrent();
} catch (final NoMoreTorrentsFileAvailableException ignored) {
} finally {
this.currentlySeedingAnnouncer.remove(stoppedAnnouncer);
this.lock.writeLock().unlock();
}
}
@Override
public void onTorrentFileAdded(final MockedTorrent torrent) {
this.eventPublisher.publishEvent(new TorrentFileAddedEvent(torrent));
if (this.stop) {
return;
}
try {
this.lock.writeLock().lock();
if (this.currentlySeedingAnnouncer.size() >= this.appConfiguration.getSimultaneousSeed()) {
return;
}
final Announcer announcer = this.announcerFactory.create(torrent);
this.currentlySeedingAnnouncer.add(announcer);
this.delayQueue.addOrReplace(AnnounceRequest.createStart(announcer), 1, ChronoUnit.SECONDS);
} finally {
this.lock.writeLock().unlock();
}
}
@Override
public void onTorrentFileRemoved(final MockedTorrent torrent) {
this.eventPublisher.publishEvent(new TorrentFileDeletedEvent(torrent));
try {
this.lock.writeLock().lock();
this.currentlySeedingAnnouncer.stream()
.filter(announcer -> announcer.getTorrentInfoHash().equals(torrent.getTorrentInfoHash()))
.findFirst()
.ifPresent(announcer ->
this.delayQueue.addOrReplace(AnnounceRequest.createStop(announcer), 1, ChronoUnit.SECONDS)
);
} finally {
this.lock.writeLock().unlock();
}
}
@Override
public List<AnnouncerFacade> getCurrentlySeedingAnnouncer() {
try {
this.lock.readLock().lock();
return Lists.newArrayList(this.currentlySeedingAnnouncer);
} finally {
this.lock.readLock().unlock();
}
}
}
package org.araymond.joal.core.ttorrent.client;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.turn.ttorrent.common.protocol.TrackerMessage.AnnounceRequestMessage.RequestEvent;
import org.araymond.joal.core.config.AppConfiguration;
import org.araymond.joal.core.events.torrent.files.TorrentFileAddedEvent;
import org.araymond.joal.core.events.torrent.files.TorrentFileDeletedEvent;
import org.araymond.joal.core.exception.NoMoreTorrentsFileAvailableException;
import org.araymond.joal.core.torrent.torrent.InfoHash;
import org.araymond.joal.core.torrent.torrent.MockedTorrent;
import org.araymond.joal.core.torrent.watcher.TorrentFileChangeAware;
import org.araymond.joal.core.torrent.watcher.TorrentFileProvider;
import org.araymond.joal.core.ttorrent.client.announcer.Announcer;
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.request.AnnounceRequest;
import org.araymond.joal.core.ttorrent.client.announcer.request.AnnouncerExecutor;
import org.slf4j.Logger;
import org.springframework.context.ApplicationEventPublisher;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;
import static org.slf4j.LoggerFactory.getLogger;
public class Client implements TorrentFileChangeAware, ClientFacade {
private static final Logger logger = getLogger(Client.class);
private final AppConfiguration appConfiguration;
private final TorrentFileProvider torrentFileProvider;
private final ApplicationEventPublisher eventPublisher;
private AnnouncerExecutor announcerExecutor;
private final List<Announcer> currentlySeedingAnnouncer;
private final DelayQueue<AnnounceRequest> delayQueue;
private final AnnouncerFactory announcerFactory;
private final ReentrantReadWriteLock lock;
private Thread thread;
private volatile boolean stop = true;
Client(final AppConfiguration appConfiguration, final TorrentFileProvider torrentFileProvider, final AnnouncerExecutor announcerExecutor, final DelayQueue<AnnounceRequest> delayQueue, final AnnouncerFactory announcerFactory, final ApplicationEventPublisher eventPublisher) {
Preconditions.checkNotNull(appConfiguration, "AppConfiguration must not be null");
Preconditions.checkNotNull(torrentFileProvider, "TorrentFileProvider must not be null");
Preconditions.checkNotNull(delayQueue, "DelayQueue must not be null");
Preconditions.checkNotNull(announcerFactory, "AnnouncerFactory must not be null");
this.eventPublisher = eventPublisher;
this.appConfiguration = appConfiguration;
this.torrentFileProvider = torrentFileProvider;
this.announcerExecutor = announcerExecutor;
this.delayQueue = delayQueue;
this.announcerFactory = announcerFactory;
this.currentlySeedingAnnouncer = new ArrayList<>();
this.lock = new ReentrantReadWriteLock();
}
@VisibleForTesting
void setAnnouncerExecutor(final AnnouncerExecutor announcerExecutor) {
this.announcerExecutor = announcerExecutor;
}
@Override
public void start() {
this.stop = false;
this.thread = new Thread(() -> {
while (!this.stop) {
final List<AnnounceRequest> availables = this.delayQueue.getAvailables();
for (final AnnounceRequest req : availables) {
this.announcerExecutor.execute(req);
try {
this.lock.writeLock().lock();
this.currentlySeedingAnnouncer.removeIf(an -> an.equals(req.getAnnouncer())); // remove the last recorded event
this.currentlySeedingAnnouncer.add(req.getAnnouncer());
} finally {
this.lock.writeLock().unlock();
}
}
try {
Thread.sleep(1000);
} catch (final InterruptedException ignored) {
}
}
});
for (int i = 0; i < this.appConfiguration.getSimultaneousSeed(); i++) {
try {
this.lock.writeLock().lock();
this.addTorrent();
} catch (final NoMoreTorrentsFileAvailableException ignored) {
break;
} finally {
this.lock.writeLock().unlock();
}
}
this.thread.setName("client-orchestrator-thread");
this.thread.start();
this.torrentFileProvider.registerListener(this);
}
private void addTorrent() throws NoMoreTorrentsFileAvailableException {
final MockedTorrent torrent = this.torrentFileProvider.getTorrentNotIn(
this.currentlySeedingAnnouncer.stream()
.map(Announcer::getTorrentInfoHash)
.collect(Collectors.toList())
);
final Announcer announcer = this.announcerFactory.create(torrent);
this.currentlySeedingAnnouncer.add(announcer);
this.delayQueue.addOrReplace(AnnounceRequest.createStart(announcer), 0, ChronoUnit.SECONDS);
}
@Override
public void stop() {
try {
this.lock.writeLock().lock();
this.stop = true;
this.torrentFileProvider.unRegisterListener(this);
this.thread.interrupt();
try {
this.thread.join();
} catch (final InterruptedException ignored) {
}
this.delayQueue.drainAll().stream()
.filter(req -> req.getEvent() != RequestEvent.STARTED)
.map(AnnounceRequest::getAnnouncer)
.map(AnnounceRequest::createStop)
.forEach(this.announcerExecutor::execute);
this.announcerExecutor.awaitForRunningTasks();
} finally {
this.lock.writeLock().unlock();
}
}
public void onTooManyFailedInARaw(final Announcer announcer) {
if (this.stop) {
this.currentlySeedingAnnouncer.remove(announcer);
return;
}
try {
this.lock.writeLock().lock();
this.currentlySeedingAnnouncer.remove(announcer); // Remove from announcers list asap, otherwise the deletion will trigger a announce stop event.
this.torrentFileProvider.moveToArchiveFolder(announcer.getTorrentInfoHash());
this.addTorrent();
} catch (final NoMoreTorrentsFileAvailableException ignored) {
} finally {
this.lock.writeLock().unlock();
}
}
public void onNoMorePeers(final InfoHash infoHash) {
if (!this.appConfiguration.shouldKeepTorrentWithZeroLeechers()) {
this.torrentFileProvider.moveToArchiveFolder(infoHash);
}
}
public void onTorrentHasStopped(final Announcer stoppedAnnouncer) {
if (this.stop) {
this.currentlySeedingAnnouncer.remove(stoppedAnnouncer);
return;
}
try {
this.lock.writeLock().lock();
this.addTorrent();
} catch (final NoMoreTorrentsFileAvailableException ignored) {
} finally {
this.currentlySeedingAnnouncer.remove(stoppedAnnouncer);
this.lock.writeLock().unlock();
}
}
@Override
public void onTorrentFileAdded(final MockedTorrent torrent) {
this.eventPublisher.publishEvent(new TorrentFileAddedEvent(torrent));
if (this.stop) {
return;
}
try {
this.lock.writeLock().lock();
if (this.currentlySeedingAnnouncer.size() >= this.appConfiguration.getSimultaneousSeed()) {
return;
}
final Announcer announcer = this.announcerFactory.create(torrent);
this.currentlySeedingAnnouncer.add(announcer);
this.delayQueue.addOrReplace(AnnounceRequest.createStart(announcer), 1, ChronoUnit.SECONDS);
} finally {
this.lock.writeLock().unlock();
}
}
@Override
public void onTorrentFileRemoved(final MockedTorrent torrent) {
this.eventPublisher.publishEvent(new TorrentFileDeletedEvent(torrent));
try {
this.lock.writeLock().lock();
this.currentlySeedingAnnouncer.stream()
.filter(announcer -> announcer.getTorrentInfoHash().equals(torrent.getTorrentInfoHash()))
.findFirst()
.ifPresent(announcer ->
this.delayQueue.addOrReplace(AnnounceRequest.createStop(announcer), 1, ChronoUnit.SECONDS)
);
} finally {
this.lock.writeLock().unlock();
}
}
@Override
public List<AnnouncerFacade> getCurrentlySeedingAnnouncer() {
try {
this.lock.readLock().lock();
return Lists.newArrayList(this.currentlySeedingAnnouncer);
} finally {
this.lock.readLock().unlock();
}
}
}

View file

@ -1,12 +1,14 @@
package org.araymond.joal.core.ttorrent.client;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.*;
import java.nio.channels.ServerSocketChannel;
@ -76,8 +78,13 @@ public class ConnectionHandler {
InetAddress readIpFromProvider(final String providerUrl) throws IOException {
final URLConnection urlConnection = new URL(providerUrl).openConnection();
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()))) {
try (final BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), Charsets.UTF_8))) {
return InetAddress.getByName(in.readLine());
}finally {
// Ensure all streams associated with http connection are closed
final InputStream err = ((HttpURLConnection) urlConnection).getErrorStream();
try { if (err != null) err.close(); }
catch (final IOException ignored) {}
}
}

View file

@ -1,90 +1,88 @@
package org.araymond.joal.core.ttorrent.client.announcer.response;
import com.turn.ttorrent.common.protocol.TrackerMessage.AnnounceRequestMessage.RequestEvent;
import org.araymond.joal.core.events.announce.FailedToAnnounceEvent;
import org.araymond.joal.core.events.announce.SuccessfullyAnnounceEvent;
import org.araymond.joal.core.events.announce.TooManyAnnouncesFailedEvent;
import org.araymond.joal.core.events.announce.WillAnnounceEvent;
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.exceptions.TooMuchAnnouncesFailedInARawException;
import org.araymond.joal.core.ttorrent.client.announcer.request.SuccessAnnounceResponse;
import org.slf4j.Logger;
import org.springframework.context.ApplicationEventPublisher;
import static org.slf4j.LoggerFactory.getLogger;
public class AnnounceEventPublisher implements AnnounceResponseHandlerChainElement {
private static final Logger logger = getLogger(AnnounceEventPublisher.class);
private final ApplicationEventPublisher eventPublisher;
public AnnounceEventPublisher(final ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
@Override
public void onAnnouncerWillAnnounce(final Announcer announcer, final RequestEvent event) {
if(logger.isDebugEnabled()) {
logger.debug("Publish WillAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
}
this.eventPublisher.publishEvent(new WillAnnounceEvent(announcer, event));
}
@Override
public void onAnnounceStartSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
if(logger.isDebugEnabled()) {
logger.debug("Publish SuccessfullyAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
}
this.eventPublisher.publishEvent(new SuccessfullyAnnounceEvent(announcer, RequestEvent.STARTED));
}
@Override
public void onAnnounceStartFails(final Announcer announcer, final Throwable throwable) {
if(logger.isDebugEnabled()) {
logger.debug("Publish FailedToAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
}
this.eventPublisher.publishEvent(new FailedToAnnounceEvent(announcer, RequestEvent.STARTED, throwable.getMessage()));
}
@Override
public void onAnnounceRegularSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
if(logger.isDebugEnabled()) {
logger.debug("Publish SuccessfullyAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
}
this.eventPublisher.publishEvent(new SuccessfullyAnnounceEvent(announcer, RequestEvent.NONE));
}
@Override
public void onAnnounceRegularFails(final Announcer announcer, final Throwable throwable) {
if(logger.isDebugEnabled()) {
logger.debug("Publish FailedToAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
}
this.eventPublisher.publishEvent(new FailedToAnnounceEvent(announcer, RequestEvent.NONE, throwable.getMessage()));
}
@Override
public void onAnnounceStopSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
if(logger.isDebugEnabled()) {
logger.debug("Publish SuccessfullyAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
}
this.eventPublisher.publishEvent(new SuccessfullyAnnounceEvent(announcer, RequestEvent.STOPPED));
}
@Override
public void onAnnounceStopFails(final Announcer announcer, final Throwable throwable) {
if(logger.isDebugEnabled()) {
logger.debug("Publish FailedToAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
}
final InfoHash infoHash = announcer.getTorrentInfoHash();
this.eventPublisher.publishEvent(new FailedToAnnounceEvent(announcer, RequestEvent.STOPPED, throwable.getMessage()));
}
@Override
public void onTooManyAnnounceFailedInARaw(final Announcer announcer, final TooMuchAnnouncesFailedInARawException e) {
if(logger.isDebugEnabled()) {
logger.debug("Publish TooManyAnnouncesFailedEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
}
this.eventPublisher.publishEvent(new TooManyAnnouncesFailedEvent(announcer));
}
}
package org.araymond.joal.core.ttorrent.client.announcer.response;
import com.turn.ttorrent.common.protocol.TrackerMessage.AnnounceRequestMessage.RequestEvent;
import org.araymond.joal.core.events.announce.FailedToAnnounceEvent;
import org.araymond.joal.core.events.announce.SuccessfullyAnnounceEvent;
import org.araymond.joal.core.events.announce.TooManyAnnouncesFailedEvent;
import org.araymond.joal.core.events.announce.WillAnnounceEvent;
import org.araymond.joal.core.ttorrent.client.announcer.Announcer;
import org.araymond.joal.core.ttorrent.client.announcer.exceptions.TooMuchAnnouncesFailedInARawException;
import org.araymond.joal.core.ttorrent.client.announcer.request.SuccessAnnounceResponse;
import org.slf4j.Logger;
import org.springframework.context.ApplicationEventPublisher;
import static org.slf4j.LoggerFactory.getLogger;
public class AnnounceEventPublisher implements AnnounceResponseHandlerChainElement {
private static final Logger logger = getLogger(AnnounceEventPublisher.class);
private final ApplicationEventPublisher eventPublisher;
public AnnounceEventPublisher(final ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
@Override
public void onAnnouncerWillAnnounce(final Announcer announcer, final RequestEvent event) {
if(logger.isDebugEnabled()) {
logger.debug("Publish WillAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
}
this.eventPublisher.publishEvent(new WillAnnounceEvent(announcer, event));
}
@Override
public void onAnnounceStartSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
if(logger.isDebugEnabled()) {
logger.debug("Publish SuccessfullyAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
}
this.eventPublisher.publishEvent(new SuccessfullyAnnounceEvent(announcer, RequestEvent.STARTED));
}
@Override
public void onAnnounceStartFails(final Announcer announcer, final Throwable throwable) {
if(logger.isDebugEnabled()) {
logger.debug("Publish FailedToAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
}
this.eventPublisher.publishEvent(new FailedToAnnounceEvent(announcer, RequestEvent.STARTED, throwable.getMessage()));
}
@Override
public void onAnnounceRegularSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
if(logger.isDebugEnabled()) {
logger.debug("Publish SuccessfullyAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
}
this.eventPublisher.publishEvent(new SuccessfullyAnnounceEvent(announcer, RequestEvent.NONE));
}
@Override
public void onAnnounceRegularFails(final Announcer announcer, final Throwable throwable) {
if(logger.isDebugEnabled()) {
logger.debug("Publish FailedToAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
}
this.eventPublisher.publishEvent(new FailedToAnnounceEvent(announcer, RequestEvent.NONE, throwable.getMessage()));
}
@Override
public void onAnnounceStopSuccess(final Announcer announcer, final SuccessAnnounceResponse result) {
if(logger.isDebugEnabled()) {
logger.debug("Publish SuccessfullyAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
}
this.eventPublisher.publishEvent(new SuccessfullyAnnounceEvent(announcer, RequestEvent.STOPPED));
}
@Override
public void onAnnounceStopFails(final Announcer announcer, final Throwable throwable) {
if(logger.isDebugEnabled()) {
logger.debug("Publish FailedToAnnounceEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
}
this.eventPublisher.publishEvent(new FailedToAnnounceEvent(announcer, RequestEvent.STOPPED, throwable.getMessage()));
}
@Override
public void onTooManyAnnounceFailedInARaw(final Announcer announcer, final TooMuchAnnouncesFailedInARawException e) {
if(logger.isDebugEnabled()) {
logger.debug("Publish TooManyAnnouncesFailedEvent event for {}.", announcer.getTorrentInfoHash().humanReadableValue());
}
this.eventPublisher.publishEvent(new TooManyAnnouncesFailedEvent(announcer));
}
}

View file

@ -80,7 +80,9 @@ public class TrackerClient {
host += ":" + announceUri.getPort();
}
request.addHeader("Host", host);
headers.forEach(entry -> request.addHeader(entry.getKey(), entry.getValue()));
for (final Map.Entry<String, String> entry : headers) {
request.addHeader(entry.getKey(), entry.getValue());
}
final Response response;
try {

View file

@ -6,7 +6,7 @@ import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptorAdapter;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
@ -19,7 +19,7 @@ import javax.inject.Inject;
*/
@ConditionalOnWebUi
@Component
public class AuthChannelInterceptorAdapter extends ChannelInterceptorAdapter {
public class AuthChannelInterceptorAdapter implements ChannelInterceptor {
static final String USERNAME_HEADER = "X-Joal-Username";
static final String TOKEN_HEADER = "X-Joal-Auth-Token";

View file

@ -1,40 +1,40 @@
package org.araymond.joal.web.messages.outgoing.impl.speed;
import org.araymond.joal.core.events.speed.SeedingSpeedsHasChangedEvent;
import org.araymond.joal.core.torrent.torrent.InfoHash;
import org.araymond.joal.web.messages.outgoing.MessagePayload;
import java.util.List;
import java.util.stream.Collectors;
public class SeedingSpeedHasChangedPayload implements MessagePayload {
private final List<SpeedPayload> speeds;
public SeedingSpeedHasChangedPayload(final SeedingSpeedsHasChangedEvent event) {
this.speeds = event.getSpeeds().entrySet().stream()
.map(entry -> new SpeedPayload(entry.getKey(), Long.valueOf(entry.getValue().getBytesPerSeconds())))
.collect(Collectors.toList());
}
public List<SpeedPayload> getSpeeds() {
return speeds;
}
public static final class SpeedPayload {
private final InfoHash infoHash;
private final Long bytesPerSeconds;
public SpeedPayload(final InfoHash infoHash, final Long bytesPerSeconds) {
this.infoHash = infoHash;
this.bytesPerSeconds = bytesPerSeconds;
}
public InfoHash getInfoHash() {
return infoHash;
}
public Long getBytesPerSeconds() {
return bytesPerSeconds;
}
}
}
package org.araymond.joal.web.messages.outgoing.impl.speed;
import org.araymond.joal.core.events.speed.SeedingSpeedsHasChangedEvent;
import org.araymond.joal.core.torrent.torrent.InfoHash;
import org.araymond.joal.web.messages.outgoing.MessagePayload;
import java.util.List;
import java.util.stream.Collectors;
public class SeedingSpeedHasChangedPayload implements MessagePayload {
private final List<SpeedPayload> speeds;
public SeedingSpeedHasChangedPayload(final SeedingSpeedsHasChangedEvent event) {
this.speeds = event.getSpeeds().entrySet().stream()
.map(entry -> new SpeedPayload(entry.getKey(), entry.getValue().getBytesPerSeconds()))
.collect(Collectors.toList());
}
public List<SpeedPayload> getSpeeds() {
return speeds;
}
public static final class SpeedPayload {
private final InfoHash infoHash;
private final Long bytesPerSeconds;
public SpeedPayload(final InfoHash infoHash, final Long bytesPerSeconds) {
this.infoHash = infoHash;
this.bytesPerSeconds = bytesPerSeconds;
}
public InfoHash getInfoHash() {
return infoHash;
}
public Long getBytesPerSeconds() {
return bytesPerSeconds;
}
}
}

View file

@ -34,7 +34,7 @@ public class WebAnnounceEventListener extends WebEventListener {
@Order(Ordered.LOWEST_PRECEDENCE)
@EventListener
void failedToAnnounce(final FailedToAnnounceEvent event) {
public void failedToAnnounce(final FailedToAnnounceEvent event) {
logger.debug("Send FailedToAnnouncePayload to clients.");
this.messagingTemplate.convertAndSend("/announce", new FailedToAnnouncePayload(event));
@ -42,7 +42,7 @@ public class WebAnnounceEventListener extends WebEventListener {
@Order(Ordered.LOWEST_PRECEDENCE)
@EventListener
void successfullyAnnounce(final SuccessfullyAnnounceEvent event) {
public void successfullyAnnounce(final SuccessfullyAnnounceEvent event) {
logger.debug("Send SuccessfullyAnnouncePayload to clients.");
this.messagingTemplate.convertAndSend("/announce", new SuccessfullyAnnouncePayload(event));
@ -50,7 +50,7 @@ public class WebAnnounceEventListener extends WebEventListener {
@Order(Ordered.LOWEST_PRECEDENCE)
@EventListener
void tooManyAnnouncesFailed(final TooManyAnnouncesFailedEvent event) {
public void tooManyAnnouncesFailed(final TooManyAnnouncesFailedEvent event) {
logger.debug("Send TooManyAnnouncesFailedPayload to clients.");
this.messagingTemplate.convertAndSend("/announce", new TooManyAnnouncesFailedPayload(event));
@ -58,7 +58,7 @@ public class WebAnnounceEventListener extends WebEventListener {
@Order(Ordered.LOWEST_PRECEDENCE)
@EventListener
void willAnnounce(final WillAnnounceEvent event) {
public void willAnnounce(final WillAnnounceEvent event) {
logger.debug("Send WillAnnouncePayload to clients.");
this.messagingTemplate.convertAndSend("/announce", new WillAnnouncePayload(event));

View file

@ -32,7 +32,7 @@ public class WebConfigEventListener extends WebEventListener {
@Order(Ordered.LOWEST_PRECEDENCE)
@EventListener
void configHasBeenLoaded(final ConfigHasBeenLoadedEvent event) {
public void configHasBeenLoaded(final ConfigHasBeenLoadedEvent event) {
logger.debug("Send ConfigHasBeenLoadedPayload to clients.");
this.messagingTemplate.convertAndSend("/config", new ConfigHasBeenLoadedPayload(event));
@ -40,7 +40,7 @@ public class WebConfigEventListener extends WebEventListener {
@Order(Ordered.LOWEST_PRECEDENCE)
@EventListener
void configIsInDirtyState(final ConfigurationIsInDirtyStateEvent event) {
public void configIsInDirtyState(final ConfigurationIsInDirtyStateEvent event) {
logger.debug("Send ConfigIsInDirtyStatePayload to clients.");
this.messagingTemplate.convertAndSend("/config", new ConfigIsInDirtyStatePayload(event));
@ -48,7 +48,7 @@ public class WebConfigEventListener extends WebEventListener {
@Order(Ordered.LOWEST_PRECEDENCE)
@EventListener
void clientFilesDiscovered(final ListOfClientFilesEvent event) {
public void clientFilesDiscovered(final ListOfClientFilesEvent event) {
logger.debug("Send ListOfClientFilesPayload to clients.");
this.messagingTemplate.convertAndSend("/config", new ListOfClientFilesPayload(event));

View file

@ -31,7 +31,7 @@ public class WebGlobalEventListener extends WebEventListener {
@Order(Ordered.LOWEST_PRECEDENCE)
@EventListener
void globalSeedStarted(final GlobalSeedStartedEvent event) {
public void globalSeedStarted(final GlobalSeedStartedEvent event) {
logger.debug("Send GlobalSeedStartedPayload to clients.");
final String client = event.getBitTorrentClient().getHeaders().stream()
@ -45,7 +45,7 @@ public class WebGlobalEventListener extends WebEventListener {
@Order(Ordered.LOWEST_PRECEDENCE)
@EventListener
void globalSeedStopped(@SuppressWarnings("unused") final GlobalSeedStoppedEvent event) {
public void globalSeedStopped(@SuppressWarnings("unused") final GlobalSeedStoppedEvent event) {
logger.debug("Send GlobalSeedStoppedPayload to clients.");
this.messagingTemplate.convertAndSend("/global", new GlobalSeedStoppedPayload());

View file

@ -1,37 +1,37 @@
package org.araymond.joal.web.services.corelistener;
import org.araymond.joal.core.events.speed.SeedingSpeedsHasChangedEvent;
import org.araymond.joal.web.annotations.ConditionalOnWebUi;
import org.araymond.joal.web.messages.outgoing.impl.speed.SeedingSpeedHasChangedPayload;
import org.araymond.joal.web.services.JoalMessageSendingTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import javax.inject.Inject;
/**
* Created by raymo on 25/06/2017.
*/
@ConditionalOnWebUi
@Service
public class WebSpeedEventListener extends WebEventListener {
private static final Logger logger = LoggerFactory.getLogger(WebSpeedEventListener.class);
@Inject
public WebSpeedEventListener(final JoalMessageSendingTemplate messagingTemplate) {
super(messagingTemplate);
}
@Order(Ordered.LOWEST_PRECEDENCE)
@EventListener
void failedToAnnounce(final SeedingSpeedsHasChangedEvent event) {
logger.debug("Send SeedingSpeedHasChangedPayload to clients.");
this.messagingTemplate.convertAndSend("/speed", new SeedingSpeedHasChangedPayload(event));
}
}
package org.araymond.joal.web.services.corelistener;
import org.araymond.joal.core.events.speed.SeedingSpeedsHasChangedEvent;
import org.araymond.joal.web.annotations.ConditionalOnWebUi;
import org.araymond.joal.web.messages.outgoing.impl.speed.SeedingSpeedHasChangedPayload;
import org.araymond.joal.web.services.JoalMessageSendingTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import javax.inject.Inject;
/**
* Created by raymo on 25/06/2017.
*/
@ConditionalOnWebUi
@Service
public class WebSpeedEventListener extends WebEventListener {
private static final Logger logger = LoggerFactory.getLogger(WebSpeedEventListener.class);
@Inject
public WebSpeedEventListener(final JoalMessageSendingTemplate messagingTemplate) {
super(messagingTemplate);
}
@Order(Ordered.LOWEST_PRECEDENCE)
@EventListener
public void failedToAnnounce(final SeedingSpeedsHasChangedEvent event) {
logger.debug("Send SeedingSpeedHasChangedPayload to clients.");
this.messagingTemplate.convertAndSend("/speed", new SeedingSpeedHasChangedPayload(event));
}
}

View file

@ -32,7 +32,7 @@ public class WebTorrentFileEventListener extends WebEventListener {
@Order(Ordered.LOWEST_PRECEDENCE)
@EventListener
void torrentFileAdded(final TorrentFileAddedEvent event) {
public void torrentFileAdded(final TorrentFileAddedEvent event) {
logger.debug("Send TorrentFileAddedPayload to clients.");
this.messagingTemplate.convertAndSend("/torrents", new TorrentFileAddedPayload(event));
@ -40,7 +40,7 @@ public class WebTorrentFileEventListener extends WebEventListener {
@Order(Ordered.LOWEST_PRECEDENCE)
@EventListener
void torrentFileDeleted(final TorrentFileDeletedEvent event) {
public void torrentFileDeleted(final TorrentFileDeletedEvent event) {
logger.debug("Send TorrentFileDeletedPayload to clients.");
this.messagingTemplate.convertAndSend("/torrents", new TorrentFileDeletedPayload(event));
@ -48,7 +48,7 @@ public class WebTorrentFileEventListener extends WebEventListener {
@Order(Ordered.LOWEST_PRECEDENCE)
@EventListener
void failedToAddTorrentFile(final FailedToAddTorrentFileEvent event) {
public void failedToAddTorrentFile(final FailedToAddTorrentFileEvent event) {
logger.debug("Send FailedToAddTorrentFilePayload to clients.");
this.messagingTemplate.convertAndSend("/torrents", new FailedToAddTorrentFilePayload(event));