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,7 +52,7 @@ public class ConfigController { ...@@ -43,7 +52,7 @@ 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,
......
...@@ -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, String> configurations;
private Map<String, Object> 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 () {
"use strict";
function t(t, e, s, n, o, r, a) {
function i(t) {
if (t)d(t.toastId); else for (var e = 0; e < O.length; e++)d(O[e].toastId)
}
function l(t, e, s) {
var n = m().iconClasses.error;
return g(n, t, e, s)
}
function c(t, e, s) {
var n = m().iconClasses.info;
return g(n, t, e, s)
}
function u(t, e, s) {
var n = m().iconClasses.success;
return g(n, t, e, s)
}
function p(t, e, s) {
var n = m().iconClasses.warning;
return g(n, t, e, s)
}
function d(e, s) {
function n(t) {
for (var e = 0; e < O.length; e++)if (O[e].toastId === t)return O[e]
}
function o() {
return !O.length
}
var i = n(e);
i && !i.deleting && (i.deleting = !0, i.isOpened = !1, t.leave(i.el).then(function () {
i.scope.options.onHidden && i.scope.options.onHidden(s), i.scope.$destroy();
var t = O.indexOf(i);
delete B[i.scope.message], O.splice(t, 1);
var e = r.maxOpened;
e && O.length >= e && O[e - 1].open.resolve(), o() && (h.remove(), h = null, T = a.defer())
}))
}
function g(t, e, s, n) {
return angular.isObject(s) && (n = s, s = null), v({iconClass: t, message: e, optionsOverride: n, title: s})
}
function m() {
return angular.extend({}, r)
}
function f(e) {
if (h)return T.promise;
h = angular.element("<div></div>"), h.attr("id", e.containerId), h.addClass(e.positionClass), h.css({"pointer-events": "auto"});
var s = angular.element(document.querySelector(e.target));
if (!s || !s.length)throw"Target for toasts doesn't exist";
return t.enter(h, s).then(function () {
T.resolve()
}), T.promise
}
function v(s) {
function r(t, e, s) {
s.allowHtml ? (t.scope.allowHtml = !0, t.scope.title = o.trustAsHtml(e.title), t.scope.message = o.trustAsHtml(e.message)) : (t.scope.title = e.title, t.scope.message = e.message), t.scope.toastType = t.iconClass, t.scope.toastId = t.toastId, t.scope.options = {
extendedTimeOut: s.extendedTimeOut,
messageClass: s.messageClass,
onHidden: s.onHidden,
onShown: s.onShown,
progressBar: s.progressBar,
tapToDismiss: s.tapToDismiss,
timeOut: s.timeOut,
titleClass: s.titleClass,
toastClass: s.toastClass
}, s.closeButton && (t.scope.options.closeHtml = s.closeHtml)
}
function i() {
function t(t) {
for (var e = ["containerId", "iconClasses", "maxOpened", "newestOnTop", "positionClass", "preventDuplicates", "preventOpenDuplicates", "templates"], s = 0, n = e.length; n > s; s++)delete t[e[s]];
return t
}
var e = {toastId: C++, isOpened: !1, scope: n.$new(), open: a.defer()};
return e.iconClass = s.iconClass, s.optionsOverride && (p = angular.extend(p, t(s.optionsOverride)), e.iconClass = s.optionsOverride.iconClass || e.iconClass), r(e, s, p), e.el = l(e.scope), e
}
function l(t) {
var s = angular.element("<div toast></div>"), n = e.get("$compile");
return n(s)(t)
}
function c() {
return p.maxOpened && O.length <= p.maxOpened || !p.maxOpened
}
function u() {
var t = p.preventDuplicates && s.message === w, e = p.preventOpenDuplicates && B[s.message];
return t || e ? !0 : (w = s.message, B[s.message] = !0, !1)
}
var p = m();
if (!u()) {
var g = i();
if (O.push(g), p.autoDismiss && p.maxOpened > 0)for (var v = O.slice(0, O.length - p.maxOpened), T = 0, $ = v.length; $ > T; T++)d(v[T].toastId);
return c() && g.open.resolve(), g.open.promise.then(function () {
f(p).then(function () {
if (g.isOpened = !0, p.newestOnTop)t.enter(g.el, h).then(function () {
g.scope.init()
}); else {
var e = h[0].lastChild ? angular.element(h[0].lastChild) : null;
t.enter(g.el, h, e).then(function () {
g.scope.init()
})
}
})
}), g
}
}
var h, C = 0, O = [], w = "", B = {}, T = a.defer(), $ = {
clear: i,
error: l,
info: c,
remove: d,
success: u,
warning: p
};
return $
}
angular.module("toastr", []).factory("toastr", t), t.$inject = ["$animate", "$injector", "$document", "$rootScope", "$sce", "toastrConfig", "$q"]
}(), function () {
"use strict";
angular.module("toastr").constant("toastrConfig", {
allowHtml: !1,
autoDismiss: !1,
closeButton: !1,
closeHtml: "<button>&times;</button>",
containerId: "toast-container",
extendedTimeOut: 1e3,
iconClasses: {error: "toast-error", info: "toast-info", success: "toast-success", warning: "toast-warning"},
maxOpened: 0,
messageClass: "toast-message",
newestOnTop: !0,
onHidden: null,
onShown: null,
positionClass: "toast-top-right",
preventDuplicates: !1,
preventOpenDuplicates: !1,
progressBar: !1,
tapToDismiss: !0,
target: "body",
templates: {toast: "directives/toast/toast.html", progressbar: "directives/progressbar/progressbar.html"},
timeOut: 5e3,
titleClass: "toast-title",
toastClass: "toast"
})
}(), function () {
"use strict";
function t(t) {
function e(t, e, s, n) {
function o() {
var t = (i - (new Date).getTime()) / a * 100;
e.css("width", t + "%")
}
var r, a, i;
n.progressBar = t, t.start = function (t) {
r && clearInterval(r), a = parseFloat(t), i = (new Date).getTime() + a, r = setInterval(o, 10)
}, t.stop = function () {
r && clearInterval(r)
}, t.$on("$destroy", function () {
clearInterval(r)
})
}
return {
replace: !0, require: "^toast", templateUrl: function () {
return t.templates.progressbar
}, link: e
}
}
angular.module("toastr").directive("progressBar", t), t.$inject = ["toastrConfig"]
}(), function () {
"use strict";
function t() {
this.progressBar = null, this.startProgressBar = function (t) {
this.progressBar && this.progressBar.start(t)
}, this.stopProgressBar = function () {
this.progressBar && this.progressBar.stop()
}
}
angular.module("toastr").controller("ToastController", t)
}(), function () {
"use strict";
function t(t, e, s, n) {
function o(s, o, r, a) {
function i(t) {
return a.startProgressBar(t), e(function () {
a.stopProgressBar(), n.remove(s.toastId)
}, t, 1)
}
function l() {
s.progressBar = !1, a.stopProgressBar()
}
function c() {
return s.options.closeHtml
}
var u;
if (s.toastClass = s.options.toastClass, s.titleClass = s.options.titleClass, s.messageClass = s.options.messageClass, s.progressBar = s.options.progressBar, c()) {
var p = angular.element(s.options.closeHtml), d = t.get("$compile");
p.addClass("toast-close-button"), p.attr("ng-click", "close()"), d(p)(s), o.prepend(p)
}
s.init = function () {
s.options.timeOut && (u = i(s.options.timeOut)), s.options.onShown && s.options.onShown()
}, o.on("mouseenter", function () {
l(), u && e.cancel(u)
}), s.tapToast = function () {
s.options.tapToDismiss && s.close(!0)
}, s.close = function (t) {
n.remove(s.toastId, t)
}, o.on("mouseleave", function () {
(0 !== s.options.timeOut || 0 !== s.options.extendedTimeOut) && (s.$apply(function () {
s.progressBar = s.options.progressBar
}), u = i(s.options.extendedTimeOut))
})
}
return {
replace: !0, templateUrl: function () {
return s.templates.toast
}, controller: "ToastController", link: o
}
}
angular.module("toastr").directive("toast", t), t.$inject = ["$injector", "$interval", "toastrConfig", "toastr"]
}(), angular.module("toastr").run(["$templateCache", function (t) {
t.put("directives/progressbar/progressbar.html", '<div class="toast-progress"></div>\n'), t.put("directives/toast/toast.html", '<div class="{{toastClass}} {{toastType}}" ng-click="tapToast()">\n <div ng-switch on="allowHtml">\n <div ng-switch-default ng-if="title" class="{{titleClass}}">{{title}}</div>\n <div ng-switch-default class="{{messageClass}}">{{message}}</div>\n <div ng-switch-when="true" ng-if="title" class="{{titleClass}}" ng-bind-html="title"></div>\n <div ng-switch-when="true" class="{{messageClass}}" ng-bind-html="message"></div>\n </div>\n <progress-bar ng-if="progressBar"></progress-bar>\n</div>\n')
}]);
(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
}
}]
})
}();
/*
* angular-ui-bootstrap
* http://angular-ui.github.io/bootstrap/
* Version: 0.13.0 - 2015-05-02
* License: MIT
*/
angular.module("ui.bootstrap", ["ui.bootstrap.collapse", "ui.bootstrap.accordion", "ui.bootstrap.alert", "ui.bootstrap.bindHtml", "ui.bootstrap.buttons", "ui.bootstrap.carousel", "ui.bootstrap.dateparser", "ui.bootstrap.position", "ui.bootstrap.datepicker", "ui.bootstrap.dropdown", "ui.bootstrap.modal", "ui.bootstrap.pagination", "ui.bootstrap.tooltip", "ui.bootstrap.popover", "ui.bootstrap.progressbar", "ui.bootstrap.rating", "ui.bootstrap.tabs", "ui.bootstrap.timepicker", "ui.bootstrap.transition", "ui.bootstrap.typeahead"]), angular.module("ui.bootstrap.collapse", []).directive("collapse", ["$animate", function (a) {
return {
link: function (b, c, d) {
function e() {
c.removeClass("collapse").addClass("collapsing"), a.addClass(c, "in", {to: {height: c[0].scrollHeight + "px"}}).then(f)
}
function f() {
c.removeClass("collapsing"), c.css({height: "auto"})
}
function g() {
c.css({height: c[0].scrollHeight + "px"}).removeClass("collapse").addClass("collapsing"), a.removeClass(c, "in", {to: {height: "0"}}).then(h)
}
function h() {
c.css({height: "0"}), c.removeClass("collapsing"), c.addClass("collapse")
}
b.$watch(d.collapse, function (a) {
a ? g() : e()
})
}
}
}]), angular.module("ui.bootstrap.accordion", ["ui.bootstrap.collapse"]).constant("accordionConfig", {closeOthers: !0}).controller("AccordionController", ["$scope", "$attrs", "accordionConfig", function (a, b, c) {
this.groups = [], this.closeOthers = function (d) {
var e = angular.isDefined(b.closeOthers) ? a.$eval(b.closeOthers) : c.closeOthers;
e && angular.forEach(this.groups, function (a) {
a !== d && (a.isOpen = !1)
})
}, this.addGroup = function (a) {
var b = this;
this.groups.push(a), a.$on("$destroy", function () {
b.removeGroup(a)
})
}, this.removeGroup = function (a) {
var b = this.groups.indexOf(a);
-1 !== b && this.groups.splice(b, 1)
}
}]).directive("accordion", function () {
return {
restrict: "EA",
controller: "AccordionController",
transclude: !0,
replace: !1,
templateUrl: "template/accordion/accordion.html"
}
}).directive("accordionGroup", function () {
return {
require: "^accordion",
restrict: "EA",
transclude: !0,
replace: !0,
templateUrl: "template/accordion/accordion-group.html",
scope: {heading: "@", isOpen: "=?", isDisabled: "=?"},
controller: function () {
this.setHeading = function (a) {
this.heading = a
}
},
link: function (a, b, c, d) {
d.addGroup(a), a.$watch("isOpen", function (b) {
b && d.closeOthers(a)
}), a.toggleOpen = function () {
a.isDisabled || (a.isOpen = !a.isOpen)
}
}
}
}).directive("accordionHeading", function () {
return {
restrict: "EA",
transclude: !0,
template: "",
replace: !0,
require: "^accordionGroup",
link: function (a, b, c, d, e) {
d.setHeading(e(a, angular.noop))
}
}
}).directive("accordionTransclude", function () {
return {
require: "^accordionGroup", link: function (a, b, c, d) {
a.$watch(function () {
return d[c.accordionTransclude]
}, function (a) {
a && (b.html(""), b.append(a))
})
}
}
}), angular.module("ui.bootstrap.alert", []).controller("AlertController", ["$scope", "$attrs", function (a, b) {
a.closeable = "close" in b, this.close = a.close
}]).directive("alert", function () {
return {
restrict: "EA",
controller: "AlertController",
templateUrl: "template/alert/alert.html",
transclude: !0,
replace: !0,
scope: {type: "@", close: "&"}
}
}).directive("dismissOnTimeout", ["$timeout", function (a) {
return {
require: "alert", link: function (b, c, d, e) {
a(function () {
e.close()
}, parseInt(d.dismissOnTimeout, 10))
}
}
}]), angular.module("ui.bootstrap.bindHtml", []).directive("bindHtmlUnsafe", function () {
return function (a, b, c) {
b.addClass("ng-binding").data("$binding", c.bindHtmlUnsafe), a.$watch(c.bindHtmlUnsafe, function (a) {
b.html(a || "")
})
}
}), angular.module("ui.bootstrap.buttons", []).constant("buttonConfig", {
activeClass: "active",
toggleEvent: "click"
}).controller("ButtonsController", ["buttonConfig", function (a) {
this.activeClass = a.activeClass || "active", this.toggleEvent = a.toggleEvent || "click"
}]).directive("btnRadio", function () {
return {
require: ["btnRadio", "ngModel"], controller: "ButtonsController", link: function (a, b, c, d) {
var e = d[0], f = d[1];
f.$render = function () {
b.toggleClass(e.activeClass, angular.equals(f.$modelValue, a.$eval(c.btnRadio)))
}, b.bind(e.toggleEvent, function () {
var d = b.hasClass(e.activeClass);
(!d || angular.isDefined(c.uncheckable)) && a.$apply(function () {
f.$setViewValue(d ? null : a.$eval(c.btnRadio)), f.$render()
})
})
}
}
}).directive("btnCheckbox", function () {
return {
require: ["btnCheckbox", "ngModel"], controller: "ButtonsController", link: function (a, b, c, d) {
function e() {
return g(c.btnCheckboxTrue, !0)
}
function f() {
return g(c.btnCheckboxFalse, !1)
}
function g(b, c) {
var d = a.$eval(b);
return angular.isDefined(d) ? d : c
}
var h = d[0], i = d[1];
i.$render = function () {
b.toggleClass(h.activeClass, angular.equals(i.$modelValue, e()))
}, b.bind(h.toggleEvent, function () {
a.$apply(function () {
i.$setViewValue(b.hasClass(h.activeClass) ? f() : e()), i.$render()
})
})
}
}
}), angular.module("ui.bootstrap.carousel", []).controller("CarouselController", ["$scope", "$interval", "$animate", function (a, b, c) {
function d(a) {
if (angular.isUndefined(k[a].index))return k[a];
{
var b;
k.length
}
for (b = 0; b < k.length; ++b)if (k[b].index == a)return k[b]
}
function e() {
f();
var c = +a.interval;
!isNaN(c) && c > 0 && (h = b(g, c))
}
function f() {
h && (b.cancel(h), h = null)
}
function g() {
var b = +a.interval;
i && !isNaN(b) && b > 0 ? a.next() : a.pause()
}
var h, i, j = this, k = j.slides = a.slides = [], l = -1;
j.currentSlide = null;
var m = !1;
j.select = a.select = function (b, d) {
function f() {
m || (angular.extend(b, {direction: d, active: !0}), angular.extend(j.currentSlide || {}, {
direction: d,
active: !1
}), c.enabled() && !a.noTransition && b.$element && (a.$currentTransition = !0, b.$element.one("$animate:close", function () {
a.$currentTransition = null
})), j.currentSlide = b, l = g, e())
}
var g = j.indexOfSlide(b);
void 0 === d && (d = g > j.getCurrentIndex() ? "next" : "prev"), b && b !== j.currentSlide && f()
}, a.$on("$destroy", function () {
m = !0
}), j.getCurrentIndex = function () {
return j.currentSlide && angular.isDefined(j.currentSlide.index) ? +j.currentSlide.index : l
}, j.indexOfSlide = function (a) {
return angular.isDefined(a.index) ? +a.index : k.indexOf(a)
}, a.next = function () {
var b = (j.getCurrentIndex() + 1) % k.length;
return a.$currentTransition ? void 0 : j.select(d(b), "next")
}, a.prev = function () {
var b = j.getCurrentIndex() - 1 < 0 ? k.length - 1 : j.getCurrentIndex() - 1;
return a.$currentTransition ? void 0 : j.select(d(b), "prev")
}, a.isActive = function (a) {
return j.currentSlide === a
}, a.$watch("interval", e), a.$on("$destroy", f), a.play = function () {
i || (i = !0, e())
}, a.pause = function () {
a.noPause || (i = !1, f())
}, j.addSlide = function (b, c) {
b.$element = c, k.push(b), 1 === k.length || b.active ? (j.select(k[k.length - 1]), 1 == k.length && a.play()) : b.active = !1
}, j.removeSlide = function (a) {
angular.isDefined(a.index) && k.sort(function (a, b) {
return +a.index > +b.index
});
var b = k.indexOf(a);
k.splice(b, 1), k.length > 0 && a.active ? j.select(b >= k.length ? k[b - 1] : k[b]) : l > b && l--
}
}]).directive("carousel", [function () {
return {
restrict: "EA",
transclude: !0,
replace: !0,
controller: "CarouselController",
require: "carousel",
templateUrl: "template/carousel/carousel.html",
scope: {interval: "=", noTransition: "=", noPause: "="}
}
}]).directive("slide", function () {
return {
require: "^carousel",
restrict: "EA",
transclude: !0,
replace: !0,
templateUrl: "template/carousel/slide.html",
scope: {active: "=?", index: "=?"},
link: function (a, b, c, d) {
d.addSlide(a, b), a.$on("$destroy", function () {
d.removeSlide(a)
}), a.$watch("active", function (b) {
b && d.select(a)
})
}
}
}).animation(".item", ["$animate", function (a) {
return {
beforeAddClass: function (b, c, d) {
if ("active" == c && b.parent() && !b.parent().scope().noTransition) {
var e = !1, f = b.isolateScope().direction, g = "next" == f ? "left" : "right";
return b.addClass(f), a.addClass(b, g).then(function () {
e || b.removeClass(g + " " + f), d()
}), function () {
e = !0
}
}
d()
}, beforeRemoveClass: function (b, c, d) {
if ("active" == c && b.parent() && !b.parent().scope().noTransition) {
var e = !1, f = b.isolateScope().direction, g = "next" == f ? "left" : "right";
return a.addClass(b, g).then(function () {
e || b.removeClass(g), d()
}), function () {
e = !0
}
}
d()
}
}
}]), angular.module("ui.bootstrap.dateparser", []).service("dateParser", ["$locale", "orderByFilter", function (a, b) {
function c(a) {
var c = [], d = a.split("");
return angular.forEach(f, function (b, e) {
var f = a.indexOf(e);
if (f > -1) {
a = a.split(""), d[f] = "(" + b.regex + ")", a[f] = "$";
for (var g = f + 1, h = f + e.length; h > g; g++)d[g] = "", a[g] = "$";
a = a.join(""), c.push({index: f, apply: b.apply})
}
}), {regex: new RegExp("^" + d.join("") + "$"), map: b(c, "index")}
}
function d(a, b, c) {
return 1 > c ? !1 : 1 === b && c > 28 ? 29 === c && (a % 4 === 0 && a % 100 !== 0 || a % 400 === 0) : 3 === b || 5 === b || 8 === b || 10 === b ? 31 > c : !0
}
var e = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
this.parsers = {};
var f = {
yyyy: {
regex: "\\d{4}", apply: function (a) {
this.year = +a
}
},
yy: {
regex: "\\d{2}", apply: function (a) {
this.year = +a + 2e3
}
},
y: {
regex: "\\d{1,4}", apply: function (a) {
this.year = +a
}
},
MMMM: {
regex: a.DATETIME_FORMATS.MONTH.join("|"), apply: function (b) {
this.month = a.DATETIME_FORMATS.MONTH.indexOf(b)
}
},
MMM: {
regex: a.DATETIME_FORMATS.SHORTMONTH.join("|"), apply: function (b) {
this.month = a.DATETIME_FORMATS.SHORTMONTH.indexOf(b)
}
},
MM: {
regex: "0[1-9]|1[0-2]", apply: function (a) {
this.month = a - 1
}
},
M: {
regex: "[1-9]|1[0-2]", apply: function (a) {
this.month = a - 1
}
},
dd: {
regex: "[0-2][0-9]{1}|3[0-1]{1}", apply: function (a) {
this.date = +a
}
},
d: {
regex: "[1-2]?[0-9]{1}|3[0-1]{1}", apply: function (a) {
this.date = +a
}
},
EEEE: {regex: a.DATETIME_FORMATS.DAY.join("|")},
EEE: {regex: a.DATETIME_FORMATS.SHORTDAY.join("|")},
HH: {
regex: "(?:0|1)[0-9]|2[0-3]", apply: function (a) {
this.hours = +a
}
},
H: {
regex: "1?[0-9]|2[0-3]", apply: function (a) {
this.hours = +a
}
},
mm: {
regex: "[0-5][0-9]", apply: function (a) {
this.minutes = +a
}
},
m: {
regex: "[0-9]|[1-5][0-9]", apply: function (a) {
this.minutes = +a
}
},
sss: {
regex: "[0-9][0-9][0-9]", apply: function (a) {
this.milliseconds = +a
}
},
ss: {
regex: "[0-5][0-9]", apply: function (a) {
this.seconds = +a
}
},
s: {
regex: "[0-9]|[1-5][0-9]", apply: function (a) {
this.seconds = +a
}
}
};
this.parse = function (b, f, g) {
if (!angular.isString(b) || !f)return b;
f = a.DATETIME_FORMATS[f] || f, f = f.replace(e, "\\$&"), this.parsers[f] || (this.parsers[f] = c(f));
var h = this.parsers[f], i = h.regex, j = h.map, k = b.match(i);
if (k && k.length) {
var l, m;
l = g ? {
year: g.getFullYear(),
month: g.getMonth(),
date: g.getDate(),
hours: g.getHours(),
minutes: g.getMinutes(),
seconds: g.getSeconds(),
milliseconds: g.getMilliseconds()
} : {year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0};
for (var n = 1, o = k.length; o > n; n++) {
var p = j[n - 1];
p.apply && p.apply.call(l, k[n])
}
return d(l.year, l.month, l.date) && (m = new Date(l.year, l.month, l.date, l.hours, l.minutes, l.seconds, l.milliseconds || 0)), m
}
}
}]), angular.module("ui.bootstrap.position", []).factory("$position", ["$document", "$window", function (a, b) {
function c(a, c) {
return a.currentStyle ? a.currentStyle[c] : b.getComputedStyle ? b.getComputedStyle(a)[c] : a.style[c]
}
function d(a) {
return "static" === (c(a, "position") || "static")
}
var e = function (b) {
for (var c = a[0], e = b.offsetParent || c; e && e !== c && d(e);)e = e.offsetParent;
return e || c
};
return {
position: function (b) {
var c = this.offset(b), d = {top: 0, left: 0}, f = e(b[0]);
f != a[0] && (d = this.offset(angular.element(f)), d.top += f.clientTop - f.scrollTop, d.left += f.clientLeft - f.scrollLeft);
var g = b[0].getBoundingClientRect();
return {
width: g.width || b.prop("offsetWidth"),
height: g.height || b.prop("offsetHeight"),
top: c.top - d.top,
left: c.left - d.left
}
}, offset: function (c) {
var d = c[0].getBoundingClientRect();
return {
width: d.width || c.prop("offsetWidth"),
height: d.height || c.prop("offsetHeight"),
top: d.top + (b.pageYOffset || a[0].documentElement.scrollTop),
left: d.left + (b.pageXOffset || a[0].documentElement.scrollLeft)
}
}, positionElements: function (a, b, c, d) {
var e, f, g, h, i = c.split("-"), j = i[0], k = i[1] || "center";
e = d ? this.offset(a) : this.position(a), f = b.prop("offsetWidth"), g = b.prop("offsetHeight");
var l = {
center: function () {
return e.left + e.width / 2 - f / 2
}, left: function () {
return e.left
}, right: function () {
return e.left + e.width
}
}, m = {
center: function () {
return e.top + e.height / 2 - g / 2
}, top: function () {
return e.top
}, bottom: function () {
return e.top + e.height
}
};
switch (j) {
case"right":
h = {top: m[k](), left: l[j]()};
break;
case"left":
h = {top: m[k](), left: e.left - f};
break;
case"bottom":
h = {top: m[j](), left: l[k]()};
break;
default:
h = {top: e.top - g, left: l[k]()}
}
return h
}
}
}]), angular.module("ui.bootstrap.datepicker", ["ui.bootstrap.dateparser", "ui.bootstrap.position"]).constant("datepickerConfig", {
formatDay: "dd",
formatMonth: "MMMM",
formatYear: "yyyy",
formatDayHeader: "EEE",
formatDayTitle: "MMMM yyyy",
formatMonthTitle: "yyyy",
datepickerMode: "day",
minMode: "day",
maxMode: "year",
showWeeks: !0,
startingDay: 0,
yearRange: 20,
minDate: null,
maxDate: null,
shortcutPropagation: !1
}).controller("DatepickerController", ["$scope", "$attrs", "$parse", "$interpolate", "$timeout", "$log", "dateFilter", "datepickerConfig", function (a, b, c, d, e, f, g, h) {
var i = this, j = {$setViewValue: angular.noop};
this.modes = ["day", "month", "year"], angular.forEach(["formatDay", "formatMonth", "formatYear", "formatDayHeader", "formatDayTitle", "formatMonthTitle", "minMode", "maxMode", "showWeeks", "startingDay", "yearRange", "shortcutPropagation"], function (c, e) {
i[c] = angular.isDefined(b[c]) ? 8 > e ? d(b[c])(a.$parent) : a.$parent.$eval(b[c]) : h[c]
}), angular.forEach(["minDate", "maxDate"], function (d) {
b[d] ? a.$parent.$watch(c(b[d]), function (a) {
i[d] = a ? new Date(a) : null, i.refreshView()
}) : i[d] = h[d] ? new Date(h[d]) : null
}), a.datepickerMode = a.datepickerMode || h.datepickerMode, a.maxMode = i.maxMode, a.uniqueId = "datepicker-" + a.$id + "-" + Math.floor(1e4 * Math.random()), angular.isDefined(b.initDate) ? (this.activeDate = a.$parent.$eval(b.initDate) || new Date, a.$parent.$watch(b.initDate, function (a) {
a && (j.$isEmpty(j.$modelValue) || j.$invalid) && (i.activeDate = a, i.refreshView())
})) : this.activeDate = new Date, a.isActive = function (b) {
return 0 === i.compare(b.date, i.activeDate) ? (a.activeDateId = b.uid, !0) : !1
}, this.init = function (a) {
j = a, j.$render = function () {
i.render()
}
}, this.render = function () {
if (j.$viewValue) {
var a = new Date(j.$viewValue), b = !isNaN(a);
b ? this.activeDate = a : f.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.'), j.$setValidity("date", b)
}
this.refreshView()
}, this.refreshView = function () {
if (this.element) {
this._refreshView();
var a = j.$viewValue ? new Date(j.$viewValue) : null;
j.$setValidity("date-disabled", !a || this.element && !this.isDisabled(a))
}
}, this.createDateObject = function (a, b) {
var c = j.$viewValue ? new Date(j.$viewValue) : null;
return {
date: a,
label: g(a, b),
selected: c && 0 === this.compare(a, c),
disabled: this.isDisabled(a),
current: 0 === this.compare(a, new Date),
customClass: this.customClass(a)
}
}, this.isDisabled = function (c) {
return this.minDate && this.compare(c, this.minDate) < 0 || this.maxDate && this.compare(c, this.maxDate) > 0 || b.dateDisabled && a.dateDisabled({
date: c,
mode: a.datepickerMode
})
}, this.customClass = function (b) {
return a.customClass({date: b, mode: a.datepickerMode})
}, this.split = function (a, b) {
for (var c = []; a.length > 0;)c.push(a.splice(0, b));
return c
}, a.select = function (b) {
if (a.datepickerMode === i.minMode) {
var c = j.$viewValue ? new Date(j.$viewValue) : new Date(0, 0, 0, 0, 0, 0, 0);
c.setFullYear(b.getFullYear(), b.getMonth(), b.getDate()), j.$setViewValue(c), j.$render()
} else i.activeDate = b, a.datepickerMode = i.modes[i.modes.indexOf(a.datepickerMode) - 1]
}, a.move = function (a) {
var b = i.activeDate.getFullYear() + a * (i.step.years || 0), c = i.activeDate.getMonth() + a * (i.step.months || 0);
i.activeDate.setFullYear(b, c, 1), i.refreshView()
}, a.toggleMode = function (b) {
b = b || 1, a.datepickerMode === i.maxMode && 1 === b || a.datepickerMode === i.minMode && -1 === b || (a.datepickerMode = i.modes[i.modes.indexOf(a.datepickerMode) + b])
}, a.keys = {
13: "enter",
32: "space",
33: "pageup",
34: "pagedown",
35: "end",
36: "home",
37: "left",
38: "up",
39: "right",
40: "down"
};
var k = function () {
e(function () {
i.element[0].focus()
}, 0, !1)
};
a.$on("datepicker.focus", k), a.keydown = function (b) {
var c = a.keys[b.which];
if (c && !b.shiftKey && !b.altKey)if (b.preventDefault(), i.shortcutPropagation || b.stopPropagation(), "enter" === c || "space" === c) {
if (i.isDisabled(i.activeDate))return;
a.select(i.activeDate), k()
} else!b.ctrlKey || "up" !== c && "down" !== c ? (i.handleKeyDown(c, b), i.refreshView()) : (a.toggleMode("up" === c ? 1 : -1), k())
}
}]).directive("datepicker", function () {
return {
restrict: "EA",
replace: !0,
templateUrl: "template/datepicker/datepicker.html",
scope: {datepickerMode: "=?", dateDisabled: "&", customClass: "&", shortcutPropagation: "&?"},
require: ["datepicker", "?^ngModel"],
controller: "DatepickerController",
link: function (a, b, c, d) {
var e = d[0], f = d[1];
f && e.init(f)
}
}
}).directive("daypicker", ["dateFilter", function (a) {
return {
restrict: "EA",
replace: !0,
templateUrl: "template/datepicker/day.html",
require: "^datepicker",
link: function (b, c, d, e) {
function f(a, b) {
return 1 !== b || a % 4 !== 0 || a % 100 === 0 && a % 400 !== 0 ? i[b] : 29
}
function g(a, b) {
var c = new Array(b), d = new Date(a), e = 0;
for (d.setHours(12); b > e;)c[e++] = new Date(d), d.setDate(d.getDate() + 1);
return c
}
function h(a) {
var b = new Date(a);
b.setDate(b.getDate() + 4 - (b.getDay() || 7));
var c = b.getTime();
return b.setMonth(0), b.setDate(1), Math.floor(Math.round((c - b) / 864e5) / 7) + 1
}
b.showWeeks = e.showWeeks, e.step = {months: 1}, e.element = c;
var i = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
e._refreshView = function () {
var c = e.activeDate.getFullYear(), d = e.activeDate.getMonth(), f = new Date(c, d, 1), i = e.startingDay - f.getDay(), j = i > 0 ? 7 - i : -i, k = new Date(f);
j > 0 && k.setDate(-j + 1);
for (var l = g(k, 42), m = 0; 42 > m; m++)l[m] = angular.extend(e.createDateObject(l[m], e.formatDay), {
secondary: l[m].getMonth() !== d,
uid: b.uniqueId + "-" + m
});
b.labels = new Array(7);
for (var n = 0; 7 > n; n++)b.labels[n] = {
abbr: a(l[n].date, e.formatDayHeader),
full: a(l[n].date, "EEEE")
};
if (b.title = a(e.activeDate, e.formatDayTitle), b.rows = e.split(l, 7), b.showWeeks) {
b.weekNumbers = [];
for (var o = (11 - e.startingDay) % 7, p = b.rows.length, q = 0; p > q; q++)b.weekNumbers.push(h(b.rows[q][o].date))
}
}, e.compare = function (a, b) {
return new Date(a.getFullYear(), a.getMonth(), a.getDate()) - new Date(b.getFullYear(), b.getMonth(), b.getDate())
}, e.handleKeyDown = function (a) {
var b = e.activeDate.getDate();
if ("left" === a)b -= 1; else if ("up" === a)b -= 7; else if ("right" === a)b += 1; else if ("down" === a)b += 7; else if ("pageup" === a || "pagedown" === a) {
var c = e.activeDate.getMonth() + ("pageup" === a ? -1 : 1);
e.activeDate.setMonth(c, 1), b = Math.min(f(e.activeDate.getFullYear(), e.activeDate.getMonth()), b)
} else"home" === a ? b = 1 : "end" === a && (b = f(e.activeDate.getFullYear(), e.activeDate.getMonth()));
e.activeDate.setDate(b)
}, e.refreshView()
}
}
}]).directive("monthpicker", ["dateFilter", function (a) {
return {
restrict: "EA",
replace: !0,
templateUrl: "template/datepicker/month.html",
require: "^datepicker",
link: function (b, c, d, e) {
e.step = {years: 1}, e.element = c, e._refreshView = function () {
for (var c = new Array(12), d = e.activeDate.getFullYear(), f = 0; 12 > f; f++)c[f] = angular.extend(e.createDateObject(new Date(d, f, 1), e.formatMonth), {uid: b.uniqueId + "-" + f});
b.title = a(e.activeDate, e.formatMonthTitle), b.rows = e.split(c, 3)
}, e.compare = function (a, b) {
return new Date(a.getFullYear(), a.getMonth()) - new Date(b.getFullYear(), b.getMonth())
}, e.handleKeyDown = function (a) {
var b = e.activeDate.getMonth();
if ("left" === a)b -= 1; else if ("up" === a)b -= 3; else if ("right" === a)b += 1; else if ("down" === a)b += 3; else if ("pageup" === a || "pagedown" === a) {
var c = e.activeDate.getFullYear() + ("pageup" === a ? -1 : 1);
e.activeDate.setFullYear(c)
} else"home" === a ? b = 0 : "end" === a && (b = 11);
e.activeDate.setMonth(b)
}, e.refreshView()
}
}
}]).directive("yearpicker", ["dateFilter", function () {
return {
restrict: "EA",
replace: !0,
templateUrl: "template/datepicker/year.html",
require: "^datepicker",
link: function (a, b, c, d) {
function e(a) {
return parseInt((a - 1) / f, 10) * f + 1
}
var f = d.yearRange;
d.step = {years: f}, d.element = b, d._refreshView = function () {
for (var b = new Array(f), c = 0, g = e(d.activeDate.getFullYear()); f > c; c++)b[c] = angular.extend(d.createDateObject(new Date(g + c, 0, 1), d.formatYear), {uid: a.uniqueId + "-" + c});
a.title = [b[0].label, b[f - 1].label].join(" - "), a.rows = d.split(b, 5)
}, d.compare = function (a, b) {
return a.getFullYear() - b.getFullYear()
}, d.handleKeyDown = function (a) {
var b = d.activeDate.getFullYear();
"left" === a ? b -= 1 : "up" === a ? b -= 5 : "right" === a ? b += 1 : "down" === a ? b += 5 : "pageup" === a || "pagedown" === a ? b += ("pageup" === a ? -1 : 1) * d.step.years : "home" === a ? b = e(d.activeDate.getFullYear()) : "end" === a && (b = e(d.activeDate.getFullYear()) + f - 1), d.activeDate.setFullYear(b)
}, d.refreshView()
}
}
}]).constant("datepickerPopupConfig", {
datepickerPopup: "yyyy-MM-dd",
html5Types: {date: "yyyy-MM-dd", "datetime-local": "yyyy-MM-ddTHH:mm:ss.sss", month: "yyyy-MM"},
currentText: "Today",
clearText: "Clear",
closeText: "Done",
closeOnDateSelection: !0,
appendToBody: !1,
showButtonBar: !0
}).directive("datepickerPopup", ["$compile", "$parse", "$document", "$position", "dateFilter", "dateParser", "datepickerPopupConfig", function (a, b, c, d, e, f, g) {
return {
restrict: "EA",
require: "ngModel",
scope: {isOpen: "=?", currentText: "@", clearText: "@", closeText: "@", dateDisabled: "&", customClass: "&"},
link: function (h, i, j, k) {
function l(a) {
return a.replace(/([A-Z])/g, function (a) {
return "-" + a.toLowerCase()
})
}
function m(a) {
if (angular.isNumber(a) && (a = new Date(a)), a) {
if (angular.isDate(a) && !isNaN(a))return a;
if (angular.isString(a)) {
var b = f.parse(a, o, h.date) || new Date(a);
return isNaN(b) ? void 0 : b
}
return void 0
}
return null
}
function n(a, b) {
var c = a || b;
if (angular.isNumber(c) && (c = new Date(c)), c) {
if (angular.isDate(c) && !isNaN(c))return !0;
if (angular.isString(c)) {
var d = f.parse(c, o) || new Date(c);
return !isNaN(d)
}
return !1
}
return !0
}
var o, p = angular.isDefined(j.closeOnDateSelection) ? h.$parent.$eval(j.closeOnDateSelection) : g.closeOnDateSelection, q = angular.isDefined(j.datepickerAppendToBody) ? h.$parent.$eval(j.datepickerAppendToBody) : g.appendToBody;
h.showButtonBar = angular.isDefined(j.showButtonBar) ? h.$parent.$eval(j.showButtonBar) : g.showButtonBar, h.getText = function (a) {
return h[a + "Text"] || g[a + "Text"]
};
var r = !1;
if (g.html5Types[j.type] ? (o = g.html5Types[j.type], r = !0) : (o = j.datepickerPopup || g.datepickerPopup, j.$observe("datepickerPopup", function (a) {
var b = a || g.datepickerPopup;
if (b !== o && (o = b, k.$modelValue = null, !o))throw new Error("datepickerPopup must have a date format specified.")
})), !o)throw new Error("datepickerPopup must have a date format specified.");
if (r && j.datepickerPopup)throw new Error("HTML5 date input types do not support custom formats.");
var s = angular.element("<div datepicker-popup-wrap><div datepicker></div></div>");
s.attr({"ng-model": "date", "ng-change": "dateSelection()"});
var t = angular.element(s.children()[0]);
if (r && "month" == j.type && (t.attr("datepicker-mode", '"month"'), t.attr("min-mode", "month")), j.datepickerOptions) {
var u = h.$parent.$eval(j.datepickerOptions);
u.initDate && (h.initDate = u.initDate, t.attr("init-date", "initDate"), delete u.initDate), angular.forEach(u, function (a, b) {
t.attr(l(b), a)
})
}
h.watchData = {}, angular.forEach(["minDate", "maxDate", "datepickerMode", "initDate", "shortcutPropagation"], function (a) {
if (j[a]) {
var c = b(j[a]);
if (h.$parent.$watch(c, function (b) {
h.watchData[a] = b
}), t.attr(l(a), "watchData." + a), "datepickerMode" === a) {
var d = c.assign;
h.$watch("watchData." + a, function (a, b) {
a !== b && d(h.$parent, a)
})
}
}
}), j.dateDisabled && t.attr("date-disabled", "dateDisabled({ date: date, mode: mode })"), j.showWeeks && t.attr("show-weeks", j.showWeeks), j.customClass && t.attr("custom-class", "customClass({ date: date, mode: mode })"), r ? k.$formatters.push(function (a) {
return h.date = a, a
}) : (k.$$parserName = "date", k.$validators.date = n, k.$parsers.unshift(m), k.$formatters.push(function (a) {
return h.date = a, k.$isEmpty(a) ? a : e(a, o)
})), h.dateSelection = function (a) {
angular.isDefined(a) && (h.date = a);
var b = h.date ? e(h.date, o) : "";
i.val(b), k.$setViewValue(b), p && (h.isOpen = !1, i[0].focus())
}, k.$viewChangeListeners.push(function () {
h.date = f.parse(k.$viewValue, o, h.date) || new Date(k.$viewValue)
});
var v = function (a) {
h.isOpen && a.target !== i[0] && h.$apply(function () {
h.isOpen = !1
})
}, w = function (a) {
h.keydown(a)
};
i.bind("keydown", w), h.keydown = function (a) {
27 === a.which ? (a.preventDefault(), h.isOpen && a.stopPropagation(), h.close()) : 40 !== a.which || h.isOpen || (h.isOpen = !0)
}, h.$watch("isOpen", function (a) {
a ? (h.$broadcast("datepicker.focus"), h.position = q ? d.offset(i) : d.position(i), h.position.top = h.position.top + i.prop("offsetHeight"), c.bind("click", v)) : c.unbind("click", v)
}), h.select = function (a) {
if ("today" === a) {
var b = new Date;
angular.isDate(h.date) ? (a = new Date(h.date), a.setFullYear(b.getFullYear(), b.getMonth(), b.getDate())) : a = new Date(b.setHours(0, 0, 0, 0))
}
h.dateSelection(a)
}, h.close = function () {
h.isOpen = !1, i[0].focus()
};
var x = a(s)(h);
s.remove(), q ? c.find("body").append(x) : i.after(x), h.$on("$destroy", function () {
x.remove(), i.unbind("keydown", w), c.unbind("click", v)
})
}
}
}]).directive("datepickerPopupWrap", function () {
return {
restrict: "EA",
replace: !0,
transclude: !0,
templateUrl: "template/datepicker/popup.html",
link: function (a, b) {
b.bind("click", function (a) {
a.preventDefault(), a.stopPropagation()
})
}
}
}), angular.module("ui.bootstrap.dropdown", ["ui.bootstrap.position"]).constant("dropdownConfig", {openClass: "open"}).service("dropdownService", ["$document", "$rootScope", function (a, b) {
var c = null;
this.open = function (b) {
c || (a.bind("click", d), a.bind("keydown", e)), c && c !== b && (c.isOpen = !1), c = b
}, this.close = function (b) {
c === b && (c = null, a.unbind("click", d), a.unbind("keydown", e))
};
var d = function (a) {
if (c && (!a || "disabled" !== c.getAutoClose())) {
var d = c.getToggleElement();
if (!(a && d && d[0].contains(a.target))) {
var e = c.getElement();
a && "outsideClick" === c.getAutoClose() && e && e[0].contains(a.target) || (c.isOpen = !1, b.$$phase || c.$apply())
}
}
}, e = function (a) {
27 === a.which && (c.focusToggleElement(), d())
}
}]).controller("DropdownController", ["$scope", "$attrs", "$parse", "dropdownConfig", "dropdownService", "$animate", "$position", "$document", function (a, b, c, d, e, f, g, h) {
var i, j = this, k = a.$new(), l = d.openClass, m = angular.noop, n = b.onToggle ? c(b.onToggle) : angular.noop, o = !1;
this.init = function (d) {
j.$element = d, b.isOpen && (i = c(b.isOpen), m = i.assign, a.$watch(i, function (a) {
k.isOpen = !!a
})), o = angular.isDefined(b.dropdownAppendToBody), o && j.dropdownMenu && (h.find("body").append(j.dropdownMenu), d.on("$destroy", function () {
j.dropdownMenu.remove()
}))
}, this.toggle = function (a) {
return k.isOpen = arguments.length ? !!a : !k.isOpen
}, this.isOpen = function () {
return k.isOpen
}, k.getToggleElement = function () {
return j.toggleElement
}, k.getAutoClose = function () {
return b.autoClose || "always"
}, k.getElement = function () {
return j.$element
}, k.focusToggleElement = function () {
j.toggleElement && j.toggleElement[0].focus()
}, k.$watch("isOpen", function (b, c) {
if (o && j.dropdownMenu) {
var d = g.positionElements(j.$element, j.dropdownMenu, "bottom-left", !0);
j.dropdownMenu.css({top: d.top + "px", left: d.left + "px", display: b ? "block" : "none"})
}
f[b ? "addClass" : "removeClass"](j.$element, l), b ? (k.focusToggleElement(), e.open(k)) : e.close(k), m(a, b), angular.isDefined(b) && b !== c && n(a, {open: !!b})
}), a.$on("$locationChangeSuccess", function () {
k.isOpen = !1
}), a.$on("$destroy", function () {
k.$destroy()
})
}]).directive("dropdown", function () {
return {
controller: "DropdownController", link: function (a, b, c, d) {
d.init(b)
}
}
}).directive("dropdownMenu", function () {
return {
restrict: "AC", require: "?^dropdown", link: function (a, b, c, d) {
d && (d.dropdownMenu = b)
}
}
}).directive("dropdownToggle", function () {
return {
require: "?^dropdown", link: function (a, b, c, d) {
if (d) {
d.toggleElement = b;
var e = function (e) {
e.preventDefault(), b.hasClass("disabled") || c.disabled || a.$apply(function () {
d.toggle()
})
};
b.bind("click", e), b.attr({
"aria-haspopup": !0,
"aria-expanded": !1
}), a.$watch(d.isOpen, function (a) {
b.attr("aria-expanded", !!a)
}), a.$on("$destroy", function () {
b.unbind("click", e)
})
}
}
}
}), angular.module("ui.bootstrap.modal", []).factory("$$stackedMap", function () {
return {
createNew: function () {
var a = [];
return {
add: function (b, c) {
a.push({key: b, value: c})
}, get: function (b) {
for (var c = 0; c < a.length; c++)if (b == a[c].key)return a[c]
}, keys: function () {
for (var b = [], c = 0; c < a.length; c++)b.push(a[c].key);
return b
}, top: function () {
return a[a.length - 1]
}, remove: function (b) {
for (var c = -1, d = 0; d < a.length; d++)if (b == a[d].key) {
c = d;
break
}
return a.splice(c, 1)[0]
}, removeTop: function () {
return a.splice(a.length - 1, 1)[0]
}, length: function () {
return a.length
}
}
}
}
}).directive("modalBackdrop", ["$timeout", function (a) {
function b(b) {
b.animate = !1, a(function () {
b.animate = !0
})
}
return {
restrict: "EA", replace: !0, templateUrl: "template/modal/backdrop.html", compile: function (a, c) {
return a.addClass(c.backdropClass), b
}
}
}]).directive("modalWindow", ["$modalStack", "$q", function (a, b) {
return {
restrict: "EA",
scope: {index: "@", animate: "="},
replace: !0,
transclude: !0,
templateUrl: function (a, b) {
return b.templateUrl || "template/modal/window.html"
},
link: function (c, d, e) {
d.addClass(e.windowClass || ""), c.size = e.size, c.close = function (b) {
var c = a.getTop();
c && c.value.backdrop && "static" != c.value.backdrop && b.target === b.currentTarget && (b.preventDefault(), b.stopPropagation(), a.dismiss(c.key, "backdrop click"))
}, c.$isRendered = !0;
var f = b.defer();
e.$observe("modalRender", function (a) {
"true" == a && f.resolve()
}), f.promise.then(function () {
c.animate = !0;
var b = d[0].querySelectorAll("[autofocus]");
b.length ? b[0].focus() : d[0].focus();
var e = a.getTop();
e && a.modalRendered(e.key)
})
}
}
}]).directive("modalAnimationClass", [function () {
return {
compile: function (a, b) {
b.modalAnimation && a.addClass(b.modalAnimationClass)
}
}
}]).directive("modalTransclude", function () {
return {
link: function (a, b, c, d, e) {
e(a.$parent, function (a) {
b.empty(), b.append(a)
})
}
}
}).factory("$modalStack", ["$animate", "$timeout", "$document", "$compile", "$rootScope", "$$stackedMap", function (a, b, c, d, e, f) {
function g() {
for (var a = -1, b = o.keys(), c = 0; c < b.length; c++)o.get(b[c]).value.backdrop && (a = c);
return a
}
function h(a) {
var b = c.find("body").eq(0), d = o.get(a).value;
o.remove(a), j(d.modalDomEl, d.modalScope, function () {
b.toggleClass(n, o.length() > 0), i()
})
}
function i() {
if (l && -1 == g()) {
var a = m;
j(l, m, function () {
a = null
}), l = void 0, m = void 0
}
}
function j(c, d, f) {
function g() {
g.done || (g.done = !0, c.remove(), d.$destroy(), f && f())
}
d.animate = !1, c.attr("modal-animation") && a.enabled() ? c.one("$animate:close", function () {
e.$evalAsync(g)
}) : b(g)
}
function k(a, b, c) {
return !a.value.modalScope.$broadcast("modal.closing", b, c).defaultPrevented
}
var l, m, n = "modal-open", o = f.createNew(), p = {};
return e.$watch(g, function (a) {
m && (m.index = a)
}), c.bind("keydown", function (a) {
var b;
27 === a.which && (b = o.top(), b && b.value.keyboard && (a.preventDefault(), e.$apply(function () {
p.dismiss(b.key, "escape key press")
})))
}), p.open = function (a, b) {
var f = c[0].activeElement;
o.add(a, {
deferred: b.deferred,
renderDeferred: b.renderDeferred,
modalScope: b.scope,
backdrop: b.backdrop,
keyboard: b.keyboard
});
var h = c.find("body").eq(0), i = g();
if (i >= 0 && !l) {
m = e.$new(!0), m.index = i;
var j = angular.element('<div modal-backdrop="modal-backdrop"></div>');
j.attr("backdrop-class", b.backdropClass), b.animation && j.attr("modal-animation", "true"), l = d(j)(m), h.append(l)
}
var k = angular.element('<div modal-window="modal-window"></div>');
k.attr({
"template-url": b.windowTemplateUrl,
"window-class": b.windowClass,
size: b.size,
index: o.length() - 1,
animate: "animate"
}).html(b.content), b.animation && k.attr("modal-animation", "true");
var p = d(k)(b.scope);
o.top().value.modalDomEl = p, o.top().value.modalOpener = f, h.append(p), h.addClass(n)
}, p.close = function (a, b) {
var c = o.get(a);
return c && k(c, b, !0) ? (c.value.deferred.resolve(b), h(a), c.value.modalOpener.focus(), !0) : !c
}, p.dismiss = function (a, b) {
var c = o.get(a);
return c && k(c, b, !1) ? (c.value.deferred.reject(b), h(a), c.value.modalOpener.focus(), !0) : !c
}, p.dismissAll = function (a) {
for (var b = this.getTop(); b && this.dismiss(b.key, a);)b = this.getTop()
}, p.getTop = function () {
return o.top()
}, p.modalRendered = function (a) {
var b = o.get(a);
b && b.value.renderDeferred.resolve()
}, p
}]).provider("$modal", function () {
var a = {
options: {animation: !0, backdrop: !0, keyboard: !0},
$get: ["$injector", "$rootScope", "$q", "$templateRequest", "$controller", "$modalStack", function (b, c, d, e, f, g) {
function h(a) {
return a.template ? d.when(a.template) : e(angular.isFunction(a.templateUrl) ? a.templateUrl() : a.templateUrl)
}
function i(a) {
var c = [];
return angular.forEach(a, function (a) {
(angular.isFunction(a) || angular.isArray(a)) && c.push(d.when(b.invoke(a)))
}), c
}
var j = {};
return j.open = function (b) {
var e = d.defer(), j = d.defer(), k = d.defer(), l = {
result: e.promise,
opened: j.promise,
rendered: k.promise,
close: function (a) {
return g.close(l, a)
},
dismiss: function (a) {
return g.dismiss(l, a)
}
};
if (b = angular.extend({}, a.options, b), b.resolve = b.resolve || {}, !b.template && !b.templateUrl)throw new Error("One of template or templateUrl options is required.");
var m = d.all([h(b)].concat(i(b.resolve)));
return m.then(function (a) {
var d = (b.scope || c).$new();
d.$close = l.close, d.$dismiss = l.dismiss;
var h, i = {}, j = 1;
b.controller && (i.$scope = d, i.$modalInstance = l, angular.forEach(b.resolve, function (b, c) {
i[c] = a[j++]
}), h = f(b.controller, i), b.controllerAs && (d[b.controllerAs] = h)), g.open(l, {
scope: d,
deferred: e,
renderDeferred: k,
content: a[0],
animation: b.animation,
backdrop: b.backdrop,
keyboard: b.keyboard,
backdropClass: b.backdropClass,
windowClass: b.windowClass,
windowTemplateUrl: b.windowTemplateUrl,
size: b.size
})
}, function (a) {
e.reject(a)
}), m.then(function () {
j.resolve(!0)
}, function (a) {
j.reject(a)
}), l
}, j
}]
};
return a
}), angular.module("ui.bootstrap.pagination", []).controller("PaginationController", ["$scope", "$attrs", "$parse", function (a, b, c) {
var d = this, e = {$setViewValue: angular.noop}, f = b.numPages ? c(b.numPages).assign : angular.noop;
this.init = function (g, h) {
e = g, this.config = h, e.$render = function () {
d.render()
}, b.itemsPerPage ? a.$parent.$watch(c(b.itemsPerPage), function (b) {
d.itemsPerPage = parseInt(b, 10), a.totalPages = d.calculateTotalPages()
}) : this.itemsPerPage = h.itemsPerPage, a.$watch("totalItems", function () {
a.totalPages = d.calculateTotalPages()
}), a.$watch("totalPages", function (b) {
f(a.$parent, b), a.page > b ? a.selectPage(b) : e.$render()
})
}, this.calculateTotalPages = function () {
var b = this.itemsPerPage < 1 ? 1 : Math.ceil(a.totalItems / this.itemsPerPage);
return Math.max(b || 0, 1)
}, this.render = function () {
a.page = parseInt(e.$viewValue, 10) || 1
}, a.selectPage = function (b, c) {
a.page !== b && b > 0 && b <= a.totalPages && (c && c.target && c.target.blur(), e.$setViewValue(b), e.$render())
}, a.getText = function (b) {
return a[b + "Text"] || d.config[b + "Text"]
}, a.noPrevious = function () {
return 1 === a.page
}, a.noNext = function () {
return a.page === a.totalPages
}
}]).constant("paginationConfig", {
itemsPerPage: 10,
boundaryLinks: !1,
directionLinks: !0,
firstText: "First",
previousText: "Previous",
nextText: "Next",
lastText: "Last",
rotate: !0
}).directive("pagination", ["$parse", "paginationConfig", function (a, b) {
return {
restrict: "EA",
scope: {totalItems: "=", firstText: "@", previousText: "@", nextText: "@", lastText: "@"},
require: ["pagination", "?ngModel"],
controller: "PaginationController",
templateUrl: "template/pagination/pagination.html",
replace: !0,
link: function (c, d, e, f) {
function g(a, b, c) {
return {number: a, text: b, active: c}
}
function h(a, b) {
var c = [], d = 1, e = b, f = angular.isDefined(k) && b > k;
f && (l ? (d = Math.max(a - Math.floor(k / 2), 1), e = d + k - 1, e > b && (e = b, d = e - k + 1)) : (d = (Math.ceil(a / k) - 1) * k + 1, e = Math.min(d + k - 1, b)));
for (var h = d; e >= h; h++) {
var i = g(h, h, h === a);
c.push(i)
}
if (f && !l) {
if (d > 1) {
var j = g(d - 1, "...", !1);
c.unshift(j)
}
if (b > e) {
var m = g(e + 1, "...", !1);
c.push(m)
}
}
return c
}
var i = f[0], j = f[1];
if (j) {
var k = angular.isDefined(e.maxSize) ? c.$parent.$eval(e.maxSize) : b.maxSize, l = angular.isDefined(e.rotate) ? c.$parent.$eval(e.rotate) : b.rotate;
c.boundaryLinks = angular.isDefined(e.boundaryLinks) ? c.$parent.$eval(e.boundaryLinks) : b.boundaryLinks, c.directionLinks = angular.isDefined(e.directionLinks) ? c.$parent.$eval(e.directionLinks) : b.directionLinks, i.init(j, b), e.maxSize && c.$parent.$watch(a(e.maxSize), function (a) {
k = parseInt(a, 10), i.render()
});
var m = i.render;
i.render = function () {
m(), c.page > 0 && c.page <= c.totalPages && (c.pages = h(c.page, c.totalPages))
}
}
}
}
}]).constant("pagerConfig", {
itemsPerPage: 10,
previousText: "« Previous",
nextText: "Next »",
align: !0
}).directive("pager", ["pagerConfig", function (a) {
return {
restrict: "EA",
scope: {totalItems: "=", previousText: "@", nextText: "@"},
require: ["pager", "?ngModel"],
controller: "PaginationController",
templateUrl: "template/pagination/pager.html",
replace: !0,
link: function (b, c, d, e) {
var f = e[0], g = e[1];
g && (b.align = angular.isDefined(d.align) ? b.$parent.$eval(d.align) : a.align, f.init(g, a))
}
}
}]), angular.module("ui.bootstrap.tooltip", ["ui.bootstrap.position", "ui.bootstrap.bindHtml"]).provider("$tooltip", function () {
function a(a) {
var b = /[A-Z]/g, c = "-";
return a.replace(b, function (a, b) {
return (b ? c : "") + a.toLowerCase()
})
}
var b = {placement: "top", animation: !0, popupDelay: 0, useContentExp: !1}, c = {
mouseenter: "mouseleave",
click: "click",
focus: "blur"
}, d = {};
this.options = function (a) {
angular.extend(d, a)
}, this.setTriggers = function (a) {
angular.extend(c, a)
}, this.$get = ["$window", "$compile", "$timeout", "$document", "$position", "$interpolate", function (e, f, g, h, i, j) {
return function (e, k, l, m) {
function n(a) {
var b = a || m.trigger || l, d = c[b] || b;
return {show: b, hide: d}
}
m = angular.extend({}, b, d, m);
var o = a(e), p = j.startSymbol(), q = j.endSymbol(), r = "<div " + o + '-popup title="' + p + "title" + q + '" ' + (m.useContentExp ? 'content-exp="contentExp()" ' : 'content="' + p + "content" + q + '" ') + 'placement="' + p + "placement" + q + '" popup-class="' + p + "popupClass" + q + '" animation="animation" is-open="isOpen"origin-scope="origScope" ></div>';
return {
restrict: "EA", compile: function () {
var a = f(r);
return function (b, c, d) {
function f() {
E.isOpen ? l() : j()
}
function j() {
(!D || b.$eval(d[k + "Enable"])) && (s(), E.popupDelay ? A || (A = g(o, E.popupDelay, !1), A.then(function (a) {
a()
})) : o()())
}
function l() {
b.$apply(function () {
p()
})
}
function o() {
return A = null, z && (g.cancel(z), z = null), (m.useContentExp ? E.contentExp() : E.content) ? (q(), x.css({
top: 0,
left: 0,
display: "block"
}), E.$digest(), F(), E.isOpen = !0, E.$apply(), F) : angular.noop
}
function p() {
E.isOpen = !1, g.cancel(A), A = null, E.animation ? z || (z = g(r, 500)) : r()
}
function q() {
x && r(), y = E.$new(), x = a(y, function (a) {
B ? h.find("body").append(a) : c.after(a)
}), y.$watch(function () {
g(F, 0, !1)
}), m.useContentExp && y.$watch("contentExp()", function (a) {
!a && E.isOpen && p()
})
}
function r() {
z = null, x && (x.remove(), x = null), y && (y.$destroy(), y = null)
}
function s() {
t(), u(), v()
}
function t() {
E.popupClass = d[k + "Class"]
}
function u() {
var a = d[k + "Placement"];
E.placement = angular.isDefined(a) ? a : m.placement
}
function v() {
var a = d[k + "PopupDelay"], b = parseInt(a, 10);
E.popupDelay = isNaN(b) ? m.popupDelay : b
}
function w() {
var a = d[k + "Trigger"];
G(), C = n(a), C.show === C.hide ? c.bind(C.show, f) : (c.bind(C.show, j), c.bind(C.hide, l))
}
var x, y, z, A, B = angular.isDefined(m.appendToBody) ? m.appendToBody : !1, C = n(void 0), D = angular.isDefined(d[k + "Enable"]), E = b.$new(!0), F = function () {
if (x) {
var a = i.positionElements(c, x, E.placement, B);
a.top += "px", a.left += "px", x.css(a)
}
};
E.origScope = b, E.isOpen = !1, E.contentExp = function () {
return b.$eval(d[e])
}, m.useContentExp || d.$observe(e, function (a) {
E.content = a, !a && E.isOpen && p()
}), d.$observe("disabled", function (a) {
a && E.isOpen && p()
}), d.$observe(k + "Title", function (a) {
E.title = a
});
var G = function () {
c.unbind(C.show, j), c.unbind(C.hide, l)
};
w();
var H = b.$eval(d[k + "Animation"]);
E.animation = angular.isDefined(H) ? !!H : m.animation;
var I = b.$eval(d[k + "AppendToBody"]);
B = angular.isDefined(I) ? I : B, B && b.$on("$locationChangeSuccess", function () {
E.isOpen && p()
}), b.$on("$destroy", function () {
g.cancel(z), g.cancel(A), G(), r(), E = null
})
}
}
}
}
}]
}).directive("tooltipTemplateTransclude", ["$animate", "$sce", "$compile", "$templateRequest", function (a, b, c, d) {
return {
link: function (e, f, g) {
var h, i, j, k = e.$eval(g.tooltipTemplateTranscludeScope), l = 0, m = function () {
i && (i.remove(), i = null), h && (h.$destroy(), h = null), j && (a.leave(j).then(function () {
i = null
}), i = j, j = null)
};
e.$watch(b.parseAsResourceUrl(g.tooltipTemplateTransclude), function (b) {
var g = ++l;
b ? (d(b, !0).then(function (d) {
if (g === l) {
var e = k.$new(), i = d, n = c(i)(e, function (b) {
m(), a.enter(b, f)
});
h = e, j = n, h.$emit("$includeContentLoaded", b)
}
}, function () {
g === l && (m(), e.$emit("$includeContentError", b))
}), e.$emit("$includeContentRequested", b)) : m()
}), e.$on("$destroy", m)
}
}
}]).directive("tooltipClasses", function () {
return {
restrict: "A", link: function (a, b, c) {
a.placement && b.addClass(a.placement), a.popupClass && b.addClass(a.popupClass), a.animation() && b.addClass(c.tooltipAnimationClass)
}
}
}).directive("tooltipPopup", function () {
return {
restrict: "EA",
replace: !0,
scope: {content: "@", placement: "@", popupClass: "@", animation: "&", isOpen: "&"},
templateUrl: "template/tooltip/tooltip-popup.html"
}
}).directive("tooltip", ["$tooltip", function (a) {
return a("tooltip", "tooltip", "mouseenter")
}]).directive("tooltipTemplatePopup", function () {
return {
restrict: "EA",
replace: !0,
scope: {contentExp: "&", placement: "@", popupClass: "@", animation: "&", isOpen: "&", originScope: "&"},
templateUrl: "template/tooltip/tooltip-template-popup.html"
}
}).directive("tooltipTemplate", ["$tooltip", function (a) {
return a("tooltipTemplate", "tooltip", "mouseenter", {useContentExp: !0})
}]).directive("tooltipHtmlPopup", function () {
return {
restrict: "EA",
replace: !0,
scope: {contentExp: "&", placement: "@", popupClass: "@", animation: "&", isOpen: "&"},
templateUrl: "template/tooltip/tooltip-html-popup.html"
}
}).directive("tooltipHtml", ["$tooltip", function (a) {
return a("tooltipHtml", "tooltip", "mouseenter", {useContentExp: !0})
}]).directive("tooltipHtmlUnsafePopup", function () {
return {
restrict: "EA",
replace: !0,
scope: {content: "@", placement: "@", popupClass: "@", animation: "&", isOpen: "&"},
templateUrl: "template/tooltip/tooltip-html-unsafe-popup.html"
}
}).value("tooltipHtmlUnsafeSuppressDeprecated", !1).directive("tooltipHtmlUnsafe", ["$tooltip", "tooltipHtmlUnsafeSuppressDeprecated", "$log", function (a, b, c) {
return b || c.warn("tooltip-html-unsafe is now deprecated. Use tooltip-html or tooltip-template instead."), a("tooltipHtmlUnsafe", "tooltip", "mouseenter")
}]), angular.module("ui.bootstrap.popover", ["ui.bootstrap.tooltip"]).directive("popoverTemplatePopup", function () {
return {
restrict: "EA",
replace: !0,
scope: {
title: "@",
contentExp: "&",
placement: "@",
popupClass: "@",
animation: "&",
isOpen: "&",
originScope: "&"
},
templateUrl: "template/popover/popover-template.html"
}
}).directive("popoverTemplate", ["$tooltip", function (a) {
return a("popoverTemplate", "popover", "click", {useContentExp: !0})
}]).directive("popoverPopup", function () {
return {
restrict: "EA",
replace: !0,
scope: {title: "@", content: "@", placement: "@", popupClass: "@", animation: "&", isOpen: "&"},
templateUrl: "template/popover/popover.html"
}
}).directive("popover", ["$tooltip", function (a) {
return a("popover", "popover", "click")
}]), angular.module("ui.bootstrap.progressbar", []).constant("progressConfig", {
animate: !0,
max: 100
}).controller("ProgressController", ["$scope", "$attrs", "progressConfig", function (a, b, c) {
var d = this, e = angular.isDefined(b.animate) ? a.$parent.$eval(b.animate) : c.animate;
this.bars = [], a.max = angular.isDefined(a.max) ? a.max : c.max, this.addBar = function (b, c) {
e || c.css({transition: "none"}), this.bars.push(b), b.$watch("value", function (c) {
b.percent = +(100 * c / a.max).toFixed(2)
}), b.$on("$destroy", function () {
c = null, d.removeBar(b)
})
}, this.removeBar = function (a) {
this.bars.splice(this.bars.indexOf(a), 1)
}
}]).directive("progress", function () {
return {
restrict: "EA",
replace: !0,
transclude: !0,
controller: "ProgressController",
require: "progress",
scope: {},
templateUrl: "template/progressbar/progress.html"
}
}).directive("bar", function () {
return {
restrict: "EA",
replace: !0,
transclude: !0,
require: "^progress",
scope: {value: "=", max: "=?", type: "@"},
templateUrl: "template/progressbar/bar.html",
link: function (a, b, c, d) {
d.addBar(a, b)
}
}
}).directive("progressbar", function () {
return {
restrict: "EA",
replace: !0,
transclude: !0,
controller: "ProgressController",
scope: {value: "=", max: "=?", type: "@"},
templateUrl: "template/progressbar/progressbar.html",
link: function (a, b, c, d) {
d.addBar(a, angular.element(b.children()[0]))
}
}
}), angular.module("ui.bootstrap.rating", []).constant("ratingConfig", {
max: 5,
stateOn: null,
stateOff: null
}).controller("RatingController", ["$scope", "$attrs", "ratingConfig", function (a, b, c) {
var d = {$setViewValue: angular.noop};
this.init = function (e) {
d = e, d.$render = this.render, d.$formatters.push(function (a) {
return angular.isNumber(a) && a << 0 !== a && (a = Math.round(a)), a
}), this.stateOn = angular.isDefined(b.stateOn) ? a.$parent.$eval(b.stateOn) : c.stateOn, this.stateOff = angular.isDefined(b.stateOff) ? a.$parent.$eval(b.stateOff) : c.stateOff;
var f = angular.isDefined(b.ratingStates) ? a.$parent.$eval(b.ratingStates) : new Array(angular.isDefined(b.max) ? a.$parent.$eval(b.max) : c.max);
a.range = this.buildTemplateObjects(f)
}, this.buildTemplateObjects = function (a) {
for (var b = 0, c = a.length; c > b; b++)a[b] = angular.extend({index: b}, {
stateOn: this.stateOn,
stateOff: this.stateOff
}, a[b]);
return a
}, a.rate = function (b) {
!a.readonly && b >= 0 && b <= a.range.length && (d.$setViewValue(b), d.$render())
}, a.enter = function (b) {
a.readonly || (a.value = b), a.onHover({value: b})
}, a.reset = function () {
a.value = d.$viewValue, a.onLeave()
}, a.onKeydown = function (b) {
/(37|38|39|40)/.test(b.which) && (b.preventDefault(), b.stopPropagation(), a.rate(a.value + (38 === b.which || 39 === b.which ? 1 : -1)))
}, this.render = function () {
a.value = d.$viewValue
}
}]).directive("rating", function () {
return {
restrict: "EA",
require: ["rating", "ngModel"],
scope: {readonly: "=?", onHover: "&", onLeave: "&"},
controller: "RatingController",
templateUrl: "template/rating/rating.html",
replace: !0,
link: function (a, b, c, d) {
var e = d[0], f = d[1];
e.init(f)
}
}
}), angular.module("ui.bootstrap.tabs", []).controller("TabsetController", ["$scope", function (a) {
var b = this, c = b.tabs = a.tabs = [];
b.select = function (a) {
angular.forEach(c, function (b) {
b.active && b !== a && (b.active = !1, b.onDeselect())
}), a.active = !0, a.onSelect()
}, b.addTab = function (a) {
c.push(a), 1 === c.length && a.active !== !1 ? a.active = !0 : a.active ? b.select(a) : a.active = !1
}, b.removeTab = function (a) {
var e = c.indexOf(a);
if (a.active && c.length > 1 && !d) {
var f = e == c.length - 1 ? e - 1 : e + 1;
b.select(c[f])
}
c.splice(e, 1)
};
var d;
a.$on("$destroy", function () {
d = !0
})
}]).directive("tabset", function () {
return {
restrict: "EA",
transclude: !0,
replace: !0,
scope: {type: "@"},
controller: "TabsetController",
templateUrl: "template/tabs/tabset.html",
link: function (a, b, c) {
a.vertical = angular.isDefined(c.vertical) ? a.$parent.$eval(c.vertical) : !1, a.justified = angular.isDefined(c.justified) ? a.$parent.$eval(c.justified) : !1
}
}
}).directive("tab", ["$parse", "$log", function (a, b) {
return {
require: "^tabset",
restrict: "EA",
replace: !0,
templateUrl: "template/tabs/tab.html",
transclude: !0,
scope: {active: "=?", heading: "@", onSelect: "&select", onDeselect: "&deselect"},
controller: function () {
},
compile: function (c, d, e) {
return function (c, d, f, g) {
c.$watch("active", function (a) {
a && g.select(c)
}), c.disabled = !1, f.disable && c.$parent.$watch(a(f.disable), function (a) {
c.disabled = !!a
}), f.disabled && (b.warn('Use of "disabled" attribute has been deprecated, please use "disable"'), c.$parent.$watch(a(f.disabled), function (a) {
c.disabled = !!a
})), c.select = function () {
c.disabled || (c.active = !0)
}, g.addTab(c), c.$on("$destroy", function () {
g.removeTab(c)
}), c.$transcludeFn = e
}
}
}
}]).directive("tabHeadingTransclude", [function () {
return {
restrict: "A", require: "^tab", link: function (a, b) {
a.$watch("headingElement", function (a) {
a && (b.html(""), b.append(a))
})
}
}
}]).directive("tabContentTransclude", function () {
function a(a) {
return a.tagName && (a.hasAttribute("tab-heading") || a.hasAttribute("data-tab-heading") || "tab-heading" === a.tagName.toLowerCase() || "data-tab-heading" === a.tagName.toLowerCase())
}
return {
restrict: "A", require: "^tabset", link: function (b, c, d) {
var e = b.$eval(d.tabContentTransclude);
e.$transcludeFn(e.$parent, function (b) {
angular.forEach(b, function (b) {
a(b) ? e.headingElement = b : c.append(b)
})
})
}
}
}), angular.module("ui.bootstrap.timepicker", []).constant("timepickerConfig", {
hourStep: 1,
minuteStep: 1,
showMeridian: !0,
meridians: null,
readonlyInput: !1,
mousewheel: !0,
arrowkeys: !0
}).controller("TimepickerController", ["$scope", "$attrs", "$parse", "$log", "$locale", "timepickerConfig", function (a, b, c, d, e, f) {
function g() {
var b = parseInt(a.hours, 10), c = a.showMeridian ? b > 0 && 13 > b : b >= 0 && 24 > b;
return c ? (a.showMeridian && (12 === b && (b = 0), a.meridian === p[1] && (b += 12)), b) : void 0
}
function h() {
var b = parseInt(a.minutes, 10);
return b >= 0 && 60 > b ? b : void 0
}
function i(a) {
return angular.isDefined(a) && a.toString().length < 2 ? "0" + a : a.toString()
}
function j(a) {
k(), o.$setViewValue(new Date(n)), l(a)
}
function k() {
o.$setValidity("time", !0), a.invalidHours = !1, a.invalidMinutes = !1
}
function l(b) {
var c = n.getHours(), d = n.getMinutes();
a.showMeridian && (c = 0 === c || 12 === c ? 12 : c % 12), a.hours = "h" === b ? c : i(c), "m" !== b && (a.minutes = i(d)), a.meridian = n.getHours() < 12 ? p[0] : p[1]
}
function m(a) {
var b = new Date(n.getTime() + 6e4 * a);
n.setHours(b.getHours(), b.getMinutes()), j()
}
var n = new Date, o = {$setViewValue: angular.noop}, p = angular.isDefined(b.meridians) ? a.$parent.$eval(b.meridians) : f.meridians || e.DATETIME_FORMATS.AMPMS;
this.init = function (c, d) {
o = c, o.$render = this.render, o.$formatters.unshift(function (a) {
return a ? new Date(a) : null
});
var e = d.eq(0), g = d.eq(1), h = angular.isDefined(b.mousewheel) ? a.$parent.$eval(b.mousewheel) : f.mousewheel;
h && this.setupMousewheelEvents(e, g);
var i = angular.isDefined(b.arrowkeys) ? a.$parent.$eval(b.arrowkeys) : f.arrowkeys;
i && this.setupArrowkeyEvents(e, g), a.readonlyInput = angular.isDefined(b.readonlyInput) ? a.$parent.$eval(b.readonlyInput) : f.readonlyInput, this.setupInputEvents(e, g)
};
var q = f.hourStep;
b.hourStep && a.$parent.$watch(c(b.hourStep), function (a) {
q = parseInt(a, 10)
});
var r = f.minuteStep;
b.minuteStep && a.$parent.$watch(c(b.minuteStep), function (a) {
r = parseInt(a, 10)
}), a.showMeridian = f.showMeridian, b.showMeridian && a.$parent.$watch(c(b.showMeridian), function (b) {
if (a.showMeridian = !!b, o.$error.time) {
var c = g(), d = h();
angular.isDefined(c) && angular.isDefined(d) && (n.setHours(c), j())
} else l()
}), this.setupMousewheelEvents = function (b, c) {
var d = function (a) {
a.originalEvent && (a = a.originalEvent);
var b = a.wheelDelta ? a.wheelDelta : -a.deltaY;
return a.detail || b > 0
};
b.bind("mousewheel wheel", function (b) {
a.$apply(d(b) ? a.incrementHours() : a.decrementHours()), b.preventDefault()
}), c.bind("mousewheel wheel", function (b) {
a.$apply(d(b) ? a.incrementMinutes() : a.decrementMinutes()), b.preventDefault()
})
}, this.setupArrowkeyEvents = function (b, c) {
b.bind("keydown", function (b) {
38 === b.which ? (b.preventDefault(), a.incrementHours(), a.$apply()) : 40 === b.which && (b.preventDefault(), a.decrementHours(), a.$apply())
}), c.bind("keydown", function (b) {
38 === b.which ? (b.preventDefault(), a.incrementMinutes(), a.$apply()) : 40 === b.which && (b.preventDefault(), a.decrementMinutes(), a.$apply())
})
}, this.setupInputEvents = function (b, c) {
if (a.readonlyInput)return a.updateHours = angular.noop, void(a.updateMinutes = angular.noop);
var d = function (b, c) {
o.$setViewValue(null), o.$setValidity("time", !1), angular.isDefined(b) && (a.invalidHours = b), angular.isDefined(c) && (a.invalidMinutes = c)
};
a.updateHours = function () {
var a = g();
angular.isDefined(a) ? (n.setHours(a), j("h")) : d(!0)
}, b.bind("blur", function () {
!a.invalidHours && a.hours < 10 && a.$apply(function () {
a.hours = i(a.hours)
})
}), a.updateMinutes = function () {
var a = h();
angular.isDefined(a) ? (n.setMinutes(a), j("m")) : d(void 0, !0)
}, c.bind("blur", function () {
!a.invalidMinutes && a.minutes < 10 && a.$apply(function () {
a.minutes = i(a.minutes)
})
})
}, this.render = function () {
var a = o.$viewValue;
isNaN(a) ? (o.$setValidity("time", !1), d.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.')) : (a && (n = a), k(), l())
}, a.incrementHours = function () {
m(60 * q)
}, a.decrementHours = function () {
m(60 * -q)
}, a.incrementMinutes = function () {
m(r)
}, a.decrementMinutes = function () {
m(-r)
}, a.toggleMeridian = function () {
m(720 * (n.getHours() < 12 ? 1 : -1))
}
}]).directive("timepicker", function () {
return {
restrict: "EA",
require: ["timepicker", "?^ngModel"],
controller: "TimepickerController",
replace: !0,
scope: {},
templateUrl: "template/timepicker/timepicker.html",
link: function (a, b, c, d) {
var e = d[0], f = d[1];
f && e.init(f, b.find("input"))
}
}
}), angular.module("ui.bootstrap.transition", []).value("$transitionSuppressDeprecated", !1).factory("$transition", ["$q", "$timeout", "$rootScope", "$log", "$transitionSuppressDeprecated", function (a, b, c, d, e) {
function f(a) {
for (var b in a)if (void 0 !== h.style[b])return a[b]
}
e || d.warn("$transition is now deprecated. Use $animate from ngAnimate instead.");
var g = function (d, e, f) {
f = f || {};
var h = a.defer(), i = g[f.animation ? "animationEndEventName" : "transitionEndEventName"], j = function () {
c.$apply(function () {
d.unbind(i, j), h.resolve(d)
})
};
return i && d.bind(i, j), b(function () {
angular.isString(e) ? d.addClass(e) : angular.isFunction(e) ? e(d) : angular.isObject(e) && d.css(e), i || h.resolve(d)
}), h.promise.cancel = function () {
i && d.unbind(i, j), h.reject("Transition cancelled")
}, h.promise
}, h = document.createElement("trans"), i = {
WebkitTransition: "webkitTransitionEnd",
MozTransition: "transitionend",
OTransition: "oTransitionEnd",
transition: "transitionend"
}, j = {
WebkitTransition: "webkitAnimationEnd",
MozTransition: "animationend",
OTransition: "oAnimationEnd",
transition: "animationend"
};
return g.transitionEndEventName = f(i), g.animationEndEventName = f(j), g
}]), angular.module("ui.bootstrap.typeahead", ["ui.bootstrap.position", "ui.bootstrap.bindHtml"]).factory("typeaheadParser", ["$parse", function (a) {
var b = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;
return {
parse: function (c) {
var d = c.match(b);
if (!d)throw new Error('Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_" but got "' + c + '".');
return {itemName: d[3], source: a(d[4]), viewMapper: a(d[2] || d[1]), modelMapper: a(d[1])}
}
}
}]).directive("typeahead", ["$compile", "$parse", "$q", "$timeout", "$document", "$position", "typeaheadParser", function (a, b, c, d, e, f, g) {
var h = [9, 13, 27, 38, 40];
return {
require: "ngModel", link: function (i, j, k, l) {
var m, n = i.$eval(k.typeaheadMinLength) || 1, o = i.$eval(k.typeaheadWaitMs) || 0, p = i.$eval(k.typeaheadEditable) !== !1, q = b(k.typeaheadLoading).assign || angular.noop, r = b(k.typeaheadOnSelect), s = k.typeaheadInputFormatter ? b(k.typeaheadInputFormatter) : void 0, t = k.typeaheadAppendToBody ? i.$eval(k.typeaheadAppendToBody) : !1, u = i.$eval(k.typeaheadFocusFirst) !== !1, v = b(k.ngModel).assign, w = g.parse(k.typeahead), x = i.$new();
i.$on("$destroy", function () {
x.$destroy()
});
var y = "typeahead-" + x.$id + "-" + Math.floor(1e4 * Math.random());
j.attr({"aria-autocomplete": "list", "aria-expanded": !1, "aria-owns": y});
var z = angular.element("<div typeahead-popup></div>");
z.attr({
id: y,
matches: "matches",
active: "activeIdx",
select: "select(activeIdx)",
query: "query",
position: "position"
}), angular.isDefined(k.typeaheadTemplateUrl) && z.attr("template-url", k.typeaheadTemplateUrl);
var A = function () {
x.matches = [], x.activeIdx = -1, j.attr("aria-expanded", !1)
}, B = function (a) {
return y + "-option-" + a
};
x.$watch("activeIdx", function (a) {
0 > a ? j.removeAttr("aria-activedescendant") : j.attr("aria-activedescendant", B(a))
});
var C = function (a) {
var b = {$viewValue: a};
q(i, !0), c.when(w.source(i, b)).then(function (c) {
var d = a === l.$viewValue;
if (d && m)if (c && c.length > 0) {
x.activeIdx = u ? 0 : -1, x.matches.length = 0;
for (var e = 0; e < c.length; e++)b[w.itemName] = c[e], x.matches.push({
id: B(e),
label: w.viewMapper(x, b),
model: c[e]
});
x.query = a, x.position = t ? f.offset(j) : f.position(j), x.position.top = x.position.top + j.prop("offsetHeight"), j.attr("aria-expanded", !0)
} else A();
d && q(i, !1)
}, function () {
A(), q(i, !1)
})
};
A(), x.query = void 0;
var D, E = function (a) {
D = d(function () {
C(a)
}, o)
}, F = function () {
D && d.cancel(D)
};
l.$parsers.unshift(function (a) {
return m = !0, a && a.length >= n ? o > 0 ? (F(), E(a)) : C(a) : (q(i, !1), F(), A()), p ? a : a ? void l.$setValidity("editable", !1) : (l.$setValidity("editable", !0), a)
}), l.$formatters.push(function (a) {
var b, c, d = {};
return p || l.$setValidity("editable", !0), s ? (d.$model = a, s(i, d)) : (d[w.itemName] = a, b = w.viewMapper(i, d), d[w.itemName] = void 0, c = w.viewMapper(i, d), b !== c ? b : a)
}), x.select = function (a) {
var b, c, e = {};
e[w.itemName] = c = x.matches[a].model, b = w.modelMapper(i, e), v(i, b), l.$setValidity("editable", !0), l.$setValidity("parse", !0), r(i, {
$item: c,
$model: b,
$label: w.viewMapper(i, e)
}), A(), d(function () {
j[0].focus()
}, 0, !1)
}, j.bind("keydown", function (a) {
0 !== x.matches.length && -1 !== h.indexOf(a.which) && (-1 != x.activeIdx || 13 !== a.which && 9 !== a.which) && (a.preventDefault(), 40 === a.which ? (x.activeIdx = (x.activeIdx + 1) % x.matches.length, x.$digest()) : 38 === a.which ? (x.activeIdx = (x.activeIdx > 0 ? x.activeIdx : x.matches.length) - 1, x.$digest()) : 13 === a.which || 9 === a.which ? x.$apply(function () {
x.select(x.activeIdx)
}) : 27 === a.which && (a.stopPropagation(), A(), x.$digest()))
}), j.bind("blur", function () {
m = !1
});
var G = function (a) {
j[0] !== a.target && (A(), x.$digest())
};
e.bind("click", G), i.$on("$destroy", function () {
e.unbind("click", G), t && H.remove(), z.remove()
});
var H = a(z)(x);
t ? e.find("body").append(H) : j.after(H)
}
}
}]).directive("typeaheadPopup", function () {
return {
restrict: "EA",
scope: {matches: "=", query: "=", active: "=", position: "=", select: "&"},
replace: !0,
templateUrl: "template/typeahead/typeahead-popup.html",
link: function (a, b, c) {
a.templateUrl = c.templateUrl, a.isOpen = function () {
return a.matches.length > 0
}, a.isActive = function (b) {
return a.active == b
}, a.selectActive = function (b) {
a.active = b
}, a.selectMatch = function (b) {
a.select({activeIdx: b})
}
}
}
}).directive("typeaheadMatch", ["$templateRequest", "$compile", "$parse", function (a, b, c) {
return {
restrict: "EA", scope: {index: "=", match: "=", query: "="}, link: function (d, e, f) {
var g = c(f.templateUrl)(d.$parent) || "template/typeahead/typeahead-match.html";
a(g).then(function (a) {
b(a.trim())(d, function (a) {
e.replaceWith(a)
})
})
}
}
}]).filter("typeaheadHighlight", function () {
function a(a) {
return a.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1")
}
return function (b, c) {
return c ? ("" + b).replace(new RegExp(a(c), "gi"), "<strong>$&</strong>") : b
}
}), !angular.$$csp() && angular.element(document).find("head").prepend('<style type="text/css">.ng-animate.item:not(.left):not(.right){-webkit-transition:0s ease-in-out left;transition:0s ease-in-out left}</style>');
This source diff could not be displayed because it is too large. You can view the blob instead.
.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