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

com.mx.path.model.mdx.web.SpringMdxGatewayManager Maven / Gradle / Ivy

The newest version!
package com.mx.path.model.mdx.web;

import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

import lombok.Getter;
import lombok.Setter;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import com.mx.path.core.common.collection.ObjectMap;
import com.mx.path.core.common.serialization.ObjectMapYamlDeserializer;
import com.mx.path.gateway.api.Gateway;
import com.mx.path.gateway.api.GatewayConfigurator;
import com.mx.path.gateway.configuration.Configurator;
import com.mx.path.gateway.configuration.ConfiguratorObserver;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.YamlMapFactoryBean;
import org.springframework.beans.factory.config.YamlProcessor;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

/**
 * Launches gateway configuration using Spring application initialize hooks.
 *
 * 

After initialization, contains registry of gateways by client id. * *

Reads configuration from gateway.yaml. */ @Component public class SpringMdxGatewayManager { private static final List> BEFORE_INITIALIZATION_CONSUMERS = new ArrayList<>(); private static final String[] STATIC_CONFIG_PATHS = { "gateway.json", "resources/gateway.json", "src/main/resources/gateway.json", "gateway.yaml", "gateway.yml", "resources/gateway.yaml", "resources/gateway.yml", "src/main/resources/gateway.yaml", "src/main/resources/gateway.yml", }; private static final HashMap STATIC_ENVIRONMENT_MAP = new HashMap() { { put("int", "integration"); put("sand", "sandbox"); } }; /** * Ordered list of possible paths to the gateway configuration file. */ private static String[] configPaths = STATIC_CONFIG_PATHS; private static final Logger LOGGER = LoggerFactory.getLogger(SpringMdxGatewayManager.class); private static Map gateways = null; private static String profiles; private static boolean initialized = false; private static Consumer gatewayFileContentPreProcessor; private static GatewayConfigurator configurator = new GatewayConfigurator(); public static ObjectMap describe() { ObjectMap description = new ObjectMap(); gateways.forEach((clientId, gateway) -> { gateway.describe(description.createMap(clientId)); }); return description; } public static List getActiveProfiles() { return Arrays.asList(profiles.split(",")); } public static Gateway getClientGateway(String clientId) { if (gateways == null) { throw new MdxWebApplicationException("Configuration error. Gateway configuration is incomplete"); } return gateways.get(clientId); } public static ConfiguratorObserver getConfiguratorObserver() { return configurator.getObserver(); } /** * Register a block of code to run after the Gateways and Facilities are initialized * * @param consumer block of code to execute * @throws MdxWebApplicationException if attempting to register a new consumer after initialization has already occurred. * * @deprecated Use {@link #getConfiguratorObserver()} to register consumer. Will be removed in next major version */ @Deprecated public static void registerAfterInitialized(BiConsumer, Map> consumer) { if (initialized) { throw new MdxWebApplicationException("Initialization order issue. Calling registerAfterInitialized after gateways already initialized."); } getConfiguratorObserver().registerGatewaysInitialized(consumer); } /** * Register listener object annotated with {@link com.google.common.eventbus.Subscribe} annotations * * @param listener listener object * * @deprecated Use {@link #getConfiguratorObserver()} to register listeners. Will be removed in next major version */ @Deprecated public static void registerAfterInitializedListener(Object listener) { if (initialized) { throw new MdxWebApplicationException("Initialization order issue. Calling registerListener after gateways already initialized."); } getConfiguratorObserver().registerListener(listener); } /** * Register consumer that will be fired after Spring context is loaded and before GatewayConfigurator is initiated. * * @param consumer */ public static void registerBeforeInitialization(Consumer consumer) { BEFORE_INITIALIZATION_CONSUMERS.add(consumer); } /** * Register block of code that is called after the gateway file contents are located and loaded, but before they are * processed by the configurator. * *

This can be used to apply pre-processing to the content of the gateway configuration file. * * @param gatewayConfigurationFileContextConsumer consumer block */ public static void registerGatewayFileContentPreprocessor(Consumer gatewayConfigurationFileContextConsumer) { gatewayFileContentPreProcessor = gatewayConfigurationFileContextConsumer; } /** * FOR TESTING PURPOSES ONLY * *

This class is loaded by Spring. If reset is called on it, there is no way for Spring to know it needs to be reinitialized. */ public static void reset() { configurator = new GatewayConfigurator(); gateways = null; profiles = null; initialized = false; setConfigPaths(STATIC_CONFIG_PATHS); gatewayFileContentPreProcessor = null; BEFORE_INITIALIZATION_CONSUMERS.clear(); } /** * Use {@link #reset()} */ @Deprecated public static void resetAfterInitialized() { initialized = false; } @SuppressFBWarnings("EI_EXPOSE_STATIC_REP2") public static void setConfigPaths(String[] configPaths) { SpringMdxGatewayManager.configPaths = configPaths; } @Value("${spring.profiles.active}") public final void setActiveProfiles(String activeProfiles) { profiles = activeProfiles; initializeFromProfiles(); } /** * Finds the gateway file in given possible locations. * * @return Path to gateway file or NULL */ public final Path configPath() { return Arrays.stream(configPaths).sequential().map(Paths::get).filter(Files::exists).findFirst().orElse(null); } public final List compiledConfigPaths() { AtomicBoolean baseFound = new AtomicBoolean(false); List returnPaths = new ArrayList<>(); getActiveProfiles().forEach(profile -> { String profileKey = STATIC_ENVIRONMENT_MAP.getOrDefault(profile, profile); String basePath = "environments/" + profileKey + ".gateway"; String partialPath = "environments/partial." + profileKey + ".gateway"; Path baseConfig = Arrays.stream(configPaths).sequential() .filter(p -> p.lastIndexOf("gateway") >= 0) .map(p -> p.replace("gateway", basePath)) .map(Paths::get).filter(Files::exists).findFirst().orElse(null); Path partial = Arrays.stream(configPaths).sequential() .filter(p -> p.lastIndexOf("gateway") >= 0) .map(p -> p.replace("gateway", partialPath)) .map(Paths::get).filter(Files::exists).findFirst().orElse(null); if (baseConfig != null) { baseFound.set(true); returnPaths.add(baseConfig); } if (partial != null) { returnPaths.add(partial); } }); // If no base config is found, fall back to non-compiled if (!baseFound.get()) { return new ArrayList<>(); } return returnPaths; } private void initializeFromProfiles() { // Notify consumers that initialization is starting BEFORE_INITIALIZATION_CONSUMERS.forEach(consumer -> consumer.accept(configurator)); GatewayConfigurationFileContext gatewayConfigurationFileContext = buildConfigFile(); // Hand off configuration file to pre-processor block if it is present if (gatewayFileContentPreProcessor != null) { gatewayFileContentPreProcessor.accept(gatewayConfigurationFileContext); } if (gatewayConfigurationFileContext.isJson()) { gateways = configurator.buildFromJson(gatewayConfigurationFileContext.contents); } else if (gatewayConfigurationFileContext.isYaml()) { gateways = configurator.buildFromYaml(gatewayConfigurationFileContext.contents); } else { throw new RuntimeException("Invalid gateway configuration format: must be JSON or YAML"); } gateways.forEach((clientId, gateway) -> { LOGGER.info("Starting service for " + clientId); gateway.startServices(); LOGGER.info("Registering remotes for " + clientId); gateway.registerRemotes(); }); initialized = true; } private GatewayConfigurationFileContext buildConfigFile() { Path configPath = configPath(); List compiledConfigPaths = compiledConfigPaths(); boolean compiledExists = !compiledConfigPaths.isEmpty(); if (configPath == null && !compiledExists) { throw new RuntimeException("Gateway configuration file does not exist"); } if (compiledExists) { compiledConfigPaths.forEach(path -> LOGGER.info("COMPILED: Loading gateway configuration from " + path)); } else { LOGGER.info("Loading gateway configuration from " + configPath); } String fileContents = compiledExists ? buildMergedConfigString(compiledConfigPaths) : readConfigFile(configPath); String path = compiledExists ? compiledConfigPaths.get(0).toString() : configPath.toString(); return new GatewayConfigurationFileContext(fileContents, path, getActiveProfiles(), SpringMdxGatewayManager.configurator); } private String buildMergedConfigString(List paths) { List resources = new ArrayList<>(); ObjectMapYamlDeserializer yamlSerializer = new ObjectMapYamlDeserializer(); YamlMapFactoryBean factory = new YamlMapFactoryBean(); factory.setResolutionMethod(YamlProcessor.ResolutionMethod.OVERRIDE_AND_IGNORE); paths.forEach(path -> resources.add(new FileSystemResource(path.toString()))); factory.setResources(resources.toArray(new Resource[0])); Map yamlValueMap = factory.getObject(); return yamlSerializer.toYaml(yamlValueMap); } private String readConfigFile(Path configPath) { String fileContents; try { fileContents = new String(Files.readAllBytes(configPath), StandardCharsets.UTF_8); } catch (Exception e) { throw new RuntimeException("Unable to read " + configPath, e); } return fileContents; } public static class GatewayConfigurationFileContext { @Getter @Setter private String contents; @Getter private final String path; @Getter private final List activeProfiles; @Getter private final GatewayConfigurator configurator; GatewayConfigurationFileContext(String contents, String path, List activeProfile, GatewayConfigurator configurator) { this.contents = contents; this.path = path; this.activeProfiles = activeProfile; this.configurator = configurator; } public final boolean isYaml() { return this.path.endsWith(".yaml") || this.path.endsWith(".yml"); } public final boolean isJson() { return this.path.endsWith(".json"); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy