提交 49c1b308 编写于 作者: J Jason Song

Add refresh config support and refactor

上级 51978dd6
......@@ -6,3 +6,4 @@ INSERT INTO Version (AppId, IsDeleted, Name, ReleaseId) VALUES (102, 0, '1.0', 2
INSERT INTO RELEASESNAPSHOT (ClusterName, IsDeleted, ReleaseId, Configurations) VALUES ('default', 0, 1, '{"apollo.foo":"bar", "apollo.bar":"foo"}');
INSERT INTO RELEASESNAPSHOT (ClusterName, IsDeleted, ReleaseId, Configurations) VALUES ('default', 0, 2, '{"demo.foo":"demo1", "demo.bar":"demo2"}');
INSERT INTO RELEASESNAPSHOT (ClusterName, IsDeleted, ReleaseId, Configurations) VALUES ('default', 0, 3, '{"apollo.foo":"another bar", "apollo.bar_new":"foo"}');
package com.ctrip.apollo.client;
import com.ctrip.apollo.client.loader.ConfigLoader;
import com.ctrip.apollo.client.loader.ConfigLoaderFactory;
import com.ctrip.apollo.client.loader.ConfigLoaderManager;
import com.ctrip.apollo.client.model.PropertyChange;
import com.ctrip.apollo.client.model.PropertySourceReloadResult;
import com.ctrip.apollo.client.util.ConfigUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
......@@ -19,6 +23,7 @@ import org.springframework.core.PriorityOrdered;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.MutablePropertySources;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
/**
......@@ -27,19 +32,18 @@ import java.util.concurrent.atomic.AtomicReference;
* @author Jason Song(song_s@ctrip.com)
*/
public class ApolloConfigManager implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ApplicationContextAware {
public static final String APOLLO_PROPERTY_SOURCE_NAME = "ApolloConfigProperties";
private static final Logger logger = LoggerFactory.getLogger(ApolloConfigManager.class);
private static AtomicReference<ApolloConfigManager> singletonProtector = new AtomicReference<ApolloConfigManager>();
private ConfigLoader configLoader;
private ConfigLoaderManager configLoaderManager;
private ConfigurableApplicationContext applicationContext;
private CompositePropertySource currentPropertySource;
private RefreshScope scope;
public ApolloConfigManager() {
if(!singletonProtector.compareAndSet(null, this)) {
throw new IllegalStateException("There should be only one ApolloConfigManager instance!");
}
this.configLoader = ConfigLoaderFactory.getInstance().getRemoteConfigLoader();
this.configLoaderManager = ConfigLoaderFactory.getInstance().getConfigLoaderManager();
}
@Override
......@@ -97,23 +101,42 @@ public class ApolloConfigManager implements BeanDefinitionRegistryPostProcessor,
}
/**
* Prepare property sources
* First try to load from remote
* If loading from remote failed, then fall back to local cached properties
* Initialize property sources
*/
void initializePropertySource() {
currentPropertySource = loadPropertySource();
//TODO stop application from starting when config cannot be loaded?
CompositePropertySource result = this.configLoaderManager.loadPropertySource();
updateEnvironmentPropertySource(result);
}
private void updateEnvironmentPropertySource(CompositePropertySource currentPropertySource) {
MutablePropertySources currentPropertySources = applicationContext.getEnvironment().getPropertySources();
if (currentPropertySources.contains(currentPropertySource.getName())) {
currentPropertySources.remove(currentPropertySource.getName());
currentPropertySources.replace(currentPropertySource.getName(), currentPropertySource);
return;
}
currentPropertySources.addFirst(currentPropertySource);
}
CompositePropertySource loadPropertySource() {
CompositePropertySource compositePropertySource = new CompositePropertySource(APOLLO_PROPERTY_SOURCE_NAME);
compositePropertySource.addPropertySource(configLoader.loadPropertySource());
return compositePropertySource;
public List<PropertyChange> updatePropertySource() {
PropertySourceReloadResult result = this.configLoaderManager.reloadPropertySource();
if (result.hasChanges()) {
updateEnvironmentPropertySource(result.getPropertySource());
refreshBeans();
}
return result.getChanges();
}
private void refreshBeans() {
if (this.scope == null) {
this.scope = applicationContext.getBean("refreshScope", RefreshScope.class);
}
if (this.scope == null) {
logger.error("Could not get refresh scope object, skip refresh beans");
}
this.scope.refreshAll();
}
}
package com.ctrip.apollo.client.enums;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public enum PropertyChangeType {
NEW("New"),
MODIFIED("Modified"),
DELETED("Deleted");
private String type;
PropertyChangeType(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
package com.ctrip.apollo.client.loader;
import org.springframework.core.env.CompositePropertySource;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.core.dto.ApolloConfig;
/**
* @author Jason Song(songs_ctrip.com)
*/
public interface ConfigLoader {
/**
* Load property source for client use
* @return property source
*/
CompositePropertySource loadPropertySource();
ApolloConfig loadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous);
void setFallBackLoader(ConfigLoader configLoader);
}
package com.ctrip.apollo.client.loader;
import com.ctrip.apollo.client.loader.impl.InMemoryConfigLoader;
import com.ctrip.apollo.client.loader.impl.LocalFileConfigLoader;
import com.ctrip.apollo.client.loader.impl.RemoteConfigLoader;
import com.ctrip.apollo.client.util.ConfigUtil;
import org.springframework.web.client.RestTemplate;
/**
* @author Jason Song(song_s@ctrip.com)
......@@ -15,7 +19,24 @@ public class ConfigLoaderFactory {
return configLoaderFactory;
}
public ConfigLoader getLocalFileConfigLoader() {
ConfigLoader configLoader = new LocalFileConfigLoader();
return configLoader;
}
public ConfigLoader getInMemoryConfigLoader() {
ConfigLoader inMemoryConfigLoader = new InMemoryConfigLoader();
inMemoryConfigLoader.setFallBackLoader(getLocalFileConfigLoader());
return inMemoryConfigLoader;
}
public ConfigLoader getRemoteConfigLoader() {
return new RemoteConfigLoader();
ConfigLoader remoteConfigLoader = new RemoteConfigLoader(new RestTemplate(), ConfigUtil.getInstance());
// remoteConfigLoader.setFallBackLoader(getInMemoryConfigLoader());
return remoteConfigLoader;
}
public ConfigLoaderManager getConfigLoaderManager() {
return new ConfigLoaderManager(getRemoteConfigLoader(), ConfigUtil.getInstance());
}
}
package com.ctrip.apollo.client.loader;
import com.ctrip.apollo.client.enums.PropertyChangeType;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.model.PropertyChange;
import com.ctrip.apollo.client.model.PropertySourceReloadResult;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.MapPropertySource;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class ConfigLoaderManager {
public static final String APOLLO_PROPERTY_SOURCE_NAME = "ApolloConfigProperties";
private static final Logger logger = LoggerFactory.getLogger(ConfigLoaderManager.class);
private ConfigLoader configLoader;
private ConfigUtil configUtil;
private final ExecutorService executorService;
private final AtomicLong counter;
private Map<ApolloRegistry, ApolloConfig> currentApolloRegistryConfigCache;
private Map<ApolloRegistry, ApolloConfig> previousApolloRegistryConfigCache;
private List<ApolloRegistry> apolloRegistries;
public ConfigLoaderManager(ConfigLoader configLoader, ConfigUtil configUtil) {
this.configLoader = configLoader;
this.configUtil = configUtil;
this.counter = new AtomicLong();
this.executorService = Executors.newFixedThreadPool(5, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "ConfigLoaderManager-" + counter.incrementAndGet());
return thread;
}
});
this.currentApolloRegistryConfigCache = Maps.newConcurrentMap();
}
public CompositePropertySource loadPropertySource() {
try {
apolloRegistries = configUtil.loadApolloRegistries();
} catch (IOException e) {
throw new RuntimeException("Load apollo config registry failed", e);
}
return loadPropertySourceWithApolloRegistries(apolloRegistries);
}
public PropertySourceReloadResult reloadPropertySource() {
CompositePropertySource composite = loadPropertySourceWithApolloRegistries(apolloRegistries);
List<ApolloConfig> previous = Lists.newArrayList(this.previousApolloRegistryConfigCache.values());
List<ApolloConfig> current = Lists.newArrayList(this.currentApolloRegistryConfigCache.values());
return new PropertySourceReloadResult(composite, calcPropertyChanges(previous, current));
}
/**
* Load property source with apollo registries provided
* Should not be invoked in parallel since there are some operations like create/destroy cache,
* writing to files etc.
* @param apolloRegistries
* @return
*/
private synchronized CompositePropertySource loadPropertySourceWithApolloRegistries(List<ApolloRegistry> apolloRegistries) {
resetApolloRegistryConfigCache();
CompositePropertySource composite = new CompositePropertySource(APOLLO_PROPERTY_SOURCE_NAME);
if (apolloRegistries == null || apolloRegistries.isEmpty()) {
logger.warn("No Apollo Registry found!");
return composite;
}
try {
List<ApolloConfig> apolloConfigList = loadApolloConfigs(apolloRegistries);
Collections.sort(apolloConfigList);
for (ApolloConfig apolloConfig : apolloConfigList) {
composite.addPropertySource(new MapPropertySource(assemblePropertySourceName(apolloConfig), apolloConfig.getConfigurations()));
}
return composite;
} catch (Throwable throwable) {
throw new RuntimeException("Load apollo configs failed", throwable);
}
}
List<PropertyChange> calcPropertyChanges(List<ApolloConfig> previous, List<ApolloConfig> current) {
Map<String, Object> previousMap = collectConfigurations(previous);
Map<String, Object> currentMap = collectConfigurations(current);
Set<String> previousKeys = previousMap.keySet();
Set<String> currentKeys = currentMap.keySet();
Set<String> commonKeys = Sets.intersection(previousKeys, currentKeys);
Set<String> newKeys = Sets.difference(currentKeys, commonKeys);
Set<String> removedKeys = Sets.difference(previousKeys, commonKeys);
List<PropertyChange> changes = Lists.newArrayList();
for (String newKey : newKeys) {
changes.add(new PropertyChange(newKey, null, currentMap.get(newKey), PropertyChangeType.NEW));
}
for (String removedKey : removedKeys) {
changes.add(new PropertyChange(removedKey, previousMap.get(removedKey), null, PropertyChangeType.DELETED));
}
for (String commonKey : commonKeys) {
if (previousMap.get(commonKey).equals(currentMap.get(commonKey))) {
continue;
}
changes.add(new PropertyChange(commonKey, previousMap.get(commonKey), currentMap.get(commonKey), PropertyChangeType.MODIFIED));
}
return changes;
}
Map<String, Object> collectConfigurations(List<ApolloConfig> apolloConfigs) {
Collections.sort(apolloConfigs);
Map<String, Object> configMap = Maps.newHashMap();
for (int i = apolloConfigs.size() - 1; i > -1; i--) {
configMap.putAll(apolloConfigs.get(i).getConfigurations());
}
return configMap;
}
List<ApolloConfig> loadApolloConfigs(List<ApolloRegistry> apolloRegistries) throws Throwable {
List<Future<ApolloConfig>> futures = Lists.newArrayList();
for (final ApolloRegistry apolloRegistry : apolloRegistries) {
futures.add(executorService.submit(new Callable<ApolloConfig>() {
@Override
public ApolloConfig call() throws Exception {
return loadSingleApolloConfig(apolloRegistry);
}
}));
}
List<ApolloConfig> apolloConfigList = Lists.newArrayList();
for (Future<ApolloConfig> future : futures) {
try {
ApolloConfig result = future.get();
if (result == null) {
continue;
}
apolloConfigList.add(result);
} catch (ExecutionException e) {
throw e.getCause();
}
}
return apolloConfigList;
}
ApolloConfig loadSingleApolloConfig(ApolloRegistry apolloRegistry) {
ApolloConfig result = configLoader.loadApolloConfig(apolloRegistry, getPreviousApolloConfig(apolloRegistry));
if (result == null) {
logger.error("Loaded config null...");
return null;
}
logger.info("Loaded config: {}", result);
updateCurrentApolloConfigCache(apolloRegistry, result);
return result;
}
void resetApolloRegistryConfigCache() {
this.previousApolloRegistryConfigCache = currentApolloRegistryConfigCache;
this.currentApolloRegistryConfigCache = Maps.newConcurrentMap();
}
ApolloConfig getPreviousApolloConfig(ApolloRegistry apolloRegistry) {
return previousApolloRegistryConfigCache.get(apolloRegistry);
}
void updateCurrentApolloConfigCache(ApolloRegistry apolloRegistry, ApolloConfig apolloConfig) {
currentApolloRegistryConfigCache.put(apolloRegistry, apolloConfig);
}
private String assemblePropertySourceName(ApolloConfig apolloConfig) {
return String.format("%d-%s-%s-%d", apolloConfig.getAppId(), apolloConfig.getCluster(), apolloConfig.getVersion(), apolloConfig.getReleaseId());
}
}
package com.ctrip.apollo.client.loader.impl;
import com.ctrip.apollo.client.loader.ConfigLoader;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.core.dto.ApolloConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public abstract class AbstractConfigLoader implements ConfigLoader {
private static final Logger logger = LoggerFactory.getLogger(AbstractConfigLoader.class);
private ConfigLoader fallback;
@Override
public ApolloConfig loadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
try {
return doLoadApolloConfig(apolloRegistry, previous);
} catch (Throwable e) {
if (this.fallback == null) {
throw new RuntimeException(String.format("Load Apollo Config failed - %s", apolloRegistry.toString()), e);
}
logger.error("Load Config via {} failed, try to use its fallback {} to load", getClass().getSimpleName(), fallback.getClass().getSimpleName(), e);
return this.fallback.loadApolloConfig(apolloRegistry, previous);
}
}
protected abstract ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous);
@Override
public void setFallBackLoader(ConfigLoader configLoader) {
this.fallback = configLoader;
}
}
package com.ctrip.apollo.client.loader.impl;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.core.dto.ApolloConfig;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class InMemoryConfigLoader extends AbstractConfigLoader{
@Override
protected ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
return null;
}
}
package com.ctrip.apollo.client.loader.impl;
import com.ctrip.apollo.client.loader.ConfigLoader;
import org.springframework.core.env.CompositePropertySource;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.core.dto.ApolloConfig;
/**
* Load config from local backup file
* @author Jason Song(song_s@ctrip.com)
*/
public class LocalConfigLoader implements ConfigLoader {
private static final String PROPERTY_SOURCE_NAME = "ApolloLocalConfigProperties";
public class LocalFileConfigLoader extends AbstractConfigLoader{
@Override
public CompositePropertySource loadPropertySource() {
public ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
return null;
}
}
package com.ctrip.apollo.client.loader.impl;
import com.ctrip.apollo.client.loader.ConfigLoader;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.MapPropertySource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
......@@ -16,126 +13,74 @@ import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.Map;
/**
* Load config from remote config server
*
* @author Jason Song(song_s@ctrip.com)
*/
public class RemoteConfigLoader implements ConfigLoader {
private static final String PROPERTY_SOURCE_NAME = "ApolloRemoteConfigProperties";
public class RemoteConfigLoader extends AbstractConfigLoader {
private static final Logger logger = LoggerFactory.getLogger(RemoteConfigLoader.class);
private final RestTemplate restTemplate;
private final ConfigUtil configUtil;
private final ExecutorService executorService;
private final AtomicLong counter;
public RemoteConfigLoader() {
this(new RestTemplate(), ConfigUtil.getInstance());
}
public RemoteConfigLoader(RestTemplate restTemplate, ConfigUtil configUtil) {
this.restTemplate = restTemplate;
this.configUtil = configUtil;
this.counter = new AtomicLong();
this.executorService = Executors.newFixedThreadPool(5, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "RemoteConfigLoader-" + counter.incrementAndGet());
return thread;
}
});
}
@Override
public CompositePropertySource loadPropertySource() {
CompositePropertySource composite = new CompositePropertySource(PROPERTY_SOURCE_NAME);
List<ApolloRegistry> apolloRegistries;
try {
apolloRegistries = configUtil.loadApolloRegistries();
} catch (IOException e) {
throw new RuntimeException("Load apollo config registry failed", e);
}
if (apolloRegistries == null || apolloRegistries.isEmpty()) {
logger.warn("No Apollo Registry found!");
return composite;
}
try {
doLoadRemoteApolloConfig(apolloRegistries, composite);
} catch (Throwable throwable) {
throw new RuntimeException("Load remote property source failed", throwable);
}
return composite;
}
void doLoadRemoteApolloConfig(List<ApolloRegistry> apolloRegistries, CompositePropertySource compositePropertySource) throws Throwable {
List<Future<MapPropertySource>> futures = Lists.newArrayList();
for (final ApolloRegistry apolloRegistry : apolloRegistries) {
futures.add(executorService.submit(new Callable<MapPropertySource>() {
@Override
public MapPropertySource call() throws Exception {
return loadSingleApolloConfig(apolloRegistry.getAppId(), apolloRegistry.getVersion());
}
}));
}
for (Future<MapPropertySource> future : futures) {
try {
MapPropertySource result = future.get();
if (result == null) {
continue;
}
compositePropertySource.addPropertySource(result);
} catch (ExecutionException e) {
throw e.getCause();
}
}
}
MapPropertySource loadSingleApolloConfig(long appId, String version) {
ApolloConfig result =
this.getRemoteConfig(restTemplate, configUtil.getConfigServerUrl(), appId, configUtil.getCluster(), version);
if (result == null) {
logger.error("Loaded config null...");
return null;
}
logger.info("Loaded config: {}", result);
return new MapPropertySource(assemblePropertySourceName(result), result.getConfigurations());
}
ApolloConfig getRemoteConfig(RestTemplate restTemplate, String uri, String cluster, ApolloRegistry apolloRegistry, ApolloConfig previousConfig) {
long appId = apolloRegistry.getAppId();
String version = apolloRegistry.getVersion();
private String assemblePropertySourceName(ApolloConfig apolloConfig) {
return String.format("%d-%s-%s-%d", apolloConfig.getAppId(), apolloConfig.getCluster(), apolloConfig.getVersion(), apolloConfig.getReleaseId());
}
ApolloConfig getRemoteConfig(RestTemplate restTemplate, String uri, long appId, String cluster, String version) {
logger.info("Loading config from {}, appId={}, cluster={}, version={}", uri, appId, cluster, version);
String path = "config/{appId}/{cluster}";
Object[] args = new String[] {String.valueOf(appId), cluster};
String path = "/config/{appId}/{cluster}";
Map<String, Object> paramMap = Maps.newHashMap();
paramMap.put("appId", appId);
paramMap.put("cluster", cluster);
if (StringUtils.hasText(version)) {
args = new String[] {String.valueOf(appId), cluster, version};
path = path + "/{version}";
paramMap.put("version", version);
}
if (previousConfig != null) {
path = path + "?releaseId={releaseId}";
paramMap.put("releaseId", previousConfig.getReleaseId());
}
ResponseEntity<ApolloConfig> response = null;
ResponseEntity<ApolloConfig> response;
try {
// TODO retry
response = restTemplate.exchange(uri
+ path, HttpMethod.GET, new HttpEntity<Void>((Void) null), ApolloConfig.class, args);
} catch (Exception e) {
+ path, HttpMethod.GET, new HttpEntity<Void>((Void) null), ApolloConfig.class, paramMap);
} catch (Throwable e) {
throw e;
}
if (response == null || response.getStatusCode() != HttpStatus.OK) {
if (response == null) {
throw new RuntimeException("Load apollo config failed, response is null");
}
if (response.getStatusCode() == HttpStatus.NOT_MODIFIED) {
return null;
}
if (response.getStatusCode() != HttpStatus.OK) {
throw new RuntimeException(String.format("Load apollo config failed, response status %s", response.getStatusCode()));
}
ApolloConfig result = response.getBody();
return result;
}
@Override
protected ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
ApolloConfig result = this.getRemoteConfig(restTemplate,
configUtil.getConfigServerUrl(), configUtil.getCluster(),
apolloRegistry, previous);
//When remote server return 304, we need to return the previous result
return result == null ? previous : result;
}
}
package com.ctrip.apollo.client.model;
import com.google.common.base.MoreObjects;
/**
* @author Jason Song(song_s@ctrip.com)
*/
......@@ -22,4 +24,13 @@ public class ApolloRegistry {
public void setVersion(String version) {
this.version = version;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.omitNullValues()
.add("appId", appId)
.add("version", version)
.toString();
}
}
package com.ctrip.apollo.client.model;
import com.ctrip.apollo.client.enums.PropertyChangeType;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class PropertyChange {
private String propertyName;
private Object oldValue;
private Object newValue;
private PropertyChangeType changeType;
public PropertyChange(String propertyName, Object oldValue, Object newValue, PropertyChangeType changeType) {
this.propertyName = propertyName;
this.oldValue = oldValue;
this.newValue = newValue;
this.changeType = changeType;
}
public String getPropertyName() {
return propertyName;
}
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
public Object getOldValue() {
return oldValue;
}
public void setOldValue(Object oldValue) {
this.oldValue = oldValue;
}
public Object getNewValue() {
return newValue;
}
public void setNewValue(Object newValue) {
this.newValue = newValue;
}
public PropertyChangeType getChangeType() {
return changeType;
}
public void setChangeType(PropertyChangeType changeType) {
this.changeType = changeType;
}
}
package com.ctrip.apollo.client.model;
import com.google.common.collect.Lists;
import org.springframework.core.env.CompositePropertySource;
import java.util.List;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class PropertySourceReloadResult {
private CompositePropertySource propertySource;
private List<PropertyChange> changes;
public PropertySourceReloadResult(CompositePropertySource propertySource) {
this.propertySource = propertySource;
changes = Lists.newArrayList();
}
public PropertySourceReloadResult(CompositePropertySource propertySource, List<PropertyChange> changes) {
this.propertySource = propertySource;
this.changes = changes;
}
public CompositePropertySource getPropertySource() {
return propertySource;
}
public void setPropertySource(CompositePropertySource propertySource) {
this.propertySource = propertySource;
}
public List<PropertyChange> getChanges() {
return changes;
}
public void setChanges(List<PropertyChange> changes) {
this.changes = changes;
}
public boolean hasChanges() {
return !changes.isEmpty();
}
}
......@@ -33,7 +33,7 @@ public class ConfigUtil {
public String getConfigServerUrl() {
// TODO return the meta server url based on different environments
return "http://localhost:8888";
return "http://localhost";
}
public String getCluster() {
......
package com.ctrip.apollo.client;
import com.ctrip.apollo.client.loader.ConfigLoaderManagerTest;
import com.ctrip.apollo.client.loader.impl.RemoteConfigLoaderTest;
import com.ctrip.apollo.client.util.ConfigUtilTest;
import org.junit.runner.RunWith;
......@@ -8,7 +9,8 @@ import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
ApolloConfigManagerTest.class, RemoteConfigLoaderTest.class, ConfigUtilTest.class
ApolloConfigManagerTest.class, ConfigLoaderManagerTest.class, RemoteConfigLoaderTest.class,
ConfigUtilTest.class
})
public class AllTests {
......
package com.ctrip.apollo.client;
import com.ctrip.apollo.client.loader.ConfigLoader;
import com.ctrip.apollo.client.loader.ConfigLoaderManager;
import com.ctrip.apollo.client.model.PropertyChange;
import com.ctrip.apollo.client.model.PropertySourceReloadResult;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
......@@ -11,6 +12,7 @@ import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.cloud.context.scope.refresh.RefreshScope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.CompositePropertySource;
......@@ -18,10 +20,11 @@ import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
/**
......@@ -31,7 +34,7 @@ import static org.mockito.Mockito.*;
public class ApolloConfigManagerTest {
private ApolloConfigManager apolloConfigManager;
@Mock
private ConfigLoader configLoader;
private ConfigLoaderManager configLoaderManager;
@Mock
private ConfigurableApplicationContext applicationContext;
@Mock
......@@ -40,6 +43,8 @@ public class ApolloConfigManagerTest {
private MutablePropertySources mutablePropertySources;
@Mock
private BeanDefinitionRegistry beanDefinitionRegistry;
@Mock
private RefreshScope scope;
@Before
public void setUp() {
......@@ -49,7 +54,8 @@ public class ApolloConfigManagerTest {
when(env.getPropertySources()).thenReturn(mutablePropertySources);
apolloConfigManager.setApplicationContext(applicationContext);
ReflectionTestUtils.setField(apolloConfigManager, "configLoader", configLoader);
ReflectionTestUtils.setField(apolloConfigManager, "configLoaderManager", configLoaderManager);
ReflectionTestUtils.setField(apolloConfigManager, "scope", scope);
}
@After
......@@ -66,21 +72,20 @@ public class ApolloConfigManagerTest {
}
@Test
public void testPreparePropertySourceSuccessfully() {
public void testInitializePropertySourceSuccessfully() {
CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
final ArgumentCaptor<CompositePropertySource> captor = ArgumentCaptor.forClass(CompositePropertySource.class);
when(configLoader.loadPropertySource()).thenReturn(somePropertySource);
when(configLoaderManager.loadPropertySource()).thenReturn(somePropertySource);
apolloConfigManager.initializePropertySource();
verify(configLoader, times(1)).loadPropertySource();
verify(configLoaderManager, times(1)).loadPropertySource();
verify(mutablePropertySources, times(1)).addFirst(captor.capture());
final CompositePropertySource insertedPropertySource = captor.getValue();
assertEquals(ApolloConfigManager.APOLLO_PROPERTY_SOURCE_NAME, insertedPropertySource.getName());
assertTrue(insertedPropertySource.getPropertySources().contains(somePropertySource));
assertEquals(insertedPropertySource, somePropertySource);
}
@Test
......@@ -92,4 +97,38 @@ public class ApolloConfigManagerTest {
verify(beanDefinitionRegistry, times(2)).registerBeanDefinition(anyString(), any(BeanDefinition.class));
}
@Test
public void testUpdatePropertySourceWithChanges() throws Exception {
PropertySourceReloadResult somePropertySourceReloadResult = mock(PropertySourceReloadResult.class);
CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
List<PropertyChange> someChanges = mock(List.class);
when(somePropertySourceReloadResult.hasChanges()).thenReturn(true);
when(somePropertySourceReloadResult.getPropertySource()).thenReturn(somePropertySource);
when(somePropertySourceReloadResult.getChanges()).thenReturn(someChanges);
when(configLoaderManager.reloadPropertySource()).thenReturn(somePropertySourceReloadResult);
List<PropertyChange> result = apolloConfigManager.updatePropertySource();
assertEquals(someChanges, result);
verify(scope, times(1)).refreshAll();
}
@Test
public void testUpdatePropertySourceWithNoChange() throws Exception {
PropertySourceReloadResult somePropertySourceReloadResult = mock(PropertySourceReloadResult.class);
CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
List<PropertyChange> emptyChanges = Collections.emptyList();
when(somePropertySourceReloadResult.hasChanges()).thenReturn(false);
when(somePropertySourceReloadResult.getPropertySource()).thenReturn(somePropertySource);
when(somePropertySourceReloadResult.getChanges()).thenReturn(emptyChanges);
when(configLoaderManager.reloadPropertySource()).thenReturn(somePropertySourceReloadResult);
List<PropertyChange> result = apolloConfigManager.updatePropertySource();
assertEquals(emptyChanges, result);
verify(scope, never()).refreshAll();
}
}
package com.ctrip.apollo.client.loader;
import com.ctrip.apollo.client.enums.PropertyChangeType;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.model.PropertyChange;
import com.ctrip.apollo.client.model.PropertySourceReloadResult;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig;
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.ObjectArrays;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith(MockitoJUnitRunner.class)
public class ConfigLoaderManagerTest {
private ConfigLoaderManager configLoaderManager;
@Mock
private ConfigLoader configLoader;
@Mock
private ConfigUtil configUtil;
@Before
public void setUp() {
configLoaderManager = spy(new ConfigLoaderManager(configLoader, configUtil));
}
@Test
public void testLoadPropertySource() throws Exception {
long someAppId = 100;
long anotherAppId = 101;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloRegistry anotherApolloRegistry = assembleSomeApolloRegistry(anotherAppId, "anotherVersion");
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
ApolloConfig anotherApolloConfig = mock(ApolloConfig.class);
Map<String, Object> someMap = mock(Map.class);
Map<String, Object> anotherMap = mock(Map.class);
when(someApolloConfig.getAppId()).thenReturn(someAppId);
when(someApolloConfig.getAppId()).thenReturn(anotherAppId);
when(configUtil.loadApolloRegistries()).thenReturn(Lists.newArrayList(someApolloRegistry, anotherApolloRegistry));
doReturn(someApolloConfig).when(configLoaderManager).loadSingleApolloConfig(someApolloRegistry);
doReturn(anotherApolloConfig).when(configLoaderManager).loadSingleApolloConfig(anotherApolloRegistry);
when(someApolloConfig.getConfigurations()).thenReturn(someMap);
when(anotherApolloConfig.getConfigurations()).thenReturn(anotherMap);
CompositePropertySource result = configLoaderManager.loadPropertySource();
assertEquals(2, result.getPropertySources().size());
List<Map<String, Object>> resultMaps = FluentIterable.from(result.getPropertySources()).transform(new Function<PropertySource<?>, Map<String, Object>>() {
@Override
public Map<String, Object> apply(PropertySource<?> input) {
return (Map<String, Object>)input.getSource();
}
}).toList();
assertTrue(resultMaps.containsAll(Lists.newArrayList(someMap, anotherMap)));
}
@Test(expected = RuntimeException.class)
public void testLoadPropertySourceWithError() throws Exception {
Exception someException = mock(Exception.class);
long someAppId = 100;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
when(configUtil.loadApolloRegistries()).thenReturn(Lists.newArrayList(someApolloRegistry));
doThrow(someException).when(configLoaderManager).loadSingleApolloConfig(someApolloRegistry);
configLoaderManager.loadPropertySource();
}
@Test
public void testLoadApolloConfigsWithNoApolloRegistry() throws Exception {
when(configUtil.loadApolloRegistries()).thenReturn(null);
CompositePropertySource result = configLoaderManager.loadPropertySource();
assertTrue(result.getPropertySources().isEmpty());
}
@Test
public void testLoadSingleApolloConfig() throws Exception {
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
Map<String, Object> someMap = Maps.newHashMap();
long someAppId = 100;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloConfig previousConfig = null;
doReturn(null).when(configLoaderManager).getPreviousApolloConfig(someApolloRegistry);
when(someApolloConfig.getConfigurations()).thenReturn(someMap);
when(configLoader.loadApolloConfig(someApolloRegistry, previousConfig)).thenReturn(someApolloConfig);
ApolloConfig result = configLoaderManager.loadSingleApolloConfig(someApolloRegistry);
assertEquals(someMap, result.getConfigurations());
}
@Test
public void testReloadPropertySource() throws Exception {
long someAppId = 100;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
Map<String, Object> someMap = mock(Map.class);
List<PropertyChange> someChanges = mock(List.class);
ReflectionTestUtils.setField(configLoaderManager, "apolloRegistries", Lists.newArrayList(someApolloRegistry));
doReturn(someApolloConfig).when(configLoaderManager).loadSingleApolloConfig(someApolloRegistry);
when(someApolloConfig.getAppId()).thenReturn(someAppId);
when(someApolloConfig.getConfigurations()).thenReturn(someMap);
doReturn(someChanges).when(configLoaderManager).calcPropertyChanges(anyList(), anyList());
PropertySourceReloadResult result = configLoaderManager.reloadPropertySource();
assertEquals(1, result.getPropertySource().getPropertySources().size());
assertEquals(someChanges, result.getChanges());
List<Map<String, Object>> resultMaps = FluentIterable.from(result.getPropertySource().getPropertySources()).transform(new Function<PropertySource<?>, Map<String, Object>>() {
@Override
public Map<String, Object> apply(PropertySource<?> input) {
return (Map<String, Object>)input.getSource();
}
}).toList();
assertTrue(resultMaps.containsAll(Lists.newArrayList(someMap)));
}
@Test
public void testCalcPropertyChanges() throws Exception {
long someAppId = 1;
Map<String, Object> someConfig = Maps.newHashMap();
someConfig.put("key1", "val1");
someConfig.put("key2", "val2");
Map<String, Object> anotherConfig = Maps.newHashMap();
anotherConfig.put("key1", "val11");
anotherConfig.put("key3", "val3");
List<ApolloConfig> previous = Lists.newArrayList(assembleApolloConfig(someAppId, someConfig));
List<ApolloConfig> current = Lists.newArrayList(assembleApolloConfig(someAppId, anotherConfig));
List<PropertyChange> changes = configLoaderManager.calcPropertyChanges(previous, current);
assertEquals(3, changes.size());
List<String> changeResult = FluentIterable.from(changes).transform(new Function<PropertyChange, String>() {
@Override
public String apply(PropertyChange input) {
return String.format("%s-%s", input.getPropertyName(), input.getChangeType());
}
}).toList();
assertTrue(changeResult.containsAll(
Lists.newArrayList(
"key1-" + PropertyChangeType.MODIFIED,
"key2-" + PropertyChangeType.DELETED,
"key3-" + PropertyChangeType.NEW
)));
}
ApolloConfig assembleApolloConfig(long appId, Map<String, Object> configurations) {
String someCluster = "someCluster";
String someVersion = "someVersion";
long someReleaseId = 1;
ApolloConfig config = new ApolloConfig(appId, someCluster, someVersion, someReleaseId);
config.setConfigurations(configurations);
return config;
}
private ApolloRegistry assembleSomeApolloRegistry(long someAppId, String someVersion) {
ApolloRegistry someApolloRegistry = new ApolloRegistry();
someApolloRegistry.setAppId(someAppId);
someApolloRegistry.setVersion(someVersion);
return someApolloRegistry;
}
}
......@@ -12,11 +12,16 @@ import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.MapPropertySource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
......@@ -29,6 +34,8 @@ public class RemoteConfigLoaderTest {
@Mock
private RestTemplate restTemplate;
private ConfigUtil configUtil;
@Mock
private ResponseEntity<ApolloConfig> someResponse;
@Before
public void setUp() {
......@@ -37,57 +44,78 @@ public class RemoteConfigLoaderTest {
}
@Test
public void testLoadPropertySource() throws Exception {
long someAppId = 100;
long anotherAppId = 101;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloRegistry anotherApolloRegistry = assembleSomeApolloRegistry(anotherAppId, "anotherVersion");
MapPropertySource somePropertySource = mock(MapPropertySource.class);
MapPropertySource anotherPropertySource = mock(MapPropertySource.class);
doReturn(Lists.newArrayList(someApolloRegistry, anotherApolloRegistry)).when(configUtil).loadApolloRegistries();
doReturn(somePropertySource).when(remoteConfigLoader).loadSingleApolloConfig(someApolloRegistry.getAppId(), someApolloRegistry.getVersion());
doReturn(anotherPropertySource).when(remoteConfigLoader).loadSingleApolloConfig(anotherApolloRegistry.getAppId(), anotherApolloRegistry.getVersion());
CompositePropertySource result = remoteConfigLoader.loadPropertySource();
assertEquals(2, result.getPropertySources().size());
assertTrue(result.getPropertySources().containsAll(Lists.newArrayList(somePropertySource, anotherPropertySource)));
public void testLoadApolloConfig() throws Exception {
String someServerUrl = "http://someUrl";
String someCluster = "some cluster";
ApolloConfig apolloConfig = mock(ApolloConfig.class);
long someAppId = 1;
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloConfig previousConfig = null;
when(configUtil.getConfigServerUrl()).thenReturn(someServerUrl);
when(configUtil.getCluster()).thenReturn(someCluster);
doReturn(apolloConfig).when(remoteConfigLoader)
.getRemoteConfig(restTemplate, someServerUrl, someCluster, apolloRegistry, previousConfig);
ApolloConfig result = remoteConfigLoader.loadApolloConfig(apolloRegistry, previousConfig);
assertEquals(apolloConfig, result);
}
@Test
public void testLoadPropertySourceWithNoApolloRegistry() throws Exception {
doReturn(null).when(configUtil).loadApolloRegistries();
public void testGetRemoteConfig() throws Exception {
long someAppId = 1;
String someServerUrl = "http://someServer";
String someClusterName = "someCluster";
String someVersionName = "someVersion";
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName);
ApolloConfig previousConfig = null;
CompositePropertySource result = remoteConfigLoader.loadPropertySource();
when(someResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(someResponse.getBody()).thenReturn(someApolloConfig);
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class),
eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
assertTrue(result.getPropertySources().isEmpty());
ApolloConfig result = remoteConfigLoader.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry, previousConfig);
assertEquals(someApolloConfig, result);
}
@Test(expected = RuntimeException.class)
public void testLoadPropertySourceWithError() throws Exception {
Exception someException = mock(Exception.class);
long someAppId = 100;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
doReturn(Lists.newArrayList(someApolloRegistry)).when(configUtil).loadApolloRegistries();
doThrow(someException).when(remoteConfigLoader).loadSingleApolloConfig(someApolloRegistry.getAppId(), someApolloRegistry.getVersion());
remoteConfigLoader.loadPropertySource();
public void testGetRemoteConfigWithServerError() throws Exception {
long someAppId = 1;
String someServerUrl = "http://someServer";
String someClusterName = "someCluster";
String someVersionName = "someVersion";
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName);
ApolloConfig previousConfig = null;
HttpStatus someErrorCode = HttpStatus.INTERNAL_SERVER_ERROR;
when(someResponse.getStatusCode()).thenReturn(someErrorCode);
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class),
eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
remoteConfigLoader.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry, previousConfig);
}
@Test
public void testLoadSingleApolloConfig() throws Exception {
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
Map<String, Object> someMap = Maps.newHashMap();
public void testGetRemoteConfigWith304Response() throws Exception {
long someAppId = 1;
String someServerUrl = "http://someServer";
String someClusterName = "someCluster";
String someVersionName = "someVersion";
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName);
ApolloConfig previousConfig = null;
when(someResponse.getStatusCode()).thenReturn(HttpStatus.NOT_MODIFIED);
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class),
eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
when(someApolloConfig.getConfigurations()).thenReturn(someMap);
doReturn(someApolloConfig).when(remoteConfigLoader).getRemoteConfig(any(RestTemplate.class), anyString(), anyLong(), anyString(), anyString());
ApolloConfig result = remoteConfigLoader.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry, previousConfig);
long someAppId = 100;
MapPropertySource result = remoteConfigLoader.loadSingleApolloConfig(someAppId, "someVersion");
assertNull(result);
assertEquals(someMap, result.getSource());
}
private ApolloRegistry assembleSomeApolloRegistry(long someAppId, String someVersion) {
......
......@@ -3,10 +3,7 @@ package com.ctrip.apollo.configservice.controller;
import com.ctrip.apollo.biz.entity.Version;
import com.ctrip.apollo.biz.service.ConfigService;
import com.ctrip.apollo.core.dto.ApolloConfig;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
......@@ -21,7 +18,7 @@ public class ConfigController {
@Resource(name = "configService")
private ConfigService configService;
@RequestMapping(value = "/{appId}/{clusterName}/{versionName:.*}")
@RequestMapping(value = "/{appId}/{clusterName}/{versionName:.*}", method = RequestMethod.GET)
public ApolloConfig queryConfig(@PathVariable long appId,
@PathVariable String clusterName,
@PathVariable String versionName,
......
......@@ -4,12 +4,13 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.MoreObjects;
import java.util.Comparator;
import java.util.Map;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class ApolloConfig {
public class ApolloConfig implements Comparable<ApolloConfig> {
private long appId;
......@@ -21,6 +22,8 @@ public class ApolloConfig {
private long releaseId;
private int order;
@JsonCreator
public ApolloConfig(@JsonProperty("appId") long appId,
@JsonProperty("cluster") String cluster,
......@@ -57,6 +60,14 @@ public class ApolloConfig {
return releaseId;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
......@@ -68,4 +79,15 @@ public class ApolloConfig {
.add("configurations", configurations)
.toString();
}
@Override
public int compareTo(ApolloConfig o) {
if (o == null || this.getOrder() > o.getOrder()) {
return 1;
}
if (o.getOrder() > this.getOrder()) {
return -1;
}
return 0;
}
}
package com.ctrip.apollo.demo.controller;
import com.ctrip.apollo.client.ApolloConfigManager;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.model.PropertyChange;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.demo.model.Config;
import com.ctrip.apollo.demo.service.DemoService;
......@@ -27,11 +29,12 @@ public class DemoController {
private Environment env;
@Autowired
private DemoService demoService;
//Apollo config client internal impl, not intended to be used by application, only for this test page
//Apollo config client internal impl, not intended to be used by application, only for this test page!
private ConfigUtil configUtil = ConfigUtil.getInstance();
//ApolloConfigManager, not intended to be used by application, only for this test page!
@Autowired
private RefreshScope scope;
private ApolloConfigManager apolloConfigManager;
@RequestMapping(value = "/config/{configName:.*}", method = RequestMethod.GET)
public Config queryConfig(@PathVariable String configName) {
......@@ -49,8 +52,8 @@ public class DemoController {
}
@RequestMapping(value = "/refresh", method = RequestMethod.POST)
public String refreshBeans() {
this.scope.refreshAll();
return "ok";
public List<PropertyChange> refreshBeans() {
List<PropertyChange> changes = this.apolloConfigManager.updatePropertySource();
return changes;
}
}
......@@ -7,11 +7,11 @@
]);
app.controller('DemoController', function ($scope, $http, $modal, toastr) {
//var NONE = "none";
var NONE = "none";
this.registries = {};
this.configQuery = {};
//this.refreshResult = NONE;
this.refreshResult = NONE;
this.injectedConfigValue = '';
var self = this;
......@@ -46,24 +46,26 @@
});
};
//this.refreshConfig = function() {
// $http.post("refresh")
// .success(function(data) {
// self.assembleRefreshResult(data);
// })
// .error(function(data, status) {
// toastr.error((data && data.msg) || 'Refresh config failed');
// });
//
//};
this.refreshConfig = function() {
$http.post("demo/refresh")
.success(function(data) {
self.assembleRefreshResult(data);
})
.error(function(data, status) {
toastr.error((data && data.msg) || 'Refresh config failed');
});
//this.assembleRefreshResult = function(changedPropertyArray) {
// if(!changedPropertyArray || !changedPropertyArray.length) {
// this.refreshResult = NONE;
// return;
// }
// this.refreshResult = changedPropertyArray.join(',');
//};
};
this.assembleRefreshResult = function(changedPropertyArray) {
if(!changedPropertyArray || !changedPropertyArray.length) {
this.refreshResult = NONE;
return;
}
this.refreshResult = _.map(changedPropertyArray, function(propertyChange) {
return propertyChange.propertyName + '(' + propertyChange.changeType + ')';
});
};
this.loadRegistries();
......
......@@ -70,12 +70,14 @@
</form>
</div>
<!--<div id="refresh-config-wrapper">-->
<!--<h3>Refresh Config:</h3>-->
<!--<button type="button" class="btn btn-primary" ng-click="demoCtrl.refreshConfig()">Refresh Config</button>-->
<!--<div id="refresh-result">-->
<!--<strong>Changed Properties:</strong> {{demoCtrl.refreshResult}}-->
<!--</div>-->
<!--</div>-->
<div id="refresh-config-wrapper">
<h3>Refresh Config:</h3>
<button type="button" class="btn btn-primary" ng-click="demoCtrl.refreshConfig()">Refresh
Config
</button>
<div id="refresh-result">
<strong>Changed Properties:</strong> {{demoCtrl.refreshResult}}
</div>
</div>
</div>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册