Commit b73ecacc by Jason Song

Add local cache file persistence

parent 761dd307
...@@ -2,12 +2,16 @@ package com.ctrip.apollo.internals; ...@@ -2,12 +2,16 @@ package com.ctrip.apollo.internals;
import com.ctrip.apollo.Config; import com.ctrip.apollo.Config;
import com.ctrip.apollo.core.utils.ClassLoaderUtil; import com.ctrip.apollo.core.utils.ClassLoaderUtil;
import com.ctrip.apollo.util.ConfigUtil;
import com.dianping.cat.Cat; import com.dianping.cat.Cat;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties; import java.util.Properties;
/** /**
...@@ -19,12 +23,14 @@ public class DefaultConfig implements Config, ConfigLoader { ...@@ -19,12 +23,14 @@ public class DefaultConfig implements Config, ConfigLoader {
private Properties m_resourceProperties; private Properties m_resourceProperties;
private Properties m_fileProperties; private Properties m_fileProperties;
private ConfigLoader m_fallbackLoader; private ConfigLoader m_fallbackLoader;
private ConfigUtil m_configUtil;
public DefaultConfig(File baseDir, String namespace, ConfigLoader fallbackLoader) { public DefaultConfig(File baseDir, String namespace, ConfigLoader fallbackLoader, ConfigUtil configUtil) {
m_namespace = namespace; m_namespace = namespace;
m_baseDir = baseDir; m_baseDir = baseDir;
m_resourceProperties = loadFromResource(m_namespace); m_resourceProperties = loadFromResource(m_namespace);
m_fallbackLoader = fallbackLoader; m_fallbackLoader = fallbackLoader;
m_configUtil = configUtil;
this.initLocalConfig(); this.initLocalConfig();
} }
...@@ -81,12 +87,29 @@ public class DefaultConfig implements Config, ConfigLoader { ...@@ -81,12 +87,29 @@ public class DefaultConfig implements Config, ConfigLoader {
return properties; return properties;
} }
private Properties loadFromFile(File baseDir, String namespace) { void initLocalConfig() {
m_fileProperties = this.loadFromLocalCacheFile(m_baseDir, m_namespace);
//TODO check if local file is expired
if (m_fileProperties != null) {
return;
}
if (m_fallbackLoader != null) {
m_fileProperties = m_fallbackLoader.loadConfig();
}
if (m_fileProperties == null) {
throw new RuntimeException(
String.format("Init Apollo Local Config failed - namespace: %s",
m_namespace));
}
persistLocalCacheFile(m_baseDir, m_namespace);
}
private Properties loadFromLocalCacheFile(File baseDir, String namespace) {
if (baseDir == null) { if (baseDir == null) {
return null; return null;
} }
File file = new File(baseDir, namespace + ".properties"); File file = assembleLocalCacheFile(baseDir, namespace);
Properties properties = null; Properties properties = null;
if (file.isFile() && file.canRead()) { if (file.isFile() && file.canRead()) {
...@@ -108,23 +131,43 @@ public class DefaultConfig implements Config, ConfigLoader { ...@@ -108,23 +131,43 @@ public class DefaultConfig implements Config, ConfigLoader {
// ignore // ignore
} }
} }
} else {
//TODO error handling
} }
return properties; return properties;
} }
void initLocalConfig() { void persistLocalCacheFile(File baseDir, String namespace) {
m_fileProperties = this.loadFromFile(m_baseDir, m_namespace); if (baseDir == null) {
//TODO check if local file is expired return;
if (m_fileProperties == null && m_fallbackLoader != null) { }
m_fileProperties = m_fallbackLoader.loadConfig(); File file = assembleLocalCacheFile(baseDir, namespace);
OutputStream out = null;
try {
out = new FileOutputStream(file);
m_fileProperties.store(out, "Persisted by DefaultConfig");
} catch (FileNotFoundException ex) {
Cat.logError(ex);
} catch (IOException ex) {
Cat.logError(ex);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
//ignore
} }
if (m_fileProperties == null) {
throw new RuntimeException(
String.format("Init Apollo Local Config failed - namespace: %s",
m_namespace));
} }
//TODO persist file }
}
File assembleLocalCacheFile(File baseDir, String namespace) {
String fileName = String.format("%s-%s-%s.properties", m_configUtil.getAppId(),
m_configUtil.getCluster(), namespace);
return new File(baseDir, fileName);
} }
@Override @Override
......
...@@ -28,14 +28,15 @@ public class RemoteConfig implements Config, ConfigLoader { ...@@ -28,14 +28,15 @@ public class RemoteConfig implements Config, ConfigLoader {
private RestTemplate m_restTemplate; private RestTemplate m_restTemplate;
private ConfigServiceLocator m_serviceLocator; private ConfigServiceLocator m_serviceLocator;
private String m_namespace; private String m_namespace;
private ConfigUtil m_configUtil;
private Properties m_remoteProperties; private Properties m_remoteProperties;
public RemoteConfig(RestTemplate restTemplate, public RemoteConfig(RestTemplate restTemplate,
ConfigServiceLocator locator, String namespace) { ConfigServiceLocator locator, String namespace, ConfigUtil configUtil) {
this.m_restTemplate = restTemplate; this.m_restTemplate = restTemplate;
this.m_serviceLocator = locator; this.m_serviceLocator = locator;
this.m_namespace = namespace; this.m_namespace = namespace;
this.m_configUtil = configUtil;
this.initialize(); this.initialize();
} }
...@@ -61,8 +62,8 @@ public class RemoteConfig implements Config, ConfigLoader { ...@@ -61,8 +62,8 @@ public class RemoteConfig implements Config, ConfigLoader {
} }
private ApolloConfig loadApolloConfig() { private ApolloConfig loadApolloConfig() {
String appId = ConfigUtil.getInstance().getAppId(); String appId = m_configUtil.getAppId();
String cluster = ConfigUtil.getInstance().getCluster(); String cluster = m_configUtil.getCluster();
try { try {
ApolloConfig result = ApolloConfig result =
this.getRemoteConfig(m_restTemplate, getConfigServiceUrl(), this.getRemoteConfig(m_restTemplate, getConfigServiceUrl(),
......
...@@ -5,6 +5,7 @@ import com.ctrip.apollo.core.utils.ClassLoaderUtil; ...@@ -5,6 +5,7 @@ import com.ctrip.apollo.core.utils.ClassLoaderUtil;
import com.ctrip.apollo.internals.ConfigServiceLocator; import com.ctrip.apollo.internals.ConfigServiceLocator;
import com.ctrip.apollo.internals.DefaultConfig; import com.ctrip.apollo.internals.DefaultConfig;
import com.ctrip.apollo.internals.RemoteConfig; import com.ctrip.apollo.internals.RemoteConfig;
import com.ctrip.apollo.util.ConfigUtil;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.unidal.lookup.annotation.Named; import org.unidal.lookup.annotation.Named;
...@@ -21,16 +22,21 @@ public class DefaultConfigFactory implements ConfigFactory { ...@@ -21,16 +22,21 @@ public class DefaultConfigFactory implements ConfigFactory {
public DefaultConfigFactory() { public DefaultConfigFactory() {
m_baseDir = new File(ClassLoaderUtil.getClassPath() + CONFIG_DIR); m_baseDir = new File(ClassLoaderUtil.getClassPath() + CONFIG_DIR);
if (!m_baseDir.exists()) {
m_baseDir.mkdir();
}
} }
@Override @Override
public Config create(String namespace) { public Config create(String namespace) {
RemoteConfig remoteConfig = this.createRemoteConfig(namespace); RemoteConfig remoteConfig = this.createRemoteConfig(namespace);
DefaultConfig defaultConfig = new DefaultConfig(m_baseDir, namespace, remoteConfig); DefaultConfig defaultConfig =
new DefaultConfig(m_baseDir, namespace, remoteConfig, ConfigUtil.getInstance());
return defaultConfig; return defaultConfig;
} }
public RemoteConfig createRemoteConfig(String namespace) { public RemoteConfig createRemoteConfig(String namespace) {
return new RemoteConfig(new RestTemplate(), new ConfigServiceLocator(), namespace); return new RemoteConfig(new RestTemplate(), new ConfigServiceLocator(), namespace,
ConfigUtil.getInstance());
} }
} }
...@@ -4,6 +4,7 @@ import com.google.common.base.Charsets; ...@@ -4,6 +4,7 @@ import com.google.common.base.Charsets;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.ctrip.apollo.core.utils.ClassLoaderUtil; import com.ctrip.apollo.core.utils.ClassLoaderUtil;
import com.ctrip.apollo.util.ConfigUtil;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
...@@ -28,16 +29,15 @@ public class DefaultConfigTest extends ComponentTestCase { ...@@ -28,16 +29,15 @@ public class DefaultConfigTest extends ComponentTestCase {
private String someNamespace; private String someNamespace;
private ConfigLoader fallbackLoader; private ConfigLoader fallbackLoader;
private Properties someProperties; private Properties someProperties;
private ConfigUtil someConfigUtil;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();
someBaseDir = new File("src/test/resources/config-cache"); someBaseDir = new File("src/test/resources/config-cache");
someBaseDir.deleteOnExit();
someBaseDir.mkdir(); someBaseDir.mkdir();
someResourceDir = new File(ClassLoaderUtil.getClassPath() + "/META-INF/config"); someResourceDir = new File(ClassLoaderUtil.getClassPath() + "/META-INF/config");
someResourceDir.deleteOnExit();
someResourceDir.mkdir(); someResourceDir.mkdir();
someNamespace = "someName"; someNamespace = "someName";
...@@ -45,23 +45,49 @@ public class DefaultConfigTest extends ComponentTestCase { ...@@ -45,23 +45,49 @@ public class DefaultConfigTest extends ComponentTestCase {
someProperties.setProperty("defaultKey", "defaultValue"); someProperties.setProperty("defaultKey", "defaultValue");
fallbackLoader = mock(RemoteConfig.class); fallbackLoader = mock(RemoteConfig.class);
when(fallbackLoader.loadConfig()).thenReturn(someProperties); when(fallbackLoader.loadConfig()).thenReturn(someProperties);
String someAppId = "someApp";
String someCluster = "someCluster";
someConfigUtil = mock(ConfigUtil.class);
when(someConfigUtil.getAppId()).thenReturn(someAppId);
when(someConfigUtil.getCluster()).thenReturn(someCluster);
} }
@Override @Override
@After @After
public void tearDown() throws Exception { public void tearDown() throws Exception {
recursiveDelete(someBaseDir);
recursiveDelete(someResourceDir);
}
//helper method to clean created files
private void recursiveDelete(File file) {
if (!file.exists()) {
return;
}
if (file.isDirectory()) {
for (File f : file.listFiles()) {
recursiveDelete(f);
}
}
file.delete();
}
private String assembleLocalCacheFileName() {
return String.format("%s-%s-%s.properties", someConfigUtil.getAppId(),
someConfigUtil.getCluster(), someNamespace);
} }
@Test @Test
public void testGetPropertyWithLocalFile() throws Exception { public void testGetPropertyWithLocalFile() throws Exception {
File file = new File(someBaseDir, someNamespace + ".properties"); File file = new File(someBaseDir, assembleLocalCacheFileName());
String someKey = "someKey"; String someKey = "someKey";
String someValue = "someValue"; String someValue = "someValue";
Files.write(someKey + "=" + someValue, file, Charsets.UTF_8); Files.write(someKey + "=" + someValue, file, Charsets.UTF_8);
DefaultConfig defaultConfig = new DefaultConfig(someBaseDir, someNamespace, fallbackLoader); DefaultConfig defaultConfig = new DefaultConfig(someBaseDir, someNamespace, fallbackLoader, someConfigUtil);
file.delete(); file.delete();
...@@ -80,7 +106,9 @@ public class DefaultConfigTest extends ComponentTestCase { ...@@ -80,7 +106,9 @@ public class DefaultConfigTest extends ComponentTestCase {
Files.write(someKey + "=" + someValue, file, Charsets.UTF_8); Files.write(someKey + "=" + someValue, file, Charsets.UTF_8);
DefaultConfig defaultConfig = new DefaultConfig(someBaseDir, someNamespace, fallbackLoader); DefaultConfig
defaultConfig =
new DefaultConfig(someBaseDir, someNamespace, fallbackLoader, someConfigUtil);
file.delete(); file.delete();
assertEquals(someValue, defaultConfig.getProperty(someKey, null)); assertEquals(someValue, defaultConfig.getProperty(someKey, null));
...@@ -101,7 +129,7 @@ public class DefaultConfigTest extends ComponentTestCase { ...@@ -101,7 +129,7 @@ public class DefaultConfigTest extends ComponentTestCase {
System.setProperty(someKey, someSystemPropertyValue); System.setProperty(someKey, someSystemPropertyValue);
//set up local file //set up local file
File localCacheFile = new File(someBaseDir, someNamespace + ".properties"); File localCacheFile = new File(someBaseDir, assembleLocalCacheFileName());
Files.write(someKey + "=" + someLocalFileValue, localCacheFile, Charsets.UTF_8); Files.write(someKey + "=" + someLocalFileValue, localCacheFile, Charsets.UTF_8);
Files.append(System.getProperty("line.separator"), localCacheFile, Charsets.UTF_8); Files.append(System.getProperty("line.separator"), localCacheFile, Charsets.UTF_8);
Files.append(anotherKey + "=" + someLocalFileValue, localCacheFile, Charsets.UTF_8); Files.append(anotherKey + "=" + someLocalFileValue, localCacheFile, Charsets.UTF_8);
...@@ -114,7 +142,9 @@ public class DefaultConfigTest extends ComponentTestCase { ...@@ -114,7 +142,9 @@ public class DefaultConfigTest extends ComponentTestCase {
Files.append(System.getProperty("line.separator"), resourceFile, Charsets.UTF_8); Files.append(System.getProperty("line.separator"), resourceFile, Charsets.UTF_8);
Files.append(lastKey + "=" + someResourceValue, resourceFile, Charsets.UTF_8); Files.append(lastKey + "=" + someResourceValue, resourceFile, Charsets.UTF_8);
DefaultConfig defaultConfig = new DefaultConfig(someBaseDir, someNamespace, fallbackLoader); DefaultConfig
defaultConfig =
new DefaultConfig(someBaseDir, someNamespace, fallbackLoader, someConfigUtil);
String someKeyValue = defaultConfig.getProperty(someKey, null); String someKeyValue = defaultConfig.getProperty(someKey, null);
String anotherKeyValue = defaultConfig.getProperty(anotherKey, null); String anotherKeyValue = defaultConfig.getProperty(anotherKey, null);
...@@ -131,7 +161,9 @@ public class DefaultConfigTest extends ComponentTestCase { ...@@ -131,7 +161,9 @@ public class DefaultConfigTest extends ComponentTestCase {
@Test @Test
public void testInitLocalConfigWithNoLocalFile() throws Exception { public void testInitLocalConfigWithNoLocalFile() throws Exception {
DefaultConfig defaultConfig = new DefaultConfig(someBaseDir, someNamespace, fallbackLoader); DefaultConfig
defaultConfig =
new DefaultConfig(someBaseDir, someNamespace, fallbackLoader, someConfigUtil);
Properties result = defaultConfig.loadConfig(); Properties result = defaultConfig.loadConfig();
...@@ -139,4 +171,24 @@ public class DefaultConfigTest extends ComponentTestCase { ...@@ -139,4 +171,24 @@ public class DefaultConfigTest extends ComponentTestCase {
"Default config's properties should be the same as fallback loader's when there is no local cache", "Default config's properties should be the same as fallback loader's when there is no local cache",
result.entrySet(), equalTo(someProperties.entrySet())); result.entrySet(), equalTo(someProperties.entrySet()));
} }
@Test
public void testInitLocalConfigWithNoLocalFileMultipleTimes() throws Exception {
Properties anotherProperties = new Properties();
anotherProperties.setProperty("anotherKey", "anotherValue");
ConfigLoader anotherLoader = mock(RemoteConfig.class);
when(anotherLoader.loadConfig()).thenReturn(anotherProperties);
DefaultConfig
defaultConfig =
new DefaultConfig(someBaseDir, someNamespace, fallbackLoader, someConfigUtil);
DefaultConfig
anotherConfig =
new DefaultConfig(someBaseDir, someNamespace, anotherLoader, someConfigUtil);
assertThat(
"Default config should persist local cache files and return that afterwards",
defaultConfig.loadConfig().entrySet(), equalTo(anotherConfig.loadConfig().entrySet()));
}
} }
...@@ -5,6 +5,7 @@ import com.google.common.collect.Maps; ...@@ -5,6 +5,7 @@ import com.google.common.collect.Maps;
import com.ctrip.apollo.core.dto.ApolloConfig; import com.ctrip.apollo.core.dto.ApolloConfig;
import com.ctrip.apollo.core.dto.ServiceDTO; import com.ctrip.apollo.core.dto.ServiceDTO;
import com.ctrip.apollo.util.ConfigUtil;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
...@@ -40,6 +41,8 @@ public class RemoteConfigTest { ...@@ -40,6 +41,8 @@ public class RemoteConfigTest {
private String someNamespace; private String someNamespace;
@Mock @Mock
private ResponseEntity<ApolloConfig> someResponse; private ResponseEntity<ApolloConfig> someResponse;
@Mock
private ConfigUtil someConfigUtil;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
...@@ -47,6 +50,11 @@ public class RemoteConfigTest { ...@@ -47,6 +50,11 @@ public class RemoteConfigTest {
String someServerUrl = "http://someServer"; String someServerUrl = "http://someServer";
mockConfigServiceLocator(someServerUrl); mockConfigServiceLocator(someServerUrl);
String someAppId = "someApp";
String someCluster = "someCluster";
when(someConfigUtil.getAppId()).thenReturn(someAppId);
when(someConfigUtil.getCluster()).thenReturn(someCluster);
} }
@Test @Test
...@@ -64,7 +72,7 @@ public class RemoteConfigTest { ...@@ -64,7 +72,7 @@ public class RemoteConfigTest {
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class),
eq(ApolloConfig.class), anyMap())).thenReturn(someResponse); eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
RemoteConfig remoteConfig = new RemoteConfig(restTemplate, configServiceLocator, someNamespace); RemoteConfig remoteConfig = new RemoteConfig(restTemplate, configServiceLocator, someNamespace, someConfigUtil);
assertEquals(someValue, remoteConfig.getProperty(someKey, null)); assertEquals(someValue, remoteConfig.getProperty(someKey, null));
assertEquals(someDefaultValue, remoteConfig.getProperty(someKeyNotExisted, someDefaultValue)); assertEquals(someDefaultValue, remoteConfig.getProperty(someKeyNotExisted, someDefaultValue));
...@@ -77,7 +85,7 @@ public class RemoteConfigTest { ...@@ -77,7 +85,7 @@ public class RemoteConfigTest {
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class),
eq(ApolloConfig.class), anyMap())).thenReturn(someResponse); eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
RemoteConfig remoteConfig = new RemoteConfig(restTemplate, configServiceLocator, someNamespace); new RemoteConfig(restTemplate, configServiceLocator, someNamespace, someConfigUtil);
} }
@Test @Test
...@@ -94,7 +102,7 @@ public class RemoteConfigTest { ...@@ -94,7 +102,7 @@ public class RemoteConfigTest {
eq(ApolloConfig.class), anyMap())).thenReturn(someResponse); eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
RemoteConfig remoteConfig = new RemoteConfig(restTemplate, configServiceLocator, someNamespace); RemoteConfig remoteConfig = new RemoteConfig(restTemplate, configServiceLocator, someNamespace, someConfigUtil);
Properties config = remoteConfig.loadConfig(); Properties config = remoteConfig.loadConfig();
......
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