All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
de.quantummaid.httpmaid.usecases.UseCasesModule Maven / Gradle / Ivy
/*
* Copyright (c) 2020 Richard Hauswald - https://quantummaid.de/.
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package de.quantummaid.httpmaid.usecases;
import de.quantummaid.eventmaid.internal.collections.filtermap.FilterMapBuilder;
import de.quantummaid.eventmaid.internal.collections.predicatemap.PredicateMapBuilder;
import de.quantummaid.eventmaid.mapping.Demapifier;
import de.quantummaid.eventmaid.mapping.Mapifier;
import de.quantummaid.eventmaid.messagebus.MessageBus;
import de.quantummaid.eventmaid.processingcontext.EventType;
import de.quantummaid.eventmaid.serializedmessagebus.SerializedMessageBus;
import de.quantummaid.eventmaid.usecases.usecaseadapter.LowLevelUseCaseAdapterBuilder;
import de.quantummaid.eventmaid.usecases.usecaseadapter.UseCaseAdapter;
import de.quantummaid.httpmaid.HttpMaidChains;
import de.quantummaid.httpmaid.PerRouteConfigurator;
import de.quantummaid.httpmaid.chains.*;
import de.quantummaid.httpmaid.events.Event;
import de.quantummaid.httpmaid.events.EventFactory;
import de.quantummaid.httpmaid.events.EventModule;
import de.quantummaid.httpmaid.generator.GenerationCondition;
import de.quantummaid.httpmaid.handler.distribution.DistributableHandler;
import de.quantummaid.httpmaid.handler.distribution.HandlerDistributors;
import de.quantummaid.httpmaid.serialization.Serializer;
import de.quantummaid.httpmaid.startupchecks.StartupChecks;
import de.quantummaid.httpmaid.usecases.instantiation.UseCaseInstantiator;
import de.quantummaid.httpmaid.usecases.instantiation.UseCaseInstantiatorFactory;
import de.quantummaid.httpmaid.usecases.method.UseCaseMethod;
import de.quantummaid.httpmaid.usecases.serializing.SerializationAndDeserializationProvider;
import de.quantummaid.httpmaid.usecases.serializing.UseCaseSerializationAndDeserialization;
import de.quantummaid.httpmaid.websockets.broadcast.Broadcasters;
import de.quantummaid.reflectmaid.resolvedtype.ResolvedType;
import de.quantummaid.reflectmaid.GenericType;
import de.quantummaid.reflectmaid.ReflectMaid;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import java.util.*;
import static de.quantummaid.eventmaid.internal.collections.filtermap.FilterMapBuilder.filterMapBuilder;
import static de.quantummaid.eventmaid.internal.collections.predicatemap.PredicateMapBuilder.predicateMapBuilder;
import static de.quantummaid.eventmaid.mapping.ExceptionMapifier.defaultExceptionMapifier;
import static de.quantummaid.eventmaid.processingcontext.EventType.eventTypeFromString;
import static de.quantummaid.eventmaid.usecases.usecaseadapter.LowLevelUseCaseAdapterBuilder.aLowLevelUseCaseInvocationBuilder;
import static de.quantummaid.httpmaid.CoreModule.REFLECT_MAID;
import static de.quantummaid.httpmaid.chains.MetaDataKey.metaDataKey;
import static de.quantummaid.httpmaid.events.EventModule.MESSAGE_BUS;
import static de.quantummaid.httpmaid.events.EventModule.eventModule;
import static de.quantummaid.httpmaid.handler.distribution.DistributableHandler.distributableHandler;
import static de.quantummaid.httpmaid.handler.distribution.HandlerDistributors.HANDLER_DISTRIBUTORS;
import static de.quantummaid.httpmaid.serialization.Serializer.SERIALIZER;
import static de.quantummaid.httpmaid.startupchecks.StartupChecks.STARTUP_CHECKS;
import static de.quantummaid.httpmaid.usecases.RegisterSerializerProcessor.registerSerializerProcessor;
import static de.quantummaid.httpmaid.usecases.eventfactories.MultipleParametersEventFactory.multipleParametersEventFactory;
import static de.quantummaid.httpmaid.usecases.eventfactories.SingleParameterEventFactory.singleParameterEventFactory;
import static de.quantummaid.httpmaid.usecases.instantiation.ZeroArgumentsConstructorUseCaseInstantiator.zeroArgumentsConstructorUseCaseInstantiator;
import static de.quantummaid.httpmaid.usecases.method.UseCaseMethod.useCaseMethodOf;
import static de.quantummaid.httpmaid.util.Validators.validateNotNull;
import static de.quantummaid.httpmaid.websockets.broadcast.Broadcasters.BROADCASTERS;
import static de.quantummaid.reflectmaid.GenericType.fromResolvedType;
import static de.quantummaid.reflectmaid.GenericType.genericType;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
@ToString
@EqualsAndHashCode
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings("java:S1905")
public final class UseCasesModule implements ChainModule {
public static final MetaDataKey SERIALIZED_MESSAGE_BUS = metaDataKey("SERIALIZED_MESSAGE_BUS");
private SerializationAndDeserializationProvider serializationAndDeserializationProvider;
private UseCaseInstantiatorFactory useCaseInstantiatorFactory;
private final Map useCaseToEventMappings = new HashMap<>();
private final List useCaseMethods = new ArrayList<>();
public static UseCasesModule useCasesModule() {
return new UseCasesModule();
}
public void setUseCaseInstantiatorFactory(final UseCaseInstantiatorFactory useCaseInstantiatorFactory) {
this.useCaseInstantiatorFactory = useCaseInstantiatorFactory;
}
public void addUseCaseToEventMapping(final ResolvedType useCaseClass,
final EventType eventType) {
validateNotNull(useCaseClass, "useCaseClass");
validateNotNull(eventType, "eventType");
useCaseToEventMappings.put(useCaseClass, eventType);
}
public void setSerializationAndDeserializationProvider(final SerializationAndDeserializationProvider serializationAndDeserializationProvider) {
this.serializationAndDeserializationProvider = serializationAndDeserializationProvider;
}
@Override
public List supplyModulesIfNotAlreadyPresent() {
return singletonList(eventModule());
}
@Override
public void init(final MetaData configurationMetaData) {
final ReflectMaid reflectMaid = configurationMetaData.get(REFLECT_MAID);
final HandlerDistributors handlerDistributors = configurationMetaData.get(HANDLER_DISTRIBUTORS);
handlerDistributors.register(handler -> handler.handler() instanceof GenericType, handler -> {
final GenericType> useCaseClass = (GenericType>) handler.handler();
final ResolvedType resolvedUseCaseClass = reflectMaid.resolve(useCaseClass);
return registerUseCase(resolvedUseCaseClass, handler.condition(), handler.perRouteConfigurators());
});
handlerDistributors.register(handler -> handler.handler() instanceof Class, handler -> {
final Class> clazz = (Class>) handler.handler();
final GenericType> useCaseClass = genericType(clazz);
final ResolvedType resolvedUseCaseClass = reflectMaid.resolve(useCaseClass);
return registerUseCase(resolvedUseCaseClass, handler.condition(), handler.perRouteConfigurators());
});
}
private List registerUseCase(final ResolvedType useCaseType,
final GenerationCondition condition,
final List perRouteConfigurators) {
final EventType eventType = eventTypeFromString(useCaseType.description());
useCaseToEventMappings.put(useCaseType, eventType);
useCaseMethods.add(useCaseMethodOf(useCaseType));
final DistributableHandler eventHandler = distributableHandler(condition, eventType, perRouteConfigurators);
return singletonList(eventHandler);
}
@Override
public void configure(final DependencyRegistry dependencyRegistry) {
final EventModule eventModule = dependencyRegistry.getDependency(EventModule.class);
useCaseMethods.forEach(useCaseMethod -> {
final ResolvedType useCaseClass = useCaseMethod.useCaseClass();
final EventType eventType = useCaseToEventMappings.get(useCaseClass);
final EventFactory eventFactory = buildEventFactory(useCaseMethod);
eventModule.setEventFactoryFor(eventType, eventFactory);
});
}
@Override
public void register(final ChainExtender extender) {
final Broadcasters broadcasters = extender.getMetaDatum(BROADCASTERS);
final List> injectionTypes = broadcasters.injectionTypes();
final List messageTypes = broadcasters.messageTypes();
final UseCaseSerializationAndDeserialization serializationAndDeserialization =
serializationAndDeserializationProvider.provide(useCaseMethods, injectionTypes, messageTypes);
final Serializer returnValueSerializer = serializationAndDeserialization.returnValueSerializer();
extender.appendProcessor(HttpMaidChains.INIT,
registerSerializerProcessor(returnValueSerializer));
extender.addMetaDatum(SERIALIZER, returnValueSerializer);
final List> useCaseClasses = useCaseMethods.stream()
.map(UseCaseMethod::useCaseClass)
.map(ResolvedType::assignableType)
.collect(toList());
if (useCaseInstantiatorFactory == null) {
final ReflectMaid reflectMaid = extender.getMetaDatum(REFLECT_MAID);
useCaseInstantiatorFactory = types -> zeroArgumentsConstructorUseCaseInstantiator(reflectMaid);
}
final UseCaseInstantiator useCaseInstantiator = useCaseInstantiatorFactory.createInstantiator(useCaseClasses);
final StartupChecks startupChecks = extender.getMetaDatum(STARTUP_CHECKS);
final LowLevelUseCaseAdapterBuilder adapterBuilder = createAdapterBuilder();
useCaseMethods.forEach(useCaseMethod -> registerUseCaseMethod(
useCaseMethod, adapterBuilder, serializationAndDeserialization, startupChecks, useCaseInstantiator
));
adapterBuilder.setUseCaseInstantiator(new de.quantummaid.eventmaid.usecases.usecaseadapter.usecaseinstantiating.UseCaseInstantiator() {
@Override
public T instantiate(final Class type) {
final GenericType genericType = genericType(type);
return useCaseInstantiator.instantiate(genericType);
}
});
final PredicateMapBuilder> exceptionSerializers = predicateMapBuilder();
exceptionSerializers.setDefaultValue(defaultExceptionMapifier());
adapterBuilder.setExceptionSerializers(exceptionSerializers);
final UseCaseAdapter useCaseAdapter = adapterBuilder.build();
final MessageBus messageBus = extender.getMetaDatum(MESSAGE_BUS);
final SerializedMessageBus serializedMessageBus = useCaseAdapter.attachAndEnhance(messageBus);
extender.addMetaDatum(SERIALIZED_MESSAGE_BUS, serializedMessageBus);
}
private void registerUseCaseMethod(final UseCaseMethod useCaseMethod,
final LowLevelUseCaseAdapterBuilder adapterBuilder,
final UseCaseSerializationAndDeserialization serializationAndDeserialization,
final StartupChecks startupChecks,
final UseCaseInstantiator useCaseInstantiator) {
final ResolvedType useCaseClass = useCaseMethod.useCaseClass();
final EventType eventType = useCaseToEventMappings.get(useCaseClass);
adapterBuilder.addUseCase(useCaseClass.assignableType(), eventType, (useCase, untypedEvent, callingContext) -> {
final Event event = (Event) untypedEvent;
final Map parameters = serializationAndDeserialization.deserializeParameters(event, useCaseClass);
final Optional returnValue = useCaseMethod.invoke(useCase, parameters, event);
return returnValue
.map(object -> serializationAndDeserialization.serializeReturnValue(object, useCaseMethod.returnType().orElseThrow()))
.orElse(null);
});
startupChecks.addStartupCheck(() -> useCaseInstantiator.check(fromResolvedType(useCaseClass)));
}
private static LowLevelUseCaseAdapterBuilder createAdapterBuilder() {
final LowLevelUseCaseAdapterBuilder adapterBuilder = aLowLevelUseCaseInvocationBuilder();
adapterBuilder.setRequestSerializers(failingPredicateMap());
adapterBuilder.setRequestDeserializers(failingFilterMap());
adapterBuilder.setReseponseSerializers(failingPredicateMap());
adapterBuilder.setResponseDeserializers(failingFilterMap());
return adapterBuilder;
}
private static FilterMapBuilder, Object, Demapifier>> failingFilterMap() {
final FilterMapBuilder, Object, Demapifier>> filterMap = filterMapBuilder();
filterMap.setDefaultValue((targetType, map) -> {
throw new UnsupportedOperationException();
});
return filterMap;
}
private static PredicateMapBuilder> failingPredicateMap() {
final PredicateMapBuilder> predicateMap = predicateMapBuilder();
predicateMap.setDefaultValue(object -> {
throw new UnsupportedOperationException();
});
return predicateMap;
}
private static EventFactory buildEventFactory(final UseCaseMethod useCaseMethod) {
if (useCaseMethod.isSingleParameterUseCase()) {
final String name = useCaseMethod.singleParameterName();
return singleParameterEventFactory(name);
} else {
final List parameterNames = useCaseMethod.parameterNames();
return multipleParametersEventFactory(parameterNames);
}
}
}