Commit 82790e5f authored by songdragon's avatar songdragon Committed by Jason Song

Add OrderedProperties to keep the property order in apollo-client

parent 42edb9da
......@@ -11,6 +11,7 @@ import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.tracer.Tracer;
import com.ctrip.framework.apollo.tracer.spi.Transaction;
import com.ctrip.framework.apollo.util.ConfigUtil;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import com.ctrip.framework.apollo.util.function.Functions;
import com.ctrip.framework.apollo.util.parser.Parsers;
import com.google.common.base.Function;
......@@ -58,6 +59,8 @@ public abstract class AbstractConfig implements Config {
private final List<Cache> allCaches;
private final AtomicLong m_configVersion; //indicate config version
protected PropertiesFactory propertiesFactory;
static {
m_executorService = Executors.newCachedThreadPool(ApolloThreadFactory
.create("Config", true));
......@@ -68,6 +71,7 @@ public abstract class AbstractConfig implements Config {
m_configVersion = new AtomicLong();
m_arrayCache = Maps.newConcurrentMap();
allCaches = Lists.newArrayList();
propertiesFactory = ApolloInjector.getInstance(PropertiesFactory.class);
}
@Override
......@@ -493,11 +497,11 @@ public abstract class AbstractConfig implements Config {
List<ConfigChange> calcPropertyChanges(String namespace, Properties previous,
Properties current) {
if (previous == null) {
previous = new Properties();
previous = propertiesFactory.getPropertiesInstance();
}
if (current == null) {
current = new Properties();
current = propertiesFactory.getPropertiesInstance();
}
Set<String> previousKeys = previous.stringPropertyNames();
......
package com.ctrip.framework.apollo.internals;
import com.ctrip.framework.apollo.build.ApolloInjector;
import com.ctrip.framework.apollo.enums.ConfigSourceType;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
......@@ -30,6 +32,7 @@ public abstract class AbstractConfigFile implements ConfigFile, RepositoryChange
protected final String m_namespace;
protected final AtomicReference<Properties> m_configProperties;
private final List<ConfigFileChangeListener> m_listeners = Lists.newCopyOnWriteArrayList();
protected final PropertiesFactory propertiesFactory;
private volatile ConfigSourceType m_sourceType = ConfigSourceType.NONE;
......@@ -42,6 +45,7 @@ public abstract class AbstractConfigFile implements ConfigFile, RepositoryChange
m_configRepository = configRepository;
m_namespace = namespace;
m_configProperties = new AtomicReference<>();
propertiesFactory = ApolloInjector.getInstance(PropertiesFactory.class);
initialize();
}
......@@ -72,7 +76,7 @@ public abstract class AbstractConfigFile implements ConfigFile, RepositoryChange
if (newProperties.equals(m_configProperties.get())) {
return;
}
Properties newConfigProperties = new Properties();
Properties newConfigProperties = propertiesFactory.getPropertiesInstance();
newConfigProperties.putAll(newProperties);
String oldValue = getContent();
......
package com.ctrip.framework.apollo.internals;
import com.ctrip.framework.apollo.build.ApolloInjector;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import java.util.List;
import java.util.Properties;
......@@ -16,6 +18,7 @@ import com.google.common.collect.Lists;
public abstract class AbstractConfigRepository implements ConfigRepository {
private static final Logger logger = LoggerFactory.getLogger(AbstractConfigRepository.class);
private List<RepositoryChangeListener> m_listeners = Lists.newCopyOnWriteArrayList();
protected PropertiesFactory propertiesFactory = ApolloInjector.getInstance(PropertiesFactory.class);
protected boolean trySync() {
try {
......
......@@ -4,12 +4,12 @@ import com.ctrip.framework.apollo.enums.ConfigSourceType;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
......@@ -88,7 +88,7 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis
// step 4: check properties file from classpath
if (value == null && m_resourceProperties != null) {
value = (String) m_resourceProperties.get(key);
value = (String) m_resourceProperties.getProperty(key);
}
if (value == null && m_configProperties.get() == null && m_warnLogRateLimiter.tryAcquire()) {
......@@ -115,7 +115,7 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis
private Set<String> stringPropertyNames(Properties properties) {
//jdk9以下版本Properties#enumerateStringProperties方法存在性能问题,keys() + get(k) 重复迭代, jdk9之后改为entrySet遍历.
Map<String, String> h = new HashMap<>();
Map<String, String> h = new LinkedHashMap<>();
for (Map.Entry<Object, Object> e : properties.entrySet()) {
Object k = e.getKey();
Object v = e.getValue();
......@@ -133,7 +133,7 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis
}
ConfigSourceType sourceType = m_configRepository.getSourceType();
Properties newConfigProperties = new Properties();
Properties newConfigProperties = propertiesFactory.getPropertiesInstance();
newConfigProperties.putAll(newProperties);
Map<String, ConfigChange> actualChanges = updateAndCalcConfigChanges(newConfigProperties, sourceType);
......@@ -213,7 +213,7 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis
Properties properties = null;
if (in != null) {
properties = new Properties();
properties = propertiesFactory.getPropertiesInstance();
try {
properties.load(in);
......
......@@ -9,6 +9,8 @@ import com.ctrip.framework.apollo.spi.DefaultConfigFactoryManager;
import com.ctrip.framework.apollo.spi.DefaultConfigRegistry;
import com.ctrip.framework.apollo.tracer.Tracer;
import com.ctrip.framework.apollo.util.ConfigUtil;
import com.ctrip.framework.apollo.util.factory.DefaultPropertiesFactory;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import com.ctrip.framework.apollo.util.http.HttpUtil;
import com.ctrip.framework.apollo.util.yaml.YamlParser;
......@@ -62,6 +64,7 @@ public class DefaultInjector implements Injector {
bind(ConfigServiceLocator.class).in(Singleton.class);
bind(RemoteConfigLongPollService.class).in(Singleton.class);
bind(YamlParser.class).in(Singleton.class);
bind(PropertiesFactory.class).to(DefaultPropertiesFactory.class).in(Singleton.class);
}
}
}
......@@ -88,7 +88,7 @@ public class LocalFileConfigRepository extends AbstractConfigRepository
if (m_fileProperties == null) {
sync();
}
Properties result = new Properties();
Properties result = propertiesFactory.getPropertiesInstance();
result.putAll(m_fileProperties);
return result;
}
......@@ -117,7 +117,7 @@ public class LocalFileConfigRepository extends AbstractConfigRepository
if (newProperties.equals(m_fileProperties)) {
return;
}
Properties newFileProperties = new Properties();
Properties newFileProperties = propertiesFactory.getPropertiesInstance();
newFileProperties.putAll(newProperties);
updateFileProperties(newFileProperties, m_upstream.getSourceType());
this.fireRepositoryChange(namespace, newProperties);
......@@ -191,8 +191,7 @@ public class LocalFileConfigRepository extends AbstractConfigRepository
try {
in = new FileInputStream(file);
properties = new Properties();
properties = propertiesFactory.getPropertiesInstance();
properties.load(in);
logger.debug("Loading local config file {} successfully!", file.getAbsolutePath());
} catch (IOException ex) {
......
......@@ -158,7 +158,7 @@ public class RemoteConfigRepository extends AbstractConfigRepository {
}
private Properties transformApolloConfigToProperties(ApolloConfig apolloConfig) {
Properties result = new Properties();
Properties result = propertiesFactory.getPropertiesInstance();
result.putAll(apolloConfig.getConfigurations());
return result;
}
......
......@@ -81,7 +81,7 @@ public class SimpleConfig extends AbstractConfig implements RepositoryChangeList
if (newProperties.equals(m_configProperties)) {
return;
}
Properties newConfigProperties = new Properties();
Properties newConfigProperties = propertiesFactory.getPropertiesInstance();
newConfigProperties.putAll(newProperties);
List<ConfigChange> changes = calcPropertyChanges(namespace, m_configProperties, newConfigProperties);
......
......@@ -61,7 +61,7 @@ public class YamlConfigFile extends PlainTextConfigFile implements PropertiesCom
private Properties toProperties() {
if (!this.hasContent()) {
return new Properties();
return propertiesFactory.getPropertiesInstance();
}
try {
......
......@@ -6,6 +6,7 @@ import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.spring.config.ConfigPropertySourceFactory;
import com.ctrip.framework.apollo.spring.config.PropertySourcesConstants;
import com.ctrip.framework.apollo.spring.util.SpringInjector;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import java.util.List;
......@@ -61,7 +62,7 @@ public class ApolloApplicationContextInitializer implements
private static final Logger logger = LoggerFactory.getLogger(ApolloApplicationContextInitializer.class);
private static final Splitter NAMESPACE_SPLITTER = Splitter.on(",").omitEmptyStrings().trimResults();
private static final String[] APOLLO_SYSTEM_PROPERTIES = {"app.id", ConfigConsts.APOLLO_CLUSTER_KEY,
"apollo.cacheDir", "apollo.accesskey.secret", ConfigConsts.APOLLO_META_KEY};
"apollo.cacheDir", "apollo.accesskey.secret", ConfigConsts.APOLLO_META_KEY, PropertiesFactory.APOLLO_PROPERTY_ORDER_ENABLE};
private final ConfigPropertySourceFactory configPropertySourceFactory = SpringInjector
.getInstance(ConfigPropertySourceFactory.class);
......
package com.ctrip.framework.apollo.util;
import static com.ctrip.framework.apollo.util.factory.PropertiesFactory.APOLLO_PROPERTY_ORDER_ENABLE;
import com.google.common.util.concurrent.RateLimiter;
import java.io.File;
import java.util.concurrent.TimeUnit;
......@@ -18,6 +20,7 @@ import com.google.common.base.Strings;
* @author Jason Song(song_s@ctrip.com)
*/
public class ConfigUtil {
private static final Logger logger = LoggerFactory.getLogger(ConfigUtil.class);
private int refreshInterval = 5;
private TimeUnit refreshIntervalTimeUnit = TimeUnit.MINUTES;
......@@ -36,6 +39,7 @@ public class ConfigUtil {
private long longPollingInitialDelayInMills = 2000;//2 seconds
private boolean autoUpdateInjectedSpringProperties = true;
private final RateLimiter warnLogRateLimiter;
private boolean propertiesOrdered = false;
public ConfigUtil() {
warnLogRateLimiter = RateLimiter.create(0.017); // 1 warning log output per minute
......@@ -47,6 +51,7 @@ public class ConfigUtil {
initMaxConfigCacheSize();
initLongPollingInitialDelayInMills();
initAutoUpdateInjectedSpringProperties();
initPropertiesOrdered();
}
/**
......@@ -281,12 +286,14 @@ public class ConfigUtil {
}
private void initLongPollingInitialDelayInMills() {
String customizedLongPollingInitialDelay = System.getProperty("apollo.longPollingInitialDelayInMills");
String customizedLongPollingInitialDelay = System
.getProperty("apollo.longPollingInitialDelayInMills");
if (!Strings.isNullOrEmpty(customizedLongPollingInitialDelay)) {
try {
longPollingInitialDelayInMills = Long.parseLong(customizedLongPollingInitialDelay);
} catch (Throwable ex) {
logger.error("Config for apollo.longPollingInitialDelayInMills is invalid: {}", customizedLongPollingInitialDelay);
logger.error("Config for apollo.longPollingInitialDelayInMills is invalid: {}",
customizedLongPollingInitialDelay);
}
}
}
......@@ -300,7 +307,8 @@ public class ConfigUtil {
String enableAutoUpdate = System.getProperty("apollo.autoUpdateInjectedSpringProperties");
if (Strings.isNullOrEmpty(enableAutoUpdate)) {
// 2. Get from app.properties
enableAutoUpdate = Foundation.app().getProperty("apollo.autoUpdateInjectedSpringProperties", null);
enableAutoUpdate = Foundation.app()
.getProperty("apollo.autoUpdateInjectedSpringProperties", null);
}
if (!Strings.isNullOrEmpty(enableAutoUpdate)) {
autoUpdateInjectedSpringProperties = Boolean.parseBoolean(enableAutoUpdate.trim());
......@@ -310,4 +318,25 @@ public class ConfigUtil {
public boolean isAutoUpdateInjectedSpringPropertiesEnabled() {
return autoUpdateInjectedSpringProperties;
}
private void initPropertiesOrdered() {
String enablePropertiesOrdered = System.getProperty(APOLLO_PROPERTY_ORDER_ENABLE);
if (Strings.isNullOrEmpty(enablePropertiesOrdered)) {
enablePropertiesOrdered = Foundation.app().getProperty(APOLLO_PROPERTY_ORDER_ENABLE, "false");
}
if (!Strings.isNullOrEmpty(enablePropertiesOrdered)) {
try {
propertiesOrdered = Boolean.parseBoolean(enablePropertiesOrdered);
} catch (Throwable ex) {
logger.warn("Config for {} is invalid: {}, set default value: false",
APOLLO_PROPERTY_ORDER_ENABLE, enablePropertiesOrdered);
}
}
}
public boolean isPropertiesOrderEnabled() {
return propertiesOrdered;
}
}
package com.ctrip.framework.apollo.util;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
/**
* An OrderedProperties instance will keep appearance order in config file.
*
* <strong>
* Warnings: 1. It should be noticed that stream APIs or JDk1.8 APIs( listed in
* https://github.com/ctripcorp/apollo/pull/2861) are not implemented here. 2. {@link Properties}
* implementation are different between JDK1.8 and later JDKs. At least, {@link Properties} have an
* individual implementation in JDK10. Hence, there should be an individual putAll method here.
* </strong>
*
* @author songdragon@zts.io
*/
public class OrderedProperties extends Properties {
private static final long serialVersionUID = -1741073539526213291L;
private final Set<String> propertyNames;
public OrderedProperties() {
propertyNames = Collections.synchronizedSet(new LinkedHashSet<String>());
}
@Override
public synchronized Object put(Object key, Object value) {
addPropertyName(key);
return super.put(key, value);
}
private void addPropertyName(Object key) {
if (key instanceof String) {
propertyNames.add((String) key);
}
}
@Override
public Set<String> stringPropertyNames() {
return propertyNames;
}
@Override
public Enumeration<?> propertyNames() {
return Collections.enumeration(propertyNames);
}
@Override
public synchronized Enumeration<Object> keys() {
return new Enumeration<Object>() {
private final Iterator<String> i = propertyNames.iterator();
@Override
public boolean hasMoreElements() {
return i.hasNext();
}
@Override
public Object nextElement() {
return i.next();
}
};
}
@Override
public Set<Object> keySet() {
return new LinkedHashSet<Object>(propertyNames);
}
@Override
public Set<Entry<Object, Object>> entrySet() {
Set<Entry<Object, Object>> original = super.entrySet();
LinkedHashMap<Object, Entry<Object, Object>> entryMap = new LinkedHashMap<>();
for (String propertyName : propertyNames) {
entryMap.put(propertyName, null);
}
for (Entry<Object, Object> entry : original) {
entryMap.put(entry.getKey(), entry);
}
return new LinkedHashSet<>(entryMap.values());
}
@Override
public synchronized void putAll(Map<?, ?> t) {
super.putAll(t);
for (Object name : t.keySet()) {
addPropertyName(name);
}
}
@Override
public synchronized void clear() {
super.clear();
this.propertyNames.clear();
}
@Override
public synchronized Object remove(Object key) {
this.propertyNames.remove(key);
return super.remove(key);
}
}
package com.ctrip.framework.apollo.util.factory;
import com.ctrip.framework.apollo.build.ApolloInjector;
import com.ctrip.framework.apollo.util.ConfigUtil;
import com.ctrip.framework.apollo.util.OrderedProperties;
import com.google.common.base.Strings;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default PropertiesFactory implementation.
*
* @author songdragon@zts.io
*/
public class DefaultPropertiesFactory implements PropertiesFactory {
private static final Logger logger = LoggerFactory.getLogger(DefaultPropertiesFactory.class);
private ConfigUtil m_configUtil;
public DefaultPropertiesFactory() {
m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
}
@Override
public Properties getPropertiesInstance() {
if (m_configUtil.isPropertiesOrderEnabled()) {
return new OrderedProperties();
} else {
return new Properties();
}
}
}
package com.ctrip.framework.apollo.util.factory;
import java.util.Properties;
/**
* Factory interface to construct Properties instances.
*
* @author songdragon@zts.io
*/
public interface PropertiesFactory {
/**
* Configuration to keep properties order as same as line order in .yml/.yaml/.properties file.
*/
public static final String APOLLO_PROPERTY_ORDER_ENABLE = "apollo.property.order.enable";
/**
* <pre>
* Default implementation:
* 1. if {@link APOLLO_PROPERTY_ORDER_ENABLE} is true return a new
* instance of {@link com.ctrip.framework.apollo.util.OrderedProperties}.
* 2. else return a new instance of {@link Properties}
* </pre>
*
* @return
*/
public Properties getPropertiesInstance();
}
package com.ctrip.framework.apollo.util.yaml;
import com.ctrip.framework.apollo.build.ApolloInjector;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.Collections;
......@@ -25,12 +27,14 @@ import com.ctrip.framework.apollo.core.utils.StringUtils;
public class YamlParser {
private static final Logger logger = LoggerFactory.getLogger(YamlParser.class);
private PropertiesFactory propertiesFactory = ApolloInjector.getInstance(PropertiesFactory.class);
/**
* Transform yaml content to properties
*/
public Properties yamlToProperties(String yamlContent) {
Yaml yaml = createYaml();
final Properties result = new Properties();
final Properties result = propertiesFactory.getPropertiesInstance();
process(new MatchCallback() {
@Override
public void process(Properties properties, Map<String, Object> map) {
......@@ -91,7 +95,7 @@ public class YamlParser {
}
private boolean process(Map<String, Object> map, MatchCallback callback) {
Properties properties = new Properties();
Properties properties = propertiesFactory.getPropertiesInstance();
properties.putAll(getFlattenedMap(map));
if (logger.isDebugEnabled()) {
......
......@@ -32,7 +32,8 @@ import com.google.gson.Gson;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public abstract class BaseIntegrationTest{
public abstract class BaseIntegrationTest {
private static final int PORT = findFreePort();
private static final String metaServiceUrl = "http://localhost:" + PORT;
private static final String someAppName = "someAppName";
......@@ -74,6 +75,7 @@ public abstract class BaseIntegrationTest{
/**
* init and start a jetty server, remember to call server.stop when the task is finished
*
* @param handlers
* @throws Exception
*/
......@@ -139,6 +141,7 @@ public abstract class BaseIntegrationTest{
}
public static class MockConfigUtil extends ConfigUtil {
@Override
public String getAppId() {
return someAppId;
......@@ -198,13 +201,19 @@ public abstract class BaseIntegrationTest{
public long getLongPollingInitialDelayInMills() {
return 0;
}
@Override
public boolean isPropertiesOrderEnabled() {
return true;
}
}
/**
* Returns a free port number on localhost.
*
* Heavily inspired from org.eclipse.jdt.launching.SocketUtil (to avoid a dependency to JDT just because of this).
* Slightly improved with close() missing in JDT. And throws exception instead of returning -1.
* <p>
* Heavily inspired from org.eclipse.jdt.launching.SocketUtil (to avoid a dependency to JDT just
* because of this). Slightly improved with close() missing in JDT. And throws exception instead
* of returning -1.
*
* @return a free port number on localhost
* @throws IllegalStateException if unable to find a free port
......@@ -230,7 +239,8 @@ public abstract class BaseIntegrationTest{
}
}
}
throw new IllegalStateException("Could not find a free TCP/IP port to start embedded Jetty HTTP Server on");
throw new IllegalStateException(
"Could not find a free TCP/IP port to start embedded Jetty HTTP Server on");
}
}
......@@ -9,9 +9,7 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
......@@ -19,6 +17,7 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
......@@ -48,11 +47,13 @@ import com.google.common.util.concurrent.SettableFuture;
* @author Jason Song(song_s@ctrip.com)
*/
public class ConfigIntegrationTest extends BaseIntegrationTest {
private String someReleaseKey;
private File configDir;
private String defaultNamespace;
private String someOtherNamespace;
private RemoteConfigLongPollService remoteConfigLongPollService;
private PropertiesFactory propertiesFactory;
@Before
public void setUp() throws Exception {
......@@ -67,6 +68,9 @@ public class ConfigIntegrationTest extends BaseIntegrationTest {
}
configDir.mkdirs();
remoteConfigLongPollService = ApolloInjector.getInstance(RemoteConfigLongPollService.class);
System.setProperty(PropertiesFactory.APOLLO_PROPERTY_ORDER_ENABLE, "true");
propertiesFactory = ApolloInjector.getInstance(PropertiesFactory.class);
}
@Override
......@@ -74,6 +78,7 @@ public class ConfigIntegrationTest extends BaseIntegrationTest {
public void tearDown() throws Exception {
ReflectionTestUtils.invokeMethod(remoteConfigLongPollService, "stopLongPollingRefresh");
recursiveDelete(configDir);
System.clearProperty(PropertiesFactory.APOLLO_PROPERTY_ORDER_ENABLE);
super.tearDown();
}
......@@ -110,6 +115,28 @@ public class ConfigIntegrationTest extends BaseIntegrationTest {
assertEquals(someDefaultValue, config.getProperty(someNonExistedKey, someDefaultValue));
}
@Test
public void testOrderGetConfigWithNoLocalFileButWithRemoteConfig() throws Exception {
String someKey1 = "someKey1";
String someValue1 = "someValue1";
String someKey2 = "someKey2";
String someValue2 = "someValue2";
Map<String, String> configurations = new LinkedHashMap<>();
configurations.put(someKey1, someValue1);
configurations.put(someKey2, someValue2);
ApolloConfig apolloConfig = assembleApolloConfig(ImmutableMap.copyOf(configurations));
ContextHandler handler = mockConfigServerHandler(HttpServletResponse.SC_OK, apolloConfig);
startServerWithHandlers(handler);
Config config = ConfigService.getAppConfig();
Set<String> propertyNames = config.getPropertyNames();
Iterator<String> it = propertyNames.iterator();
assertEquals(someKey1, it.next());
assertEquals(someKey2, it.next());
}
@Test
public void testGetConfigWithLocalFileAndWithRemoteConfig() throws Exception {
String someKey = "someKey";
......@@ -128,6 +155,45 @@ public class ConfigIntegrationTest extends BaseIntegrationTest {
assertEquals(anotherValue, config.getProperty(someKey, null));
}
@Test
public void testOrderGetConfigWithLocalFileAndWithRemoteConfig() throws Exception {
String someKey = "someKey";
String someValue = "someValue";
String anotherValue = "anotherValue";
String someKey1 = "someKey1";
String someValue1 = "someValue1";
String anotherValue1 = "anotherValue1";
String someKey2 = "someKey2";
String someValue2 = "someValue2";
Properties properties = propertiesFactory.getPropertiesInstance();
properties.put(someKey, someValue);
properties.put(someKey1, someValue1);
properties.put(someKey2, someValue2);
createLocalCachePropertyFile(properties);
Map<String, String> configurations = new LinkedHashMap<>();
configurations.put(someKey, anotherValue);
configurations.put(someKey1, anotherValue1);
configurations.put(someKey2, someValue2);
ApolloConfig apolloConfig = assembleApolloConfig(ImmutableMap.copyOf(configurations));
ContextHandler handler = mockConfigServerHandler(HttpServletResponse.SC_OK, apolloConfig);
startServerWithHandlers(handler);
Config config = ConfigService.getAppConfig();
assertEquals(anotherValue, config.getProperty(someKey, null));
Set<String> propertyNames = config.getPropertyNames();
Iterator<String> it = propertyNames.iterator();
assertEquals(someKey, it.next());
assertEquals(someKey1, it.next());
assertEquals(someKey2, it.next());
assertEquals(anotherValue1, config.getProperty(someKey1, ""));
}
@Test
public void testGetConfigWithNoLocalFileAndRemoteConfigError() throws Exception {
ContextHandler handler =
......@@ -158,6 +224,31 @@ public class ConfigIntegrationTest extends BaseIntegrationTest {
assertEquals(someValue, config.getProperty(someKey, null));
}
@Test
public void testOrderGetConfigWithLocalFileAndRemoteConfigError() throws Exception {
String someKey1 = "someKey1";
String someValue1 = "someValue1";
String someKey2 = "someKey2";
String someValue2 = "someValue2";
Properties properties = propertiesFactory.getPropertiesInstance();
properties.put(someKey1, someValue1);
properties.put(someKey2, someValue2);
createLocalCachePropertyFile(properties);
ContextHandler handler =
mockConfigServerHandler(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null);
startServerWithHandlers(handler);
Config config = ConfigService.getAppConfig();
assertEquals(someValue1, config.getProperty(someKey1, null));
assertEquals(someValue2, config.getProperty(someKey2, null));
Set<String> propertyNames = config.getPropertyNames();
Iterator<String> it = propertyNames.iterator();
assertEquals(someKey1, it.next());
assertEquals(someKey2, it.next());
}
@Test
public void testGetConfigWithNoLocalFileAndRemoteMetaServiceRetry() throws Exception {
String someKey = "someKey";
......@@ -279,7 +370,8 @@ public class ConfigIntegrationTest extends BaseIntegrationTest {
}
@Test
public void testLongPollRefreshWithMultipleNamespacesAndOnlyOneNamespaceNotified() throws Exception {
public void testLongPollRefreshWithMultipleNamespacesAndOnlyOneNamespaceNotified()
throws Exception {
final String someKey = "someKey";
final String someValue = "someValue";
final String anotherValue = "anotherValue";
......@@ -323,7 +415,8 @@ public class ConfigIntegrationTest extends BaseIntegrationTest {
}
@Test
public void testLongPollRefreshWithMultipleNamespacesAndMultipleNamespaceNotified() throws Exception {
public void testLongPollRefreshWithMultipleNamespacesAndMultipleNamespaceNotified()
throws Exception {
final String someKey = "someKey";
final String someValue = "someValue";
final String anotherValue = "anotherValue";
......
......@@ -10,6 +10,8 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.ctrip.framework.apollo.enums.ConfigSourceType;
import com.ctrip.framework.apollo.util.factory.DefaultPropertiesFactory;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.io.File;
......@@ -57,6 +59,7 @@ public class DefaultConfigTest {
public void setUp() throws Exception {
MockInjector.reset();
MockInjector.setInstance(ConfigUtil.class, new MockConfigUtil());
MockInjector.setInstance(PropertiesFactory.class, new DefaultPropertiesFactory());
someResourceDir = new File(ClassLoaderUtil.getClassPath() + "/META-INF/config");
someResourceDir.mkdirs();
......
package com.ctrip.framework.apollo.internals;
import static org.hamcrest.Matchers.both;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.everyItem;
import static org.hamcrest.Matchers.isIn;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
......@@ -10,11 +14,15 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.ctrip.framework.apollo.enums.ConfigSourceType;
import com.ctrip.framework.apollo.util.factory.DefaultPropertiesFactory;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map.Entry;
import java.util.Properties;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
......@@ -31,6 +39,7 @@ import com.google.common.io.Files;
* Created by Jason on 4/9/16.
*/
public class LocalFileConfigRepositoryTest {
private File someBaseDir;
private String someNamespace;
private ConfigRepository upstreamRepo;
......@@ -40,6 +49,7 @@ public class LocalFileConfigRepositoryTest {
private String defaultKey;
private String defaultValue;
private ConfigSourceType someSourceType;
private MockConfigUtil configUtil;
@Before
public void setUp() throws Exception {
......@@ -57,7 +67,10 @@ public class LocalFileConfigRepositoryTest {
when(upstreamRepo.getSourceType()).thenReturn(someSourceType);
MockInjector.reset();
MockInjector.setInstance(ConfigUtil.class, new MockConfigUtil());
configUtil = new MockConfigUtil();
MockInjector.setInstance(ConfigUtil.class, configUtil);
MockInjector.setInstance(PropertiesFactory.class, new DefaultPropertiesFactory());
}
@After
......@@ -110,7 +123,8 @@ public class LocalFileConfigRepositoryTest {
Files.write(defaultKey + "=" + someValue, file, Charsets.UTF_8);
LocalFileConfigRepository localRepo = new LocalFileConfigRepository(someNamespace, upstreamRepo);
LocalFileConfigRepository localRepo = new LocalFileConfigRepository(someNamespace,
upstreamRepo);
localRepo.setLocalCacheDir(someBaseDir, true);
Properties properties = localRepo.getConfig();
......@@ -127,9 +141,20 @@ public class LocalFileConfigRepositoryTest {
Properties result = localFileConfigRepository.getConfig();
if (!isJDK11() || (isJDK11() && configUtil.isPropertiesOrderEnabled())) {
assertThat(
"LocalFileConfigRepository's properties should be the same as fallback repo's when there is no local cache",
result.entrySet(), equalTo(someProperties.entrySet()));
} else {
//In JDK11 Properties return EntrySet without customize equals implementation, so equalTo will check
//whether they are same object. This is why two statements are used to check items in entryset.
assertThat(
"LocalFileConfigRepository's properties should be the same as fallback repo's when there is no local cache",
result.entrySet(), everyItem(isIn(someProperties.entrySet())));
assertThat(
"LocalFileConfigRepository's properties should be the same as fallback repo's when there is no local cache",
someProperties.entrySet(), everyItem(isIn(result.entrySet())));
}
assertEquals(someSourceType, localFileConfigRepository.getSourceType());
}
......@@ -148,9 +173,20 @@ public class LocalFileConfigRepositoryTest {
Properties anotherProperties = anotherLocalRepoWithNoFallback.getConfig();
if (!isJDK11() || (isJDK11() && configUtil.isPropertiesOrderEnabled())) {
assertThat(
"LocalFileConfigRepository should persist local cache files and return that afterwards",
someProperties.entrySet(), equalTo(anotherProperties.entrySet()));
} else {
//In JDK11 Properties return EntrySet without customize equals implementation, so equalTo will check
//whether they are same object. This is why two statements are used to check items in entryset.
assertThat(
"LocalFileConfigRepository's properties should be the same as fallback repo's when there is no local cache",
someProperties.entrySet(), everyItem(isIn(anotherProperties.entrySet())));
assertThat(
"LocalFileConfigRepository's properties should be the same as fallback repo's when there is no local cache",
anotherProperties.entrySet(), everyItem(isIn(someProperties.entrySet())));
}
assertEquals(someSourceType, localRepo.getSourceType());
}
......@@ -185,6 +221,7 @@ public class LocalFileConfigRepositoryTest {
}
public static class MockConfigUtil extends ConfigUtil {
@Override
public String getAppId() {
return someAppId;
......@@ -194,6 +231,10 @@ public class LocalFileConfigRepositoryTest {
public String getCluster() {
return someCluster;
}
public String getJavaVersion() {
return System.getProperty("java.version");
}
}
private File createLocalCachePropertyFile(Properties properties) throws IOException {
......@@ -209,4 +250,8 @@ public class LocalFileConfigRepositoryTest {
}
return file;
}
private boolean isJDK11() {
return configUtil.getJavaVersion().startsWith("11.");
}
}
......@@ -7,8 +7,12 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
import com.ctrip.framework.apollo.ConfigFileChangeListener;
import com.ctrip.framework.apollo.build.MockInjector;
import com.ctrip.framework.apollo.enums.PropertyChangeType;
import com.ctrip.framework.apollo.model.ConfigFileChangeEvent;
import com.ctrip.framework.apollo.util.ConfigUtil;
import com.ctrip.framework.apollo.util.factory.DefaultPropertiesFactory;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import com.google.common.util.concurrent.SettableFuture;
import java.util.Properties;
......@@ -26,6 +30,7 @@ import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
*/
@RunWith(MockitoJUnitRunner.class)
public class PropertiesConfigFileTest {
private String someNamespace;
@Mock
private ConfigRepository configRepository;
......@@ -33,6 +38,9 @@ public class PropertiesConfigFileTest {
@Before
public void setUp() throws Exception {
someNamespace = "someName";
MockInjector.reset();
MockInjector.setInstance(ConfigUtil.class, new ConfigUtil());
MockInjector.setInstance(PropertiesFactory.class, new DefaultPropertiesFactory());
}
@Test
......
package com.ctrip.framework.apollo.internals;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
......@@ -22,6 +23,8 @@ import com.ctrip.framework.apollo.core.signature.Signature;
import com.ctrip.framework.apollo.enums.ConfigSourceType;
import com.ctrip.framework.apollo.exceptions.ApolloConfigException;
import com.ctrip.framework.apollo.util.ConfigUtil;
import com.ctrip.framework.apollo.util.factory.DefaultPropertiesFactory;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import com.ctrip.framework.apollo.util.http.HttpRequest;
import com.ctrip.framework.apollo.util.http.HttpResponse;
import com.ctrip.framework.apollo.util.http.HttpUtil;
......@@ -37,6 +40,7 @@ import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletResponse;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -45,12 +49,14 @@ import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.springframework.test.util.ReflectionTestUtils;
/**
* Created by Jason on 4/9/16.
*/
@RunWith(MockitoJUnitRunner.class)
public class RemoteConfigRepositoryTest {
@Mock
private ConfigServiceLocator configServiceLocator;
private String someNamespace;
......@@ -92,16 +98,26 @@ public class RemoteConfigRepositoryTest {
MockInjector.setInstance(RemoteConfigLongPollService.class, remoteConfigLongPollService);
System.setProperty(PropertiesFactory.APOLLO_PROPERTY_ORDER_ENABLE, "true");
PropertiesFactory propertiesFactory = new DefaultPropertiesFactory();
MockInjector.setInstance(PropertiesFactory.class, propertiesFactory);
someAppId = "someAppId";
someCluster = "someCluster";
}
@After
public void tearDown() throws Exception {
System.clearProperty(PropertiesFactory.APOLLO_PROPERTY_ORDER_ENABLE);
}
@Test
public void testLoadConfig() throws Exception {
String someKey = "someKey";
String someValue = "someValue";
Map<String, String> configurations = Maps.newHashMap();
Map<String, String> configurations = Maps.newLinkedHashMap();
configurations.put(someKey, someValue);
configurations.put("someKey2", "someValue2");
ApolloConfig someApolloConfig = assembleApolloConfig(configurations);
when(someResponse.getStatusCode()).thenReturn(200);
......@@ -111,9 +127,16 @@ public class RemoteConfigRepositoryTest {
Properties config = remoteConfigRepository.getConfig();
assertEquals(configurations, config);
// assertEquals(configurations, config);
assertTrue(configurations.equals(config));
assertEquals(ConfigSourceType.REMOTE, remoteConfigRepository.getSourceType());
remoteConfigLongPollService.stopLongPollingRefresh();
if (configUtil.isPropertiesOrderEnabled()) {
String[] actualArrays = config.keySet().toArray(new String[]{});
String[] expectedArrays = {"someKey", "someKey2"};
assertArrayEquals(expectedArrays, actualArrays);
}
}
@Test
......@@ -236,7 +259,8 @@ public class RemoteConfigRepositoryTest {
verify(someListener, times(1)).onRepositoryChange(eq(someNamespace), captor.capture());
assertEquals(newConfigurations, captor.getValue());
final ArgumentCaptor<HttpRequest> httpRequestArgumentCaptor = ArgumentCaptor.forClass(HttpRequest.class);
final ArgumentCaptor<HttpRequest> httpRequestArgumentCaptor = ArgumentCaptor
.forClass(HttpRequest.class);
verify(httpUtil, atLeast(2)).doGet(httpRequestArgumentCaptor.capture(), eq(ApolloConfig.class));
HttpRequest request = httpRequestArgumentCaptor.getValue();
......@@ -265,7 +289,8 @@ public class RemoteConfigRepositoryTest {
when(someApolloConfig.getReleaseKey()).thenReturn(someReleaseKey);
String queryConfigUrl = remoteConfigRepository
.assembleQueryConfigUrl(someUri, someAppId, someCluster, someNamespace, null, notificationMessages,
.assembleQueryConfigUrl(someUri, someAppId, someCluster, someNamespace, null,
notificationMessages,
someApolloConfig);
remoteConfigLongPollService.stopLongPollingRefresh();
......@@ -275,7 +300,8 @@ public class RemoteConfigRepositoryTest {
assertTrue(queryConfigUrl
.contains("releaseKey=20160705193346-583078ef5716c055%2B20160705193308-31c471ddf9087c3f"));
assertTrue(queryConfigUrl
.contains("messages=" + UrlEscapers.urlFormParameterEscaper().escape(gson.toJson(notificationMessages))));
.contains("messages=" + UrlEscapers.urlFormParameterEscaper()
.escape(gson.toJson(notificationMessages))));
}
private ApolloConfig assembleApolloConfig(Map<String, String> configurations) {
......@@ -291,6 +317,7 @@ public class RemoteConfigRepositoryTest {
}
public static class MockConfigUtil extends ConfigUtil {
@Override
public String getAppId() {
return someAppId;
......@@ -338,9 +365,10 @@ public class RemoteConfigRepositoryTest {
}
public static class MockHttpUtil extends HttpUtil {
@Override
public <T> HttpResponse<T> doGet(HttpRequest httpRequest, Class<T> responseType) {
if (someResponse.getStatusCode() == 200 || someResponse.getStatusCode() == 304 ) {
if (someResponse.getStatusCode() == 200 || someResponse.getStatusCode() == 304) {
return (HttpResponse<T>) someResponse;
}
throw new ApolloConfigException(String.format("Http request failed due to status code: %d",
......
......@@ -4,10 +4,14 @@ import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.ctrip.framework.apollo.build.MockInjector;
import com.ctrip.framework.apollo.enums.ConfigSourceType;
import com.ctrip.framework.apollo.util.factory.DefaultPropertiesFactory;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -27,6 +31,7 @@ import com.google.common.util.concurrent.SettableFuture;
*/
@RunWith(MockitoJUnitRunner.class)
public class SimpleConfigTest {
private String someNamespace;
@Mock
private ConfigRepository configRepository;
......@@ -35,6 +40,15 @@ public class SimpleConfigTest {
@Before
public void setUp() throws Exception {
someNamespace = "someName";
System.setProperty(PropertiesFactory.APOLLO_PROPERTY_ORDER_ENABLE, "true");
PropertiesFactory propertiesFactory = new DefaultPropertiesFactory();
MockInjector.setInstance(PropertiesFactory.class, propertiesFactory);
}
@After
public void tearDown() throws Exception {
System.clearProperty(PropertiesFactory.APOLLO_PROPERTY_ORDER_ENABLE);
}
@Test
......
......@@ -7,12 +7,16 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
import com.ctrip.framework.apollo.ConfigFileChangeListener;
import com.ctrip.framework.apollo.build.MockInjector;
import com.ctrip.framework.apollo.enums.PropertyChangeType;
import com.ctrip.framework.apollo.model.ConfigFileChangeEvent;
import com.ctrip.framework.apollo.util.factory.DefaultPropertiesFactory;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import com.google.common.util.concurrent.SettableFuture;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -27,6 +31,7 @@ import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
*/
@RunWith(MockitoJUnitRunner.class)
public class XmlConfigFileTest {
private String someNamespace;
@Mock
private ConfigRepository configRepository;
......@@ -34,6 +39,15 @@ public class XmlConfigFileTest {
@Before
public void setUp() throws Exception {
someNamespace = "someName";
System.setProperty(PropertiesFactory.APOLLO_PROPERTY_ORDER_ENABLE, "true");
PropertiesFactory propertiesFactory = new DefaultPropertiesFactory();
MockInjector.setInstance(PropertiesFactory.class, propertiesFactory);
}
@After
public void tearDown() throws Exception {
System.clearProperty(PropertiesFactory.APOLLO_PROPERTY_ORDER_ENABLE);
}
@Test
......
......@@ -7,8 +7,12 @@ import com.ctrip.framework.apollo.build.MockInjector;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.enums.ConfigSourceType;
import com.ctrip.framework.apollo.exceptions.ApolloConfigException;
import com.ctrip.framework.apollo.util.ConfigUtil;
import com.ctrip.framework.apollo.util.factory.DefaultPropertiesFactory;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import com.ctrip.framework.apollo.util.yaml.YamlParser;
import java.util.Properties;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -17,6 +21,7 @@ import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class YamlConfigFileTest {
private String someNamespace;
@Mock
private ConfigRepository configRepository;
......@@ -29,8 +34,17 @@ public class YamlConfigFileTest {
public void setUp() throws Exception {
someNamespace = "someName";
System.setProperty(PropertiesFactory.APOLLO_PROPERTY_ORDER_ENABLE, "true");
MockInjector.reset();
MockInjector.setInstance(YamlParser.class, yamlParser);
MockInjector.setInstance(ConfigUtil.class, new ConfigUtil());
MockInjector.setInstance(PropertiesFactory.class, new DefaultPropertiesFactory());
}
@After
public void tearDown() throws Exception {
System.clearProperty(PropertiesFactory.APOLLO_PROPERTY_ORDER_ENABLE);
}
@Test
......@@ -54,6 +68,30 @@ public class YamlConfigFileTest {
assertSame(yamlProperties, configFile.asProperties());
}
@Test
public void testWhenHasContentWithOrder() throws Exception {
Properties someProperties = new Properties();
String key = ConfigConsts.CONFIG_FILE_CONTENT_KEY;
String someContent = "someKey: 'someValue'\nsomeKey2: 'someValue2'";
someProperties.setProperty(key, someContent);
someSourceType = ConfigSourceType.LOCAL;
Properties yamlProperties = new YamlParser().yamlToProperties(someContent);
when(configRepository.getConfig()).thenReturn(someProperties);
when(configRepository.getSourceType()).thenReturn(someSourceType);
when(yamlParser.yamlToProperties(someContent)).thenReturn(yamlProperties);
YamlConfigFile configFile = new YamlConfigFile(someNamespace, configRepository);
assertSame(someContent, configFile.getContent());
assertSame(yamlProperties, configFile.asProperties());
String[] actualArrays = configFile.asProperties().keySet().toArray(new String[]{});
String[] expectedArrays = {"someKey", "someKey2"};
assertArrayEquals(expectedArrays, actualArrays);
}
@Test
public void testWhenHasNoContent() throws Exception {
when(configRepository.getConfig()).thenReturn(null);
......@@ -78,7 +116,8 @@ public class YamlConfigFileTest {
when(configRepository.getConfig()).thenReturn(someProperties);
when(configRepository.getSourceType()).thenReturn(someSourceType);
when(yamlParser.yamlToProperties(someInvalidContent)).thenThrow(new RuntimeException("some exception"));
when(yamlParser.yamlToProperties(someInvalidContent))
.thenThrow(new RuntimeException("some exception"));
YamlConfigFile configFile = new YamlConfigFile(someNamespace, configRepository);
......
......@@ -2,6 +2,7 @@ package com.ctrip.framework.apollo.util;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import java.io.File;
import org.junit.After;
import org.junit.Test;
......@@ -15,6 +16,7 @@ import static org.mockito.Mockito.when;
* @author Jason Song(song_s@ctrip.com)
*/
public class ConfigUtilTest {
@After
public void tearDown() throws Exception {
System.clearProperty(ConfigConsts.APOLLO_CLUSTER_KEY);
......@@ -27,6 +29,7 @@ public class ConfigUtilTest {
System.clearProperty("apollo.longPollingInitialDelayInMills");
System.clearProperty("apollo.autoUpdateInjectedSpringProperties");
System.clearProperty("apollo.cacheDir");
System.clearProperty(PropertiesFactory.APOLLO_PROPERTY_ORDER_ENABLE);
}
@Test
......@@ -162,7 +165,8 @@ public class ConfigUtilTest {
@Test
public void testCustomizeLongPollingInitialDelayInMills() throws Exception {
long someLongPollingDelayInMills = 1;
System.setProperty("apollo.longPollingInitialDelayInMills", String.valueOf(someLongPollingDelayInMills));
System.setProperty("apollo.longPollingInitialDelayInMills",
String.valueOf(someLongPollingDelayInMills));
ConfigUtil configUtil = new ConfigUtil();
......@@ -221,4 +225,16 @@ public class ConfigUtilTest {
assertEquals("/opt/data/" + someAppId, configUtil.getDefaultLocalCacheDir());
}
@Test
public void testCustomizePropertiesOrdered() {
boolean propertiesOrdered = true;
System.setProperty(PropertiesFactory.APOLLO_PROPERTY_ORDER_ENABLE,
String.valueOf(propertiesOrdered));
ConfigUtil configUtil = new ConfigUtil();
assertEquals(propertiesOrdered,
configUtil.isPropertiesOrderEnabled());
}
}
package com.ctrip.framework.apollo.util;
import static org.junit.Assert.*;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Properties;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
public class OrderedPropertiesTest {
private OrderedProperties orderedProperties;
private Properties legacyProperties;
@Before
public void setUp() {
orderedProperties = new OrderedProperties();
orderedProperties.setProperty("key1", "value1");
orderedProperties.setProperty("key2", "value2");
}
@Test
public void testOrderedPropertiesInvokedAsLegacyProperties() {
legacyProperties = orderedProperties;
assertEquals(orderedProperties.size(), legacyProperties.size());
legacyProperties.put("key3", "value3");
assertEquals(orderedProperties.size(), legacyProperties.size());
assertEquals(orderedProperties.getProperty("key3"), legacyProperties.getProperty("key3"));
assertEquals(orderedProperties.get("key3"), legacyProperties.get("key3"));
assertEquals(orderedProperties.containsKey("key2"), legacyProperties.containsKey("key2"));
assertEquals(orderedProperties.containsValue("key2"), legacyProperties.containsValue("key2"));
assertEquals(orderedProperties.containsValue("value2"),
legacyProperties.containsValue("value2"));
assertEquals(orderedProperties.entrySet(), legacyProperties.entrySet());
assertEquals(orderedProperties.keySet(), legacyProperties.keySet());
}
@Test
public void testClear() {
orderedProperties.clear();
assertEquals(0, orderedProperties.size());
assertTrue(orderedProperties.isEmpty());
}
@Test
public void testClone() {
OrderedProperties clone = (OrderedProperties) orderedProperties.clone();
assertNotSame(clone, orderedProperties);
assertEquals(orderedProperties, clone);
}
@Test
public void testRemove() {
Object value1 = orderedProperties.remove("key1");
assertEquals("value1", value1);
value1 = orderedProperties.remove("key1");
assertNull(value1);
assertNull(orderedProperties.get("key1"));
assertFalse(orderedProperties.keySet().contains("key1"));
}
@Test
public void testValues() {
Collection<Object> values = orderedProperties.values();
assertEquals(2, values.size());
assertTrue(values.contains("value1"));
assertTrue(values.contains("value2"));
}
@Test(expected = NullPointerException.class)
public void testPutNull() {
orderedProperties.put("key3", null);
}
@Test
public void testPropertyNames() {
Enumeration<String> propertyNames = (Enumeration<String>) orderedProperties.propertyNames();
assertTrue(propertyNames.nextElement().equals("key1"));
assertTrue(propertyNames.nextElement().equals("key2"));
}
@Test
public void testKeys() {
Enumeration<Object> keys = orderedProperties.keys();
assertTrue(keys.hasMoreElements());
assertEquals("key1", keys.nextElement());
assertTrue(keys.hasMoreElements());
assertEquals("key2", keys.nextElement());
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment