From e0a4779ab61f7c5ca2b7ba833668e4c4b2dce062 Mon Sep 17 00:00:00 2001 From: Risto Lahtela <24460436+AuroraLS3@users.noreply.github.com> Date: Sun, 21 Nov 2021 15:49:10 +0200 Subject: [PATCH] Rewrote DataService implementation to always have identifiers. --- .../java/com/djrapitops/plan/DataService.java | 71 +++----- .../java/com/djrapitops/plan/DataSvc.java | 166 ++++++++---------- ...ataModule.java => DataPipelineModule.java} | 12 +- 3 files changed, 111 insertions(+), 138 deletions(-) rename Plan/common/src/main/java/com/djrapitops/plan/modules/{GatheringDataModule.java => DataPipelineModule.java} (70%) diff --git a/Plan/common/src/main/java/com/djrapitops/plan/DataService.java b/Plan/common/src/main/java/com/djrapitops/plan/DataService.java index 722bb20ba..d0f94875f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/DataService.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/DataService.java @@ -17,67 +17,46 @@ package com.djrapitops.plan; import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.transactions.Transaction; +import com.djrapitops.plan.utilities.java.TriConsumer; import java.util.Optional; -import java.util.function.Consumer; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; -/** - * Service for sourcing, mapping and consuming data. - *

- * The service is in charge of two data flows: - * - push, given to consumers - * - pull, obtained from sources - *

- * The mappers facilitate a one way type transformation if needed. - *

- * The interface is very abstract about how data is obtained, - * but here are my general ideas of where the abstraction is coming from. - * - push cause one or multiple consumers to modify stored data - * - pull cause one or multiple suppliers to fetch stored data - * - mappers are stateless type transformers in memory - *

- * Example use-case: - * - PlayerJoinEvent is mapped to a generic event - * - that generic event is then consumed and mapped until the data is in a database. - *

- * - Some kind of data is wanted to place on a web page - * - It is requested and the suppliers and mappers give the wanted type of data. - *

- * Further research needed: - * - Can this limited type system represent types of data that need parameters - * (such as differentiate between two servers data) - */ public interface DataService { - DataService push(Class type, M data); - - Optional pull(Class type); - - Optional pull(Class type, P parameter); - - B mapTo(Class toType, A from); - - default Optional pull(Class type, Class

parameterType) { - return pull(type, () -> pull(parameterType).orElse(null)); + default void push(K identifier, T value) { + push(identifier, value, (Class) value.getClass()); } - default Optional pull(Class type, Supplier

parameter) { - return pull(type, parameter.get()); - } + void push(K identifier, T value, Class type); - DataService registerMapper(Class typeA, Class typeB, Function mapper); + DataService registerMapper(Class identifierType, Class from, Class to, BiFunction mapper); - DataService registerConsumer(Class type, Consumer consumer); + DataService registerMapper(Class identifierType, Class from, Class to, Function mapper); - DataService registerSupplier(Class type, Supplier supplier); + DataService registerMapper(Class fromIdentifier, Class from, Class toIdentifier, Class to, TriConsumer> mapper); - DataService registerSupplier(Class type, Class

parameterType, Function supplierWithParameter); + DataService registerSink(Class identifierType, Class type, BiConsumer consumer); - DataService registerDBSupplier(Class type, Class

parameterType, Function> supplierWithParameter); + DataService registerDatabaseSink(Class identifierType, Class type, BiFunction consumer); - interface Mapping { + Optional pull(Class type, K identifier); + + Optional pull(Class type); + + DataService registerPullSource(Class identifierType, Class type, Function source); + + DataService registerDatabasePullSource(Class identifierType, Class type, Function> source); + + DataService registerPullSource(Class type, Supplier source); + + DataService registerDatabasePullSource(Class type, Supplier> source); + + interface Pipeline { void register(DataService service); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/DataSvc.java b/Plan/common/src/main/java/com/djrapitops/plan/DataSvc.java index 3f9f9b6ea..916d2b62b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/DataSvc.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/DataSvc.java @@ -18,121 +18,122 @@ package com.djrapitops.plan; import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.transactions.Transaction; +import com.djrapitops.plan.utilities.java.TriConsumer; import dagger.Lazy; import javax.inject.Inject; import javax.inject.Singleton; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Consumer; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; @Singleton public class DataSvc implements DataService { - private final MultiHashMap mappers; - private final MultiHashMap mappersReverse; - private final Map suppliers; - private final Map suppliersWithParameter; - private final MultiHashMap consumers; - private final Lazy dbSystem; + private final Map pullSources; + private final Map noIdentifierPullSources; + + private final MultiHashMap sinks; + + private final MultiHashMap mappers; + @Inject public DataSvc( Lazy dbSystem ) { this.dbSystem = dbSystem; + pullSources = new HashMap<>(); + noIdentifierPullSources = new HashMap<>(); + sinks = new MultiHashMap<>(); mappers = new MultiHashMap<>(); - mappersReverse = new MultiHashMap<>(); - suppliers = new ConcurrentHashMap<>(); - suppliersWithParameter = new ConcurrentHashMap<>(); - consumers = new MultiHashMap<>(); } @Override - public DataService push(Class type, A data) { - if (data == null) return this; - List mappers = this.mappers.get(type); - for (Mapper mapper : mappers) { - push(mapper.typeB, mapper.func.apply(data)); + public void push(K identifier, T value, Class type) { + ClassPair classPair = new ClassPair<>((Class) identifier.getClass(), type); + for (BiConsumer sink : sinks.get(classPair)) { + sink.accept(identifier, value); } - List consumers = this.consumers.get(type); - for (Consumer consumer : consumers) { - consumer.accept(data); - } - if (mappers.isEmpty() && consumers.isEmpty()) { - System.out.println("WARN: Nothing consumed " + type); + + for (Mapper mapper : mappers.get(classPair)) { + Class convertingTo = mapper.typeB; + push(identifier, mapper.func.apply(identifier, value), convertingTo); } + } + + @Override + public DataService registerMapper(Class identifierType, Class from, Class to, BiFunction mapper) { + ClassPair classPair = new ClassPair<>(identifierType, from); + mappers.putOne(classPair, new Mapper<>(from, to, mapper)); return this; } + @Override + public DataService registerMapper(Class identifierType, Class from, Class to, Function mapper) { + return registerMapper(identifierType, from, to, (id, value) -> mapper.apply(value)); + } + + @Override + public DataService registerMapper(Class fromIdentifier, Class from, Class toIdentifier, Class to, TriConsumer> mapper) { + ClassPair classPair = new ClassPair<>(fromIdentifier, from); + sinks.putOne(classPair, (id, value) -> mapper.accept(fromIdentifier.cast(id), from.cast(value), this::push)); + return this; + } + + @Override + public DataService registerSink(Class identifierType, Class type, BiConsumer consumer) { + ClassPair classPair = new ClassPair<>(identifierType, type); + sinks.putOne(classPair, consumer); + return this; + } + + @Override + public DataService registerDatabaseSink(Class identifierType, Class type, BiFunction consumer) { + return registerSink(identifierType, type, (id, value) -> dbSystem.get().getDatabase().executeTransaction(consumer.apply(id, value))); + } + + @Override + public Optional pull(Class type, K identifier) { + ClassPair classPair = new ClassPair<>((Class) identifier.getClass(), type); + return Optional.ofNullable(pullSources.get(classPair)) + .map(source -> source.apply(identifier)) + .map(type::cast); + } + @Override public Optional pull(Class type) { - Supplier present = this.suppliers.get(type); - if (present != null) return Optional.ofNullable(present.get()); - - List mappers = this.mappersReverse.get(type); - for (Mapper mapper : mappers) { - Optional found = pull(mapper.typeA).map(mapper.func); - if (found.isPresent()) return found; - } - - System.out.println("WARN: Nothing supplied " + type); - return Optional.empty(); + return Optional.ofNullable(noIdentifierPullSources.get(type)) + .map(Supplier::get) + .map(type::cast); } @Override - public B mapTo(Class toType, A from) { - List mappers = this.mappers.get(from.getClass()); - for (Mapper mapper : mappers) { - if (mapper.typeB.equals(toType)) { - return toType.cast(mapper.func); - } - } - // TODO Figure out type mapping resolution when it needs more than one mapping - System.out.println("WARN: No mapper for " + from.getClass() + " -> " + toType); - return null; - } - - @Override - public DataService registerMapper(Class typeA, Class typeB, Function mapper) { - Mapper asWrapper = new Mapper<>(typeA, typeB, mapper); - // TODO Prevent two mappers for same 2 types with a data structure - mappers.putOne(typeA, asWrapper); - mappersReverse.putOne(typeB, asWrapper); + public DataService registerPullSource(Class identifierType, Class type, Function source) { + ClassPair classPair = new ClassPair<>(identifierType, type); + pullSources.put(classPair, source); return this; } @Override - public DataService registerConsumer(Class type, Consumer consumer) { - consumers.putOne(type, consumer); + public DataService registerDatabasePullSource(Class identifierType, Class type, Function> source) { + return registerPullSource(identifierType, type, identifier -> dbSystem.get().getDatabase().query(source.apply(identifier))); + } + + @Override + public DataService registerPullSource(Class type, Supplier source) { + noIdentifierPullSources.put(type, source); return this; } @Override - public DataService registerSupplier(Class type, Supplier supplier) { - suppliers.put(type, supplier); - return this; - } - - @Override - public Optional pull(Class type, P parameter) { - if (parameter == null) return Optional.empty(); - Function function = suppliersWithParameter.get(new ClassPair<>(type, parameter.getClass())); - return function != null ? Optional.of(function.apply(parameter)) : Optional.empty(); - } - - @Override - public DataService registerSupplier(Class type, Class

parameterType, Function supplierWithParameter) { - suppliersWithParameter.put(new ClassPair<>(type, parameterType), supplierWithParameter); - return this; - } - - @Override - public DataService registerDBSupplier(Class type, Class

parameterType, Function> queryVisitor) { - return registerSupplier(type, parameterType, parameter -> dbSystem.get().getDatabase().query(queryVisitor.apply(parameter))); + public DataService registerDatabasePullSource(Class type, Supplier> source) { + return registerPullSource(type, () -> dbSystem.get().getDatabase().query(source.get())); } private static class ClassPair { @@ -159,16 +160,6 @@ public class DataSvc implements DataService { } } - private static class KeyValuePair { - final K key; - final V value; - - public KeyValuePair(K key, V value) { - this.key = key; - this.value = value; - } - } - private static class MultiHashMap extends ConcurrentHashMap> { void putOne(A key, B value) { @@ -176,15 +167,14 @@ public class DataSvc implements DataService { values.add(value); put(key, values); } - } - private static class Mapper { + private static class Mapper { final Class typeA; final Class typeB; - final Function func; + final BiFunction func; - public Mapper(Class typeA, Class typeB, Function func) { + public Mapper(Class typeA, Class typeB, BiFunction func) { this.typeA = typeA; this.typeB = typeB; this.func = func; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/modules/GatheringDataModule.java b/Plan/common/src/main/java/com/djrapitops/plan/modules/DataPipelineModule.java similarity index 70% rename from Plan/common/src/main/java/com/djrapitops/plan/modules/GatheringDataModule.java rename to Plan/common/src/main/java/com/djrapitops/plan/modules/DataPipelineModule.java index 482844fa9..92ed1062a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/modules/GatheringDataModule.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/modules/DataPipelineModule.java @@ -17,6 +17,7 @@ package com.djrapitops.plan.modules; import com.djrapitops.plan.DataService; +import com.djrapitops.plan.gathering.cache.SessionCache; import com.djrapitops.plan.gathering.domain.ActiveSession; import com.djrapitops.plan.gathering.domain.event.PlayerJoin; import dagger.Module; @@ -24,15 +25,18 @@ import dagger.Provides; import dagger.multibindings.IntoSet; import javax.inject.Singleton; +import java.util.UUID; -@Module -public class GatheringDataModule { +@Module // NOTE: not yet in use +public class DataPipelineModule { @Provides @Singleton @IntoSet - DataService.Mapping playerJoinToSession() { - return service -> service.registerMapper(PlayerJoin.class, ActiveSession.class, ActiveSession::fromPlayerJoin); + DataService.Pipeline playerJoinToSession(SessionCache sessionCache) { + return service -> service + .registerMapper(UUID.class, PlayerJoin.class, ActiveSession.class, ActiveSession::fromPlayerJoin) + .registerSink(UUID.class, ActiveSession.class, sessionCache::cacheSession); } }