All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.fluxcapacitor.javaclient.configuration.DefaultFluxCapacitor Maven / Gradle / Ivy

There is a newer version: 0.1015.0
Show newest version
/*
 * Copyright (c) 2016-2018 Flux Capacitor.
 *
 * Licensed 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 io.fluxcapacitor.javaclient.configuration;

import io.fluxcapacitor.common.MessageType;
import io.fluxcapacitor.common.handling.ParameterResolver;
import io.fluxcapacitor.javaclient.FluxCapacitor;
import io.fluxcapacitor.javaclient.common.caching.DefaultCache;
import io.fluxcapacitor.javaclient.common.metrics.ApplicationMonitor;
import io.fluxcapacitor.javaclient.common.serialization.DeserializingMessage;
import io.fluxcapacitor.javaclient.common.serialization.MessageSerializer;
import io.fluxcapacitor.javaclient.common.serialization.Serializer;
import io.fluxcapacitor.javaclient.common.serialization.jackson.JacksonSerializer;
import io.fluxcapacitor.javaclient.configuration.client.Client;
import io.fluxcapacitor.javaclient.eventsourcing.DefaultEventSourcing;
import io.fluxcapacitor.javaclient.eventsourcing.DefaultEventStore;
import io.fluxcapacitor.javaclient.eventsourcing.DefaultSnapshotRepository;
import io.fluxcapacitor.javaclient.eventsourcing.EventSourcing;
import io.fluxcapacitor.javaclient.eventsourcing.EventStore;
import io.fluxcapacitor.javaclient.eventsourcing.EventStoreSerializer;
import io.fluxcapacitor.javaclient.keyvalue.DefaultKeyValueStore;
import io.fluxcapacitor.javaclient.keyvalue.KeyValueStore;
import io.fluxcapacitor.javaclient.publishing.CommandGateway;
import io.fluxcapacitor.javaclient.publishing.DefaultCommandGateway;
import io.fluxcapacitor.javaclient.publishing.DefaultErrorGateway;
import io.fluxcapacitor.javaclient.publishing.DefaultEventGateway;
import io.fluxcapacitor.javaclient.publishing.DefaultGenericGateway;
import io.fluxcapacitor.javaclient.publishing.DefaultMetricsGateway;
import io.fluxcapacitor.javaclient.publishing.DefaultQueryGateway;
import io.fluxcapacitor.javaclient.publishing.DefaultRequestHandler;
import io.fluxcapacitor.javaclient.publishing.DefaultResultGateway;
import io.fluxcapacitor.javaclient.publishing.DispatchInterceptor;
import io.fluxcapacitor.javaclient.publishing.ErrorGateway;
import io.fluxcapacitor.javaclient.publishing.EventGateway;
import io.fluxcapacitor.javaclient.publishing.GenericGateway;
import io.fluxcapacitor.javaclient.publishing.MetricsGateway;
import io.fluxcapacitor.javaclient.publishing.QueryGateway;
import io.fluxcapacitor.javaclient.publishing.RequestHandler;
import io.fluxcapacitor.javaclient.publishing.ResultGateway;
import io.fluxcapacitor.javaclient.publishing.correlation.CorrelatingInterceptor;
import io.fluxcapacitor.javaclient.publishing.correlation.CorrelationDataProvider;
import io.fluxcapacitor.javaclient.publishing.correlation.MessageOriginProvider;
import io.fluxcapacitor.javaclient.publishing.routing.MessageRoutingInterceptor;
import io.fluxcapacitor.javaclient.scheduling.DefaultScheduler;
import io.fluxcapacitor.javaclient.scheduling.Scheduler;
import io.fluxcapacitor.javaclient.tracking.BatchInterceptor;
import io.fluxcapacitor.javaclient.tracking.ConsumerConfiguration;
import io.fluxcapacitor.javaclient.tracking.DefaultTracking;
import io.fluxcapacitor.javaclient.tracking.Tracking;
import io.fluxcapacitor.javaclient.tracking.TrackingException;
import io.fluxcapacitor.javaclient.tracking.handling.DefaultHandlerFactory;
import io.fluxcapacitor.javaclient.tracking.handling.DeserializingMessageParameterResolver;
import io.fluxcapacitor.javaclient.tracking.handling.HandleCommand;
import io.fluxcapacitor.javaclient.tracking.handling.HandleError;
import io.fluxcapacitor.javaclient.tracking.handling.HandleEvent;
import io.fluxcapacitor.javaclient.tracking.handling.HandleMetrics;
import io.fluxcapacitor.javaclient.tracking.handling.HandleNotification;
import io.fluxcapacitor.javaclient.tracking.handling.HandleQuery;
import io.fluxcapacitor.javaclient.tracking.handling.HandleResult;
import io.fluxcapacitor.javaclient.tracking.handling.HandleSchedule;
import io.fluxcapacitor.javaclient.tracking.handling.HandlerInterceptor;
import io.fluxcapacitor.javaclient.tracking.handling.MetadataParameterResolver;
import io.fluxcapacitor.javaclient.tracking.handling.PayloadParameterResolver;
import io.fluxcapacitor.javaclient.tracking.handling.validation.ValidatingInterceptor;
import io.fluxcapacitor.javaclient.tracking.metrics.HandlerMonitor;
import io.fluxcapacitor.javaclient.tracking.metrics.TrackerMonitor;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;

import java.lang.annotation.Annotation;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.UnaryOperator;

import static io.fluxcapacitor.common.MessageType.COMMAND;
import static io.fluxcapacitor.common.MessageType.ERROR;
import static io.fluxcapacitor.common.MessageType.EVENT;
import static io.fluxcapacitor.common.MessageType.METRICS;
import static io.fluxcapacitor.common.MessageType.QUERY;
import static io.fluxcapacitor.common.MessageType.RESULT;
import static io.fluxcapacitor.common.MessageType.SCHEDULE;
import static java.util.Arrays.stream;
import static java.util.Collections.singletonList;
import static java.util.Collections.unmodifiableMap;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;

@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class DefaultFluxCapacitor implements FluxCapacitor {

    private final Map trackingSupplier;
    private final CommandGateway commandGateway;
    private final QueryGateway queryGateway;
    private final EventGateway eventGateway;
    private final ResultGateway resultGateway;
    private final ErrorGateway errorGateway;
    private final MetricsGateway metricsGateway;
    private final EventSourcing eventSourcing;
    private final KeyValueStore keyValueStore;
    private final Scheduler scheduler;
    private final Client client;

    public static Builder builder() {
        return new Builder();
    }

    @Override
    public EventSourcing eventSourcing() {
        return eventSourcing;
    }

    @Override
    public Scheduler scheduler() {
        return scheduler;
    }

    @Override
    public KeyValueStore keyValueStore() {
        return keyValueStore;
    }

    @Override
    public CommandGateway commandGateway() {
        return commandGateway;
    }

    @Override
    public QueryGateway queryGateway() {
        return queryGateway;
    }

    @Override
    public EventGateway eventGateway() {
        return eventGateway;
    }

    @Override
    public ResultGateway resultGateway() {
        return resultGateway;
    }

    @Override
    public ErrorGateway errorGateway() {
        return errorGateway;
    }

    @Override
    public MetricsGateway metricsGateway() {
        return metricsGateway;
    }

    @Override
    public Client client() {
        return client;
    }

    @Override
    public Tracking tracking(MessageType messageType) {
        return Optional.ofNullable(trackingSupplier.get(messageType)).orElseThrow(
                () -> new TrackingException(String.format("Tracking is not supported for type %s", messageType)));
    }

    public static class Builder implements FluxCapacitorBuilder {

        private Serializer serializer = new JacksonSerializer();
        private Serializer snapshotSerializer = serializer;
        private final Map> consumerConfigurations = defaultConfigurations();
        private final List> handlerParameterResolvers =
                defaultHandlerParameterResolvers();
        private final Map dispatchInterceptors =
                Arrays.stream(MessageType.values()).collect(toMap(identity(), m -> f -> f));
        private final Map handlerInterceptors =
                Arrays.stream(MessageType.values()).collect(toMap(identity(), m -> (f, h, c) -> f));
        private final Set correlationDataProviders = new LinkedHashSet<>();
        private DispatchInterceptor messageRoutingInterceptor = new MessageRoutingInterceptor();
        private HandlerInterceptor commandValidationInterceptor = new ValidatingInterceptor();
        private boolean disableMessageCorrelation;
        private boolean disableCommandValidation;
        private boolean collectTrackingMetrics;
        private boolean collectApplicationMetrics;

        protected List> defaultHandlerParameterResolvers() {
            return new ArrayList<>(Arrays.asList(new PayloadParameterResolver(), new MetadataParameterResolver(), new DeserializingMessageParameterResolver()));
        }

        protected Map> defaultConfigurations() {
            return unmodifiableMap(stream(MessageType.values()).collect(toMap(identity(), messageType ->
                    new ArrayList<>(singletonList(ConsumerConfiguration.getDefault(messageType))))));
        }

        @Override
        public Builder serializer(Serializer serializer) {
            if (snapshotSerializer == this.serializer) {
                snapshotSerializer = serializer;
            }
            this.serializer = serializer;
            return this;
        }

        @Override
        public Builder snapshotSerializer(Serializer serializer) {
            this.snapshotSerializer = serializer;
            return this;
        }

        @Override
        public Builder configureDefaultConsumer(MessageType messageType,
                                                UnaryOperator updateFunction) {
            List configurations = consumerConfigurations.get(messageType);
            ConsumerConfiguration defaultConfiguration = configurations.get(configurations.size() - 1);
            configurations.set(configurations.size() - 1, updateFunction.apply(defaultConfiguration));
            return this;
        }

        @Override
        public Builder addConsumerConfiguration(MessageType messageType, ConsumerConfiguration consumerConfiguration) {
            List configurations = consumerConfigurations.get(messageType);
            configurations.add(configurations.size() - 1, consumerConfiguration);
            return this;
        }

        @Override
        public Builder addHandlerParameterResolver(ParameterResolver parameterResolver) {
            handlerParameterResolvers.add(parameterResolver);
            return this;
        }

        @Override
        public Builder addDispatchInterceptor(DispatchInterceptor interceptor, MessageType... forTypes) {
            Arrays.stream(forTypes.length == 0 ? MessageType.values() : forTypes)
                    .forEach(type -> dispatchInterceptors.compute(type, (t, i) -> i.merge(interceptor)));
            return this;
        }

        @Override
        public Builder addHandlerInterceptor(HandlerInterceptor interceptor, MessageType... forTypes) {
            Arrays.stream(forTypes.length == 0 ? MessageType.values() : forTypes)
                    .forEach(type -> handlerInterceptors.compute(type, (t, i) -> i.merge(interceptor)));
            return this;
        }

        @Override
        public Builder addCorrelationDataProvider(CorrelationDataProvider dataProvider) {
            correlationDataProviders.add(dataProvider);
            return this;
        }

        @Override
        public Builder changeMessageRoutingInterceptor(DispatchInterceptor messageRoutingInterceptor) {
            this.messageRoutingInterceptor = messageRoutingInterceptor;
            return this;
        }

        @Override
        public Builder disableMessageCorrelation() {
            disableMessageCorrelation = true;
            return this;
        }

        @Override
        public Builder disableCommandValidation() {
            disableCommandValidation = true;
            return this;
        }

        @Override
        public FluxCapacitorBuilder collectTrackingMetrics() {
            collectTrackingMetrics = true;
            return this;
        }

        @Override
        public FluxCapacitorBuilder collectApplicationMetrics() {
            collectApplicationMetrics = true;
            return this;
        }

        @Override
        public Builder changeCommandValidationInterceptor(HandlerInterceptor validationInterceptor) {
            this.commandValidationInterceptor = validationInterceptor;
            return this;
        }

        @Override
        public FluxCapacitor build(Client client) {
            Map dispatchInterceptors = new HashMap<>(this.dispatchInterceptors);
            Map handlerInterceptors = new HashMap<>(this.handlerInterceptors);
            Map> consumerConfigurations =
                    new HashMap<>(this.consumerConfigurations);

            //enable message routing
            Arrays.stream(MessageType.values())
                    .forEach(type -> dispatchInterceptors.compute(type, (t, i) -> i.merge(messageRoutingInterceptor)));

            //enable message correlation
            if (!disableMessageCorrelation) {
                Set dataProviders = new LinkedHashSet<>(this.correlationDataProviders);
                dataProviders.add(new MessageOriginProvider());
                CorrelatingInterceptor correlatingInterceptor = new CorrelatingInterceptor(dataProviders);
                Arrays.stream(MessageType.values()).forEach(type -> {
                    dispatchInterceptors.compute(type, (t, i) -> correlatingInterceptor.merge(i));
                    handlerInterceptors.compute(type, (t, i) -> correlatingInterceptor.merge(i));
                });
            }

            //enable command validation
            if (!disableCommandValidation) {
                handlerInterceptors.compute(COMMAND, (t, i) -> i.merge(commandValidationInterceptor));
            }

            //collect metrics about consumers and handlers
            if (collectTrackingMetrics) {
                BatchInterceptor batchInterceptor = new TrackerMonitor();
                HandlerMonitor handlerMonitor = new HandlerMonitor();
                Arrays.stream(MessageType.values()).forEach(type -> {
                    consumerConfigurations.compute(type, (t, list) ->
                            t == METRICS ? list : list.stream().map(c -> c.toBuilder().trackingConfiguration(
                                    c.getTrackingConfiguration().toBuilder().batchInterceptor(batchInterceptor).build())
                                    .build()).collect(toList()));
                    handlerInterceptors.compute(type, (t, i) -> t == METRICS ? i : handlerMonitor.merge(i));
                });
            }

            //event sourcing
            EventStore eventStore = new DefaultEventStore(client.getEventStoreClient(),
                                                          new EventStoreSerializer(serializer,
                                                                                   dispatchInterceptors.get(EVENT)));
            DefaultSnapshotRepository snapshotRepository =
                    new DefaultSnapshotRepository(client.getKeyValueClient(), snapshotSerializer);
            DefaultEventSourcing eventSourcing =
                    new DefaultEventSourcing(eventStore, snapshotRepository, new DefaultCache());

            //register event sourcing as the outermost handler interceptor
            handlerInterceptors.compute(COMMAND, (t, i) -> i.merge(eventSourcing));

            //create gateways
            ResultGateway resultGateway =
                    new DefaultResultGateway(client.getGatewayClient(RESULT),
                                             new MessageSerializer(serializer, dispatchInterceptors.get(RESULT),
                                                                   RESULT));
            RequestHandler requestHandler =
                    new DefaultRequestHandler(client.getTrackingClient(RESULT), serializer, client.id());
            CommandGateway commandGateway =
                    new DefaultCommandGateway(createGenericGateway(client, COMMAND, requestHandler,
                                                                   dispatchInterceptors.get(COMMAND),
                                                                   new DefaultHandlerFactory(COMMAND,
                                                                                             handlerInterceptors.get(COMMAND),
                                                                                             handlerParameterResolvers)));
            QueryGateway queryGateway =
                    new DefaultQueryGateway(createGenericGateway(client, QUERY, requestHandler,
                                                                 dispatchInterceptors.get(QUERY),
                                                                 new DefaultHandlerFactory(QUERY,
                                                                                           handlerInterceptors.get(QUERY),
                                                                                           handlerParameterResolvers)));
            EventGateway eventGateway =
                    new DefaultEventGateway(createGenericGateway(client, EVENT, requestHandler,
                                                                 dispatchInterceptors.get(EVENT),
                                                                 new DefaultHandlerFactory(EVENT,
                                                                                           handlerInterceptors.get(EVENT),
                                                                                           handlerParameterResolvers)));
            ErrorGateway errorGateway =
                    new DefaultErrorGateway(client.getGatewayClient(ERROR), new MessageSerializer(
                            serializer, dispatchInterceptors.get(ERROR), ERROR));

            MetricsGateway metricsGateway =
                    new DefaultMetricsGateway(client.getGatewayClient(METRICS), new MessageSerializer(
                            serializer, dispatchInterceptors.get(METRICS), METRICS));
            

            //tracking
            Map trackingMap = stream(MessageType.values())
                    .collect(toMap(identity(),
                                   m -> new DefaultTracking(m, getHandlerAnnotation(m), client.getTrackingClient(m),
                                                            resultGateway, errorGateway,
                                                            consumerConfigurations.get(m), serializer,
                                                            handlerInterceptors.get(m), handlerParameterResolvers)));

            //misc
            KeyValueStore keyValueStore = new DefaultKeyValueStore(client.getKeyValueClient(), serializer);
            Scheduler scheduler = new DefaultScheduler(client.getSchedulingClient(),
                                                       new MessageSerializer(serializer,
                                                                             dispatchInterceptors.get(SCHEDULE),
                                                                             SCHEDULE));

            //and finally...
            FluxCapacitor fluxCapacitor = doBuild(trackingMap, commandGateway, queryGateway, eventGateway,
                                                  resultGateway, errorGateway, metricsGateway, eventSourcing,
                                                  keyValueStore, scheduler, client);

            //collect application metrics
            if (collectApplicationMetrics) {
                ApplicationMonitor.start(fluxCapacitor, Duration.ofSeconds(1));
            }

            return fluxCapacitor;
        }

        protected FluxCapacitor doBuild(Map trackingSupplier,
                                        CommandGateway commandGateway, QueryGateway queryGateway,
                                        EventGateway eventGateway, ResultGateway resultGateway,
                                        ErrorGateway errorGateway,
                                        MetricsGateway metricsGateway, EventSourcing eventSourcing,
                                        KeyValueStore keyValueStore,
                                        Scheduler scheduler, Client client) {
            return new DefaultFluxCapacitor(trackingSupplier, commandGateway, queryGateway, eventGateway, resultGateway,
                                            errorGateway, metricsGateway, eventSourcing, keyValueStore, scheduler,
                                            client);
        }

        protected Class getHandlerAnnotation(MessageType messageType) {
            switch (messageType) {
                case COMMAND:
                    return HandleCommand.class;
                case EVENT:
                    return HandleEvent.class;
                case NOTIFICATION:
                    return HandleNotification.class;
                case QUERY:
                    return HandleQuery.class;
                case RESULT:
                    return HandleResult.class;
                case ERROR:
                    return HandleError.class;
                case SCHEDULE:
                    return HandleSchedule.class;
                case METRICS:
                    return HandleMetrics.class;
                default:
                    throw new ConfigurationException(String.format("Unrecognized type: %s", messageType));
            }
        }

        protected GenericGateway createGenericGateway(Client client, MessageType messageType,
                                                      RequestHandler requestHandler,
                                                      DispatchInterceptor dispatchInterceptor,
                                                      DefaultHandlerFactory handlerFactory) {
            return new DefaultGenericGateway(messageType, client.getGatewayClient(messageType), requestHandler,
                                             new MessageSerializer(serializer, dispatchInterceptor, messageType),
                                             handlerFactory);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy