Commit 61dd036a authored by Jason Song's avatar Jason Song

rewriting config client, this is an incomplete version

parent 9d2cf93e
...@@ -26,8 +26,8 @@ public class ConfigService { ...@@ -26,8 +26,8 @@ public class ConfigService {
private ObjectMapper objectMapper = new ObjectMapper(); private ObjectMapper objectMapper = new ObjectMapper();
private TypeReference<Map<String, Object>> configurationTypeReference = private TypeReference<Map<String, String>> configurationTypeReference =
new TypeReference<Map<String, Object>>() {}; new TypeReference<Map<String, String>>() {};
public Release findRelease(String appId, String clusterName, String namespaceName) { public Release findRelease(String appId, String clusterName, String namespaceName) {
Release release = releaseRepository.findLatest(appId, clusterName, namespaceName); Release release = releaseRepository.findLatest(appId, clusterName, namespaceName);
...@@ -37,17 +37,17 @@ public class ConfigService { ...@@ -37,17 +37,17 @@ public class ConfigService {
/** /**
* Load configuration from database * Load configuration from database
*/ */
public ApolloConfig loadConfig(Release release, String namespaceName, String versionName) { public ApolloConfig loadConfig(Release release, String namespaceName) {
if (release == null) { if (release == null) {
return null; return null;
} }
ApolloConfig config = new ApolloConfig(release.getAppId(), release.getClusterName(), ApolloConfig config = new ApolloConfig(release.getAppId(), release.getClusterName(),
namespaceName, versionName, release.getId()); namespaceName, release.getId());
config.setConfigurations(transformConfigurationToMap(release.getConfigurations())); config.setConfigurations(transformConfigurationToMap(release.getConfigurations()));
return config; return config;
} }
Map<String, Object> transformConfigurationToMap(String configurations) { Map<String, String> transformConfigurationToMap(String configurations) {
try { try {
return objectMapper.readValue(configurations, configurationTypeReference); return objectMapper.readValue(configurations, configurationTypeReference);
} catch (IOException e) { } catch (IOException e) {
......
...@@ -134,7 +134,7 @@ public class ConfigServiceTest { ...@@ -134,7 +134,7 @@ public class ConfigServiceTest {
when(objectMapper.readValue(eq(someValidConfiguration), (TypeReference) anyObject())) when(objectMapper.readValue(eq(someValidConfiguration), (TypeReference) anyObject()))
.thenReturn(someMap); .thenReturn(someMap);
Map<String, Object> result = configService.transformConfigurationToMap(someValidConfiguration); Map<String, String> result = configService.transformConfigurationToMap(someValidConfiguration);
assertEquals(someMap, result); assertEquals(someMap, result);
verify(objectMapper, times(1)) verify(objectMapper, times(1))
...@@ -147,7 +147,7 @@ public class ConfigServiceTest { ...@@ -147,7 +147,7 @@ public class ConfigServiceTest {
when(objectMapper.readValue(eq(someInvalidConfiguration), (TypeReference) anyObject())) when(objectMapper.readValue(eq(someInvalidConfiguration), (TypeReference) anyObject()))
.thenThrow(IOException.class); .thenThrow(IOException.class);
Map<String, Object> Map<String, String>
result = result =
configService.transformConfigurationToMap(someInvalidConfiguration); configService.transformConfigurationToMap(someInvalidConfiguration);
......
...@@ -20,15 +20,15 @@ ...@@ -20,15 +20,15 @@
<artifactId>apollo-core</artifactId> <artifactId>apollo-core</artifactId>
</dependency> </dependency>
<!-- end of apollo --> <!-- end of apollo -->
<dependency>
<groupId>com.dianping.cat</groupId>
<artifactId>cat-client</artifactId>
</dependency>
<!-- spring --> <!-- spring -->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId> <artifactId>spring-web</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</dependency>
<!-- end of spring --> <!-- end of spring -->
<dependency> <dependency>
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
......
package com.ctrip.apollo;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface Config {
public String getProperty(String key, String defaultValue);
}
package com.ctrip.apollo;
import com.ctrip.apollo.core.ConfigConsts;
import com.ctrip.apollo.internals.ConfigManager;
import com.ctrip.apollo.spi.ConfigFactory;
import com.ctrip.apollo.spi.ConfigRegistry;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.unidal.lookup.ContainerLoader;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class ConfigService {
private static final ConfigService s_instance = new ConfigService();
private PlexusContainer m_container;
private ConfigService() {
m_container = ContainerLoader.getDefaultContainer();
}
public static Config getConfig() {
return getConfig(ConfigConsts.NAMESPACE_APPLICATION);
}
public static Config getConfig(String namespace) {
return getManager().getConfig(namespace);
}
private static ConfigManager getManager() {
try {
return s_instance.m_container.lookup(ConfigManager.class);
} catch (ComponentLookupException e) {
throw new IllegalStateException("Unable to load ConfigManager!", e);
}
}
private static ConfigRegistry getRegistry() {
try {
return s_instance.m_container.lookup(ConfigRegistry.class);
} catch (ComponentLookupException e) {
throw new IllegalStateException("Unable to load ConfigRegistry!", e);
}
}
public static void setConfig(Config config) {
setConfig(ConfigConsts.NAMESPACE_APPLICATION, config);
}
public static void setConfig(String namespace, final Config config) {
getRegistry().register(namespace, new ConfigFactory() {
@Override
public Config create(String namespace) {
return config;
}
});
}
public static void setConfigFactory(ConfigFactory factory) {
setConfigFactory(ConfigConsts.NAMESPACE_APPLICATION, factory);
}
public static void setConfigFactory(String namespace, ConfigFactory factory) {
getRegistry().register(namespace, factory);
}
// for test only
public static void setContainer(PlexusContainer m_container) {
s_instance.m_container = m_container;
}
}
package com.ctrip.apollo.build;
import com.ctrip.apollo.internals.DefaultConfigManager;
import com.ctrip.apollo.spi.DefaultConfigFactory;
import com.ctrip.apollo.spi.DefaultConfigFactoryManager;
import com.ctrip.apollo.spi.DefaultConfigRegistry;
import org.unidal.lookup.configuration.AbstractResourceConfigurator;
import org.unidal.lookup.configuration.Component;
import java.util.ArrayList;
import java.util.List;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class ComponentConfigurator extends AbstractResourceConfigurator {
public static void main(String[] args) {
generatePlexusComponentsXmlFile(new ComponentConfigurator());
}
@Override
public List<Component> defineComponents() {
List<Component> all = new ArrayList<>();
all.add(A(DefaultConfigManager.class));
all.add(A(DefaultConfigFactory.class));
all.add(A(DefaultConfigRegistry.class));
all.add(A(DefaultConfigFactoryManager.class));
return all;
}
}
package com.ctrip.apollo.client;
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 com.ctrip.apollo.core.utils.ApolloThreadFactory;
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;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.cloud.context.scope.refresh.RefreshScope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.Ordered;
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.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicReference;
/**
* Apollo Config for Spring Application
* Requires Spring 3.1+
*
* @author Jason Song(song_s@ctrip.com)
*/
public class ApolloConfigManager
implements
BeanDefinitionRegistryPostProcessor,
PriorityOrdered,
ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(ApolloConfigManager.class);
private static AtomicReference<ApolloConfigManager> singletonProtector =
new AtomicReference<ApolloConfigManager>();
private ConfigLoaderManager configLoaderManager;
private ConfigurableApplicationContext applicationContext;
private ConfigUtil configUtil;
private ScheduledExecutorService executorService;
private RefreshScope scope;
public ApolloConfigManager() {
if (!singletonProtector.compareAndSet(null, this)) {
throw new IllegalStateException("There should be only one ApolloConfigManager instance!");
}
this.configLoaderManager = ConfigLoaderFactory.getInstance().getConfigLoaderManager();
this.configUtil = ConfigUtil.getInstance();
executorService =
Executors.newScheduledThreadPool(1, ApolloThreadFactory.create("ApolloConfigManager", true));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (!(applicationContext instanceof ConfigurableApplicationContext)) {
throw new RuntimeException(String.format(
"ApplicationContext must implement ConfigurableApplicationContext, but found: %s",
applicationContext.getClass().getName()));
}
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
}
/**
* This is the first method invoked, so we could prepare the property sources here. Specifically
* we need to finish preparing property source before PropertySourcesPlaceholderConfigurer so that
* configurations could be injected correctly
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
throws BeansException {
registerDependentBeans(registry);
initializePropertySource();
schedulePeriodicRefresh();
}
/**
* Register beans needed for Apollo Config Client
* <li>- RefreshScope: used to refresh beans when configurations changes</li>
* <li>- PropertySourcesPlaceholderConfigurer: used to support placeholder configuration injection
* </li>
*/
private void registerDependentBeans(BeanDefinitionRegistry registry) {
BeanDefinition refreshScope =
BeanDefinitionBuilder.genericBeanDefinition(RefreshScope.class).getBeanDefinition();
registry.registerBeanDefinition("refreshScope", refreshScope);
BeanDefinition propertySourcesPlaceholderConfigurer = BeanDefinitionBuilder
.genericBeanDefinition(PropertySourcesPlaceholderConfigurer.class).getBeanDefinition();
registry.registerBeanDefinition("propertySourcesPlaceholderConfigurer",
propertySourcesPlaceholderConfigurer);
}
/**
* This is executed after postProcessBeanDefinitionRegistry
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {}
/**
* Make sure this bean is called before other beans
*/
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
/**
* Initialize property sources
*/
void initializePropertySource() {
// 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.replace(currentPropertySource.getName(), currentPropertySource);
return;
}
currentPropertySources.addFirst(currentPropertySource);
}
void schedulePeriodicRefresh() {
logger.info("Schedule periodic refresh with interval: {} {}",
configUtil.getRefreshInterval(), configUtil.getRefreshTimeUnit());
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
updatePropertySource();
} catch (Throwable ex) {
logger.error("Refreshing config failed", ex);
}
}
}, configUtil.getRefreshInterval(), configUtil.getRefreshInterval(),
configUtil.getRefreshTimeUnit());
}
public List<PropertyChange> updatePropertySource() {
PropertySourceReloadResult result = this.configLoaderManager.reloadPropertySource();
if (result.hasChanges()) {
logger.info("Found changes, refresh environment and refreshscope beans.");
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");
return;
}
this.scope.refreshAll();
}
}
package com.ctrip.apollo.client;
import com.ctrip.apollo.client.model.PropertyChange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.CompositePropertySource;
import java.util.List;
/**
* Apollo config for non-Spring application
*
* @author Jason Song(song_s@ctrip.com)
*/
public class ApolloEnvironment {
private static final Logger logger = LoggerFactory.getLogger(ApolloEnvironment.class);
private static ApolloEnvironment instance = new ApolloEnvironment();
private volatile CompositePropertySource propertySource;
private ApolloEnvironmentManager apolloEnvironmentManager;
private ApolloEnvironment() {
apolloEnvironmentManager = new ApolloEnvironmentManager(this);
}
public void init() {
this.apolloEnvironmentManager.init();
}
public static ApolloEnvironment getInstance() {
return instance;
}
/**
* Return the property value with the given key, or {@code null}
* if the key doesn't exist.
*
* @param key the property name
* @return the property value
*/
public String getProperty(String key) {
if (this.propertySource == null) {
throw new IllegalStateException(
"ApolloEnvironment not initialized, please call ApolloEnvironment.init() first");
}
Object value = this.propertySource.getProperty(key);
if (value == null) {
return null;
}
return (String) value;
}
/**
* Return the property value with the given key, or
* {@code defaultValue} if the key doesn't exist.
*/
public String getProperty(String key, String defaultValue) {
String value = getProperty(key);
return value == null ? defaultValue : value;
}
void updatePropertySource(CompositePropertySource propertySource) {
this.propertySource = propertySource;
}
void updatePropertySource(CompositePropertySource propertySource, List<PropertyChange> changes) {
this.updatePropertySource(propertySource);
//TODO broadcast changes
}
}
package com.ctrip.apollo.client;
import com.ctrip.apollo.client.loader.ConfigLoaderFactory;
import com.ctrip.apollo.client.loader.ConfigLoaderManager;
import com.ctrip.apollo.client.model.PropertySourceReloadResult;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.utils.ApolloThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author Jason Song(song_s@ctrip.com)
*/
class ApolloEnvironmentManager {
private static final Logger logger = LoggerFactory.getLogger(ApolloEnvironmentManager.class);
private ConfigLoaderManager configLoaderManager;
private ConfigUtil configUtil;
private ScheduledExecutorService executorService;
private ApolloEnvironment apolloEnvironment;
private AtomicBoolean initDone;
ApolloEnvironmentManager(ApolloEnvironment apolloEnvironment) {
this.apolloEnvironment = apolloEnvironment;
this.configLoaderManager = ConfigLoaderFactory.getInstance().getConfigLoaderManager();
this.configUtil = ConfigUtil.getInstance();
this.executorService =
Executors
.newScheduledThreadPool(1,
ApolloThreadFactory.create("ApolloEnvironmentManager", true));
this.initDone = new AtomicBoolean();
}
synchronized void init() {
if (initDone.get()) {
logger.warn("ApolloEnvironmentManager init already done");
return;
}
this.apolloEnvironment.updatePropertySource(this.configLoaderManager.loadPropertySource());
this.schedulePeriodicRefresh();
initDone.set(true);
}
void schedulePeriodicRefresh() {
logger.info("Schedule periodic refresh with interval: {} {}",
configUtil.getRefreshInterval(), configUtil.getRefreshTimeUnit());
this.executorService.scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
try {
updatePropertySource();
} catch (Throwable ex) {
logger.error("Refreshing config failed", ex);
}
}
}, configUtil.getRefreshInterval(), configUtil.getRefreshInterval(),
configUtil.getRefreshTimeUnit());
}
void updatePropertySource() {
PropertySourceReloadResult result = this.configLoaderManager.reloadPropertySource();
if (result.hasChanges()) {
logger.info("Found changes, refresh environment.");
this.apolloEnvironment.updatePropertySource(result.getPropertySource(), result.getChanges());
}
}
}
package com.ctrip.apollo.client;
import com.google.common.collect.Lists;
import com.ctrip.apollo.client.config.Config;
import com.ctrip.apollo.client.manager.ConfigManager;
import com.ctrip.apollo.client.manager.ConfigManagerManager;
import com.ctrip.apollo.core.ConfigConsts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.ServiceLoader;
/**
* Entry point for client config use
* @author Jason Song(song_s@ctrip.com)
*/
public class ConfigService {
private static final Logger logger = LoggerFactory.getLogger(ConfigService.class);
private static ConfigService instance = new ConfigService();
private List<ConfigManager> configManagers;
private ConfigService() {
this.loadConfigManagers();
}
/**
* Get the config instance with default namespace
* @return config instance
* @throws RuntimeException if config could not be loaded for the default namespace
*/
public static Config getConfig() {
return getConfig(ConfigConsts.NAMESPACE_APPLICATION);
}
/**
* Get the config instance for the namespace
* @param namespace the namespace of the config
* @return config instance
* @throws RuntimeException if config could not be loaded for the specified namespace
*/
public static Config getConfig(String namespace) {
return instance.doGetConfig(namespace);
}
Config doGetConfig(String namespace) {
for (ConfigManager configManager : this.configManagers) {
try {
return configManager.findOrCreate(namespace);
} catch (Throwable th) {
logger.error("Get config failed for namespace {} using config manager {}", namespace,
configManager.getClass(), th);
}
}
throw new RuntimeException(String.format("Could not get config for namespace: %s", namespace));
}
void loadConfigManagers() {
configManagers = Lists.newArrayList();
ServiceLoader<ConfigManagerManager> configManagerManagers =
ServiceLoader.load(ConfigManagerManager.class);
for (ConfigManagerManager configManagerManager : configManagerManagers) {
configManagers.add(configManagerManager.getConfigManager());
}
}
}
package com.ctrip.apollo.client.config;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface Config {
/**
* Return the property value with the given key, or {@code null}
* if the key doesn't exist.
*
* @param key the property name
* @return the property value
*/
String getProperty(String key);
/**
* Return the property value with the given key, or
* {@code defaultValue} if the key doesn't exist.
* @param key the property name
* @param defaultValue the default value is key is not found
* @return the property value
*/
String getProperty(String key, String defaultValue);
}
package com.ctrip.apollo.client.config.impl;
import com.ctrip.apollo.client.config.Config;
import com.ctrip.apollo.client.loader.ConfigLoader;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class LocalFileConfig implements Config {
private ConfigLoader configLoader;
private String namespace;
public LocalFileConfig(ConfigLoader configLoader, String namespace) {
this.configLoader = configLoader;
this.namespace = namespace;
}
@Override
public String getProperty(String key) {
return null;
}
@Override
public String getProperty(String key, String defaultValue) {
String value = getProperty(key);
return value == null ? defaultValue : value;
}
}
package com.ctrip.apollo.client.config.impl;
import com.ctrip.apollo.client.config.Config;
import com.ctrip.apollo.client.loader.ConfigLoader;
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.core.dto.ApolloConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.PropertySource;
import java.util.List;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class RemoteConfig implements Config {
private static final Logger logger = LoggerFactory.getLogger(RemoteConfig.class);
private ConfigLoader configLoader;
private ConfigUtil configUtil;
private String namespace;
private ApolloConfig currentApolloConfig;
private ApolloConfig previousApolloConfig;
public RemoteConfig(ConfigLoader configLoader, String namespace) {
this.configLoader = configLoader;
this.namespace = namespace;
this.configUtil = ConfigUtil.getInstance();
this.initialize();
}
void initialize() {
this.loadApolloConfig(
new ApolloRegistry(this.configUtil.getAppId(), this.configUtil.getCluster(),
namespace));
}
@Override
public String getProperty(String key) {
String value = this.currentApolloConfig.getProperty(key);
if (value == null) {
return null;
}
return value;
}
@Override
public String getProperty(String key, String defaultValue) {
String value = getProperty(key);
return value == null ? defaultValue : value;
}
synchronized ApolloConfig loadApolloConfig(ApolloRegistry apolloRegistry) {
resetApolloRegistryConfigCache();
ApolloConfig result =
configLoader.loadApolloConfig(apolloRegistry, getPreviousApolloConfig());
if (result == null) {
logger.error("Loaded config null...");
return null;
}
logger.info("Loaded config: {}", result);
updateCurrentApolloConfigCache(result);
return result;
}
void resetApolloRegistryConfigCache() {
this.previousApolloConfig = currentApolloConfig;
this.currentApolloConfig = null;
}
ApolloConfig getPreviousApolloConfig() {
return this.previousApolloConfig;
}
void updateCurrentApolloConfigCache(ApolloConfig apolloConfig) {
this.currentApolloConfig = apolloConfig;
}
}
...@@ -4,8 +4,5 @@ package com.ctrip.apollo.client.constants; ...@@ -4,8 +4,5 @@ package com.ctrip.apollo.client.constants;
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class Constants { public class Constants {
public static final String APP_ID = "app.id";
public static final String VERSION = "version";
public static final String DEFAULT_VERSION_NAME = "latest-release";
public static final String ENV = "env"; public static final String ENV = "env";
} }
package com.ctrip.apollo.client.factory;
import com.ctrip.apollo.client.config.Config;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface ConfigFactory {
/**
* create config instance for the namespace
* @param namespace
* @return
*/
Config createConfig(String namespace);
}
package com.ctrip.apollo.client.factory;
import com.ctrip.apollo.client.loader.ConfigLoader;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface ConfigLoaderFactory {
/**
* create config loader
* @return
*/
ConfigLoader createConfigLoader();
}
package com.ctrip.apollo.client.factory.impl;
import com.ctrip.apollo.client.config.Config;
import com.ctrip.apollo.client.config.impl.LocalFileConfig;
import com.ctrip.apollo.client.factory.ConfigFactory;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class LocalConfigFactory implements ConfigFactory {
private static final LocalConfigFactory instance = new LocalConfigFactory();
private LocalConfigFactory() {
}
public static LocalConfigFactory getInstance() {
return instance;
}
@Override
public Config createConfig(String namespace) {
return new LocalFileConfig(LocalConfigLoaderFactory.getInstance().createConfigLoader(),
namespace);
}
}
package com.ctrip.apollo.client.factory.impl;
import com.ctrip.apollo.client.factory.ConfigLoaderFactory;
import com.ctrip.apollo.client.loader.ConfigLoader;
import com.ctrip.apollo.client.loader.ConfigServiceLocator;
import com.ctrip.apollo.client.loader.impl.LocalFileConfigLoader;
import com.ctrip.apollo.client.loader.impl.RemoteConfigLoader;
import org.springframework.web.client.RestTemplate;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class LocalConfigLoaderFactory implements ConfigLoaderFactory {
private static LocalConfigLoaderFactory instance = new LocalConfigLoaderFactory();
private LocalConfigLoaderFactory() {
}
public static LocalConfigLoaderFactory getInstance() {
return instance;
}
@Override
public ConfigLoader createConfigLoader() {
ConfigLoader configLoader = new LocalFileConfigLoader();
return configLoader;
}
}
package com.ctrip.apollo.client.factory.impl;
import com.ctrip.apollo.client.config.Config;
import com.ctrip.apollo.client.config.impl.RemoteConfig;
import com.ctrip.apollo.client.factory.ConfigFactory;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class RemoteConfigFactory implements ConfigFactory {
private static final RemoteConfigFactory instance = new RemoteConfigFactory();
private RemoteConfigFactory() {
}
public static RemoteConfigFactory getInstance() {
return instance;
}
@Override
public Config createConfig(String namespace) {
return new RemoteConfig(RemoteConfigLoaderFactory.getInstance().createConfigLoader(), namespace);
}
}
package com.ctrip.apollo.client.factory.impl;
import com.ctrip.apollo.client.factory.ConfigLoaderFactory;
import com.ctrip.apollo.client.loader.ConfigLoader;
import com.ctrip.apollo.client.loader.ConfigServiceLocator;
import com.ctrip.apollo.client.loader.impl.RemoteConfigLoader;
import org.springframework.web.client.RestTemplate;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class RemoteConfigLoaderFactory implements ConfigLoaderFactory {
private static RemoteConfigLoaderFactory configLoaderFactory = new RemoteConfigLoaderFactory();
private RemoteConfigLoaderFactory() {
}
public static RemoteConfigLoaderFactory getInstance() {
return configLoaderFactory;
}
@Override
public ConfigLoader createConfigLoader() {
ConfigLoader
remoteConfigLoader =
new RemoteConfigLoader(new RestTemplate(), new ConfigServiceLocator());
return remoteConfigLoader;
}
}
...@@ -7,7 +7,11 @@ import com.ctrip.apollo.core.dto.ApolloConfig; ...@@ -7,7 +7,11 @@ import com.ctrip.apollo.core.dto.ApolloConfig;
* @author Jason Song(songs_ctrip.com) * @author Jason Song(songs_ctrip.com)
*/ */
public interface ConfigLoader { public interface ConfigLoader {
/**
* Load apollo config
* @param apolloRegistry
* @param previous
* @return
*/
ApolloConfig loadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous); ApolloConfig loadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous);
void setFallBackLoader(ConfigLoader configLoader);
} }
package com.ctrip.apollo.client.loader;
import com.ctrip.apollo.Apollo.Env;
import com.ctrip.apollo.client.env.ClientEnvironment;
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)
*/
public class ConfigLoaderFactory {
private static ConfigLoaderFactory configLoaderFactory = new ConfigLoaderFactory();
private ConfigLoaderFactory() {
}
public static ConfigLoaderFactory getInstance() {
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() {
ConfigLoader
remoteConfigLoader =
new RemoteConfigLoader(new RestTemplate(), ConfigUtil.getInstance(),
new ConfigServiceLocator());
// remoteConfigLoader.setFallBackLoader(getInMemoryConfigLoader());
return remoteConfigLoader;
}
public ConfigLoaderManager getConfigLoaderManager() {
ClientEnvironment env = ClientEnvironment.getInstance();
if (env.getEnv().equals(Env.LOCAL)) {
return new ConfigLoaderManager(getLocalFileConfigLoader(), ConfigUtil.getInstance());
} else {
return new ConfigLoaderManager(getRemoteConfigLoader(), ConfigUtil.getInstance());
}
}
}
package com.ctrip.apollo.client.loader;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
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.ctrip.apollo.core.utils.ApolloThreadFactory;
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.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* @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 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.executorService =
Executors.newFixedThreadPool(5, ApolloThreadFactory.create("ConfigLoaderManager", true));
this.currentApolloRegistryConfigCache = Maps.newConcurrentMap();
}
public CompositePropertySource loadPropertySource() {
try {
apolloRegistries = configUtil.loadApolloRegistries();
} catch (IOException ex) {
throw new RuntimeException("Load apollo config registry failed", ex);
}
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.
*/
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 ex) {
throw ex.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("%s-%s-%s-%d", apolloConfig.getAppId(), apolloConfig.getCluster(),
apolloConfig.getVersion(), apolloConfig.getReleaseId());
}
}
...@@ -4,36 +4,21 @@ import com.ctrip.apollo.client.loader.ConfigLoader; ...@@ -4,36 +4,21 @@ import com.ctrip.apollo.client.loader.ConfigLoader;
import com.ctrip.apollo.client.model.ApolloRegistry; import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.core.dto.ApolloConfig; import com.ctrip.apollo.core.dto.ApolloConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public abstract class AbstractConfigLoader implements ConfigLoader { public abstract class AbstractConfigLoader implements ConfigLoader {
private static final Logger logger = LoggerFactory.getLogger(AbstractConfigLoader.class);
private ConfigLoader fallback;
@Override @Override
public ApolloConfig loadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) { public ApolloConfig loadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
try { try {
return doLoadApolloConfig(apolloRegistry, previous); return doLoadApolloConfig(apolloRegistry, previous);
} catch (Throwable ex) { } catch (Throwable ex) {
if (this.fallback == null) { throw new RuntimeException(
throw new RuntimeException( String.format("Load Apollo Config failed - %s", apolloRegistry.toString()), ex);
String.format("Load Apollo Config failed - %s", apolloRegistry.toString()), ex);
}
logger.error("Load Config via {} failed, try to use its fallback {} to load",
getClass().getSimpleName(), fallback.getClass().getSimpleName(), ex);
return this.fallback.loadApolloConfig(apolloRegistry, previous);
} }
} }
protected abstract ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, protected abstract ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry,
ApolloConfig previous); 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;
}
}
...@@ -11,6 +11,6 @@ import com.ctrip.apollo.core.dto.ApolloConfig; ...@@ -11,6 +11,6 @@ import com.ctrip.apollo.core.dto.ApolloConfig;
public class LocalFileConfigLoader extends AbstractConfigLoader { public class LocalFileConfigLoader extends AbstractConfigLoader {
@Override @Override
public ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) { public ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
return null; throw new IllegalStateException("Not implemented yet!");
} }
} }
...@@ -28,31 +28,30 @@ import java.util.Map; ...@@ -28,31 +28,30 @@ import java.util.Map;
public class RemoteConfigLoader extends AbstractConfigLoader { public class RemoteConfigLoader extends AbstractConfigLoader {
private static final Logger logger = LoggerFactory.getLogger(RemoteConfigLoader.class); private static final Logger logger = LoggerFactory.getLogger(RemoteConfigLoader.class);
private final RestTemplate restTemplate; private final RestTemplate restTemplate;
private final ConfigUtil configUtil;
private final ConfigServiceLocator serviceLocator; private final ConfigServiceLocator serviceLocator;
public RemoteConfigLoader(RestTemplate restTemplate, ConfigUtil configUtil, public RemoteConfigLoader(RestTemplate restTemplate,
ConfigServiceLocator locator) { ConfigServiceLocator locator) {
this.restTemplate = restTemplate; this.restTemplate = restTemplate;
this.configUtil = configUtil;
this.serviceLocator = locator; this.serviceLocator = locator;
} }
ApolloConfig getRemoteConfig(RestTemplate restTemplate, String uri, String cluster, ApolloConfig getRemoteConfig(RestTemplate restTemplate, String uri,
ApolloRegistry apolloRegistry, ApolloConfig previousConfig) { ApolloRegistry apolloRegistry, ApolloConfig previousConfig) {
String appId = apolloRegistry.getAppId(); String appId = apolloRegistry.getAppId();
String version = apolloRegistry.getVersion(); String cluster = apolloRegistry.getClusterName();
String namespace = apolloRegistry.getNamespace();
logger.info("Loading config from {}, appId={}, cluster={}, version={}", uri, appId, cluster, logger.info("Loading config from {}, appId={}, cluster={}, namespace={}", uri, appId, cluster,
version); namespace);
String path = "/config/{appId}/{cluster}"; String path = "/config/{appId}/{cluster}";
Map<String, Object> paramMap = Maps.newHashMap(); Map<String, Object> paramMap = Maps.newHashMap();
paramMap.put("appId", appId); paramMap.put("appId", appId);
paramMap.put("cluster", cluster); paramMap.put("cluster", cluster);
if (StringUtils.hasText(version)) { if (StringUtils.hasText(namespace)) {
path = path + "/{version}"; path = path + "/{namespace}";
paramMap.put("version", version); paramMap.put("namespace", namespace);
} }
if (previousConfig != null) { if (previousConfig != null) {
path = path + "?releaseId={releaseId}"; path = path + "?releaseId={releaseId}";
...@@ -89,8 +88,7 @@ public class RemoteConfigLoader extends AbstractConfigLoader { ...@@ -89,8 +88,7 @@ public class RemoteConfigLoader extends AbstractConfigLoader {
@Override @Override
protected ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) { protected ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
ApolloConfig result = this.getRemoteConfig(restTemplate, ApolloConfig result = this.getRemoteConfig(restTemplate,
getConfigServiceUrl(), configUtil.getCluster(), getConfigServiceUrl(), apolloRegistry, previous);
apolloRegistry, previous);
//When remote server return 304, we need to return the previous result //When remote server return 304, we need to return the previous result
return result == null ? previous : result; return result == null ? previous : result;
} }
......
package com.ctrip.apollo.client.manager;
import com.ctrip.apollo.client.config.Config;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface ConfigManager {
/**
* get config according to the namespace
* @param namespace the namespace of the config
* @return config instance
*/
Config findOrCreate(String namespace);
}
package com.ctrip.apollo.client.manager;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface ConfigManagerManager {
/**
* get the config manager
* @return config manager instance
*/
ConfigManager getConfigManager();
}
package com.ctrip.apollo.client.manager.impl;
import com.ctrip.apollo.client.config.Config;
import com.ctrip.apollo.client.factory.ConfigFactory;
import com.ctrip.apollo.client.manager.ConfigManager;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public abstract class AbstractConfigManager implements ConfigManager {
private ConcurrentMap<String, Config> configs;
private ConfigFactory configFactory;
public AbstractConfigManager(
ConfigFactory configFactory) {
this.configs = new ConcurrentHashMap<>();
this.configFactory = configFactory;
}
@Override
public Config findOrCreate(String namespace) {
Config config = configs.get(namespace);
if (config == null) {
synchronized (configs) {
config = configs.get(namespace);
if (config == null) {
config = configFactory.createConfig(namespace);
configs.put(namespace, config);
}
}
}
return config;
}
}
package com.ctrip.apollo.client.manager.impl;
import com.ctrip.apollo.client.factory.ConfigFactory;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class LocalFileConfigManager extends AbstractConfigManager {
public LocalFileConfigManager(ConfigFactory configFactory) {
super(configFactory);
}
}
package com.ctrip.apollo.client.manager.impl;
import com.ctrip.apollo.client.factory.impl.LocalConfigFactory;
import com.ctrip.apollo.client.manager.ConfigManager;
import com.ctrip.apollo.client.manager.ConfigManagerManager;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class LocalFileConfigManagerManager implements ConfigManagerManager{
@Override
public ConfigManager getConfigManager() {
return new LocalFileConfigManager(LocalConfigFactory.getInstance());
}
}
package com.ctrip.apollo.client.manager.impl;
import com.ctrip.apollo.client.factory.ConfigFactory;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class RemoteConfigManager extends AbstractConfigManager{
public RemoteConfigManager(ConfigFactory configFactory) {
super(configFactory);
}
}
package com.ctrip.apollo.client.manager.impl;
import com.ctrip.apollo.client.factory.impl.RemoteConfigFactory;
import com.ctrip.apollo.client.manager.ConfigManager;
import com.ctrip.apollo.client.manager.ConfigManagerManager;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class RemoteConfigManagerManager implements ConfigManagerManager {
@Override
public ConfigManager getConfigManager() {
return new RemoteConfigManager(RemoteConfigFactory.getInstance());
}
}
...@@ -7,22 +7,25 @@ import com.google.common.base.MoreObjects; ...@@ -7,22 +7,25 @@ import com.google.common.base.MoreObjects;
*/ */
public class ApolloRegistry { public class ApolloRegistry {
private String appId; private String appId;
private String version; private String clusterName;
private String namespace;
public String getAppId() { public ApolloRegistry(String appId, String clusterName, String namespace) {
return appId; this.appId = appId;
this.clusterName = clusterName;
this.namespace = namespace;
} }
public void setAppId(String appId) { public String getAppId() {
this.appId = appId; return appId;
} }
public String getVersion() { public String getClusterName() {
return version; return clusterName;
} }
public void setVersion(String version) { public String getNamespace() {
this.version = version; return namespace;
} }
@Override @Override
...@@ -30,7 +33,8 @@ public class ApolloRegistry { ...@@ -30,7 +33,8 @@ public class ApolloRegistry {
return MoreObjects.toStringHelper(this) return MoreObjects.toStringHelper(this)
.omitNullValues() .omitNullValues()
.add("appId", appId) .add("appId", appId)
.add("version", version) .add("clusterName", clusterName)
.add("namespace", namespace)
.toString(); .toString();
} }
} }
package com.ctrip.apollo.client.util; package com.ctrip.apollo.client.util;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.ctrip.apollo.client.constants.Constants;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.core.utils.ClassLoaderUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
...@@ -38,6 +28,11 @@ public class ConfigUtil { ...@@ -38,6 +28,11 @@ public class ConfigUtil {
return configUtil; return configUtil;
} }
public String getAppId() {
// TODO return the actual app id
return "101";
}
public String getCluster() { public String getCluster() {
// TODO return the actual cluster // TODO return the actual cluster
return "default"; return "default";
...@@ -50,37 +45,4 @@ public class ConfigUtil { ...@@ -50,37 +45,4 @@ public class ConfigUtil {
public TimeUnit getRefreshTimeUnit() { public TimeUnit getRefreshTimeUnit() {
return refreshIntervalTimeUnit; return refreshIntervalTimeUnit;
} }
public List<ApolloRegistry> loadApolloRegistries() throws IOException {
List<URL> resourceUrls =
Collections.list(ClassLoaderUtil.getLoader().getResources(APOLLO_PROPERTY));
List<ApolloRegistry> registries =
FluentIterable.from(resourceUrls).transform(new Function<URL, ApolloRegistry>() {
@Override
public ApolloRegistry apply(URL input) {
Properties properties = loadPropertiesFromResourceURL(input);
if (properties == null || !properties.containsKey(Constants.APP_ID)) {
return null;
}
ApolloRegistry registry = new ApolloRegistry();
registry.setAppId(properties.getProperty(Constants.APP_ID));
registry.setVersion(
properties.getProperty(Constants.VERSION, Constants.DEFAULT_VERSION_NAME));
return registry;
}
}).filter(Predicates.notNull()).toList();
return registries;
}
Properties loadPropertiesFromResourceURL(URL resourceUrl) {
try {
InputStream inputStream = resourceUrl.openStream();
Properties prop = new Properties();
prop.load(inputStream);
return prop;
} catch (IOException ex) {
logger.error("Load properties from {} failed", resourceUrl.toExternalForm(), ex);
}
return null;
}
} }
package com.ctrip.apollo.internals;
import com.ctrip.apollo.Config;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface ConfigManager {
public Config getConfig(String namespace);
}
package com.ctrip.apollo.internals;
import com.ctrip.apollo.Config;
import com.dianping.cat.Cat;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class DefaultConfig implements Config {
private final String m_namespace;
private Properties m_resourceProperties;
private Properties m_fileProperties;
public DefaultConfig(File baseDir, String namespace) {
m_namespace = namespace;
m_resourceProperties = loadFromResource(namespace);
m_fileProperties = loadFromFile(baseDir, namespace);
}
private Properties loadFromResource(String namespace) {
String name = String.format("/META-INF/config/%s.properties", namespace);
InputStream in = getClass().getResourceAsStream(name);
Properties properties = null;
if (in != null) {
properties = new Properties();
try {
properties.load(in);
} catch (IOException e) {
Cat.logError(e);
} finally {
try {
in.close();
} catch (IOException e) {
// ignore
}
}
}
return properties;
}
private Properties loadFromFile(File baseDir, String namespace) {
if (baseDir == null) {
return null;
}
File file = new File(baseDir, namespace + ".properties");
Properties properties = null;
if (file.isFile() && file.canRead()) {
InputStream in = null;
try {
in = new FileInputStream(file);
properties = new Properties();
properties.load(in);
} catch (IOException e) {
Cat.logError(e);
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
// ignore
}
}
}
return properties;
}
@Override
public String getProperty(String key, String defaultValue) {
// step 1: check system properties, i.e. -Dkey=value
String value = System.getProperty(key);
// step 2: check local cached properties file
if (value == null) {
if (m_fileProperties != null) {
value = (String) m_fileProperties.get(key);
}
}
// step 3: check env variable, i.e. PATH=...
if (value == null) {
value = System.getenv(key); // TODO fix naming issues
}
// step 4: check properties file from classpath
if (value == null) {
if (m_resourceProperties != null) {
value = (String) m_resourceProperties.get(key);
}
}
return value;
}
}
package com.ctrip.apollo.internals;
import com.google.common.collect.Maps;
import com.ctrip.apollo.Config;
import com.ctrip.apollo.spi.ConfigFactory;
import com.ctrip.apollo.spi.ConfigFactoryManager;
import org.unidal.lookup.annotation.Inject;
import org.unidal.lookup.annotation.Named;
import java.util.Map;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Named(type = ConfigManager.class)
public class DefaultConfigManager implements ConfigManager {
@Inject
private ConfigFactoryManager m_factoryManager;
private Map<String, Config> m_configs = Maps.newHashMap();
@Override
public Config getConfig(String namespace) {
Config config = m_configs.get(namespace);
if (config == null) {
synchronized (this) {
config = m_configs.get(namespace);
if (config == null) {
ConfigFactory factory = m_factoryManager.getFactory(namespace);
config = factory.create(namespace);
m_configs.put(namespace, config);
}
}
}
return config;
}
}
package com.ctrip.apollo.spi;
import com.ctrip.apollo.Config;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface ConfigFactory {
public Config create(String namespace);
}
package com.ctrip.apollo.spi;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface ConfigFactoryManager {
public ConfigFactory getFactory(String namespace);
}
package com.ctrip.apollo.spi;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface ConfigRegistry {
public void register(String namespace, ConfigFactory factory);
public ConfigFactory getFactory(String namespace);
}
package com.ctrip.apollo.spi;
import com.ctrip.apollo.Config;
import com.ctrip.apollo.internals.DefaultConfig;
import org.unidal.lookup.annotation.Named;
import java.io.File;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Named(type = ConfigFactory.class, value = "default")
public class DefaultConfigFactory implements ConfigFactory {
private File m_baseDir;
@Override
public Config create(String namespace) {
return new DefaultConfig(m_baseDir, namespace);
}
}
package com.ctrip.apollo.spi;
import com.google.common.collect.Maps;
import org.unidal.lookup.ContainerHolder;
import org.unidal.lookup.LookupException;
import org.unidal.lookup.annotation.Inject;
import org.unidal.lookup.annotation.Named;
import java.util.Map;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Named(type = ConfigFactoryManager.class)
public class DefaultConfigFactoryManager extends ContainerHolder implements ConfigFactoryManager {
@Inject
private ConfigRegistry m_registry;
private Map<String, ConfigFactory> m_factories = Maps.newConcurrentMap();
@Override
public ConfigFactory getFactory(String namespace) {
// step 1: check hacked factory
ConfigFactory factory = m_registry.getFactory(namespace);
if (factory != null) {
return factory;
}
// step 2: check cache
factory = m_factories.get(namespace);
if (factory != null) {
return factory;
}
// step 3: check declared config factory
try {
factory = lookup(ConfigFactory.class, namespace);
} catch (LookupException e) {
// ignore it
e.printStackTrace();
}
// step 4: check default config factory
if (factory == null) {
factory = lookup(ConfigFactory.class, "default");
}
m_factories.put(namespace, factory);
// factory should not be null
return factory;
}
}
package com.ctrip.apollo.spi;
import com.google.common.collect.Maps;
import org.codehaus.plexus.logging.LogEnabled;
import org.codehaus.plexus.logging.Logger;
import org.unidal.lookup.annotation.Named;
import java.util.Map;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Named(type = ConfigRegistry.class)
public class DefaultConfigRegistry implements ConfigRegistry, LogEnabled {
private Map<String, ConfigFactory> m_instances = Maps.newConcurrentMap();
private Logger m_logger;
@Override
public void register(String namespace, ConfigFactory factory) {
if (m_instances.containsKey(namespace)) {
m_logger.warn(
String.format("ConfigFactory(%s) is overridden by %s!", namespace, factory.getClass()));
}
m_instances.put(namespace, factory);
}
@Override
public ConfigFactory getFactory(String namespace) {
ConfigFactory config = m_instances.get(namespace);
return config;
}
@Override
public void enableLogging(Logger logger) {
m_logger = logger;
}
}
<plexus>
<components>
<component>
<role>com.ctrip.apollo.internals.ConfigManager</role>
<implementation>com.ctrip.apollo.internals.DefaultConfigManager</implementation>
<requirements>
<requirement>
<role>com.ctrip.apollo.spi.ConfigFactoryManager</role>
</requirement>
</requirements>
</component>
<component>
<role>com.ctrip.apollo.spi.ConfigFactory</role>
<implementation>com.ctrip.apollo.spi.DefaultConfigFactory</implementation>
</component>
<component>
<role>com.ctrip.apollo.spi.ConfigRegistry</role>
<implementation>com.ctrip.apollo.spi.DefaultConfigRegistry</implementation>
</component>
<component>
<role>com.ctrip.apollo.spi.ConfigFactoryManager</role>
<implementation>com.ctrip.apollo.spi.DefaultConfigFactoryManager</implementation>
<requirements>
<requirement>
<role>com.ctrip.apollo.spi.ConfigRegistry</role>
</requirement>
</requirements>
</component>
</components>
</plexus>
com.ctrip.apollo.client.manager.impl.RemoteConfigManagerManager
com.ctrip.apollo.client.manager.impl.LocalFileConfigManagerManager
package com.ctrip.apollo;
import com.ctrip.apollo.spi.ConfigFactory;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.unidal.lookup.ComponentTestCase;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class ConfigServiceTest extends ComponentTestCase {
@Override
@Before
public void setUp() throws Exception {
super.setUp();
ConfigService.setContainer(getContainer());
}
@Test
public void testHackConfig() {
ConfigService.setConfig(new MockConfig("hack"));
Config config = ConfigService.getConfig();
Assert.assertEquals("hack:first", config.getProperty("first", null));
Assert.assertEquals(null, config.getProperty("unknown", null));
}
@Test
public void testMockConfigFactory() throws Exception {
defineComponent(ConfigFactory.class, "mock", MockConfigFactory.class);
Config config = ConfigService.getConfig("mock");
Assert.assertEquals("mock:first", config.getProperty("first", null));
Assert.assertEquals(null, config.getProperty("unknown", null));
}
private static class MockConfig implements Config {
private final String m_namespace;
public MockConfig(String namespace) {
m_namespace = namespace;
}
@Override
public String getProperty(String key, String defaultValue) {
if (key.equals("unknown")) {
return null;
}
return m_namespace + ":" + key;
}
}
public static class MockConfigFactory implements ConfigFactory {
@Override
public Config create(String namespace) {
return new MockConfig(namespace);
}
}
}
package com.ctrip.apollo.client; 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.loader.impl.RemoteConfigLoaderTest;
import com.ctrip.apollo.client.util.ConfigUtilTest;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Suite; import org.junit.runners.Suite;
...@@ -10,8 +8,7 @@ import org.junit.runners.Suite.SuiteClasses; ...@@ -10,8 +8,7 @@ import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class) @RunWith(Suite.class)
@SuiteClasses({ @SuiteClasses({
ApolloConfigManagerTest.class, ConfigLoaderManagerTest.class, RemoteConfigLoaderTest.class, RemoteConfigLoaderTest.class
ConfigUtilTest.class, ApolloEnvironmentTest.class, ApolloEnvironmentManagerTest.class
}) })
public class AllTests { public class AllTests {
......
package com.ctrip.apollo.client;
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.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
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;
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.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith(MockitoJUnitRunner.class)
public class ApolloConfigManagerTest {
private ApolloConfigManager apolloConfigManager;
@Mock
private ConfigLoaderManager configLoaderManager;
@Mock
private ConfigurableApplicationContext applicationContext;
@Mock
private ConfigurableEnvironment env;
@Mock
private MutablePropertySources mutablePropertySources;
@Mock
private BeanDefinitionRegistry beanDefinitionRegistry;
@Mock
private RefreshScope scope;
@Before
public void setUp() {
apolloConfigManager = spy(new ApolloConfigManager());
when(applicationContext.getEnvironment()).thenReturn(env);
when(env.getPropertySources()).thenReturn(mutablePropertySources);
apolloConfigManager.setApplicationContext(applicationContext);
ReflectionTestUtils.setField(apolloConfigManager, "configLoaderManager", configLoaderManager);
ReflectionTestUtils.setField(apolloConfigManager, "scope", scope);
}
@After
public void tearDown() throws Exception {
AtomicReference<ApolloConfigManager> singletonProtector =
(AtomicReference<ApolloConfigManager>) ReflectionTestUtils
.getField(ApolloConfigManager.class, "singletonProtector");
singletonProtector.set(null);
}
@Test(expected = RuntimeException.class)
public void testInvalidApplicationContext() {
ApplicationContext someInvalidApplication = mock(ApplicationContext.class);
apolloConfigManager.setApplicationContext(someInvalidApplication);
}
@Test
public void testInitializePropertySourceSuccessfully() {
CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
final ArgumentCaptor<CompositePropertySource>
captor =
ArgumentCaptor.forClass(CompositePropertySource.class);
when(configLoaderManager.loadPropertySource()).thenReturn(somePropertySource);
apolloConfigManager.initializePropertySource();
verify(configLoaderManager, times(1)).loadPropertySource();
verify(mutablePropertySources, times(1)).addFirst(captor.capture());
final CompositePropertySource insertedPropertySource = captor.getValue();
assertEquals(insertedPropertySource, somePropertySource);
}
@Test
public void testPostProcessBeanDefinitionRegistry() {
doNothing().when(apolloConfigManager).initializePropertySource();
apolloConfigManager.postProcessBeanDefinitionRegistry(beanDefinitionRegistry);
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;
import com.google.common.collect.Lists;
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.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.test.util.ReflectionTestUtils;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith(MockitoJUnitRunner.class)
public class ApolloEnvironmentManagerTest {
private ApolloEnvironmentManager apolloEnvironmentManager;
@Mock
private ConfigLoaderManager configLoaderManager;
@Mock
private ConfigUtil configUtil;
@Mock
private ApolloEnvironment apolloEnvironment;
@Before
public void setUp() throws Exception {
apolloEnvironmentManager = spy(new ApolloEnvironmentManager(apolloEnvironment));
ReflectionTestUtils
.setField(apolloEnvironmentManager, "configLoaderManager", configLoaderManager);
ReflectionTestUtils.setField(apolloEnvironmentManager, "configUtil", configUtil);
int someInterval = 1;
TimeUnit someUnit = TimeUnit.MINUTES;
when(configUtil.getRefreshInterval()).thenReturn(someInterval);
when(configUtil.getRefreshTimeUnit()).thenReturn(someUnit);
}
@Test
public void testInit() throws Exception {
CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
when(configLoaderManager.loadPropertySource()).thenReturn(somePropertySource);
apolloEnvironmentManager.init();
verify(configLoaderManager, times(1)).loadPropertySource();
verify(apolloEnvironment, times(1)).updatePropertySource(somePropertySource);
}
@Test
public void testUpdatePropertySource() throws Exception {
PropertySourceReloadResult someResult = mock(PropertySourceReloadResult.class);
CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
List<PropertyChange> someChanges = Lists.newArrayList();
when(someResult.hasChanges()).thenReturn(true);
when(someResult.getPropertySource()).thenReturn(somePropertySource);
when(someResult.getChanges()).thenReturn(someChanges);
when(configLoaderManager.reloadPropertySource()).thenReturn(someResult);
apolloEnvironmentManager.updatePropertySource();
verify(configLoaderManager, times(1)).reloadPropertySource();
verify(apolloEnvironment, times(1)).updatePropertySource(somePropertySource, someChanges);
}
}
package com.ctrip.apollo.client;
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.test.util.ReflectionTestUtils;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith(MockitoJUnitRunner.class)
public class ApolloEnvironmentTest {
private ApolloEnvironment apolloEnvironment;
@Mock
private ApolloEnvironmentManager apolloEnvironmentManager;
@Before
public void setUp() throws Exception {
apolloEnvironment = spy(ApolloEnvironment.getInstance());
ReflectionTestUtils
.setField(apolloEnvironment, "apolloEnvironmentManager", apolloEnvironmentManager);
}
@Test
public void testInit() throws Exception {
apolloEnvironment.init();
verify(apolloEnvironmentManager, times(1)).init();
}
@Test
public void testGetProperty() throws Exception {
CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
String someKey = "someKey";
String someValue = "someValue";
apolloEnvironment.updatePropertySource(somePropertySource);
when(somePropertySource.getProperty(someKey)).thenReturn(someValue);
String result = apolloEnvironment.getProperty(someKey);
assertEquals(someValue, result);
}
@Test
public void testGetPropertyWithDefaultValue() throws Exception {
CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
String someKey = "someKey";
String someDefaultValue = "someDefault";
apolloEnvironment.updatePropertySource(somePropertySource);
when(somePropertySource.getProperty(someKey)).thenReturn(null);
String result = apolloEnvironment.getProperty(someKey, someDefaultValue);
assertEquals(someDefaultValue, result);
}
@Test(expected = IllegalStateException.class)
public void testGetPropertyWithNoPropertySource() throws Exception {
String someKey = "someKey";
apolloEnvironment.getProperty(someKey);
}
}
package com.ctrip.apollo.client.loader;
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.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 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.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.anyList;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
/**
* @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 {
String someAppId = "100";
String 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);
String 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();
String 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 {
String 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 {
String 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(String appId, Map<String, Object> configurations) {
String someCluster = "someCluster";
String someGroup = "someGroup";
String someVersion = "someVersion";
long someReleaseId = 1;
ApolloConfig config = new ApolloConfig(appId, someCluster, someGroup, someVersion, someReleaseId);
config.setConfigurations(configurations);
return config;
}
private ApolloRegistry assembleSomeApolloRegistry(String someAppId, String someVersion) {
ApolloRegistry someApolloRegistry = new ApolloRegistry();
someApolloRegistry.setAppId(someAppId);
someApolloRegistry.setVersion(someVersion);
return someApolloRegistry;
}
}
...@@ -48,7 +48,7 @@ public class RemoteConfigLoaderTest { ...@@ -48,7 +48,7 @@ public class RemoteConfigLoaderTest {
@Before @Before
public void setUp() { public void setUp() {
remoteConfigLoader = spy(new RemoteConfigLoader(restTemplate, configUtil, serviceLocater)); remoteConfigLoader = spy(new RemoteConfigLoader(restTemplate, serviceLocater));
} }
@Test @Test
...@@ -56,8 +56,9 @@ public class RemoteConfigLoaderTest { ...@@ -56,8 +56,9 @@ public class RemoteConfigLoaderTest {
String someServerUrl = "http://someUrl"; String someServerUrl = "http://someUrl";
String someCluster = "some cluster"; String someCluster = "some cluster";
ApolloConfig apolloConfig = mock(ApolloConfig.class); ApolloConfig apolloConfig = mock(ApolloConfig.class);
String someAppId = "1"; ApolloRegistry
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion"); apolloRegistry =
assembleSomeApolloRegistry("someAppId", "someCluster", "someNamespace");
ApolloConfig previousConfig = null; ApolloConfig previousConfig = null;
ServiceDTO someService = new ServiceDTO(); ServiceDTO someService = new ServiceDTO();
...@@ -67,7 +68,7 @@ public class RemoteConfigLoaderTest { ...@@ -67,7 +68,7 @@ public class RemoteConfigLoaderTest {
when(serviceLocater.getConfigServices()).thenReturn(someServices); when(serviceLocater.getConfigServices()).thenReturn(someServices);
when(configUtil.getCluster()).thenReturn(someCluster); when(configUtil.getCluster()).thenReturn(someCluster);
doReturn(apolloConfig).when(remoteConfigLoader) doReturn(apolloConfig).when(remoteConfigLoader)
.getRemoteConfig(restTemplate, someServerUrl, someCluster, apolloRegistry, previousConfig); .getRemoteConfig(restTemplate, someServerUrl, apolloRegistry, previousConfig);
ApolloConfig result = remoteConfigLoader.loadApolloConfig(apolloRegistry, previousConfig); ApolloConfig result = remoteConfigLoader.loadApolloConfig(apolloRegistry, previousConfig);
...@@ -76,12 +77,11 @@ public class RemoteConfigLoaderTest { ...@@ -76,12 +77,11 @@ public class RemoteConfigLoaderTest {
@Test @Test
public void testGetRemoteConfig() throws Exception { public void testGetRemoteConfig() throws Exception {
String someAppId = "1";
String someServerUrl = "http://someServer"; String someServerUrl = "http://someServer";
String someClusterName = "someCluster";
String someVersionName = "someVersion";
ApolloConfig someApolloConfig = mock(ApolloConfig.class); ApolloConfig someApolloConfig = mock(ApolloConfig.class);
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName); ApolloRegistry
apolloRegistry =
assembleSomeApolloRegistry("someAppId", "someCluster", "someNamespace");
ApolloConfig previousConfig = null; ApolloConfig previousConfig = null;
when(someResponse.getStatusCode()).thenReturn(HttpStatus.OK); when(someResponse.getStatusCode()).thenReturn(HttpStatus.OK);
...@@ -92,7 +92,7 @@ public class RemoteConfigLoaderTest { ...@@ -92,7 +92,7 @@ public class RemoteConfigLoaderTest {
ApolloConfig ApolloConfig
result = result =
remoteConfigLoader remoteConfigLoader
.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry, .getRemoteConfig(restTemplate, someServerUrl, apolloRegistry,
previousConfig); previousConfig);
assertEquals(someApolloConfig, result); assertEquals(someApolloConfig, result);
...@@ -100,11 +100,10 @@ public class RemoteConfigLoaderTest { ...@@ -100,11 +100,10 @@ public class RemoteConfigLoaderTest {
@Test(expected = RuntimeException.class) @Test(expected = RuntimeException.class)
public void testGetRemoteConfigWithServerError() throws Exception { public void testGetRemoteConfigWithServerError() throws Exception {
String someAppId = "1";
String someServerUrl = "http://someServer"; String someServerUrl = "http://someServer";
String someClusterName = "someCluster"; ApolloRegistry
String someVersionName = "someVersion"; apolloRegistry =
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName); assembleSomeApolloRegistry("someAppId", "someCluster", "someNamespace");
ApolloConfig previousConfig = null; ApolloConfig previousConfig = null;
HttpStatus someErrorCode = HttpStatus.INTERNAL_SERVER_ERROR; HttpStatus someErrorCode = HttpStatus.INTERNAL_SERVER_ERROR;
...@@ -112,17 +111,16 @@ public class RemoteConfigLoaderTest { ...@@ -112,17 +111,16 @@ public class RemoteConfigLoaderTest {
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);
remoteConfigLoader.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry, remoteConfigLoader.getRemoteConfig(restTemplate, someServerUrl, apolloRegistry,
previousConfig); previousConfig);
} }
@Test @Test
public void testGetRemoteConfigWith304Response() throws Exception { public void testGetRemoteConfigWith304Response() throws Exception {
String someAppId = "1";
String someServerUrl = "http://someServer"; String someServerUrl = "http://someServer";
String someClusterName = "someCluster"; ApolloRegistry
String someVersionName = "someVersion"; apolloRegistry =
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, someVersionName); assembleSomeApolloRegistry("someAppId", "someCluster", "someNamespace");
ApolloConfig previousConfig = null; ApolloConfig previousConfig = null;
when(someResponse.getStatusCode()).thenReturn(HttpStatus.NOT_MODIFIED); when(someResponse.getStatusCode()).thenReturn(HttpStatus.NOT_MODIFIED);
...@@ -132,16 +130,17 @@ public class RemoteConfigLoaderTest { ...@@ -132,16 +130,17 @@ public class RemoteConfigLoaderTest {
ApolloConfig ApolloConfig
result = result =
remoteConfigLoader remoteConfigLoader
.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry, .getRemoteConfig(restTemplate, someServerUrl, apolloRegistry,
previousConfig); previousConfig);
assertNull(result); assertNull(result);
} }
private ApolloRegistry assembleSomeApolloRegistry(String someAppId, String someVersion) { private ApolloRegistry assembleSomeApolloRegistry(String someAppId, String someClusterName,
ApolloRegistry someApolloRegistry = new ApolloRegistry(); String someNamespace) {
someApolloRegistry.setAppId(someAppId); ApolloRegistry
someApolloRegistry.setVersion(someVersion); someApolloRegistry =
new ApolloRegistry(someAppId, someClusterName, someNamespace);
return someApolloRegistry; return someApolloRegistry;
} }
......
package com.ctrip.apollo.client.util;
import com.google.common.collect.Lists;
import com.ctrip.apollo.client.constants.Constants;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.core.utils.ClassLoaderUtil;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith(MockitoJUnitRunner.class)
public class ConfigUtilTest {
private ConfigUtil configUtil;
@Before
public void setUp() throws Exception {
configUtil = spy(ConfigUtil.getInstance());
}
@Test
public void testLoadApolloRegistriesSuccessfully() throws Exception {
Properties someProperties = mock(Properties.class);
preparePropertiesFromLocalResource(someProperties);
String someAppId = "1";
String someVersionId = "someVersion";
when(someProperties.containsKey(Constants.APP_ID)).thenReturn(true);
when(someProperties.getProperty(Constants.APP_ID)).thenReturn(someAppId);
when(someProperties.getProperty(eq(Constants.VERSION), anyString())).thenReturn(someVersionId);
List<ApolloRegistry> apolloRegistries = configUtil.loadApolloRegistries();
ApolloRegistry apolloRegistry = apolloRegistries.get(0);
assertEquals(1, apolloRegistries.size());
assertEquals(someAppId, apolloRegistry.getAppId());
assertEquals(someVersionId, apolloRegistry.getVersion());
}
@Test
public void testLoadApolloRegistriesError() throws Exception {
preparePropertiesFromLocalResource(null);
List<ApolloRegistry> apolloRegistries = configUtil.loadApolloRegistries();
assertTrue(apolloRegistries.isEmpty());
}
private void preparePropertiesFromLocalResource(Properties someProperties) throws IOException {
ClassLoader someClassLoader = mock(ClassLoader.class);
ReflectionTestUtils.setField(ClassLoaderUtil.class, "loader", someClassLoader);
URL someUrl = new URL("http", "somepath/", "someFile");
Enumeration<URL> someResourceUrls = Collections.enumeration(Lists.newArrayList(someUrl));
when(someClassLoader.getResources(anyString())).thenReturn(someResourceUrls);
doReturn(someProperties).when(configUtil).loadPropertiesFromResourceURL(someUrl);
}
}
package com.ctrip.apollo.configservice.controller; package com.ctrip.apollo.configservice.controller;
import java.io.IOException; import com.ctrip.apollo.biz.entity.Release;
import com.ctrip.apollo.biz.service.ConfigService;
import javax.servlet.http.HttpServletResponse; import com.ctrip.apollo.core.ConfigConsts;
import com.ctrip.apollo.core.dto.ApolloConfig;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
...@@ -11,9 +12,9 @@ import org.springframework.web.bind.annotation.RequestMethod; ...@@ -11,9 +12,9 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import com.ctrip.apollo.biz.entity.Release; import java.io.IOException;
import com.ctrip.apollo.biz.service.ConfigService;
import com.ctrip.apollo.core.dto.ApolloConfig; import javax.servlet.http.HttpServletResponse;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
...@@ -24,17 +25,25 @@ public class ConfigController { ...@@ -24,17 +25,25 @@ public class ConfigController {
@Autowired @Autowired
private ConfigService configService; private ConfigService configService;
@RequestMapping(value = "/{appId}/{clusterName}/{groupName}/{versionName:.*}", method = RequestMethod.GET) @RequestMapping(value = "/{appId}/{clusterName}", method = RequestMethod.GET)
public ApolloConfig queryConfig(@PathVariable String appId, @PathVariable String clusterName,
@RequestParam(value = "releaseId", defaultValue = "-1") long clientSideReleaseId,
HttpServletResponse response) throws IOException {
return this.queryConfig(appId, clusterName, ConfigConsts.NAMESPACE_APPLICATION, clientSideReleaseId,
response);
}
@RequestMapping(value = "/{appId}/{clusterName}/{namespace}", method = RequestMethod.GET)
public ApolloConfig queryConfig(@PathVariable String appId, @PathVariable String clusterName, public ApolloConfig queryConfig(@PathVariable String appId, @PathVariable String clusterName,
@PathVariable String groupName, @PathVariable String versionName, @PathVariable String namespace,
@RequestParam(value = "releaseId", defaultValue = "-1") long clientSideReleaseId, @RequestParam(value = "releaseId", defaultValue = "-1") long clientSideReleaseId,
HttpServletResponse response) throws IOException { HttpServletResponse response) throws IOException {
Release release = configService.findRelease(appId, clusterName, groupName); Release release = configService.findRelease(appId, clusterName, namespace);
if (release == null) { if (release == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, response.sendError(HttpServletResponse.SC_NOT_FOUND,
String.format( String.format(
"Could not load version with appId: %s, clusterName: %s, groupName: %s, versionName: %s", "Could not load version with appId: %s, clusterName: %s, namespace: %s",
appId, clusterName, groupName, versionName)); appId, clusterName, namespace));
return null; return null;
} }
if (release.getId() == clientSideReleaseId) { if (release.getId() == clientSideReleaseId) {
...@@ -43,12 +52,12 @@ public class ConfigController { ...@@ -43,12 +52,12 @@ public class ConfigController {
return null; return null;
} }
ApolloConfig apolloConfig = configService.loadConfig(release, groupName, versionName); ApolloConfig apolloConfig = configService.loadConfig(release, namespace);
if (apolloConfig == null) { if (apolloConfig == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, response.sendError(HttpServletResponse.SC_NOT_FOUND,
String.format("Could not load config with releaseId: %d, clusterName: %s", String.format("Could not load config with releaseId: %d, clusterName: %s",
release.getId(), clusterName)); release.getId(), clusterName));
return null; return null;
} }
......
...@@ -2,6 +2,8 @@ package com.ctrip.apollo.core; ...@@ -2,6 +2,8 @@ package com.ctrip.apollo.core;
public interface ConfigConsts { public interface ConfigConsts {
String DEFAULT_CLUSTER_NAME = "default"; String CLUSTER_NAME_DEFAULT = "default";
String NAMESPACE_APPLICATION = "application";
} }
...@@ -10,47 +10,30 @@ import java.util.Map; ...@@ -10,47 +10,30 @@ import java.util.Map;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class ApolloConfig implements Comparable<ApolloConfig> { public class ApolloConfig {
private String appId; private String appId;
private String cluster; private String cluster;
private String group; private String namespace;
private String version;
private Map<String, Object> configurations; private Map<String, String> configurations;
private long releaseId; private long releaseId;
private int order;
@JsonCreator @JsonCreator
public ApolloConfig(@JsonProperty("appId") String appId, public ApolloConfig(@JsonProperty("appId") String appId,
@JsonProperty("cluster") String cluster, @JsonProperty("cluster") String cluster,
@JsonProperty("group") String group, @JsonProperty("namespace") String namespace,
@JsonProperty("version") String version,
@JsonProperty("releaseId") long releaseId) { @JsonProperty("releaseId") long releaseId) {
super(); super();
this.appId = appId; this.appId = appId;
this.cluster = cluster; this.cluster = cluster;
this.group = group; this.namespace = namespace;
this.version = version;
this.releaseId = releaseId; this.releaseId = releaseId;
} }
@Override
public int compareTo(ApolloConfig toCompare) {
if (toCompare == null || this.getOrder() > toCompare.getOrder()) {
return 1;
}
if (toCompare.getOrder() > this.getOrder()) {
return -1;
}
return 0;
}
public String getAppId() { public String getAppId() {
return appId; return appId;
} }
...@@ -59,36 +42,20 @@ public class ApolloConfig implements Comparable<ApolloConfig> { ...@@ -59,36 +42,20 @@ public class ApolloConfig implements Comparable<ApolloConfig> {
return cluster; return cluster;
} }
public Map<String, Object> getConfigurations() { public String getNamespace() {
return configurations; return namespace;
}
public String getGroup() {
return group;
}
public int getOrder() {
return order;
} }
public long getReleaseId() { public long getReleaseId() {
return releaseId; return releaseId;
} }
public String getVersion() { public void setConfigurations(Map<String, String> configurations) {
return version;
}
public void setConfigurations(Map<String, Object> configurations) {
this.configurations = configurations; this.configurations = configurations;
} }
public void setGroup(String group) { public String getProperty(String key) {
this.group = group; return this.configurations.get(key);
}
public void setOrder(int order) {
this.order = order;
} }
@Override @Override
...@@ -97,8 +64,7 @@ public class ApolloConfig implements Comparable<ApolloConfig> { ...@@ -97,8 +64,7 @@ public class ApolloConfig implements Comparable<ApolloConfig> {
.omitNullValues() .omitNullValues()
.add("appId", appId) .add("appId", appId)
.add("cluster", cluster) .add("cluster", cluster)
.add("group", group) .add("namespace", namespace)
.add("version", version)
.add("releaseId", releaseId) .add("releaseId", releaseId)
.add("configurations", configurations) .add("configurations", configurations)
.toString(); .toString();
......
import com.ctrip.apollo.client.ApolloEnvironment; import com.ctrip.apollo.client.ConfigService;
import com.ctrip.apollo.client.config.Config;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
...@@ -8,15 +9,14 @@ import java.io.InputStreamReader; ...@@ -8,15 +9,14 @@ import java.io.InputStreamReader;
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class ApolloConfigDemo { public class ApolloConfigDemo {
private ApolloEnvironment env; private Config config;
public ApolloConfigDemo() { public ApolloConfigDemo() {
env = ApolloEnvironment.getInstance(); config = ConfigService.getConfig();
env.init();
} }
private String getConfig(String key) { private String getConfig(String key) {
String result = env.getProperty(key, "undefined"); String result = config.getProperty(key, "undefined");
System.out.println(String.format("Loading key: %s with value: %s", key, result)); System.out.println(String.format("Loading key: %s with value: %s", key, result));
return result; return result;
} }
......
...@@ -210,7 +210,7 @@ public class ConfigService { ...@@ -210,7 +210,7 @@ public class ConfigService {
clusterName = entry.getKey(); clusterName = entry.getKey();
clusterConfigs = entry.getValue(); clusterConfigs = entry.getValue();
if (ConfigConsts.DEFAULT_CLUSTER_NAME.equals(clusterName)) { if (ConfigConsts.CLUSTER_NAME_DEFAULT.equals(clusterName)) {
// default cluster configs // default cluster configs
collectDefaultClusterConfigs(appId, clusterConfigs, defaultClusterConfigs, collectDefaultClusterConfigs(appId, clusterConfigs, defaultClusterConfigs,
overrideAppConfigs); overrideAppConfigs);
......
...@@ -177,7 +177,7 @@ public class ConfigServiceTest { ...@@ -177,7 +177,7 @@ public class ConfigServiceTest {
private ReleaseDTO[] assembleReleaseSnapShots() { private ReleaseDTO[] assembleReleaseSnapShots() {
ReleaseDTO[] releaseSnapShots = new ReleaseDTO[3]; ReleaseDTO[] releaseSnapShots = new ReleaseDTO[3];
releaseSnapShots[0] = assembleReleaseSnapShot(11111, ConfigConsts.DEFAULT_CLUSTER_NAME, releaseSnapShots[0] = assembleReleaseSnapShot(11111, ConfigConsts.CLUSTER_NAME_DEFAULT,
"{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\",\"3333.foo\":\"1008\",\"4444.bar\":\"99901\"}"); "{\"6666.foo\":\"demo1\", \"6666.bar\":\"demo2\",\"3333.foo\":\"1008\",\"4444.bar\":\"99901\"}");
releaseSnapShots[1] = assembleReleaseSnapShot(11111, "cluster1", "{\"6666.foo\":\"demo1\"}"); releaseSnapShots[1] = assembleReleaseSnapShot(11111, "cluster1", "{\"6666.foo\":\"demo1\"}");
releaseSnapShots[2] = assembleReleaseSnapShot(11111, "cluster2", "{\"6666.bar\":\"bar2222\"}"); releaseSnapShots[2] = assembleReleaseSnapShot(11111, "cluster2", "{\"6666.bar\":\"bar2222\"}");
...@@ -195,7 +195,7 @@ public class ConfigServiceTest { ...@@ -195,7 +195,7 @@ public class ConfigServiceTest {
private ClusterDTO[] assembleClusters() { private ClusterDTO[] assembleClusters() {
ClusterDTO[] clusters = new ClusterDTO[2]; ClusterDTO[] clusters = new ClusterDTO[2];
clusters[0] = assembleCluster(100, "6666", ConfigConsts.DEFAULT_CLUSTER_NAME); clusters[0] = assembleCluster(100, "6666", ConfigConsts.CLUSTER_NAME_DEFAULT);
clusters[1] = assembleCluster(101, "6666", "cluster1"); clusters[1] = assembleCluster(101, "6666", "cluster1");
return clusters; return clusters;
} }
...@@ -210,10 +210,10 @@ public class ConfigServiceTest { ...@@ -210,10 +210,10 @@ public class ConfigServiceTest {
private ItemDTO[] assembleConfigItems() { private ItemDTO[] assembleConfigItems() {
ItemDTO[] configItems = new ItemDTO[5]; ItemDTO[] configItems = new ItemDTO[5];
configItems[0] = assembleConfigItem(100, ConfigConsts.DEFAULT_CLUSTER_NAME, "6666", "6666.k1", "6666.v1"); configItems[0] = assembleConfigItem(100, ConfigConsts.CLUSTER_NAME_DEFAULT, "6666", "6666.k1", "6666.v1");
configItems[1] = assembleConfigItem(100, ConfigConsts.DEFAULT_CLUSTER_NAME, "6666", "6666.k2", "6666.v2"); configItems[1] = assembleConfigItem(100, ConfigConsts.CLUSTER_NAME_DEFAULT, "6666", "6666.k2", "6666.v2");
configItems[2] = assembleConfigItem(100, ConfigConsts.DEFAULT_CLUSTER_NAME, "6666", "6666.k3", "6666.v3"); configItems[2] = assembleConfigItem(100, ConfigConsts.CLUSTER_NAME_DEFAULT, "6666", "6666.k3", "6666.v3");
configItems[3] = assembleConfigItem(100, ConfigConsts.DEFAULT_CLUSTER_NAME, "5555", "5555.k1", "5555.v1"); configItems[3] = assembleConfigItem(100, ConfigConsts.CLUSTER_NAME_DEFAULT, "5555", "5555.k1", "5555.v1");
configItems[4] = assembleConfigItem(101, "cluster1", "6666", "6666.k1", "6666.v1"); configItems[4] = assembleConfigItem(101, "cluster1", "6666", "6666.k1", "6666.v1");
return configItems; return configItems;
} }
......
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>apollo</artifactId>
<groupId>com.ctrip.apollo</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-spring-demo</artifactId>
<packaging>war</packaging>
<name>Apollo Spring Demo</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.tuckey</groupId>
<artifactId>urlrewritefilter</artifactId>
<version>4.0.4</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</dependency>
</dependencies>
<build>
<finalName>apollo-spring-demo</finalName>
</build>
</project>
package com.ctrip.apollo.demo;
import com.ctrip.apollo.client.ApolloConfigManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Configuration
@ComponentScan(value = "com.ctrip.apollo.demo")
public class AppConfig {
@Bean
public ApolloConfigManager apolloConfigManager() {
return new ApolloConfigManager();
}
// @Bean
// public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
// return new PropertySourcesPlaceholderConfigurer();
// }
//
// @Bean
// public static RefreshScope refreshScope() {
// return new RefreshScope();
// }
}
package com.ctrip.apollo.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("welcome");
registry.addViewController("/index").setViewName("welcome");
}
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver
= new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("/");
}
}
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;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.List;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RestController
@RequestMapping("/demo")
@PropertySource("classpath:application.properties")
public class DemoController {
@Autowired
private Environment env;
@Autowired
private DemoService demoService;
//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 ApolloConfigManager apolloConfigManager;
@RequestMapping(value = "/config/{configName:.*}", method = RequestMethod.GET)
public Config queryConfig(@PathVariable String configName) {
return new Config(configName, env.getProperty(configName, "undefined"));
}
@RequestMapping(value = "/injected/config", method = RequestMethod.GET)
public Config queryInjectedConfig() {
return new Config("apollo.foo", demoService.getFoo());
}
@RequestMapping(value = "/client/registries", method = RequestMethod.GET)
public List<ApolloRegistry> loadApolloRegistries() throws IOException {
return configUtil.loadApolloRegistries();
}
@RequestMapping(value = "/refresh", method = RequestMethod.POST)
public List<PropertyChange> refreshBeans() {
List<PropertyChange> changes = this.apolloConfigManager.updatePropertySource();
return changes;
}
}
package com.ctrip.apollo.demo.exception;
import com.ctrip.apollo.demo.model.ErrorResult;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
/**
* Created by Jason on 7/6/15.
*/
@ControllerAdvice
public class RestExceptionHandler {
@ExceptionHandler(Exception.class)
ResponseEntity<ErrorResult> handleWebExceptions(Exception ex,
WebRequest request)
throws JsonProcessingException {
ErrorResult error = new ErrorResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getMessage());
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(error);
}
}
package com.ctrip.apollo.demo.model;
/**
* Created by Jason on 2/25/16.
*/
public class Config {
private final String name;
private final String value;
public Config(String name, String value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
}
package com.ctrip.apollo.demo.model;
/**
* Created by Jason on 7/6/15.
*/
public class ErrorResult {
private final int code;
private final String msg;
public ErrorResult(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
package com.ctrip.apollo.demo.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Service
@RefreshScope
public class DemoService {
private String foo;
@Value("${101.foo}")
private void setFoo(String foo) {
this.foo = foo;
}
public String getFoo() {
return foo;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<configuration monitorInterval="60">
<appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="[apollo-demo][%t]%d %-5p [%c] %m%n"/>
</Console>
<Async name="Async" includeLocation="true">
<AppenderRef ref="Console"/>
</Async>
</appenders>
<loggers>
<logger name="com.ctrip.apollo" additivity="false" level="trace">
<AppenderRef ref="Async" level="DEBUG"/>
</logger>
<root level="INFO">
<AppenderRef ref="Async"/>
</root>
</loggers>
</configuration>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.0//EN" "http://tuckey.org/res/dtds/urlrewrite3.0.dtd">
<urlrewrite default-match-type="wildcard">
<rule>
<from>/favicon.ico</from>
<to>/s/favicon.ico</to>
</rule>
<rule>
<from>/images/**</from>
<to>/s/images/$1</to>
</rule>
<rule>
<from>/scripts/**</from>
<to>/s/scripts/$1</to>
</rule>
<rule>
<from>/styles/**</from>
<to>/s/styles/$1</to>
</rule>
<rule>
<from>/templates/**</from>
<to>/s/templates/$1</to>
</rule>
<rule match-type="regex">
<from>^\/(index\..*)?</from>
<to>/app/</to>
</rule>
<rule>
<from>/**</from>
<to>/app/$1</to>
</rule>
<outbound-rule>
<from>/app/**</from>
<to>/$1</to>
</outbound-rule>
</urlrewrite>
<!DOCTYPE html>
<%@page contentType="text/html" pageEncoding="UTF-8" %>
<html ng-app="Demo">
<meta http-equiv=Content-Type content="text/html;charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Apollo Config Client</title>
<link rel="stylesheet" type="text/css" href="http://apps.bdimg.com/libs/bootstrap/3.3.0/css/bootstrap.min.css"/>
<link rel="stylesheet" type="text/css" href="/styles/angular-toastr-1.4.1.min.css"/>
<link rel='stylesheet' href='/styles/loading-bar.min.css' type='text/css' media='all'/>
<link rel="stylesheet" type="text/css" href="/styles/app.css"/>
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="http://apps.bdimg.com/libs/angular.js/1.3.9/angular.min.js"></script>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]-->
<body>
<div class="container-fluid" ng-include="'/templates/list.html'"></div>
<script type="text/javascript" src="//cdn.bootcss.com/underscore.js/1.8.3/underscore-min.js"></script>
<script type="text/javascript" src="/scripts/ui-bootstrap-0.13.0.min.js"></script>
<script type="text/javascript" src="/scripts/ui-bootstrap-tpls-0.13.0.min.js"></script>
<script type="text/javascript" src="/scripts/angular-toastr-1.4.1.tpls.min.js"></script>
<script type='text/javascript' src='/scripts/loading-bar.min.js'></script>
<script type="text/javascript" src="/scripts/http.js"></script>
<script type="text/javascript" src="/scripts/app.js"></script>
</body>
</html>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Apollo demo</display-name>
<!-- Enables clean URLs with JSP views e.g. /welcome instead of /app/welcome -->
<filter>
<filter-name>UrlRewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>UrlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
com.ctrip.apollo.demo.AppConfig
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
(function ($) {
var app = angular.module('Demo', [
'ui.bootstrap',
'toastr',
'angular-loading-bar',
'httpInterceptors' //custom http interceptor
]);
app.controller('DemoController', function ($scope, $http, $modal, toastr) {
var NONE = "none";
this.registries = {};
this.configQuery = {};
this.refreshResult = NONE;
this.injectedConfigValue = '';
var self = this;
this.loadRegistries = function () {
$http.get("demo/client/registries")
.success(function (data) {
self.registries = data;
})
.error(function (data, status) {
toastr.error((data && data.msg) || 'Loading registries failed');
});
};
this.queryConfig = function () {
$http.get("demo/config/" + encodeURIComponent(this.configQuery.configName))
.success(function (data) {
self.configQuery.configValue = data.value;
})
.error(function (data, status) {
toastr.error((data && data.msg) || 'Load config failed');
});
};
this.queryInjectedConfig = function () {
$http.get("demo/injected/config")
.success(function (data) {
self.injectedConfigValue = data.value;
})
.error(function (data, status) {
toastr.error((data && data.msg) || 'Load injected 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 = _.map(changedPropertyArray, function (propertyChange) {
return propertyChange.propertyName + '(' + propertyChange.changeType + ')';
});
};
this.loadRegistries();
});
})(jQuery);
(function ($) {
var httpInterceptors = angular.module('httpInterceptors', []);
httpInterceptors.factory('httpInterceptor', function ($q) {
return {
'request': function (config) {
var t = new Date().getTime();
if (config.url.indexOf('.htm') != -1 || config.url.indexOf('?_=') != -1) {
return config;
}
config.url = config.url + '?_=' + t;
return config;
},
'response': function (response) {
if (typeof response.data === 'object') {
if (response.data.code != null && response.data.code != 200) {
return $q.reject(response);
}
}
return response;
}
};
});
httpInterceptors.config(function ($httpProvider) {
$httpProvider.interceptors.push('httpInterceptor');
});
})(jQuery);
/*!
* angular-loading-bar v0.8.0
* https://chieffancypants.github.io/angular-loading-bar
* Copyright (c) 2015 Wes Cruver
* License: MIT
*/
!function () {
"use strict";
angular.module("angular-loading-bar", ["cfp.loadingBarInterceptor"]), angular.module("chieffancypants.loadingBar", ["cfp.loadingBarInterceptor"]), angular.module("cfp.loadingBarInterceptor", ["cfp.loadingBar"]).config(["$httpProvider", function (a) {
var b = ["$q", "$cacheFactory", "$timeout", "$rootScope", "$log", "cfpLoadingBar", function (b, c, d, e, f, g) {
function h() {
d.cancel(j), g.complete(), l = 0, k = 0
}
function i(b) {
var d, e = c.get("$http"), f = a.defaults;
!b.cache && !f.cache || b.cache === !1 || "GET" !== b.method && "JSONP" !== b.method || (d = angular.isObject(b.cache) ? b.cache : angular.isObject(f.cache) ? f.cache : e);
var g = void 0 !== d ? void 0 !== d.get(b.url) : !1;
return void 0 !== b.cached && g !== b.cached ? b.cached : (b.cached = g, g)
}
var j, k = 0, l = 0, m = g.latencyThreshold;
return {
request: function (a) {
return a.ignoreLoadingBar || i(a) || (e.$broadcast("cfpLoadingBar:loading", {url: a.url}), 0 === k && (j = d(function () {
g.start()
}, m)), k++, g.set(l / k)), a
}, response: function (a) {
return a && a.config ? (a.config.ignoreLoadingBar || i(a.config) || (l++, e.$broadcast("cfpLoadingBar:loaded", {
url: a.config.url,
result: a
}), l >= k ? h() : g.set(l / k)), a) : (f.error("Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"), a)
}, responseError: function (a) {
return a && a.config ? (a.config.ignoreLoadingBar || i(a.config) || (l++, e.$broadcast("cfpLoadingBar:loaded", {
url: a.config.url,
result: a
}), l >= k ? h() : g.set(l / k)), b.reject(a)) : (f.error("Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"), b.reject(a))
}
}
}];
a.interceptors.push(b)
}]), angular.module("cfp.loadingBar", []).provider("cfpLoadingBar", function () {
this.autoIncrement = !0, this.includeSpinner = !0, this.includeBar = !0, this.latencyThreshold = 100, this.startSize = .02, this.parentSelector = "body", this.spinnerTemplate = '<div id="loading-bar-spinner"><div class="spinner-icon"></div></div>', this.loadingBarTemplate = '<div id="loading-bar"><div class="bar"><div class="peg"></div></div></div>', this.$get = ["$injector", "$document", "$timeout", "$rootScope", function (a, b, c, d) {
function e() {
k || (k = a.get("$animate"));
var e = b.find(n).eq(0);
c.cancel(m), r || (d.$broadcast("cfpLoadingBar:started"), r = !0, v && k.enter(o, e, angular.element(e[0].lastChild)), u && k.enter(q, e, angular.element(e[0].lastChild)), f(w))
}
function f(a) {
if (r) {
var b = 100 * a + "%";
p.css("width", b), s = a, t && (c.cancel(l), l = c(function () {
g()
}, 250))
}
}
function g() {
if (!(h() >= 1)) {
var a = 0, b = h();
a = b >= 0 && .25 > b ? (3 * Math.random() + 3) / 100 : b >= .25 && .65 > b ? 3 * Math.random() / 100 : b >= .65 && .9 > b ? 2 * Math.random() / 100 : b >= .9 && .99 > b ? .005 : 0;
var c = h() + a;
f(c)
}
}
function h() {
return s
}
function i() {
s = 0, r = !1
}
function j() {
k || (k = a.get("$animate")), d.$broadcast("cfpLoadingBar:completed"), f(1), c.cancel(m), m = c(function () {
var a = k.leave(o, i);
a && a.then && a.then(i), k.leave(q)
}, 500)
}
var k, l, m, n = this.parentSelector, o = angular.element(this.loadingBarTemplate), p = o.find("div").eq(0), q = angular.element(this.spinnerTemplate), r = !1, s = 0, t = this.autoIncrement, u = this.includeSpinner, v = this.includeBar, w = this.startSize;
return {
start: e,
set: f,
status: h,
inc: g,
complete: j,
autoIncrement: this.autoIncrement,
includeSpinner: this.includeSpinner,
latencyThreshold: this.latencyThreshold,
parentSelector: this.parentSelector,
startSize: this.startSize
}
}]
})
}();
.toast-title {
font-weight: 700
}
.toast-message {
word-wrap: break-word
}
.toast-message a, .toast-message label {
color: #fff
}
.toast-message a:hover {
color: #ccc;
text-decoration: none
}
.toast-close-button {
position: relative;
right: -.3em;
top: -.3em;
float: right;
font-size: 20px;
font-weight: 700;
color: #fff;
-webkit-text-shadow: 0 1px 0 #fff;
text-shadow: 0 1px 0 #fff;
opacity: .8
}
.toast-close-button:focus, .toast-close-button:hover {
color: #000;
text-decoration: none;
cursor: pointer;
opacity: .4
}
button.toast-close-button {
padding: 0;
cursor: pointer;
background: 0 0;
border: 0;
-webkit-appearance: none
}
.toast-top-center {
top: 0;
right: 0;
width: 100%
}
.toast-bottom-center {
bottom: 0;
right: 0;
width: 100%
}
.toast-top-full-width {
top: 0;
right: 0;
width: 100%
}
.toast-bottom-full-width {
bottom: 0;
right: 0;
width: 100%
}
.toast-top-left {
top: 12px;
left: 12px
}
.toast-top-right {
top: 12px;
right: 12px
}
.toast-bottom-right {
right: 12px;
bottom: 12px
}
.toast-bottom-left {
bottom: 12px;
left: 12px
}
#toast-container {
position: fixed;
z-index: 999999
}
#toast-container * {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box
}
#toast-container > div {
position: relative;
overflow: hidden;
margin: 0 0 6px;
padding: 15px 15px 15px 50px;
width: 300px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
background-position: 15px center;
background-repeat: no-repeat;
-moz-box-shadow: 0 0 12px #999;
-webkit-box-shadow: 0 0 12px #999;
box-shadow: 0 0 12px #999;
color: #fff;
opacity: .8
}
#toast-container > :hover {
-moz-box-shadow: 0 0 12px #000;
-webkit-box-shadow: 0 0 12px #000;
box-shadow: 0 0 12px #000;
opacity: 1;
cursor: pointer
}
#toast-container > .toast-info {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=) !important
}
#toast-container > .toast-error {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=) !important
}
#toast-container > .toast-success {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==) !important
}
#toast-container > .toast-warning {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=) !important
}
#toast-container.toast-bottom-center > div, #toast-container.toast-top-center > div {
width: 300px;
margin: auto
}
#toast-container.toast-bottom-full-width > div, #toast-container.toast-top-full-width > div {
width: 96%;
margin: auto
}
.toast {
background-color: #030303
}
.toast-success {
background-color: #51a351
}
.toast-error {
background-color: #bd362f
}
.toast-info {
background-color: #2f96b4
}
.toast-warning {
background-color: #f89406
}
.toast-progress {
position: absolute;
left: 0;
bottom: 0;
height: 4px;
background-color: #000;
opacity: .4
}
.toast {
opacity: 1 !important
}
.toast.ng-enter {
opacity: 0 !important;
transition: opacity .3s linear
}
.toast.ng-enter.ng-enter-active {
opacity: 1 !important
}
.toast.ng-leave {
opacity: 1;
transition: opacity .3s linear
}
.toast.ng-leave.ng-leave-active {
opacity: 0 !important
}
@media all and (max-width: 240px) {
#toast-container > div {
padding: 8px 8px 8px 50px;
width: 11em
}
#toast-container .toast-close-button {
right: -.2em;
top: -.2em
}
}
@media all and (min-width: 241px)and (max-width: 480px) {
#toast-container > div {
padding: 8px 8px 8px 50px;
width: 18em
}
#toast-container .toast-close-button {
right: -.2em;
top: -.2em
}
}
@media all and (min-width: 481px)and (max-width: 768px) {
#toast-container > div {
padding: 15px 15px 15px 50px;
width: 25em
}
}
#config-value-wrapper, #refresh-result {
margin-top: 5px;
}
\ No newline at end of file
#loading-bar, #loading-bar-spinner {
pointer-events: none;
-webkit-pointer-events: none;
-webkit-transition: 350ms linear all;
-moz-transition: 350ms linear all;
-o-transition: 350ms linear all;
transition: 350ms linear all
}
#loading-bar-spinner.ng-enter, #loading-bar-spinner.ng-leave.ng-leave-active, #loading-bar.ng-enter, #loading-bar.ng-leave.ng-leave-active {
opacity: 0
}
#loading-bar-spinner.ng-enter.ng-enter-active, #loading-bar-spinner.ng-leave, #loading-bar.ng-enter.ng-enter-active, #loading-bar.ng-leave {
opacity: 1
}
#loading-bar .bar {
-webkit-transition: width 350ms;
-moz-transition: width 350ms;
-o-transition: width 350ms;
transition: width 350ms;
background: #29d;
position: fixed;
z-index: 10002;
top: 0;
left: 0;
width: 100%;
height: 2px;
border-bottom-right-radius: 1px;
border-top-right-radius: 1px
}
#loading-bar .peg {
position: absolute;
width: 70px;
right: 0;
top: 0;
height: 2px;
opacity: .45;
-moz-box-shadow: #29d 1px 0 6px 1px;
-ms-box-shadow: #29d 1px 0 6px 1px;
-webkit-box-shadow: #29d 1px 0 6px 1px;
box-shadow: #29d 1px 0 6px 1px;
-moz-border-radius: 100%;
-webkit-border-radius: 100%;
border-radius: 100%
}
#loading-bar-spinner {
display: block;
position: fixed;
z-index: 10002;
top: 10px;
left: 10px
}
#loading-bar-spinner .spinner-icon {
width: 14px;
height: 14px;
border: 2px solid transparent;
border-top-color: #29d;
border-left-color: #29d;
border-radius: 50%;
-webkit-animation: loading-bar-spinner 400ms linear infinite;
-moz-animation: loading-bar-spinner 400ms linear infinite;
-ms-animation: loading-bar-spinner 400ms linear infinite;
-o-animation: loading-bar-spinner 400ms linear infinite;
animation: loading-bar-spinner 400ms linear infinite
}
@-webkit-keyframes loading-bar-spinner {
0% {
-webkit-transform: rotate(0);
transform: rotate(0)
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg)
}
}
@-moz-keyframes loading-bar-spinner {
0% {
-moz-transform: rotate(0);
transform: rotate(0)
}
100% {
-moz-transform: rotate(360deg);
transform: rotate(360deg)
}
}
@-o-keyframes loading-bar-spinner {
0% {
-o-transform: rotate(0);
transform: rotate(0)
}
100% {
-o-transform: rotate(360deg);
transform: rotate(360deg)
}
}
@-ms-keyframes loading-bar-spinner {
0% {
-ms-transform: rotate(0);
transform: rotate(0)
}
100% {
-ms-transform: rotate(360deg);
transform: rotate(360deg)
}
}
@keyframes loading-bar-spinner {
0% {
transform: rotate(0)
}
100% {
transform: rotate(360deg)
}
}
<div ng-controller="DemoController as demoCtrl">
<h1>Apollo Config Client Demo</h1>
<div id="env-wrapper">
<h3>Current Client Side Registries:</h3>
<table class="table table-bordered table-hover">
<thead>
<tr class="bg-info">
<th>AppId</th>
<th>Version</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in demoCtrl.registries">
<td>{{item.appId}}</td>
<td>{{item.version}}</td>
</tr>
</tbody>
</table>
</div>
<div id="load-config-wrapper">
<h3>Load Config:</h3>
<form name="loadConfigForm" class="form-horizontal" novalidate
ng-submit="demoCtrl.queryConfig()">
<div class="form-group"
ng-class="{ 'has-error' : loadConfigForm.configName.$invalid && loadConfigForm.configName.$dirty}">
<div id="input-config-wrapper" class="clearfix">
<label class="col-sm-2 control-label">Config Name</label>
<div class="col-sm-3">
<input type="text" name="configName" class="form-control"
ng-model="demoCtrl.configQuery.configName"
autofocus="true" required autocomplete="off"/>
<p ng-show="loadConfigForm.configName.$invalid && loadConfigForm.configName.$dirty"
class="help-block">Please fill
in the content</p>
</div>
<input type="submit" class="btn btn-primary col-sm-1" value="query"
ng-disabled="loadConfigForm.$pristine || loadConfigForm.$invalid"/>
</div>
<div id="config-value-wrapper" class="clearfix">
<label class="col-sm-2 control-label">Config Value</label>
<div class="col-sm-3">
<textarea rows="2" cols="38" readonly
class="form-control">{{demoCtrl.configQuery.configValue}}</textarea>
</div>
</div>
</div>
</form>
</div>
<div>
<h3>Load Injected Config:</h3>
<form name="loadInjectedConfigForm" class="form-horizontal" novalidate
ng-submit="demoCtrl.queryInjectedConfig()">
<div class="form-group">
<div class="clearfix">
<label class="col-sm-2 control-label">101.foo</label>
<div class="col-sm-3">
<input type="text" name="injectedConfigValue" class="form-control"
ng-model="demoCtrl.injectedConfigValue" readonly/>
</div>
<input type="submit" class="btn btn-primary col-sm-1" value="query"/>
</div>
</div>
</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>
...@@ -93,7 +93,6 @@ ...@@ -93,7 +93,6 @@
<module>apollo-configservice</module> <module>apollo-configservice</module>
<module>apollo-adminservice</module> <module>apollo-adminservice</module>
<module>apollo-portal</module> <module>apollo-portal</module>
<module>apollo-spring-demo</module>
<module>apollo-demo</module> <module>apollo-demo</module>
</modules> </modules>
......
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