mirror of
https://github.com/anthonyraymond/joal.git
synced 2024-11-10 09:02:31 +08:00
Compare commits
2 commits
c346e61a12
...
9157a4976e
Author | SHA1 | Date | |
---|---|---|---|
|
9157a4976e | ||
|
a55c8d0e10 |
11 changed files with 117 additions and 53 deletions
|
@ -1,4 +1,4 @@
|
||||||
package org.araymond.joalcore.core.infohash.domain;
|
package org.araymond.joalcore.core.metadata.domain;
|
||||||
|
|
||||||
import org.araymond.joalcore.annotations.ddd.ValueObject;
|
import org.araymond.joalcore.annotations.ddd.ValueObject;
|
||||||
|
|
|
@ -1,21 +1,10 @@
|
||||||
package org.araymond.joalcore.core.metadata.domain;
|
package org.araymond.joalcore.core.metadata.domain;
|
||||||
|
|
||||||
import org.araymond.joalcore.core.infohash.domain.InfoHash;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class TorrentMetadata {
|
public record TorrentMetadata(InfoHash infoHash, TorrentSize size) {
|
||||||
private final InfoHash infoHash;
|
public TorrentMetadata {
|
||||||
private final long totalSize;
|
Objects.requireNonNull(infoHash, "TorrentMetadata requires a non-null [infoHash]");
|
||||||
|
Objects.requireNonNull(size, "TorrentMetadata requires a non-null [size]");
|
||||||
public TorrentMetadata(InfoHash infoHash, long totalSize) {
|
|
||||||
this.infoHash = infoHash;
|
|
||||||
this.totalSize = totalSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InfoHash infoHash() {
|
|
||||||
return infoHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long totalSize() {
|
|
||||||
return totalSize;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
package org.araymond.joalcore.core.metadata.domain;
|
||||||
|
|
||||||
|
import org.araymond.joalcore.annotations.ddd.ValueObject;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@ValueObject
|
||||||
|
public final class TorrentSize {
|
||||||
|
private final long bytes;
|
||||||
|
|
||||||
|
public static TorrentSize ofBytes(long bytes) {
|
||||||
|
return new TorrentSize(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TorrentSize(long bytes) {
|
||||||
|
this.bytes = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long bytes() {
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) return true;
|
||||||
|
if (obj == null || obj.getClass() != this.getClass()) return false;
|
||||||
|
var that = (TorrentSize) obj;
|
||||||
|
return this.bytes == that.bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TorrentSize[" +
|
||||||
|
"bytes=" + bytes + ']';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,16 +1,16 @@
|
||||||
package org.araymond.joalcore.core.sharing.application;
|
package org.araymond.joalcore.core.sharing.application;
|
||||||
|
|
||||||
import org.araymond.joalcore.core.infohash.domain.InfoHash;
|
import org.araymond.joalcore.core.metadata.domain.InfoHash;
|
||||||
import org.araymond.joalcore.core.sharing.domain.Contribution;
|
import org.araymond.joalcore.core.sharing.domain.Contribution;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface PersistentStats {
|
public interface OverallContributions {
|
||||||
Optional<Contribution> overallContributions(InfoHash infoHash);
|
Optional<Contribution> load(InfoHash infoHash);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: implementation should have the while map in memory. a call to persistOverallContribution update the map.
|
TODO: implementation should have the while map in memory. a call to persistOverallContribution update the map.
|
||||||
Once in a while, the map is wrote to the disk asynchronously
|
Once in a while, the map is wrote to the disk asynchronously
|
||||||
*/
|
*/
|
||||||
void persistOverallContribution(InfoHash infoHash, Contribution contribution);
|
void save(InfoHash infoHash, Contribution contribution);
|
||||||
}
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package org.araymond.joalcore.core.sharing.application;
|
||||||
|
|
||||||
|
import org.araymond.joalcore.core.sharing.domain.services.PeerElection;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class SharedTorrentConfig {
|
||||||
|
private final Supplier<PeerElection> peersElection;
|
||||||
|
private final Supplier<Boolean> skipDownload;
|
||||||
|
|
||||||
|
public SharedTorrentConfig(Supplier<PeerElection> peersElection, Supplier<Boolean> skipDownload) {
|
||||||
|
this.peersElection = peersElection;
|
||||||
|
this.skipDownload = skipDownload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Supplier<PeerElection> peersElection() {
|
||||||
|
return peersElection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Supplier<Boolean> skipDownload() {
|
||||||
|
return skipDownload;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,10 @@
|
||||||
package org.araymond.joalcore.core.sharing.application;
|
package org.araymond.joalcore.core.sharing.application;
|
||||||
|
|
||||||
import org.araymond.joalcore.core.infohash.domain.InfoHash;
|
import org.araymond.joalcore.core.metadata.domain.InfoHash;
|
||||||
import org.araymond.joalcore.core.metadata.domain.TorrentMetadata;
|
import org.araymond.joalcore.core.metadata.domain.TorrentMetadata;
|
||||||
import org.araymond.joalcore.core.sharing.application.exceptions.UnknownSharedTorrentException;
|
import org.araymond.joalcore.core.sharing.application.exceptions.UnknownSharedTorrentException;
|
||||||
import org.araymond.joalcore.core.sharing.domain.*;
|
import org.araymond.joalcore.core.sharing.domain.*;
|
||||||
import org.araymond.joalcore.core.sharing.domain.events.TorrentCreatedEvent;
|
import org.araymond.joalcore.core.sharing.domain.events.TorrentCreatedEvent;
|
||||||
import org.araymond.joalcore.core.sharing.domain.services.PeerElection;
|
|
||||||
import org.araymond.joalcore.events.DomainEvent;
|
import org.araymond.joalcore.events.DomainEvent;
|
||||||
import org.araymond.joalcore.events.DomainEventPublisher;
|
import org.araymond.joalcore.events.DomainEventPublisher;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -13,35 +12,27 @@ import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class SharedTorrentService {
|
public class SharedTorrentService {
|
||||||
private final SharedTorrentRepository torrents;
|
private final SharedTorrentRepository torrents;
|
||||||
private final DomainEventPublisher publisher;
|
private final DomainEventPublisher publisher;
|
||||||
private final Supplier<PeerElection> electionSupplier;
|
private final OverallContributions overallContributions;
|
||||||
private final PersistentStats persistentStats;
|
private final SharedTorrentConfig config;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public SharedTorrentService(SharedTorrentRepository torrents, DomainEventPublisher publisher, Supplier<PeerElection> electionSupplier, PersistentStats persistentStats) {
|
public SharedTorrentService(SharedTorrentRepository torrents, DomainEventPublisher publisher, OverallContributions overallContributions, SharedTorrentConfig config) {
|
||||||
this.torrents = torrents;
|
this.torrents = torrents;
|
||||||
this.publisher = publisher;
|
this.publisher = publisher;
|
||||||
this.electionSupplier = electionSupplier;
|
this.overallContributions = overallContributions;
|
||||||
this.persistentStats = persistentStats;
|
this.config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void create(TorrentMetadata metadata) {
|
public void create(TorrentMetadata metadata) {
|
||||||
var events = new ArrayList<DomainEvent>();
|
var events = new ArrayList<DomainEvent>();
|
||||||
|
|
||||||
var overallContributions = persistentStats.overallContributions(metadata.infoHash()).orElse(Contribution.ZERO);
|
var overallContributions = loadOverallContribution(metadata);
|
||||||
|
var left = new Left(Math.max(metadata.size().bytes() - overallContributions.downloaded().bytes(), 0));
|
||||||
var left = new Left(Math.max(metadata.totalSize() - overallContributions.downloaded().bytes(), 0));
|
|
||||||
/* TODO:
|
|
||||||
if (!config.downloadTorrentFirst()) {
|
|
||||||
left = 0;
|
|
||||||
saveTorrentStats(new Contribution(metadata.totalSize(), overallContribs.uploaded()));
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
var torrent = new SharedTorrent(metadata.infoHash(), overallContributions, left);
|
var torrent = new SharedTorrent(metadata.infoHash(), overallContributions, left);
|
||||||
events.add(new TorrentCreatedEvent(torrent.id()));
|
events.add(new TorrentCreatedEvent(torrent.id()));
|
||||||
|
@ -54,10 +45,24 @@ public class SharedTorrentService {
|
||||||
publish(events);
|
publish(events);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Contribution loadOverallContribution(TorrentMetadata metadata) {
|
||||||
|
return this.overallContributions.load(metadata.infoHash())
|
||||||
|
.orElseGet(() -> {
|
||||||
|
Contribution overall = Contribution.ZERO;
|
||||||
|
if (config.skipDownload().get()) {
|
||||||
|
// return a fully Downloaded contribution when the torrent is not yet known and skip download is true
|
||||||
|
overall = new Contribution(new DownloadAmount(metadata.size().bytes()), new UploadAmount(0));
|
||||||
|
}
|
||||||
|
this.overallContributions.save(metadata.infoHash(), overall);
|
||||||
|
|
||||||
|
return overall;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void registerPeers(InfoHash infoHash, Swarm.TrackerUniqueIdentifier identifier, Peers peers) {
|
public void registerPeers(InfoHash infoHash, Swarm.TrackerUniqueIdentifier identifier, Peers peers) {
|
||||||
var torrent = torrents.findByTorrentInfoHash(infoHash).orElseThrow(() -> new UnknownSharedTorrentException("No torrent found for %s".formatted(infoHash)));
|
var torrent = torrents.findByTorrentInfoHash(infoHash).orElseThrow(() -> new UnknownSharedTorrentException("No torrent found for %s".formatted(infoHash)));
|
||||||
|
|
||||||
var events = torrent.registerPeers(identifier, peers, electionSupplier.get());
|
var events = torrent.registerPeers(identifier, peers, config.peersElection().get());
|
||||||
torrents.save(torrent);
|
torrents.save(torrent);
|
||||||
|
|
||||||
publisher.publish(events);
|
publisher.publish(events);
|
||||||
|
@ -86,7 +91,7 @@ public class SharedTorrentService {
|
||||||
|
|
||||||
torrent.add(upload);
|
torrent.add(upload);
|
||||||
torrents.save(torrent);
|
torrents.save(torrent);
|
||||||
persistentStats.persistOverallContribution(torrent.infoHash(), torrent.overallContributions());
|
overallContributions.save(torrent.infoHash(), torrent.overallContributions());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDownload(SharedTorrentId id, DownloadAmount download) {
|
public void addDownload(SharedTorrentId id, DownloadAmount download) {
|
||||||
|
@ -96,7 +101,7 @@ public class SharedTorrentService {
|
||||||
torrents.save(torrent);
|
torrents.save(torrent);
|
||||||
|
|
||||||
publish(events);
|
publish(events);
|
||||||
persistentStats.persistOverallContribution(torrent.infoHash(), torrent.overallContributions());
|
overallContributions.save(torrent.infoHash(), torrent.overallContributions());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void publish(List<DomainEvent> events) {
|
private void publish(List<DomainEvent> events) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package org.araymond.joalcore.core.sharing.domain;
|
package org.araymond.joalcore.core.sharing.domain;
|
||||||
|
|
||||||
import org.araymond.joalcore.annotations.ddd.AggregateRoot;
|
import org.araymond.joalcore.annotations.ddd.AggregateRoot;
|
||||||
import org.araymond.joalcore.core.infohash.domain.InfoHash;
|
import org.araymond.joalcore.core.metadata.domain.InfoHash;
|
||||||
import org.araymond.joalcore.core.sharing.domain.events.DoneDownloadingEvent;
|
import org.araymond.joalcore.core.sharing.domain.events.DoneDownloadingEvent;
|
||||||
import org.araymond.joalcore.core.sharing.domain.events.TorrentPausedEvent;
|
import org.araymond.joalcore.core.sharing.domain.events.TorrentPausedEvent;
|
||||||
import org.araymond.joalcore.core.sharing.domain.events.TorrentPeersChangedEvent;
|
import org.araymond.joalcore.core.sharing.domain.events.TorrentPeersChangedEvent;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.araymond.joalcore.core.sharing.domain;
|
package org.araymond.joalcore.core.sharing.domain;
|
||||||
|
|
||||||
import org.araymond.joalcore.core.infohash.domain.InfoHash;
|
import org.araymond.joalcore.core.metadata.domain.InfoHash;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.araymond.joalcore.core.fixtures;
|
package org.araymond.joalcore.core.fixtures;
|
||||||
|
|
||||||
import org.araymond.joalcore.core.infohash.domain.InfoHash;
|
import org.araymond.joalcore.core.metadata.domain.InfoHash;
|
||||||
import org.araymond.joalcore.core.sharing.domain.*;
|
import org.araymond.joalcore.core.sharing.domain.*;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.araymond.joalcore.core.infohash.domain;
|
package org.araymond.joalcore.core.metadata.domain;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package org.araymond.joalcore.core.sharing.application;
|
package org.araymond.joalcore.core.sharing.application;
|
||||||
|
|
||||||
import org.araymond.joalcore.core.fixtures.TestFixtures;
|
import org.araymond.joalcore.core.fixtures.TestFixtures;
|
||||||
import org.araymond.joalcore.core.infohash.domain.InfoHash;
|
import org.araymond.joalcore.core.metadata.domain.InfoHash;
|
||||||
import org.araymond.joalcore.core.metadata.domain.TorrentMetadata;
|
import org.araymond.joalcore.core.metadata.domain.TorrentMetadata;
|
||||||
|
import org.araymond.joalcore.core.metadata.domain.TorrentSize;
|
||||||
import org.araymond.joalcore.core.sharing.domain.*;
|
import org.araymond.joalcore.core.sharing.domain.*;
|
||||||
import org.araymond.joalcore.core.sharing.domain.events.*;
|
import org.araymond.joalcore.core.sharing.domain.events.*;
|
||||||
import org.araymond.joalcore.core.sharing.domain.services.PeerElection;
|
import org.araymond.joalcore.core.sharing.domain.services.PeerElection;
|
||||||
|
@ -18,17 +19,20 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
class SharedTorrentServiceTest {
|
class SharedTorrentServiceTest {
|
||||||
private InMemorySharedTorrentRepository repo;
|
private InMemorySharedTorrentRepository repo;
|
||||||
private FakePublisher publisher;
|
private FakePublisher publisher;
|
||||||
private PersistentStats persistentStats;
|
private OverallContributions overallContributions;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
this.repo = new InMemorySharedTorrentRepository();
|
this.repo = new InMemorySharedTorrentRepository();
|
||||||
this.publisher = new FakePublisher();
|
this.publisher = new FakePublisher();
|
||||||
this.persistentStats = new ZeroPersistentStats();
|
this.overallContributions = new ZeroOverallContributions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SharedTorrentService newService() {
|
public SharedTorrentService newService() {
|
||||||
return new SharedTorrentService(repo, publisher, () -> PeerElection.MOST_LEECHED, persistentStats);
|
return new SharedTorrentService(repo, publisher, overallContributions, new SharedTorrentConfig(
|
||||||
|
() -> PeerElection.MOST_LEECHED,
|
||||||
|
() -> false
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -36,7 +40,7 @@ class SharedTorrentServiceTest {
|
||||||
var service = newService();
|
var service = newService();
|
||||||
|
|
||||||
var infoHash = TestFixtures.randomInfoHash();
|
var infoHash = TestFixtures.randomInfoHash();
|
||||||
service.create(new TorrentMetadata(infoHash, 5000));
|
service.create(new TorrentMetadata(infoHash, TorrentSize.ofBytes(5000)));
|
||||||
|
|
||||||
assertThat(publisher.events).hasSizeGreaterThanOrEqualTo(1).first().isInstanceOf(TorrentCreatedEvent.class);
|
assertThat(publisher.events).hasSizeGreaterThanOrEqualTo(1).first().isInstanceOf(TorrentCreatedEvent.class);
|
||||||
var torrentId = ((TorrentCreatedEvent) publisher.events.getFirst()).sharedTorrentId();
|
var torrentId = ((TorrentCreatedEvent) publisher.events.getFirst()).sharedTorrentId();
|
||||||
|
@ -74,14 +78,14 @@ class SharedTorrentServiceTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class ZeroPersistentStats implements PersistentStats {
|
private static final class ZeroOverallContributions implements OverallContributions {
|
||||||
@Override
|
@Override
|
||||||
public Optional<Contribution> overallContributions(InfoHash infoHash) {
|
public Optional<Contribution> load(InfoHash infoHash) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void persistOverallContribution(InfoHash infoHash, Contribution contribution) {
|
public void save(InfoHash infoHash, Contribution contribution) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue