diff --git a/README.md b/README.md index 8929e98a9..07f0a973e 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,10 @@ import com.exactpro.th2.common.schema.factory.CommonFactory Then you will create an instance of imported class, by choosing one of the following options: -1. Create factory with configs from the default path (`/var/th2/config/*`): +1. Create factory with configs from the `th2.common.configuration-directory` environment variable or default path (`/var/th2/config/*`): ``` var factory = CommonFactory(); ``` -1. Create factory with configs from the specified file paths: - ``` - var factory = CommonFactory(rabbitMQ, routerMQ, routerGRPC, cradle, custom, prometheus, dictionariesDir); - ``` 1. Create factory with configs from the specified arguments: ``` var factory = CommonFactory.createFromArguments(args); @@ -34,7 +30,8 @@ Then you will create an instance of imported class, by choosing one of the follo * --dictionariesDir - path to the directory which contains files with the encoded dictionaries * --prometheusConfiguration - path to json file with configuration for prometheus metrics server * --boxConfiguration - path to json file with boxes configuration and information - * -c/--configs - folder with json files for schemas configurations with special names: + * -c/--configs - folder with json files for schemas configurations with special names. + If you doesn't specify -c/--configs common factory uses configs from the `th2.common.configuration-directory` environment variable or default path (`/var/th2/config/*`) 1. rabbitMq.json - configuration for RabbitMQ 2. mq.json - configuration for MessageRouter @@ -494,6 +491,11 @@ dependencies { ## Release notes +### 5.5.0-dev + +#### Changed: ++ Provided the ability to define configs directory using the `th2.common.configuration-directory` environment variable + ### 5.4.2-dev #### Fix @@ -919,7 +921,7 @@ dependencies { ### 3.13.0 + reads dictionaries from the /var/th2/config/dictionary folder. -+ uses mq_router, grpc_router, cradle_manager optional JSON configs from the /var/th2/config folder ++ uses mq_router, grpc_router, cradle_manager optional JSON configs from the `/var/th2/config` folder ### 3.11.0 diff --git a/build.gradle b/build.gradle index 78630087b..802991382 100644 --- a/build.gradle +++ b/build.gradle @@ -258,6 +258,9 @@ dependencies { testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5' testImplementation "org.testcontainers:testcontainers:1.17.1" testImplementation "org.testcontainers:rabbitmq:1.17.1" + testImplementation("org.junit-pioneer:junit-pioneer:2.1.0") { + because("system property tests") + } testFixturesImplementation group: 'org.jetbrains.kotlin', name: 'kotlin-test-junit5', version: kotlin_version testFixturesImplementation "org.junit.jupiter:junit-jupiter:${junitVersion}" diff --git a/gradle.properties b/gradle.properties index b876306b8..2eae5e594 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -release_version=5.4.2 +release_version=5.5.0 description='th2 common library (Java)' vcs_url=https://github.com/th2-net/th2-common-j kapt.include.compile.classpath=false diff --git a/src/main/java/com/exactpro/th2/common/schema/factory/AbstractCommonFactory.java b/src/main/java/com/exactpro/th2/common/schema/factory/AbstractCommonFactory.java index d316c5129..4cc7e554f 100644 --- a/src/main/java/com/exactpro/th2/common/schema/factory/AbstractCommonFactory.java +++ b/src/main/java/com/exactpro/th2/common/schema/factory/AbstractCommonFactory.java @@ -104,8 +104,8 @@ public abstract class AbstractCommonFactory implements AutoCloseable { * @deprecated please use {@link #LOG4J_PROPERTIES_DEFAULT_PATH} */ @Deprecated - protected static final String LOG4J_PROPERTIES_DEFAULT_PATH_OLD = "/home/etc"; - protected static final String LOG4J_PROPERTIES_DEFAULT_PATH = "/var/th2/config"; + protected static final Path LOG4J_PROPERTIES_DEFAULT_PATH_OLD = Path.of("/home/etc"); + protected static final Path LOG4J_PROPERTIES_DEFAULT_PATH = Path.of("/var/th2/config"); protected static final String LOG4J2_PROPERTIES_NAME = "log4j2.properties"; public static final ObjectMapper MAPPER = new ObjectMapper(); @@ -168,71 +168,6 @@ public AbstractCommonFactory(FactorySettings settings) { stringSubstitutor = new StringSubstitutor(key -> defaultIfBlank(settings.getVariables().get(key), System.getenv(key))); } - /** - * Create factory with default implementation schema classes - * - * @deprecated Please use {@link AbstractCommonFactory#AbstractCommonFactory(FactorySettings)} - */ - @Deprecated(since = "4.0.0", forRemoval = true) - public AbstractCommonFactory() { - this(new FactorySettings()); - } - - /** - * Create factory with non-default implementations schema classes - * - * @param messageRouterParsedBatchClass Class for {@link MessageRouter} which work with {@link MessageBatch} - * @param messageRouterRawBatchClass Class for {@link MessageRouter} which work with {@link RawMessageBatch} - * @param eventBatchRouterClass Class for {@link MessageRouter} which work with {@link EventBatch} - * @param grpcRouterClass Class for {@link GrpcRouter} - * @deprecated Please use {@link AbstractCommonFactory#AbstractCommonFactory(FactorySettings)} - */ - @Deprecated(since = "4.0.0", forRemoval = true) - public AbstractCommonFactory( - @NotNull Class> messageRouterParsedBatchClass, - @NotNull Class> messageRouterRawBatchClass, - @NotNull Class> messageRouterMessageGroupBatchClass, - @NotNull Class> eventBatchRouterClass, - @NotNull Class grpcRouterClass - ) { - this(new FactorySettings() - .messageRouterParsedBatchClass(messageRouterParsedBatchClass) - .messageRouterRawBatchClass(messageRouterRawBatchClass) - .messageRouterMessageGroupBatchClass(messageRouterMessageGroupBatchClass) - .eventBatchRouterClass(eventBatchRouterClass) - .grpcRouterClass(grpcRouterClass) - ); - } - - /** - * Create factory with non-default implementations schema classes - * - * @param messageRouterParsedBatchClass Class for {@link MessageRouter} which work with {@link MessageBatch} - * @param messageRouterRawBatchClass Class for {@link MessageRouter} which work with {@link RawMessageBatch} - * @param eventBatchRouterClass Class for {@link MessageRouter} which work with {@link EventBatch} - * @param grpcRouterClass Class for {@link GrpcRouter} - * @param environmentVariables map with additional environment variables - * @deprecated Please use {@link AbstractCommonFactory#AbstractCommonFactory(FactorySettings)} - */ - @Deprecated(since = "4.0.0", forRemoval = true) - protected AbstractCommonFactory( - @NotNull Class> messageRouterParsedBatchClass, - @NotNull Class> messageRouterRawBatchClass, - @NotNull Class> messageRouterMessageGroupBatchClass, - @NotNull Class> eventBatchRouterClass, - @NotNull Class grpcRouterClass, - @NotNull Map environmentVariables - ) { - this(new FactorySettings() - .messageRouterParsedBatchClass(messageRouterParsedBatchClass) - .messageRouterRawBatchClass(messageRouterRawBatchClass) - .messageRouterMessageGroupBatchClass(messageRouterMessageGroupBatchClass) - .eventBatchRouterClass(eventBatchRouterClass) - .grpcRouterClass(grpcRouterClass) - .variables(environmentVariables) - ); - } - public void start() { DefaultExports.initialize(); PrometheusConfiguration prometheusConfiguration = loadPrometheusConfiguration(); @@ -855,8 +790,8 @@ public void close() { LOGGER.info("Common factory has been closed"); } - protected static void configureLogger(String... paths) { - List listPath = new ArrayList<>(); + protected static void configureLogger(Path... paths) { + List listPath = new ArrayList<>(); listPath.add(LOG4J_PROPERTIES_DEFAULT_PATH); listPath.add(LOG4J_PROPERTIES_DEFAULT_PATH_OLD); listPath.addAll(Arrays.asList(requireNonNull(paths, "Paths can't be null"))); diff --git a/src/main/java/com/exactpro/th2/common/schema/factory/CommonFactory.java b/src/main/java/com/exactpro/th2/common/schema/factory/CommonFactory.java index 4374acfbf..8fe4353c3 100644 --- a/src/main/java/com/exactpro/th2/common/schema/factory/CommonFactory.java +++ b/src/main/java/com/exactpro/th2/common/schema/factory/CommonFactory.java @@ -16,28 +16,19 @@ package com.exactpro.th2.common.schema.factory; import com.exactpro.cradle.cassandra.CassandraStorageSettings; -import com.exactpro.th2.common.grpc.EventBatch; -import com.exactpro.th2.common.grpc.MessageBatch; -import com.exactpro.th2.common.grpc.MessageGroupBatch; -import com.exactpro.th2.common.grpc.RawMessageBatch; import com.exactpro.th2.common.metrics.PrometheusConfiguration; import com.exactpro.th2.common.schema.box.configuration.BoxConfiguration; import com.exactpro.th2.common.schema.configuration.ConfigurationManager; import com.exactpro.th2.common.schema.cradle.CradleConfidentialConfiguration; import com.exactpro.th2.common.schema.cradle.CradleNonConfidentialConfiguration; import com.exactpro.th2.common.schema.dictionary.DictionaryType; -import com.exactpro.th2.common.schema.event.EventBatchRouter; import com.exactpro.th2.common.schema.grpc.configuration.GrpcConfiguration; import com.exactpro.th2.common.schema.grpc.configuration.GrpcRouterConfiguration; import com.exactpro.th2.common.schema.grpc.router.GrpcRouter; -import com.exactpro.th2.common.schema.grpc.router.impl.DefaultGrpcRouter; import com.exactpro.th2.common.schema.message.MessageRouter; import com.exactpro.th2.common.schema.message.configuration.MessageRouterConfiguration; import com.exactpro.th2.common.schema.message.impl.rabbitmq.configuration.ConnectionManagerConfiguration; import com.exactpro.th2.common.schema.message.impl.rabbitmq.configuration.RabbitMQConfiguration; -import com.exactpro.th2.common.schema.message.impl.rabbitmq.group.RabbitMessageGroupBatchRouter; -import com.exactpro.th2.common.schema.message.impl.rabbitmq.parsed.RabbitParsedBatchRouter; -import com.exactpro.th2.common.schema.message.impl.rabbitmq.raw.RabbitRawBatchRouter; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ConfigMapList; import io.fabric8.kubernetes.api.model.Secret; @@ -66,6 +57,7 @@ import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.Base64; import java.util.HashMap; @@ -79,7 +71,7 @@ import static com.exactpro.th2.common.schema.util.ArchiveUtils.getGzipBase64StringDecoder; import static java.util.Collections.emptyMap; import static java.util.Objects.requireNonNull; -import static java.util.Objects.requireNonNullElse; +import static java.util.Objects.requireNonNullElseGet; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; /** @@ -87,23 +79,25 @@ */ public class CommonFactory extends AbstractCommonFactory { - private static final Path CONFIG_DEFAULT_PATH = Path.of("/var/th2/config/"); - - private static final String RABBIT_MQ_FILE_NAME = "rabbitMQ.json"; - private static final String ROUTER_MQ_FILE_NAME = "mq.json"; - private static final String GRPC_FILE_NAME = "grpc.json"; - private static final String ROUTER_GRPC_FILE_NAME = "grpc_router.json"; - private static final String CRADLE_CONFIDENTIAL_FILE_NAME = "cradle.json"; - private static final String PROMETHEUS_FILE_NAME = "prometheus.json"; - private static final String CUSTOM_FILE_NAME = "custom.json"; - private static final String BOX_FILE_NAME = "box.json"; - private static final String CONNECTION_MANAGER_CONF_FILE_NAME = "mq_router.json"; - private static final String CRADLE_NON_CONFIDENTIAL_FILE_NAME = "cradle_manager.json"; + public static final String TH2_COMMON_SYSTEM_PROPERTY = "th2.common"; + public static final String TH2_COMMON_CONFIGURATION_DIRECTORY_SYSTEM_PROPERTY = TH2_COMMON_SYSTEM_PROPERTY + '.' + "configuration-directory"; + static final Path CONFIG_DEFAULT_PATH = Path.of("/var/th2/config/"); + + static final String RABBIT_MQ_FILE_NAME = "rabbitMQ.json"; + static final String ROUTER_MQ_FILE_NAME = "mq.json"; + static final String GRPC_FILE_NAME = "grpc.json"; + static final String ROUTER_GRPC_FILE_NAME = "grpc_router.json"; + static final String CRADLE_CONFIDENTIAL_FILE_NAME = "cradle.json"; + static final String PROMETHEUS_FILE_NAME = "prometheus.json"; + static final String CUSTOM_FILE_NAME = "custom.json"; + static final String BOX_FILE_NAME = "box.json"; + static final String CONNECTION_MANAGER_CONF_FILE_NAME = "mq_router.json"; + static final String CRADLE_NON_CONFIDENTIAL_FILE_NAME = "cradle_manager.json"; /** @deprecated please use {@link #DICTIONARY_ALIAS_DIR_NAME} */ @Deprecated - private static final String DICTIONARY_TYPE_DIR_NAME = "dictionary"; - private static final String DICTIONARY_ALIAS_DIR_NAME = "dictionaries"; + static final String DICTIONARY_TYPE_DIR_NAME = "dictionary"; + static final String DICTIONARY_ALIAS_DIR_NAME = "dictionaries"; private static final String RABBITMQ_SECRET_NAME = "rabbitmq"; private static final String CASSANDRA_SECRET_NAME = "cassandra"; @@ -123,96 +117,20 @@ public class CommonFactory extends AbstractCommonFactory { private final Path dictionaryTypesDir; private final Path dictionaryAliasesDir; private final Path oldDictionariesDir; - private final ConfigurationManager configurationManager; + final ConfigurationManager configurationManager; private static final Logger LOGGER = LoggerFactory.getLogger(CommonFactory.class.getName()); - /** - * @deprecated Please use {@link CommonFactory#CommonFactory(FactorySettings)} - */ - @Deprecated(since = "4.0.0", forRemoval = true) - protected CommonFactory(Class> messageRouterParsedBatchClass, - Class> messageRouterRawBatchClass, - Class> messageRouterMessageGroupBatchClass, - Class> eventBatchRouterClass, - Class grpcRouterClass, - @Nullable Path custom, - @Nullable Path dictionaryTypesDir, - @Nullable Path dictionaryAliasesDir, - @Nullable Path oldDictionariesDir, - Map environmentVariables) { - this(new FactorySettings() - .messageRouterParsedBatchClass(messageRouterParsedBatchClass) - .messageRouterRawBatchClass(messageRouterRawBatchClass) - .messageRouterMessageGroupBatchClass(messageRouterMessageGroupBatchClass) - .eventBatchRouterClass(eventBatchRouterClass) - .grpcRouterClass(grpcRouterClass) - .variables(environmentVariables) - .custom(custom) - .dictionaryTypesDir(dictionaryTypesDir) - .dictionaryAliasesDir(dictionaryAliasesDir) - .oldDictionariesDir(oldDictionariesDir) - ); - } - public CommonFactory(FactorySettings settings) { super(settings); custom = defaultPathIfNull(settings.getCustom(), CUSTOM_FILE_NAME); dictionaryTypesDir = defaultPathIfNull(settings.getDictionaryTypesDir(), DICTIONARY_TYPE_DIR_NAME); dictionaryAliasesDir = defaultPathIfNull(settings.getDictionaryAliasesDir(), DICTIONARY_ALIAS_DIR_NAME); - oldDictionariesDir = requireNonNullElse(settings.getOldDictionariesDir(), CONFIG_DEFAULT_PATH); + oldDictionariesDir = requireNonNullElseGet(settings.getOldDictionariesDir(), CommonFactory::getConfigPath); configurationManager = createConfigurationManager(settings); start(); } - /** - * @deprecated Please use {@link CommonFactory#CommonFactory(FactorySettings)} - */ - @Deprecated(since = "3.10.0", forRemoval = true) - public CommonFactory(Class> messageRouterParsedBatchClass, - Class> messageRouterRawBatchClass, - Class> messageRouterMessageGroupBatchClass, - Class> eventBatchRouterClass, - Class grpcRouterClass, - Path rabbitMQ, Path routerMQ, Path routerGRPC, Path cradle, Path custom, Path prometheus, Path dictionariesDir, Path boxConfiguration) { - this(new FactorySettings() - .messageRouterParsedBatchClass(messageRouterParsedBatchClass) - .messageRouterRawBatchClass(messageRouterRawBatchClass) - .messageRouterMessageGroupBatchClass(messageRouterMessageGroupBatchClass) - .eventBatchRouterClass(eventBatchRouterClass) - .grpcRouterClass(grpcRouterClass) - .rabbitMQ(rabbitMQ) - .routerMQ(routerMQ) - .routerGRPC(routerGRPC) - .cradleConfidential(cradle) - .prometheus(prometheus) - .boxConfiguration(boxConfiguration) - .custom(custom) - .dictionaryTypesDir(dictionariesDir) - ); - } - - /** - * @deprecated Please use {@link CommonFactory#CommonFactory(FactorySettings)} - */ - @Deprecated(since = "3.10.0", forRemoval = true) - public CommonFactory(Path rabbitMQ, Path routerMQ, Path routerGRPC, Path cradle, Path custom, Path prometheus, Path dictionariesDir, Path boxConfiguration) { - this(RabbitParsedBatchRouter.class, RabbitRawBatchRouter.class, RabbitMessageGroupBatchRouter.class, EventBatchRouter.class, DefaultGrpcRouter.class, - rabbitMQ ,routerMQ ,routerGRPC ,cradle ,custom ,dictionariesDir ,prometheus ,boxConfiguration); - } - - /** - * @deprecated Please use {@link CommonFactory#CommonFactory(FactorySettings)} - */ - @Deprecated(since = "3.10.0", forRemoval = true) - public CommonFactory(Class> messageRouterParsedBatchClass, - Class> messageRouterRawBatchClass, - Class> messageRouterMessageGroupBatchClass, - Class> eventBatchRouterClass, - Class grpcRouterClass) { - this(new FactorySettings(messageRouterParsedBatchClass, messageRouterRawBatchClass, messageRouterMessageGroupBatchClass, eventBatchRouterClass, grpcRouterClass)); - } - public CommonFactory() { this(new FactorySettings()); } @@ -318,7 +236,7 @@ public static CommonFactory createFromArguments(String... args) { try { CommandLine cmd = new DefaultParser().parse(options, args); - String configs = cmd.getOptionValue(configOption.getLongOpt()); + Path configs = getConfigPath(cmd.getOptionValue(configOption.getLongOpt())); if (cmd.hasOption(namespaceOption.getLongOpt()) && cmd.hasOption(boxNameOption.getLongOpt())) { String namespace = cmd.getOptionValue(namespaceOption.getLongOpt()); @@ -349,7 +267,7 @@ public static CommonFactory createFromArguments(String... args) { return createFromKubernetes(namespace, boxName, contextName, dictionaries); } - if (configs != null) { + if (!CONFIG_DEFAULT_PATH.equals(configs)) { configureLogger(configs); } FactorySettings settings = new FactorySettings(); @@ -366,7 +284,7 @@ public static CommonFactory createFromArguments(String... args) { settings.setDictionaryTypesDir(calculatePath(cmd, dictionariesDirOption, configs, DICTIONARY_TYPE_DIR_NAME)); settings.setDictionaryAliasesDir(calculatePath(cmd, dictionariesDirOption, configs, DICTIONARY_ALIAS_DIR_NAME)); String oldDictionariesDir = cmd.getOptionValue(dictionariesDirOption.getLongOpt()); - settings.setOldDictionariesDir(oldDictionariesDir == null ? (configs == null ? CONFIG_DEFAULT_PATH : Path.of(configs)) : Path.of(oldDictionariesDir)); + settings.setOldDictionariesDir(oldDictionariesDir == null ? configs : Path.of(oldDictionariesDir)); return new CommonFactory(settings); } catch (ParseException e) { @@ -477,7 +395,7 @@ public static CommonFactory createFromKubernetes(String namespace, String boxNam if (loggingData != null) { writeFile(configPath.resolve(LOG4J2_PROPERTIES_NAME), loggingData); - configureLogger(configPath.toString()); + configureLogger(configPath); } settings.setRabbitMQ(writeFile(configPath, RABBIT_MQ_FILE_NAME, rabbitMqData)); @@ -632,6 +550,41 @@ public InputStream readDictionary(DictionaryType dictionaryType) { } } + static @NotNull Path getConfigPath() { + String pathString = System.getProperty(TH2_COMMON_CONFIGURATION_DIRECTORY_SYSTEM_PROPERTY); + if (pathString != null) { + Path path = Paths.get(pathString); + if (Files.exists(path) && Files.isDirectory(path)) { + return path; + } + LOGGER.warn("'{}' config directory passed via '{}' system property doesn't exist or it is not a directory", + pathString, + TH2_COMMON_CONFIGURATION_DIRECTORY_SYSTEM_PROPERTY); + } else { + LOGGER.debug("Skipped blank environment variable path for configs directory"); + } + + if (!Files.exists(CONFIG_DEFAULT_PATH)) { + LOGGER.error("'{}' default config directory doesn't exist", CONFIG_DEFAULT_PATH); + } + return CONFIG_DEFAULT_PATH; + } + + static @NotNull Path getConfigPath(@Nullable String cmdPath) { + String pathString = StringUtils.trim(cmdPath); + if (pathString != null) { + Path path = Paths.get(pathString); + if (Files.exists(path) && Files.isDirectory(path)) { + return path; + } + LOGGER.warn("'{}' config directory passed via CMD doesn't exist or it is not a directory", cmdPath); + } else { + LOGGER.debug("Skipped blank CMD path for configs directory"); + } + + return getConfigPath(); + } + private static Path writeFile(Path configPath, String fileName, Map configMap) throws IOException { Path file = configPath.resolve(fileName); writeFile(file, configMap.get(fileName)); @@ -712,7 +665,7 @@ private static ConfigurationManager createConfigurationManager(FactorySettings s } private static Path defaultPathIfNull(Path path, String name) { - return path == null ? CONFIG_DEFAULT_PATH.resolve(name) : path; + return path == null ? getConfigPath().resolve(name) : path; } private static void writeFile(Path path, String data) throws IOException { @@ -734,15 +687,15 @@ private static Option createLongOption(Options options, String optionName) { return option; } - private static Path calculatePath(String path, String configsPath, String fileName) { - return path != null ? Path.of(path) : (configsPath != null ? Path.of(configsPath, fileName) : CONFIG_DEFAULT_PATH.resolve(fileName)); + private static Path calculatePath(String path, @NotNull Path configsPath, String fileName) { + return path != null ? Path.of(path) : configsPath.resolve(fileName); } - private static Path calculatePath(CommandLine cmd, Option option, String configs, String fileName) { + private static Path calculatePath(CommandLine cmd, Option option, @NotNull Path configs, String fileName) { return calculatePath(cmd.getOptionValue(option.getLongOpt()), configs, fileName); } - private static Path calculatePath(CommandLine cmd, Option current, Option deprecated, String configs, String fileName) { + private static Path calculatePath(CommandLine cmd, Option current, Option deprecated, @NotNull Path configs, String fileName) { return calculatePath(defaultIfNull(cmd.getOptionValue(current.getLongOpt()), cmd.getOptionValue(deprecated.getLongOpt())), configs, fileName); } } diff --git a/src/main/java/com/exactpro/th2/common/schema/util/Log4jConfigUtils.kt b/src/main/java/com/exactpro/th2/common/schema/util/Log4jConfigUtils.kt index b8ca23b45..7562fa357 100644 --- a/src/main/java/com/exactpro/th2/common/schema/util/Log4jConfigUtils.kt +++ b/src/main/java/com/exactpro/th2/common/schema/util/Log4jConfigUtils.kt @@ -24,11 +24,11 @@ import org.slf4j.LoggerFactory class Log4jConfigUtils { fun configure( - pathList: List, + pathList: List, fileName: String, ) { pathList.asSequence() - .map { Path.of(it, fileName) } + .map { it.resolve(fileName) } .filter(Files::exists) .firstOrNull() ?.let { path -> diff --git a/src/main/kotlin/com/exactpro/th2/common/schema/configuration/ConfigurationManager.kt b/src/main/kotlin/com/exactpro/th2/common/schema/configuration/ConfigurationManager.kt index cebcad3db..f7eb10d60 100644 --- a/src/main/kotlin/com/exactpro/th2/common/schema/configuration/ConfigurationManager.kt +++ b/src/main/kotlin/com/exactpro/th2/common/schema/configuration/ConfigurationManager.kt @@ -26,6 +26,8 @@ import java.util.concurrent.ConcurrentHashMap class ConfigurationManager(private val configurationPath: Map, Path>) { private val configurations: MutableMap, Any?> = ConcurrentHashMap() + operator fun get(clazz: Class<*>): Path? = configurationPath[clazz] + fun loadConfiguration( objectMapper: ObjectMapper, stringSubstitutor: StringSubstitutor, @@ -48,6 +50,7 @@ class ConfigurationManager(private val configurationPath: Map, Path>) { } } + @Suppress("UNCHECKED_CAST") fun getConfigurationOrLoad( objectMapper: ObjectMapper, stringSubstitutor: StringSubstitutor, diff --git a/src/test/kotlin/com/exactpro/th2/common/schema/factory/CommonFactoryTest.kt b/src/test/kotlin/com/exactpro/th2/common/schema/factory/CommonFactoryTest.kt new file mode 100644 index 000000000..c918f8879 --- /dev/null +++ b/src/test/kotlin/com/exactpro/th2/common/schema/factory/CommonFactoryTest.kt @@ -0,0 +1,123 @@ +/* + * Copyright 2023 Exactpro (Exactpro Systems Limited) + * 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 com.exactpro.th2.common.schema.factory + +import com.exactpro.cradle.cassandra.CassandraStorageSettings +import com.exactpro.th2.common.metrics.PrometheusConfiguration +import com.exactpro.th2.common.schema.box.configuration.BoxConfiguration +import com.exactpro.th2.common.schema.cradle.CradleConfidentialConfiguration +import com.exactpro.th2.common.schema.cradle.CradleNonConfidentialConfiguration +import com.exactpro.th2.common.schema.factory.CommonFactory.CONFIG_DEFAULT_PATH +import com.exactpro.th2.common.schema.factory.CommonFactory.CUSTOM_FILE_NAME +import com.exactpro.th2.common.schema.factory.CommonFactory.DICTIONARY_ALIAS_DIR_NAME +import com.exactpro.th2.common.schema.factory.CommonFactory.DICTIONARY_TYPE_DIR_NAME +import com.exactpro.th2.common.schema.factory.CommonFactory.TH2_COMMON_CONFIGURATION_DIRECTORY_SYSTEM_PROPERTY +import com.exactpro.th2.common.schema.grpc.configuration.GrpcConfiguration +import com.exactpro.th2.common.schema.grpc.configuration.GrpcRouterConfiguration +import com.exactpro.th2.common.schema.message.configuration.MessageRouterConfiguration +import com.exactpro.th2.common.schema.message.impl.rabbitmq.configuration.ConnectionManagerConfiguration +import com.exactpro.th2.common.schema.message.impl.rabbitmq.configuration.RabbitMQConfiguration +import org.junit.jupiter.api.Test +import org.junitpioneer.jupiter.SetSystemProperty +import java.nio.file.Path +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + + +class CommonFactoryTest { + + @Test + fun `test load config by default path (default constructor)`() { + CommonFactory().use { commonFactory -> + assertConfigs(commonFactory, CONFIG_DEFAULT_PATH) + } + } + + @Test + fun `test load config by default path (createFromArguments(empty))`() { + CommonFactory.createFromArguments().use { commonFactory -> + assertConfigs(commonFactory, CONFIG_DEFAULT_PATH) + } + } + + @Test + fun `test load config by custom path (createFromArguments(not empty))`() { + CommonFactory.createFromArguments("-c", CONFIG_DIR_IN_RESOURCE).use { commonFactory -> + assertConfigs(commonFactory, Path.of(CONFIG_DIR_IN_RESOURCE)) + } + } + + @Test + @SetSystemProperty(key = TH2_COMMON_CONFIGURATION_DIRECTORY_SYSTEM_PROPERTY, value = CONFIG_DIR_IN_RESOURCE) + fun `test load config by environment variable path (default constructor)`() { + CommonFactory().use { commonFactory -> + assertConfigs(commonFactory, Path.of(CONFIG_DIR_IN_RESOURCE)) + } + } + + @Test + @SetSystemProperty(key = TH2_COMMON_CONFIGURATION_DIRECTORY_SYSTEM_PROPERTY, value = CONFIG_DIR_IN_RESOURCE) + fun `test load config by environment variable path (createFromArguments(empty))`() { + CommonFactory.createFromArguments().use { commonFactory -> + assertConfigs(commonFactory, Path.of(CONFIG_DIR_IN_RESOURCE)) + } + } + + @Test + @SetSystemProperty(key = TH2_COMMON_CONFIGURATION_DIRECTORY_SYSTEM_PROPERTY, value = CONFIG_DIR_IN_RESOURCE) + fun `test load config by custom path (createFromArguments(not empty) + environment variable)`() { + CommonFactory.createFromArguments("-c", CONFIG_DIR_IN_RESOURCE).use { commonFactory -> + assertConfigs(commonFactory, Path.of(CONFIG_DIR_IN_RESOURCE)) + } + } + + + private fun assertConfigs(commonFactory: CommonFactory, configPath: Path) { + CONFIG_NAME_TO_COMMON_FACTORY_SUPPLIER.forEach { (configName, actualPathSupplier) -> + assertEquals(configPath.resolve(configName), commonFactory.actualPathSupplier(), "Configured config path: $configPath, config name: $configName") + } + assertConfigurationManager(commonFactory, configPath) + } + + private fun assertConfigurationManager(commonFactory: CommonFactory, configPath: Path) { + CONFIG_CLASSES.forEach { clazz -> + assertNotNull(commonFactory.configurationManager[clazz]) + assertEquals(configPath, commonFactory.configurationManager[clazz]?.parent , "Configured config path: $configPath, config class: $clazz") + } + } + + companion object { + private const val CONFIG_DIR_IN_RESOURCE = "src/test/resources/test_common_factory_load_configs" + + private val CONFIG_NAME_TO_COMMON_FACTORY_SUPPLIER: Map Path> = mapOf( + CUSTOM_FILE_NAME to { pathToCustomConfiguration }, + DICTIONARY_ALIAS_DIR_NAME to { pathToDictionaryAliasesDir }, + DICTIONARY_TYPE_DIR_NAME to { pathToDictionaryTypesDir }, + ) + + private val CONFIG_CLASSES: Set> = setOf( + RabbitMQConfiguration::class.java, + MessageRouterConfiguration::class.java, + ConnectionManagerConfiguration::class.java, + GrpcConfiguration::class.java, + GrpcRouterConfiguration::class.java, + CradleConfidentialConfiguration::class.java, + CradleNonConfidentialConfiguration::class.java, + CassandraStorageSettings::class.java, + PrometheusConfiguration::class.java, + BoxConfiguration::class.java, + ) + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/exactpro/th2/common/schema/message/impl/rabbitmq/transport/CodecsTest.kt b/src/test/kotlin/com/exactpro/th2/common/schema/message/impl/rabbitmq/transport/CodecsTest.kt index 227ae3f38..c4c706bdc 100644 --- a/src/test/kotlin/com/exactpro/th2/common/schema/message/impl/rabbitmq/transport/CodecsTest.kt +++ b/src/test/kotlin/com/exactpro/th2/common/schema/message/impl/rabbitmq/transport/CodecsTest.kt @@ -26,6 +26,8 @@ import java.time.Instant import java.time.LocalDate import java.time.LocalDateTime import java.time.LocalTime +import java.time.format.DateTimeFormatter +import java.time.temporal.TemporalAccessor class CodecsTest { @@ -129,20 +131,23 @@ class CodecsTest { @TestFactory fun dateTypesTests(): Collection { - val testData = listOf( - LocalDate.now(), - LocalTime.now(), - LocalDateTime.now(), - Instant.now(), + LocalTime.parse("16:36:38.035420").toString() + val testData = listOf String>>( + LocalDate.now() to TemporalAccessor::toString, + LocalTime.now() to DateTimeFormatter.ISO_LOCAL_TIME::format, + // Check case when LocalTime.toString() around nanos to 1000 + LocalTime.parse("16:36:38.035420") to DateTimeFormatter.ISO_LOCAL_TIME::format, + LocalDateTime.now() to DateTimeFormatter.ISO_LOCAL_DATE_TIME::format, + Instant.now() to TemporalAccessor::toString, ) - return testData.map { - DynamicTest.dynamicTest("serializes ${it::class.simpleName} as field") { + return testData.map { (value, formatter) -> + DynamicTest.dynamicTest("serializes ${value::class.simpleName} as field") { val parsedMessage = ParsedMessage.builder().apply { setId(MessageId.DEFAULT) setType("test") setBody( linkedMapOf( - "field" to it, + "field" to value, ) ) }.build() @@ -154,7 +159,7 @@ class CodecsTest { assertEquals(parsedMessage, decoded, "unexpected parsed result decoded") assertEquals( - "{\"field\":\"$it\"}", + "{\"field\":\"${formatter(value)}\"}", decoded.rawBody.toString(Charsets.UTF_8), "unexpected raw body", ) diff --git a/src/test/resources/test_common_factory_load_configs/custom.json b/src/test/resources/test_common_factory_load_configs/custom.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/src/test/resources/test_common_factory_load_configs/custom.json @@ -0,0 +1 @@ +{} \ No newline at end of file