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

com.fluxtion.server.FluxtionServer Maven / Gradle / Ivy

/*
 * SPDX-FileCopyrightText: © 2024 Gregory Higgins 
 * SPDX-License-Identifier: AGPL-3.0-only
 *
 */

package com.fluxtion.server;

import com.fluxtion.agrona.ErrorHandler;
import com.fluxtion.agrona.concurrent.AgentRunner;
import com.fluxtion.agrona.concurrent.DynamicCompositeAgent;
import com.fluxtion.agrona.concurrent.IdleStrategy;
import com.fluxtion.agrona.concurrent.UnsafeBuffer;
import com.fluxtion.agrona.concurrent.status.AtomicCounter;
import com.fluxtion.runtime.StaticEventProcessor;
import com.fluxtion.runtime.annotations.feature.Experimental;
import com.fluxtion.runtime.audit.EventLogControlEvent;
import com.fluxtion.runtime.audit.LogRecordListener;
import com.fluxtion.runtime.service.Service;
import com.fluxtion.runtime.service.ServiceRegistryNode;
import com.fluxtion.server.config.*;
import com.fluxtion.server.dispatch.EventFlowService;
import com.fluxtion.server.dutycycle.ComposingEventProcessorAgent;
import com.fluxtion.server.dutycycle.ComposingServiceAgent;
import com.fluxtion.server.dutycycle.GlobalErrorHandler;
import com.fluxtion.server.dutycycle.ServiceAgent;
import com.fluxtion.server.service.scheduler.DeadWheelScheduler;
import lombok.SneakyThrows;
import lombok.Value;
import lombok.extern.java.Log;
import org.yaml.snakeyaml.Yaml;

import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

@Experimental
@Log
public class FluxtionServer {

    public static final String CONFIG_FILE_PROPERTY = "fluxtionserver.config.file";
    private final com.fluxtion.server.dispatch.EventFlowManager flowManager = new com.fluxtion.server.dispatch.EventFlowManager();
    private final ConcurrentHashMap composingEventProcessorAgents = new ConcurrentHashMap<>();
    private final ConcurrentHashMap composingServiceAgents = new ConcurrentHashMap<>();
    private final ConcurrentHashMap> registeredServices = new ConcurrentHashMap<>();
    private final Set> registeredAgentServices = ConcurrentHashMap.newKeySet();
    private ErrorHandler errorHandler = m -> log.severe(m.getMessage());
    private final ServiceRegistryNode serviceRegistry = new ServiceRegistryNode();

    public static FluxtionServer bootServer(Reader reader, LogRecordListener logRecordListener) {
        log.info("booting server loading config from reader");
        Yaml yaml = new Yaml();
        AppConfig appConfig = yaml.loadAs(reader, AppConfig.class);
        log.info("successfully loaded config from reader");

        return bootServer(appConfig, logRecordListener);
    }

    @SneakyThrows
    public static FluxtionServer bootServer(LogRecordListener logRecordListener) {
        String configFileName = System.getProperty(CONFIG_FILE_PROPERTY);
        Objects.requireNonNull(configFileName, "fluxtion config file must be specified by system property: " + CONFIG_FILE_PROPERTY);
        File configFile = new File(configFileName);
        log.info("booting fluxtion server with config file:" + configFile + " specified by system property:" + CONFIG_FILE_PROPERTY);
        try (FileReader reader = new FileReader(configFileName)) {
            return bootServer(reader, logRecordListener);
        }
    }

    public static FluxtionServer bootServer(AppConfig appConfig, LogRecordListener logRecordListener) {
        log.info("booting fluxtion server");
        log.fine("config:" + appConfig);
        FluxtionServer fluxtionServer = new FluxtionServer();
        fluxtionServer.setDefaultErrorHandler(new GlobalErrorHandler());

        //event sources
        if (appConfig.getEventSources() != null) {
            appConfig.getEventSources().forEach(fluxtionServer::registerEventSource);
        }

        //service
        if (appConfig.getServices() != null) {
            appConfig.getServices().stream()
                    .map(ServiceConfig::toService)
                    .forEach(fluxtionServer::registerService);
        }

        //service on workers

        if (appConfig.getAgentHostedServices() != null) {
            appConfig.getAgentHostedServices()
                    .forEach(server -> {
                        fluxtionServer.registerWorkerService(server.toServiceAgent());
                    });
        }

        //add market maker processors
        if (appConfig.getEventHandlerAgents() != null) {
            appConfig.getEventHandlerAgents().forEach(cfg -> {
                final EventLogControlEvent.LogLevel defaultLogLevel = cfg.getLogLevel() == null ? EventLogControlEvent.LogLevel.INFO : cfg.getLogLevel();
                String groupName = cfg.getAgentName();
                IdleStrategy ideIdleStrategy = cfg.getIdleStrategy();
                cfg.getEventHandlers().entrySet().forEach(handlerEntry -> {
                    fluxtionServer.addEventProcessor(groupName, ideIdleStrategy,
                            () -> {
                                String name = handlerEntry.getKey();
                                EventProcessorConfig eventProcessorConfig = handlerEntry.getValue();
                                var eventProcessor = eventProcessorConfig.getEventHandler();
                                var logLevel = eventProcessorConfig.getLogLevel() == null ? defaultLogLevel : eventProcessorConfig.getLogLevel();
                                @SuppressWarnings("unckecked")
                                ConfigMap configMap = eventProcessorConfig.getConfig();

                                eventProcessor.setAuditLogProcessor(logRecordListener);
                                eventProcessor.setAuditLogLevel(logLevel);
                                eventProcessor.init();

                                eventProcessor.consumeServiceIfExported(ConfigListener.class, l -> l.initialConfig(configMap));
                                return eventProcessor;
                            });
                });
            });
        }

        //start
        fluxtionServer.init();
        fluxtionServer.start();

        return fluxtionServer;
    }

    public void setDefaultErrorHandler(ErrorHandler errorHandler) {
        this.errorHandler = errorHandler;
    }

    public void registerEventMapperFactory(Supplier eventMapper, com.fluxtion.server.dispatch.CallBackType type) {
        log.info("registerEventMapperFactory:" + eventMapper);
        flowManager.registerEventMapperFactory(eventMapper, type);
    }

    public  void registerEventSource(String sourceName, com.fluxtion.server.dispatch.EventSource eventSource) {
        log.info("registerEventSource name:" + sourceName + " eventSource:" + eventSource);
        flowManager.registerEventSource(sourceName, eventSource);
    }

    public void registerService(Service... services) {
        for (Service service : services) {
            String serviceName = service.serviceName();
            log.info("registerService:" + service);
            if (registeredServices.containsKey(serviceName)) {
                throw new IllegalArgumentException("cannot register service name is already assigned:" + serviceName);
            }
            registeredServices.put(serviceName, service);
            Object instance = service.instance();
            if (instance instanceof com.fluxtion.server.dispatch.EventFlowService) {
                ((EventFlowService) instance).setEventFlowManager(flowManager, serviceName);
            }
        }
    }

    public void registerAgentService(Service... services) {
        registerService(services);
        registeredAgentServices.addAll(Arrays.asList(services));
    }

    public void registerWorkerService(ServiceAgent service) {
        String agentGroup = service.getAgentGroup();
        IdleStrategy idleStrategy = service.getIdleStrategy();
        ComposingWorkerServiceAgentRunner composingAgentRunner = composingServiceAgents.computeIfAbsent(
                agentGroup,
                ket -> {
                    //build a subscriber group
                    ComposingServiceAgent group = new ComposingServiceAgent(agentGroup, flowManager, this, new DeadWheelScheduler());
                    //threading to be configured by file
                    AtomicCounter errorCounter = new AtomicCounter(new UnsafeBuffer(new byte[4096]), 0);
                    //run subscriber group
                    AgentRunner groupRunner = new AgentRunner(
                            idleStrategy,
                            errorHandler,
                            errorCounter,
                            group);
                    return new ComposingWorkerServiceAgentRunner(group, groupRunner);
                });

        composingAgentRunner.getGroup().registerServer(service);
    }

    public void addEventProcessor(
            String groupName,
            IdleStrategy idleStrategy,
            Supplier feedConsumer) {
        ComposingAgentRunner composingAgentRunner = composingEventProcessorAgents.computeIfAbsent(
                groupName,
                ket -> {
                    //build a subscriber group
                    ComposingEventProcessorAgent group = new ComposingEventProcessorAgent(groupName, flowManager, this, new DeadWheelScheduler(), registeredServices);
                    //threading to be configured by file
//                    IdleStrategy idleStrategy = new SleepingMillisIdleStrategy(100);
                    AtomicCounter errorCounter = new AtomicCounter(new UnsafeBuffer(new byte[4096]), 0);
                    //run subscriber group
                    AgentRunner groupRunner = new AgentRunner(
                            idleStrategy,
                            errorHandler,
                            errorCounter,
                            group);
                    return new ComposingAgentRunner(group, groupRunner);
                });

        composingAgentRunner.getGroup().addEventFeedConsumer(feedConsumer);
    }

    public void init() {
        log.info("init");
        registeredServices.values().forEach(svc -> {
            if (!(svc.instance() instanceof com.fluxtion.server.dispatch.LifeCycleEventSource)) {
                svc.init();
            }
        });

        flowManager.init();

        registeredServices.values().forEach(svc -> {
            if (!(registeredAgentServices.contains(svc))) {
                serviceRegistry.nodeRegistered(svc.instance(), svc.serviceName());
                servicesRegistered().forEach(serviceRegistry::registerService);
            }
        });

    }

    @SneakyThrows
    public void start() {
        log.info("start");

        log.info("start registered services");
        registeredServices.values().forEach(svc -> {
            if (!(svc.instance() instanceof com.fluxtion.server.dispatch.LifeCycleEventSource)) {
                svc.start();
            }
        });

        log.info("start flowManager");
        flowManager.start();

        log.info("start service agent workers");
        composingServiceAgents.forEach((k, v) -> {
            log.info("starting composing service agent " + k);
            AgentRunner.startOnThread(v.getGroupRunner());
        });

        boolean waiting = true;
        while (waiting) {
            log.info("waiting for service agents to start");
            waiting = composingServiceAgents.values().stream()
                    .anyMatch(f -> f.group.status() != DynamicCompositeAgent.Status.ACTIVE);
            Thread.sleep(100);
        }

        log.info("start event processor agent workers");
        composingEventProcessorAgents.forEach((k, v) -> {
            log.info("starting composing event processor agent " + k);
            AgentRunner.startOnThread(v.getGroupRunner());
        });

    }

    public Collection> servicesRegistered() {
        return Collections.unmodifiableCollection(registeredServices.values());
    }

    @Value
    private static class ComposingAgentRunner {
        ComposingEventProcessorAgent group;
        AgentRunner groupRunner;
    }

    @Value
    private static class ComposingWorkerServiceAgentRunner {
        ComposingServiceAgent group;
        AgentRunner groupRunner;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy