mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-09-30 07:27:36 +02:00
Rewrote DataService implementation to always have identifiers.
This commit is contained in:
parent
8fbf4bb161
commit
e0a4779ab6
@ -17,67 +17,46 @@
|
|||||||
package com.djrapitops.plan;
|
package com.djrapitops.plan;
|
||||||
|
|
||||||
import com.djrapitops.plan.storage.database.queries.Query;
|
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.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.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
|
||||||
* Service for sourcing, mapping and consuming data.
|
|
||||||
* <p>
|
|
||||||
* The service is in charge of two data flows:
|
|
||||||
* - push, given to consumers
|
|
||||||
* - pull, obtained from sources
|
|
||||||
* <p>
|
|
||||||
* The mappers facilitate a one way type transformation if needed.
|
|
||||||
* <p>
|
|
||||||
* 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
|
|
||||||
* <p>
|
|
||||||
* 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.
|
|
||||||
* <p>
|
|
||||||
* - 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.
|
|
||||||
* <p>
|
|
||||||
* 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 {
|
public interface DataService {
|
||||||
|
|
||||||
<M> DataService push(Class<M> type, M data);
|
default <K, T> void push(K identifier, T value) {
|
||||||
|
push(identifier, value, (Class<T>) value.getClass());
|
||||||
<S> Optional<S> pull(Class<S> type);
|
|
||||||
|
|
||||||
<S, P> Optional<S> pull(Class<S> type, P parameter);
|
|
||||||
|
|
||||||
<A, B> B mapTo(Class<B> toType, A from);
|
|
||||||
|
|
||||||
default <S, P> Optional<S> pull(Class<S> type, Class<P> parameterType) {
|
|
||||||
return pull(type, () -> pull(parameterType).orElse(null));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default <S, P> Optional<S> pull(Class<S> type, Supplier<P> parameter) {
|
<K, T> void push(K identifier, T value, Class<T> type);
|
||||||
return pull(type, parameter.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
<A, B> DataService registerMapper(Class<A> typeA, Class<B> typeB, Function<A, B> mapper);
|
<K, A, B> DataService registerMapper(Class<K> identifierType, Class<A> from, Class<B> to, BiFunction<K, A, B> mapper);
|
||||||
|
|
||||||
<M> DataService registerConsumer(Class<M> type, Consumer<M> consumer);
|
<K, A, B> DataService registerMapper(Class<K> identifierType, Class<A> from, Class<B> to, Function<A, B> mapper);
|
||||||
|
|
||||||
<S> DataService registerSupplier(Class<S> type, Supplier<S> supplier);
|
<K1, K2, A, B> DataService registerMapper(Class<K1> fromIdentifier, Class<A> from, Class<K2> toIdentifier, Class<B> to, TriConsumer<K1, A, BiConsumer<K2, B>> mapper);
|
||||||
|
|
||||||
<P, S> DataService registerSupplier(Class<S> type, Class<P> parameterType, Function<P, S> supplierWithParameter);
|
<K, T> DataService registerSink(Class<K> identifierType, Class<T> type, BiConsumer<K, T> consumer);
|
||||||
|
|
||||||
<P, S> DataService registerDBSupplier(Class<S> type, Class<P> parameterType, Function<P, Query<S>> supplierWithParameter);
|
<K, T> DataService registerDatabaseSink(Class<K> identifierType, Class<T> type, BiFunction<K, T, Transaction> consumer);
|
||||||
|
|
||||||
interface Mapping {
|
<K, T> Optional<T> pull(Class<T> type, K identifier);
|
||||||
|
|
||||||
|
<T> Optional<T> pull(Class<T> type);
|
||||||
|
|
||||||
|
<K, T> DataService registerPullSource(Class<K> identifierType, Class<T> type, Function<K, T> source);
|
||||||
|
|
||||||
|
<K, T> DataService registerDatabasePullSource(Class<K> identifierType, Class<T> type, Function<K, Query<T>> source);
|
||||||
|
|
||||||
|
<T> DataService registerPullSource(Class<T> type, Supplier<T> source);
|
||||||
|
|
||||||
|
<T> DataService registerDatabasePullSource(Class<T> type, Supplier<Query<T>> source);
|
||||||
|
|
||||||
|
interface Pipeline {
|
||||||
void register(DataService service);
|
void register(DataService service);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,121 +18,122 @@ package com.djrapitops.plan;
|
|||||||
|
|
||||||
import com.djrapitops.plan.storage.database.DBSystem;
|
import com.djrapitops.plan.storage.database.DBSystem;
|
||||||
import com.djrapitops.plan.storage.database.queries.Query;
|
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 dagger.Lazy;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
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.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class DataSvc implements DataService {
|
public class DataSvc implements DataService {
|
||||||
|
|
||||||
private final MultiHashMap<Class, Mapper> mappers;
|
|
||||||
private final MultiHashMap<Class, Mapper> mappersReverse;
|
|
||||||
private final Map<Class, Supplier> suppliers;
|
|
||||||
private final Map<ClassPair, Function> suppliersWithParameter;
|
|
||||||
private final MultiHashMap<Class, Consumer> consumers;
|
|
||||||
|
|
||||||
private final Lazy<DBSystem> dbSystem;
|
private final Lazy<DBSystem> dbSystem;
|
||||||
|
|
||||||
|
private final Map<ClassPair, Function> pullSources;
|
||||||
|
private final Map<Class, Supplier> noIdentifierPullSources;
|
||||||
|
|
||||||
|
private final MultiHashMap<ClassPair, BiConsumer> sinks;
|
||||||
|
|
||||||
|
private final MultiHashMap<ClassPair, Mapper> mappers;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public DataSvc(
|
public DataSvc(
|
||||||
Lazy<DBSystem> dbSystem
|
Lazy<DBSystem> dbSystem
|
||||||
) {
|
) {
|
||||||
this.dbSystem = dbSystem;
|
this.dbSystem = dbSystem;
|
||||||
|
pullSources = new HashMap<>();
|
||||||
|
noIdentifierPullSources = new HashMap<>();
|
||||||
|
sinks = new MultiHashMap<>();
|
||||||
mappers = new MultiHashMap<>();
|
mappers = new MultiHashMap<>();
|
||||||
mappersReverse = new MultiHashMap<>();
|
|
||||||
suppliers = new ConcurrentHashMap<>();
|
|
||||||
suppliersWithParameter = new ConcurrentHashMap<>();
|
|
||||||
consumers = new MultiHashMap<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <A> DataService push(Class<A> type, A data) {
|
public <K, T> void push(K identifier, T value, Class<T> type) {
|
||||||
if (data == null) return this;
|
ClassPair<K, T> classPair = new ClassPair<>((Class<K>) identifier.getClass(), type);
|
||||||
List<Mapper> mappers = this.mappers.get(type);
|
for (BiConsumer<K, T> sink : sinks.get(classPair)) {
|
||||||
for (Mapper mapper : mappers) {
|
sink.accept(identifier, value);
|
||||||
push(mapper.typeB, mapper.func.apply(data));
|
|
||||||
}
|
}
|
||||||
List<Consumer> consumers = this.consumers.get(type);
|
|
||||||
for (Consumer<A> consumer : consumers) {
|
for (Mapper mapper : mappers.get(classPair)) {
|
||||||
consumer.accept(data);
|
Class convertingTo = mapper.typeB;
|
||||||
}
|
push(identifier, mapper.func.apply(identifier, value), convertingTo);
|
||||||
if (mappers.isEmpty() && consumers.isEmpty()) {
|
|
||||||
System.out.println("WARN: Nothing consumed " + type);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <K, A, B> DataService registerMapper(Class<K> identifierType, Class<A> from, Class<B> to, BiFunction<K, A, B> mapper) {
|
||||||
|
ClassPair<K, A> classPair = new ClassPair<>(identifierType, from);
|
||||||
|
mappers.putOne(classPair, new Mapper<>(from, to, mapper));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <K, A, B> DataService registerMapper(Class<K> identifierType, Class<A> from, Class<B> to, Function<A, B> mapper) {
|
||||||
|
return registerMapper(identifierType, from, to, (id, value) -> mapper.apply(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <K1, K2, A, B> DataService registerMapper(Class<K1> fromIdentifier, Class<A> from, Class<K2> toIdentifier, Class<B> to, TriConsumer<K1, A, BiConsumer<K2, B>> mapper) {
|
||||||
|
ClassPair<K1, A> classPair = new ClassPair<>(fromIdentifier, from);
|
||||||
|
sinks.putOne(classPair, (id, value) -> mapper.accept(fromIdentifier.cast(id), from.cast(value), this::push));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <K, T> DataService registerSink(Class<K> identifierType, Class<T> type, BiConsumer<K, T> consumer) {
|
||||||
|
ClassPair<K, T> classPair = new ClassPair<>(identifierType, type);
|
||||||
|
sinks.putOne(classPair, consumer);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <K, T> DataService registerDatabaseSink(Class<K> identifierType, Class<T> type, BiFunction<K, T, Transaction> consumer) {
|
||||||
|
return registerSink(identifierType, type, (id, value) -> dbSystem.get().getDatabase().executeTransaction(consumer.apply(id, value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <K, T> Optional<T> pull(Class<T> type, K identifier) {
|
||||||
|
ClassPair<K, T> classPair = new ClassPair<>((Class<K>) identifier.getClass(), type);
|
||||||
|
return Optional.ofNullable(pullSources.get(classPair))
|
||||||
|
.map(source -> source.apply(identifier))
|
||||||
|
.map(type::cast);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Optional<T> pull(Class<T> type) {
|
public <T> Optional<T> pull(Class<T> type) {
|
||||||
Supplier<T> present = this.suppliers.get(type);
|
return Optional.ofNullable(noIdentifierPullSources.get(type))
|
||||||
if (present != null) return Optional.ofNullable(present.get());
|
.map(Supplier::get)
|
||||||
|
.map(type::cast);
|
||||||
List<Mapper> mappers = this.mappersReverse.get(type);
|
|
||||||
for (Mapper mapper : mappers) {
|
|
||||||
Optional<T> found = pull(mapper.typeA).map(mapper.func);
|
|
||||||
if (found.isPresent()) return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("WARN: Nothing supplied " + type);
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <A, B> B mapTo(Class<B> toType, A from) {
|
public <K, T> DataService registerPullSource(Class<K> identifierType, Class<T> type, Function<K, T> source) {
|
||||||
List<Mapper> mappers = this.mappers.get(from.getClass());
|
ClassPair<K, T> classPair = new ClassPair<>(identifierType, type);
|
||||||
for (Mapper mapper : mappers) {
|
pullSources.put(classPair, source);
|
||||||
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 <A, B> DataService registerMapper(Class<A> typeA, Class<B> typeB, Function<A, B> mapper) {
|
|
||||||
Mapper<A, B> 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);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <A> DataService registerConsumer(Class<A> type, Consumer<A> consumer) {
|
public <K, T> DataService registerDatabasePullSource(Class<K> identifierType, Class<T> type, Function<K, Query<T>> source) {
|
||||||
consumers.putOne(type, consumer);
|
return registerPullSource(identifierType, type, identifier -> dbSystem.get().getDatabase().query(source.apply(identifier)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> DataService registerPullSource(Class<T> type, Supplier<T> source) {
|
||||||
|
noIdentifierPullSources.put(type, source);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <A> DataService registerSupplier(Class<A> type, Supplier<A> supplier) {
|
public <T> DataService registerDatabasePullSource(Class<T> type, Supplier<Query<T>> source) {
|
||||||
suppliers.put(type, supplier);
|
return registerPullSource(type, () -> dbSystem.get().getDatabase().query(source.get()));
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <S, P> Optional<S> pull(Class<S> type, P parameter) {
|
|
||||||
if (parameter == null) return Optional.empty();
|
|
||||||
Function<P, S> function = suppliersWithParameter.get(new ClassPair<>(type, parameter.getClass()));
|
|
||||||
return function != null ? Optional.of(function.apply(parameter)) : Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <P, S> DataService registerSupplier(Class<S> type, Class<P> parameterType, Function<P, S> supplierWithParameter) {
|
|
||||||
suppliersWithParameter.put(new ClassPair<>(type, parameterType), supplierWithParameter);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <P, S> DataService registerDBSupplier(Class<S> type, Class<P> parameterType, Function<P, Query<S>> queryVisitor) {
|
|
||||||
return registerSupplier(type, parameterType, parameter -> dbSystem.get().getDatabase().query(queryVisitor.apply(parameter)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ClassPair<A, B> {
|
private static class ClassPair<A, B> {
|
||||||
@ -159,16 +160,6 @@ public class DataSvc implements DataService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class KeyValuePair<K, V> {
|
|
||||||
final K key;
|
|
||||||
final V value;
|
|
||||||
|
|
||||||
public KeyValuePair(K key, V value) {
|
|
||||||
this.key = key;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class MultiHashMap<A, B> extends ConcurrentHashMap<A, List<B>> {
|
private static class MultiHashMap<A, B> extends ConcurrentHashMap<A, List<B>> {
|
||||||
|
|
||||||
void putOne(A key, B value) {
|
void putOne(A key, B value) {
|
||||||
@ -176,15 +167,14 @@ public class DataSvc implements DataService {
|
|||||||
values.add(value);
|
values.add(value);
|
||||||
put(key, values);
|
put(key, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Mapper<A, B> {
|
private static class Mapper<K, A, B> {
|
||||||
final Class<A> typeA;
|
final Class<A> typeA;
|
||||||
final Class<B> typeB;
|
final Class<B> typeB;
|
||||||
final Function<A, B> func;
|
final BiFunction<K, A, B> func;
|
||||||
|
|
||||||
public Mapper(Class<A> typeA, Class<B> typeB, Function<A, B> func) {
|
public Mapper(Class<A> typeA, Class<B> typeB, BiFunction<K, A, B> func) {
|
||||||
this.typeA = typeA;
|
this.typeA = typeA;
|
||||||
this.typeB = typeB;
|
this.typeB = typeB;
|
||||||
this.func = func;
|
this.func = func;
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package com.djrapitops.plan.modules;
|
package com.djrapitops.plan.modules;
|
||||||
|
|
||||||
import com.djrapitops.plan.DataService;
|
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.ActiveSession;
|
||||||
import com.djrapitops.plan.gathering.domain.event.PlayerJoin;
|
import com.djrapitops.plan.gathering.domain.event.PlayerJoin;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
@ -24,15 +25,18 @@ import dagger.Provides;
|
|||||||
import dagger.multibindings.IntoSet;
|
import dagger.multibindings.IntoSet;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
@Module
|
@Module // NOTE: not yet in use
|
||||||
public class GatheringDataModule {
|
public class DataPipelineModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@IntoSet
|
@IntoSet
|
||||||
DataService.Mapping playerJoinToSession() {
|
DataService.Pipeline playerJoinToSession(SessionCache sessionCache) {
|
||||||
return service -> service.registerMapper(PlayerJoin.class, ActiveSession.class, ActiveSession::fromPlayerJoin);
|
return service -> service
|
||||||
|
.registerMapper(UUID.class, PlayerJoin.class, ActiveSession.class, ActiveSession::fromPlayerJoin)
|
||||||
|
.registerSink(UUID.class, ActiveSession.class, sessionCache::cacheSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user