diff --git a/.travis.yml b/.travis.yml index e9f71e4dc..10a95d197 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,11 @@ jdk: install: true addons: srcclr: true +env: + - optimizely_default_parser=GSON_CONFIG_PARSER + - optimizely_default_parser=JACKSON_CONFIG_PARSER + - optimizely_default_parser=JSON_CONFIG_PARSER + - optimizely_default_parser=JSON_SIMPLE_CONFIG_PARSER script: - "./gradlew clean" - "./gradlew exhaustiveTest" diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/DefaultConfigParser.java b/core-api/src/main/java/com/optimizely/ab/config/parser/DefaultConfigParser.java index d75435e1a..0d103eb11 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/DefaultConfigParser.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/DefaultConfigParser.java @@ -16,10 +16,12 @@ */ package com.optimizely.ab.config.parser; +import com.optimizely.ab.internal.PropertyUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; +import java.util.function.Supplier; /** * Factory for generating {@link ConfigParser} instances, based on the json parser available on the classpath. @@ -37,6 +39,33 @@ public static ConfigParser getInstance() { //======== Helper methods ========// + public enum ConfigParserSupplier { + GSON_CONFIG_PARSER("com.google.gson.Gson", GsonConfigParser::new), + JACKSON_CONFIG_PARSER("com.fasterxml.jackson.databind.ObjectMapper", JacksonConfigParser::new), + JSON_CONFIG_PARSER("org.json.JSONObject", JsonConfigParser::new), + JSON_SIMPLE_CONFIG_PARSER("org.json.simple.JSONObject", JsonSimpleConfigParser::new); + + private final String className; + private final Supplier supplier; + + ConfigParserSupplier(String className, Supplier supplier) { + this.className = className; + this.supplier = supplier; + } + + ConfigParser get() { + return supplier.get(); + } + + private boolean isPresent() { + try { + Class.forName(className); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + } /** * Creates and returns a {@link ConfigParser} using a json parser available on the classpath. * @@ -45,35 +74,39 @@ public static ConfigParser getInstance() { */ private static @Nonnull ConfigParser create() { - ConfigParser configParser; - - if (isPresent("com.fasterxml.jackson.databind.ObjectMapper")) { - configParser = new JacksonConfigParser(); - } else if (isPresent("com.google.gson.Gson")) { - configParser = new GsonConfigParser(); - } else if (isPresent("org.json.simple.JSONObject")) { - configParser = new JsonSimpleConfigParser(); - } else if (isPresent("org.json.JSONObject")) { - configParser = new JsonConfigParser(); - } else { - throw new MissingJsonParserException("unable to locate a JSON parser. " - + "Please see for more information"); + + String configParserName = PropertyUtils.get("default_parser"); + + if (configParserName != null) { + try { + ConfigParserSupplier supplier = ConfigParserSupplier.valueOf(configParserName); + if (supplier.isPresent()) { + ConfigParser configParser = supplier.get(); + logger.debug("using json parser: {}, based on override config", configParser.getClass().getSimpleName()); + return configParser; + } + + logger.warn("configured parser {} is not available in the classpath", configParserName); + } catch (IllegalArgumentException e) { + logger.warn("configured parser {} is not a valid value", configParserName); + } } - logger.info("using json parser: {}", configParser.getClass().getSimpleName()); - return configParser; - } + for (ConfigParserSupplier supplier: ConfigParserSupplier.values()) { + if (!supplier.isPresent()) { + continue; + } - private static boolean isPresent(@Nonnull String className) { - try { - Class.forName(className); - return true; - } catch (ClassNotFoundException e) { - return false; + ConfigParser configParser = supplier.get(); + logger.info("using json parser: {}", configParser.getClass().getSimpleName()); + return configParser; } + + throw new MissingJsonParserException("unable to locate a JSON parser. " + + "Please see for more information"); } - //======== Lazy-init Holder ========// + //======== Lazy-init Holder ========// private static class LazyHolder { private static final ConfigParser INSTANCE = create(); diff --git a/core-api/src/test/java/com/optimizely/ab/config/parser/DefaultConfigParserTest.java b/core-api/src/test/java/com/optimizely/ab/config/parser/DefaultConfigParserTest.java index 5911932f8..dfc130f21 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/parser/DefaultConfigParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/parser/DefaultConfigParserTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2017, Optimizely and contributors + * Copyright 2016-2017, 2019, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +16,89 @@ */ package com.optimizely.ab.config.parser; +import com.optimizely.ab.config.ProjectConfig; +import com.optimizely.ab.internal.PropertyUtils; +import org.hamcrest.CoreMatchers; +import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; + +import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.*; +import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.validConfigJsonV4; +import static junit.framework.TestCase.fail; /** * Tests for {@link DefaultConfigParser}. */ +@RunWith(Parameterized.class) public class DefaultConfigParserTest { + @Parameterized.Parameters(name = "{index}") + public static Collection data() throws IOException { + return Arrays.asList(new Object[][]{ + { + validConfigJsonV2(), + validProjectConfigV2() + }, + { + validConfigJsonV3(), + validProjectConfigV3() + }, + { + validConfigJsonV4(), + validProjectConfigV4() + } + }); + } + + @Parameterized.Parameter(0) + public String validDatafile; + + @Parameterized.Parameter(1) + public ProjectConfig validProjectConfig; + + /** + * This method is to test DefaultConfigParser when different default_parser gets set. + * For example: when optimizely_default_parser environment variable will be set to "GSON_CONFIG_PARSER" than + * "DefaultConfigParser.getInstance()" returns "GsonConfigParser" and parse ProjectConfig using it. Also + * this test will assertThat "configParser" (Provided in env variable) is instance of "GsonConfigParser.class" + * + * @throws Exception + */ @Test - public void createThrowException() throws Exception { - // FIXME - mdodsworth: hmmm, this isn't going to be the easiest thing to test + public void testPropertyDefaultParser() throws Exception { + String defaultParser = PropertyUtils.get("default_parser"); + ConfigParser configParser = DefaultConfigParser.getInstance(); + ProjectConfig actual = configParser.parseProjectConfig(validDatafile); + ProjectConfig expected = validProjectConfig; + verifyProjectConfig(actual, expected); + Class expectedParser = GsonConfigParser.class; + + if(defaultParser != null) { + DefaultConfigParser.ConfigParserSupplier defaultParserSupplier = DefaultConfigParser.ConfigParserSupplier.valueOf(defaultParser); + switch (defaultParserSupplier) { + case GSON_CONFIG_PARSER: + expectedParser = GsonConfigParser.class; + break; + case JACKSON_CONFIG_PARSER: + expectedParser = JacksonConfigParser.class; + break; + case JSON_CONFIG_PARSER: + expectedParser = JsonConfigParser.class; + break; + case JSON_SIMPLE_CONFIG_PARSER: + expectedParser = JsonSimpleConfigParser.class; + break; + default: + fail("Not a valid config parser"); + } + } + + Assert.assertThat(configParser, CoreMatchers.instanceOf(expectedParser)); } }