Commit b351b484 authored by Yiming Liu's avatar Yiming Liu

Merge pull request #96 from nobodyiam/client-integration-test-merge

Client integration test and integrate foundation framework
parents 36076667 c6b4dd50
<?xml version="1.0" encoding="UTF-8"?>
<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>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-client</artifactId>
<name>Apollo Client</name>
<properties>
<java.version>1.7</java.version>
</properties>
<dependencies>
<!-- apollo -->
<dependency>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-core</artifactId>
</dependency>
<!-- end of apollo -->
<dependency>
<groupId>com.ctrip.framework</groupId>
<artifactId>framework-foundation</artifactId>
</dependency>
<dependency>
<groupId>com.dianping.cat</groupId>
<artifactId>cat-client</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</exclusion>
</exclusions>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-client</artifactId>
<name>Apollo Client</name>
<properties>
<java.version>1.7</java.version>
</properties>
<dependencies>
<!-- apollo -->
<dependency>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-core</artifactId>
</dependency>
<!-- end of apollo -->
<!-- foundation service -->
<dependency>
<groupId>com.ctrip.framework</groupId>
<artifactId>framework-foundation</artifactId>
</dependency>
<!-- end of foundation service -->
<!-- cat -->
<dependency>
<groupId>com.dianping.cat</groupId>
<artifactId>cat-client</artifactId>
</dependency>
<!-- end of cat -->
<!-- log -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</exclusion>
</exclusions>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<scope>provided</scope>
</dependency>
<!-- end of log -->
<!-- test -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<scope>test</scope>
</dependency>
<!-- end of test -->
</dependencies>
</project>
......@@ -13,5 +13,9 @@ public interface Config {
*/
public String getProperty(String key, String defaultValue);
/**
* Add change listener to this config instance.
* @param listener the config change listener
*/
public void addChangeListener(ConfigChangeListener listener);
}
......@@ -6,5 +6,9 @@ import com.ctrip.apollo.model.ConfigChangeEvent;
* @author Jason Song(song_s@ctrip.com)
*/
public interface ConfigChangeListener {
/**
* Invoked when there is any config change for the namespace.
* @param changeEvent the event for this change
*/
public void onChange(ConfigChangeEvent changeEvent);
}
......@@ -23,7 +23,7 @@ public class ConfigService {
}
/**
* Get the config instance with default namespace
* Get the config instance with default namespace.
* @return config instance
*/
public static Config getConfig() {
......@@ -31,7 +31,7 @@ public class ConfigService {
}
/**
* Get the config instance for the namespace
* Get the config instance for the namespace.
* @param namespace the namespace of the config
* @return config instance
*/
......@@ -42,16 +42,16 @@ public class ConfigService {
private static ConfigManager getManager() {
try {
return s_instance.m_container.lookup(ConfigManager.class);
} catch (ComponentLookupException e) {
throw new IllegalStateException("Unable to load ConfigManager!", e);
} catch (ComponentLookupException ex) {
throw new IllegalStateException("Unable to load ConfigManager!", ex);
}
}
private static ConfigRegistry getRegistry() {
try {
return s_instance.m_container.lookup(ConfigRegistry.class);
} catch (ComponentLookupException e) {
throw new IllegalStateException("Unable to load ConfigRegistry!", e);
} catch (ComponentLookupException ex) {
throw new IllegalStateException("Unable to load ConfigRegistry!", ex);
}
}
......@@ -59,6 +59,11 @@ public class ConfigService {
setConfig(ConfigConsts.NAMESPACE_APPLICATION, config);
}
/**
* Manually set the config for the namespace specified, use with caution!
* @param namespace the namespace
* @param config the config instance
*/
public static void setConfig(String namespace, final Config config) {
getRegistry().register(namespace, new ConfigFactory() {
@Override
......@@ -72,6 +77,11 @@ public class ConfigService {
setConfigFactory(ConfigConsts.NAMESPACE_APPLICATION, factory);
}
/**
* Manually set the config factory for the namespace specified, use with caution!
* @param namespace the namespace
* @param factory the factory instance
*/
public static void setConfigFactory(String namespace, ConfigFactory factory) {
getRegistry().register(namespace, factory);
}
......
package com.ctrip.apollo.env;
import com.google.common.base.Strings;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.framework.foundation.Foundation;
public class Apollo {
private static Env s_env;
private static String s_appId;
private static String s_cluster;
static {
setEnv(Foundation.server().getEnvType());
s_appId = Foundation.app().getAppId();
s_cluster = System.getProperty("apollo.cluster");
}
public static String getAppId() {
return s_appId;
}
public static Env getEnv() {
return s_env;
}
public static String getCluster() {
return s_cluster;
}
private static void setEnv(String envName) {
if (Strings.isNullOrEmpty(envName)) {
return;
}
switch (envName.toUpperCase()) {
case "LPT":
s_env = Env.LPT;
break;
case "FAT":
s_env = Env.FAT;
break;
case "UAT":
s_env = Env.UAT;
break;
case "PRO":
s_env = Env.PRO;
break;
case "DEV":
s_env = Env.DEV;
break;
case "LOCAL":
s_env = Env.LOCAL;
break;
default:
//do nothing
break;
}
}
}
package com.ctrip.apollo.env;
import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.Apollo.Env;
import com.ctrip.apollo.constants.Constants;
import com.ctrip.apollo.core.MetaDomainConsts;
import com.ctrip.apollo.core.utils.ResourceUtils;
import com.ctrip.apollo.core.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
public class ClientEnvironment {
private static final Logger logger = LoggerFactory.getLogger(ClientEnvironment.class);
private static final String DEFAULT_FILE = "apollo.properties";
private AtomicReference<Env> env = new AtomicReference<Env>();
private static ClientEnvironment instance = new ClientEnvironment();
private ClientEnvironment() {
}
public static ClientEnvironment getInstance() {
return instance;
}
public Env getEnv() {
if (env.get() == null) {
Env resultEnv = Apollo.getEnv();
Properties apolloProperties = null;
apolloProperties = ResourceUtils.readConfigFile(DEFAULT_FILE, null);
if (apolloProperties != null) {
String strEnv = apolloProperties.getProperty(Constants.ENV);
if (!StringUtils.isBlank(strEnv)) {
resultEnv = Env.valueOf(strEnv.trim().toUpperCase());
}
}
env.compareAndSet(null, resultEnv);
}
if (env.get() == null) {
throw new IllegalArgumentException("Apollo env is not set");
}
return env.get();
}
public String getMetaServerDomainName() {
return MetaDomainConsts.getDomain(getEnv());
}
}
......@@ -35,8 +35,8 @@ public abstract class AbstractConfig implements Config {
for (ConfigChangeListener listener : m_listeners) {
try {
listener.onChange(changeEvent);
} catch (Throwable t) {
logger.error("Failed to invoke config change listener {}", listener.getClass(), t);
} catch (Throwable ex) {
logger.error("Failed to invoke config change listener {}", listener.getClass(), ex);
}
}
}
......@@ -61,7 +61,8 @@ public abstract class AbstractConfig implements Config {
List<ConfigChange> changes = Lists.newArrayList();
for (String newKey : newKeys) {
changes.add(new ConfigChange(newKey, null, current.getProperty(newKey), PropertyChangeType.NEW));
changes
.add(new ConfigChange(newKey, null, current.getProperty(newKey), PropertyChangeType.NEW));
}
for (String removedKey : removedKeys) {
......
......@@ -15,6 +15,16 @@ public abstract class AbstractConfigRepository implements ConfigRepository {
private static final Logger logger = LoggerFactory.getLogger(AbstractConfigRepository.class);
private List<RepositoryChangeListener> m_listeners = Lists.newCopyOnWriteArrayList();
protected void trySync() {
try {
sync();
} catch (Throwable ex) {
logger.error("Sync config failed with repository {}", this.getClass(), ex);
}
}
protected abstract void sync();
@Override
public void addChangeListener(RepositoryChangeListener listener) {
if (!m_listeners.contains(listener)) {
......@@ -31,8 +41,8 @@ public abstract class AbstractConfigRepository implements ConfigRepository {
for (RepositoryChangeListener listener : m_listeners) {
try {
listener.onRepositoryChange(namespace, newProperties);
} catch (Throwable t) {
logger.error("Failed to invoke repository change listener {}", listener.getClass(), t);
} catch (Throwable ex) {
logger.error("Failed to invoke repository change listener {}", listener.getClass(), ex);
}
}
}
......
......@@ -6,5 +6,10 @@ import com.ctrip.apollo.Config;
* @author Jason Song(song_s@ctrip.com)
*/
public interface ConfigManager {
public Config getConfig(String namespace);
/**
* Get the config instance for the namespace specified.
* @param namespace the namespace
* @return the config instance for the namespace
*/
public Config getConfig(String namespace);
}
......@@ -7,18 +7,26 @@ import java.util.Properties;
*/
public interface ConfigRepository {
/**
* Get the config from this repository
* @return
* Get the config from this repository.
* @return config
*/
public Properties getConfig();
/**
* Set the fallback repo for this repository
* @param fallbackConfigRepository
* Set the fallback repo for this repository.
* @param fallbackConfigRepository the fallback repo
*/
public void setFallback(ConfigRepository fallbackConfigRepository);
/**
* Add change listener.
* @param listener the listener to observe the changes
*/
public void addChangeListener(RepositoryChangeListener listener);
/**
* Remove change listener.
* @param listener the listener to remove
*/
public void removeChangeListener(RepositoryChangeListener listener);
}
package com.ctrip.apollo.internals;
import com.ctrip.apollo.core.dto.ServiceDTO;
import com.ctrip.apollo.env.ClientEnvironment;
import com.ctrip.apollo.util.ConfigUtil;
import com.ctrip.apollo.util.http.HttpRequest;
import com.ctrip.apollo.util.http.HttpResponse;
import com.ctrip.apollo.util.http.HttpUtil;
......@@ -19,11 +19,16 @@ public class ConfigServiceLocator {
private static final Logger logger = LoggerFactory.getLogger(ConfigServiceLocator.class);
@Inject
private HttpUtil m_httpUtil;
@Inject
private ConfigUtil m_configUtil;
private List<ServiceDTO> serviceCaches = new ArrayList<>();
/**
* Get the config service info from remote meta server.
* @return the services dto
*/
public List<ServiceDTO> getConfigServices() {
ClientEnvironment env = ClientEnvironment.getInstance();
String domainName = env.getMetaServerDomainName();
String domainName = m_configUtil.getMetaServerDomainName();
String url = domainName + "/services/config";
HttpRequest request = new HttpRequest(url);
......@@ -37,9 +42,9 @@ public class ConfigServiceLocator {
serviceCaches.add(service);
}
}
} catch (Throwable t) {
logger.error("Get config services failed", t);
throw new RuntimeException("Get config services failed", t);
} catch (Throwable ex) {
logger.error("Get config services failed", ex);
throw new RuntimeException("Get config services failed", ex);
}
return serviceCaches;
......
......@@ -17,6 +17,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author Jason Song(song_s@ctrip.com)
......@@ -25,25 +26,31 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis
private static final Logger logger = LoggerFactory.getLogger(DefaultConfig.class);
private final String m_namespace;
private Properties m_resourceProperties;
private Properties m_configProperties;
private AtomicReference<Properties> m_configProperties;
private ConfigRepository m_configRepository;
/**
* Constructor.
*
* @param namespace the namespace of this config instance
* @param configRepository the config repository for this config instance
*/
public DefaultConfig(String namespace, ConfigRepository configRepository) {
m_namespace = namespace;
m_resourceProperties = loadFromResource(m_namespace);
m_configRepository = configRepository;
m_configProperties = new AtomicReference<>();
initialize();
}
private void initialize() {
try {
m_configProperties = m_configRepository.getConfig();
m_configProperties.set(m_configRepository.getConfig());
m_configRepository.addChangeListener(this);
} catch (Throwable ex) {
String message = String.format("Init Apollo Local Config failed - namespace: %s",
m_namespace);
logger.error(message, ex);
throw new RuntimeException(message, ex);
}
}
......@@ -53,8 +60,8 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis
String value = System.getProperty(key);
// step 2: check local cached properties file
if (value == null) {
value = m_configProperties.getProperty(key);
if (value == null && m_configProperties.get() != null) {
value = m_configProperties.get().getProperty(key);
}
/**
......@@ -67,10 +74,12 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis
}
// step 4: check properties file from classpath
if (value == null) {
if (m_resourceProperties != null) {
value = (String) m_resourceProperties.get(key);
}
if (value == null && m_resourceProperties != null) {
value = (String) m_resourceProperties.get(key);
}
if (value == null && m_configProperties.get() == null) {
logger.error("Config initialization failed, always return default value!");
}
return value == null ? defaultValue : value;
......@@ -78,7 +87,7 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis
@Override
public synchronized void onRepositoryChange(String namespace, Properties newProperties) {
if (newProperties.equals(m_configProperties)) {
if (newProperties.equals(m_configProperties.get())) {
return;
}
Properties newConfigProperties = new Properties();
......@@ -91,8 +100,7 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis
private Map<String, ConfigChange> updateAndCalcConfigChanges(Properties newConfigProperties) {
List<ConfigChange> configChanges =
calcPropertyChanges(m_configProperties, newConfigProperties);
// List<ConfigChange> actualChanges = Lists.newArrayListWithCapacity(configChanges.size());
calcPropertyChanges(m_configProperties.get(), newConfigProperties);
ImmutableMap.Builder<String, ConfigChange> actualChanges =
new ImmutableMap.Builder<>();
......@@ -105,7 +113,7 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis
}
//2. update m_configProperties
m_configProperties = newConfigProperties;
m_configProperties.set(newConfigProperties);
//3. use getProperty to update configChange's new value and calc the final changes
for (ConfigChange change : configChanges) {
......@@ -134,6 +142,9 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis
}
actualChanges.put(change.getPropertyName(), change);
break;
default:
//do nothing
break;
}
}
return actualChanges.build();
......@@ -149,13 +160,13 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis
try {
properties.load(in);
} catch (IOException e) {
logger.error("Load resource config for namespace {} failed", namespace, e);
Cat.logError(e);
} catch (IOException ex) {
logger.error("Load resource config for namespace {} failed", namespace, ex);
Cat.logError(ex);
} finally {
try {
in.close();
} catch (IOException e) {
} catch (IOException ex) {
// ignore
}
}
......
......@@ -32,21 +32,27 @@ public class LocalFileConfigRepository extends AbstractConfigRepository
private volatile Properties m_fileProperties;
private volatile ConfigRepository m_fallback;
/**
* Constructor.
* @param baseDir the base dir for this local file config repository
* @param namespace the namespace
*/
public LocalFileConfigRepository(File baseDir, String namespace) {
m_baseDir = baseDir;
m_namespace = namespace;
m_container = ContainerLoader.getDefaultContainer();
try {
m_configUtil = m_container.lookup(ConfigUtil.class);
} catch (ComponentLookupException e) {
throw new IllegalStateException("Unable to load component!", e);
} catch (ComponentLookupException ex) {
throw new IllegalStateException("Unable to load component!", ex);
}
this.trySync();
}
@Override
public Properties getConfig() {
if (m_fileProperties == null) {
initLocalConfig();
sync();
}
Properties result = new Properties();
result.putAll(m_fileProperties);
......@@ -60,47 +66,59 @@ public class LocalFileConfigRepository extends AbstractConfigRepository
m_fallback.removeChangeListener(this);
}
m_fallback = fallbackConfigRepository;
trySyncFromFallback();
fallbackConfigRepository.addChangeListener(this);
}
@Override
public synchronized void onRepositoryChange(String namespace, Properties newProperties) {
public void onRepositoryChange(String namespace, Properties newProperties) {
if (newProperties.equals(m_fileProperties)) {
return;
}
Properties newFileProperties = new Properties();
newFileProperties.putAll(newProperties);
this.m_fileProperties = newFileProperties;
persistLocalCacheFile(m_baseDir, m_namespace);
updateFileProperties(newFileProperties);
this.fireRepositoryChange(namespace, newProperties);
}
void initLocalConfig() {
@Override
protected void sync() {
try {
m_fileProperties = this.loadFromLocalCacheFile(m_baseDir, m_namespace);
} catch (Throwable ex) {
logger.error("Load config from local config cache file failed", ex);
ex.printStackTrace();
//ignore
}
//TODO check whether properties is expired or should we return after it's synced with fallback?
if (m_fileProperties != null) {
return;
}
//sync with fallback immediately
trySyncFromFallback();
if (m_fallback == null) {
if (m_fileProperties == null) {
throw new RuntimeException(
"Load config from local config cache failed and there is no fallback repository!");
"Load config from local config failed!");
}
}
private void trySyncFromFallback() {
if (m_fallback == null) {
return;
}
try {
m_fileProperties = m_fallback.getConfig();
persistLocalCacheFile(m_baseDir, m_namespace);
Properties properties = m_fallback.getConfig();
updateFileProperties(properties);
} catch (Throwable ex) {
String message =
String.format("Load config from fallback repository %s failed", m_fallback.getClass());
logger.error(message, ex);
throw new RuntimeException(message, ex);
String.format("Sync config from fallback repository %s failed", m_fallback.getClass());
logger.warn(message, ex);
}
}
private synchronized void updateFileProperties(Properties newProperties) {
if (newProperties.equals(m_fileProperties)) {
return;
}
this.m_fileProperties = newProperties;
persistLocalCacheFile(m_baseDir, m_namespace);
}
private Properties loadFromLocalCacheFile(File baseDir, String namespace) throws IOException {
......@@ -117,16 +135,16 @@ public class LocalFileConfigRepository extends AbstractConfigRepository
properties = new Properties();
properties.load(in);
} catch (IOException e) {
logger.error("Loading config from local cache file {} failed", file.getAbsolutePath(), e);
Cat.logError(e);
throw e;
} catch (IOException ex) {
logger.error("Loading config from local cache file {} failed", file.getAbsolutePath(), ex);
Cat.logError(ex);
throw ex;
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
} catch (IOException ex) {
// ignore
}
}
......@@ -158,7 +176,7 @@ public class LocalFileConfigRepository extends AbstractConfigRepository
if (out != null) {
try {
out.close();
} catch (IOException e) {
} catch (IOException ex) {
//ignore
}
}
......
......@@ -26,7 +26,7 @@ import java.util.concurrent.atomic.AtomicReference;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class RemoteConfigRepository extends AbstractConfigRepository{
public class RemoteConfigRepository extends AbstractConfigRepository {
private static final Logger logger = LoggerFactory.getLogger(RemoteConfigRepository.class);
private PlexusContainer m_container;
private final ConfigServiceLocator m_serviceLocator;
......@@ -36,6 +36,10 @@ public class RemoteConfigRepository extends AbstractConfigRepository{
private final String m_namespace;
private final ScheduledExecutorService m_executorService;
/**
* Constructor.
* @param namespace the namespace
*/
public RemoteConfigRepository(String namespace) {
m_namespace = namespace;
m_configCache = new AtomicReference<>();
......@@ -44,18 +48,19 @@ public class RemoteConfigRepository extends AbstractConfigRepository{
m_configUtil = m_container.lookup(ConfigUtil.class);
m_httpUtil = m_container.lookup(HttpUtil.class);
m_serviceLocator = m_container.lookup(ConfigServiceLocator.class);
} catch (ComponentLookupException e) {
throw new IllegalStateException("Unable to load component!", e);
} catch (ComponentLookupException ex) {
throw new IllegalStateException("Unable to load component!", ex);
}
this.m_executorService = Executors.newScheduledThreadPool(1,
ApolloThreadFactory.create("RemoteConfigRepository", true));
ApolloThreadFactory.create("RemoteConfigRepository", true));
this.trySync();
this.schedulePeriodicRefresh();
}
@Override
public Properties getConfig() {
if (m_configCache.get() == null) {
this.loadRemoteConfig();
this.sync();
}
return transformApolloConfigToProperties(m_configCache.get());
}
......@@ -72,17 +77,14 @@ public class RemoteConfigRepository extends AbstractConfigRepository{
new Runnable() {
@Override
public void run() {
try {
loadRemoteConfig();
} catch (Throwable ex) {
logger.error("Refreshing config failed", ex);
}
trySync();
}
}, m_configUtil.getRefreshInterval(), m_configUtil.getRefreshInterval(),
m_configUtil.getRefreshTimeUnit());
}
synchronized void loadRemoteConfig() {
@Override
protected synchronized void sync() {
ApolloConfig previous = m_configCache.get();
ApolloConfig current = loadApolloConfig();
......@@ -108,7 +110,9 @@ public class RemoteConfigRepository extends AbstractConfigRepository{
private ApolloConfig loadApolloConfig() {
String appId = m_configUtil.getAppId();
String cluster = m_configUtil.getCluster();
String url = assembleUrl(getConfigServiceUrl(), appId, cluster, m_namespace, m_configCache.get());
String
url =
assembleUrl(getConfigServiceUrl(), appId, cluster, m_namespace, m_configCache.get());
logger.info("Loading config from {}", url);
HttpRequest request = new HttpRequest(url);
......@@ -123,12 +127,12 @@ public class RemoteConfigRepository extends AbstractConfigRepository{
logger.info("Loaded config: {}", response.getBody());
return response.getBody();
} catch (Throwable t) {
} catch (Throwable ex) {
String message =
String.format("Load Apollo Config failed - appId: %s, cluster: %s, namespace: %s", appId,
cluster, m_namespace);
logger.error(message, t);
throw new RuntimeException(message, t);
logger.error(message, ex);
throw new RuntimeException(message, ex);
}
}
......
......@@ -6,5 +6,10 @@ import java.util.Properties;
* @author Jason Song(song_s@ctrip.com)
*/
public interface RepositoryChangeListener {
/**
* Invoked when config repository changes.
* @param namespace the namespace of this repository change
* @param newProperties the properties after change
*/
public void onRepositoryChange(String namespace, Properties newProperties);
}
......@@ -22,6 +22,11 @@ public class SimpleConfig extends AbstractConfig implements RepositoryChangeList
private final ConfigRepository m_configRepository;
private volatile Properties m_configProperties;
/**
* Constructor.
* @param namespace the namespace for this config instance
* @param configRepository the config repository for this config instance
*/
public SimpleConfig(String namespace, ConfigRepository configRepository) {
m_namespace = namespace;
m_configRepository = configRepository;
......@@ -36,12 +41,15 @@ public class SimpleConfig extends AbstractConfig implements RepositoryChangeList
String message = String.format("Init Apollo Simple Config failed - namespace: %s",
m_namespace);
logger.error(message, ex);
throw new RuntimeException(message, ex);
}
}
@Override
public String getProperty(String key, String defaultValue) {
if (m_configProperties == null) {
logger.error("Config initialization failed, always return default value!");
return defaultValue;
}
return this.m_configProperties.getProperty(key, defaultValue);
}
......
......@@ -6,6 +6,7 @@ import com.google.common.base.MoreObjects;
import com.ctrip.apollo.enums.PropertyChangeType;
/**
* Holds the information for a config change.
* @author Jason Song(song_s@ctrip.com)
*/
public class ConfigChange {
......@@ -14,6 +15,13 @@ public class ConfigChange {
private String newValue;
private PropertyChangeType changeType;
/**
* Constructor.
* @param propertyName the key whose value is changed
* @param oldValue the value before change
* @param newValue the value after change
* @param changeType the change type
*/
public ConfigChange(String propertyName, String oldValue, String newValue,
PropertyChangeType changeType) {
this.propertyName = propertyName;
......
......@@ -4,34 +4,53 @@ import java.util.Map;
import java.util.Set;
/**
* A change event when a namespace's config is changed.
* @author Jason Song(song_s@ctrip.com)
*/
public class ConfigChangeEvent {
private final String m_namespace;
private final Map<String, ConfigChange> changes;
public ConfigChangeEvent(String m_namespace,
/**
* Constructor.
* @param namespace the namespace of this change
* @param changes the actual changes
*/
public ConfigChangeEvent(String namespace,
Map<String, ConfigChange> changes) {
this.m_namespace = m_namespace;
this.m_namespace = namespace;
this.changes = changes;
}
/**
* Get the keys changed.
* @return the list of the keys
*/
public Set<String> changedKeys() {
return changes.keySet();
}
/**
* Get a specific change instance for the key specified.
* @param key the changed key
* @return the change instance
*/
public ConfigChange getChange(String key) {
return changes.get(key);
}
/**
* Please note that the returned Map is immutable
* Get the changes. Please note that the returned Map is immutable.
* @return changes
*/
public Map<String, ConfigChange> getChanges() {
return changes;
}
/**
* Get the namespace of this change event.
* @return the namespace
*/
public String getNamespace() {
return m_namespace;
}
......
......@@ -6,5 +6,11 @@ import com.ctrip.apollo.Config;
* @author Jason Song(song_s@ctrip.com)
*/
public interface ConfigFactory {
public Config create(String namespace);
/**
* Create the config instance for the namespace.
*
* @param namespace the namespace
* @return the newly created config instance
*/
public Config create(String namespace);
}
......@@ -4,5 +4,11 @@ package com.ctrip.apollo.spi;
* @author Jason Song(song_s@ctrip.com)
*/
public interface ConfigFactoryManager {
/**
* Get the config factory for the namespace.
*
* @param namespace the namespace
* @return the config factory for this namespace
*/
public ConfigFactory getFactory(String namespace);
}
package com.ctrip.apollo.spi;
/**
* The manually config registry, use with caution!
*
* @author Jason Song(song_s@ctrip.com)
*/
public interface ConfigRegistry {
/**
* Register the config factory for the namespace specified.
*
* @param namespace the namespace
* @param factory the factory for this namespace
*/
public void register(String namespace, ConfigFactory factory);
/**
* Get the registered config factory for the namespace.
*
* @param namespace the namespace
* @return the factory registered for this namespace
*/
public ConfigFactory getFactory(String namespace);
}
......@@ -6,6 +6,8 @@ import com.ctrip.apollo.internals.DefaultConfig;
import com.ctrip.apollo.internals.LocalFileConfigRepository;
import com.ctrip.apollo.internals.RemoteConfigRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.unidal.lookup.annotation.Named;
import java.io.File;
......@@ -15,13 +17,19 @@ import java.io.File;
*/
@Named(type = ConfigFactory.class, value = "default")
public class DefaultConfigFactory implements ConfigFactory {
private static final Logger logger = LoggerFactory.getLogger(DefaultConfigFactory.class);
private static final String CONFIG_DIR = "/config-cache";
private File m_baseDir;
/**
* Create the config factory.
*/
public DefaultConfigFactory() {
m_baseDir = new File(ClassLoaderUtil.getClassPath() + CONFIG_DIR);
if (!m_baseDir.exists()) {
m_baseDir.mkdir();
if(!m_baseDir.mkdir()){
logger.error("Creating local cache dir failed.");
}
}
}
......@@ -33,10 +41,10 @@ public class DefaultConfigFactory implements ConfigFactory {
}
LocalFileConfigRepository createLocalConfigRepository(String namespace) {
LocalFileConfigRepository localFileConfigLoader =
LocalFileConfigRepository localFileConfigRepository =
new LocalFileConfigRepository(m_baseDir, namespace);
localFileConfigLoader.setFallback(createRemoteConfigRepository(namespace));
return localFileConfigLoader;
localFileConfigRepository.setFallback(createRemoteConfigRepository(namespace));
return localFileConfigRepository;
}
RemoteConfigRepository createRemoteConfigRepository(String namespace) {
......
......@@ -38,7 +38,7 @@ public class DefaultConfigFactoryManager extends ContainerHolder implements Conf
// step 3: check declared config factory
try {
factory = lookup(ConfigFactory.class, namespace);
} catch (LookupException e) {
} catch (LookupException ex) {
// ignore it
}
......
package com.ctrip.apollo.util;
import com.google.common.base.Preconditions;
import com.ctrip.apollo.core.ConfigConsts;
import com.ctrip.apollo.core.MetaDomainConsts;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.env.Apollo;
import org.unidal.lookup.annotation.Named;
import java.util.concurrent.TimeUnit;
......@@ -15,14 +22,42 @@ public class ConfigUtil {
private static final int connectTimeout = 5000; //5 seconds
private static final int readTimeout = 10000; //10 seconds
/**
* Get the app id for the current application.
* @return the app id
* @throws IllegalStateException if app id is not set
*/
public String getAppId() {
// TODO return the actual app id
return "100003171";
String appId = Apollo.getAppId();
Preconditions.checkState(appId != null, "app.id is not set");
return appId;
}
/**
* Get the cluster name for the current application.
* @return the cluster name, or "default" if not specified
*/
public String getCluster() {
// TODO return the actual cluster
return "default";
String cluster = Apollo.getCluster();
if (cluster == null) {
cluster = ConfigConsts.CLUSTER_NAME_DEFAULT;
}
return cluster;
}
/**
* Get the current environment.
* @return the env
* @throws IllegalStateException if env is set
*/
public Env getApolloEnv() {
Env env = Apollo.getEnv();
Preconditions.checkState(env != null, "env is not set");
return env;
}
public String getMetaServerDomainName() {
return MetaDomainConsts.getDomain(getApolloEnv());
}
public int getConnectTimeout() {
......
......@@ -8,6 +8,10 @@ public class HttpRequest {
private int m_connectTimeout;
private int m_readTimeout;
/**
* Create the request for the url.
* @param url the url
*/
public HttpRequest(String url) {
this.m_url = url;
m_connectTimeout = -1;
......
......@@ -28,8 +28,9 @@ public class HttpUtil {
}
/**
* Do get operation for the http request
* Do get operation for the http request.
*
* @return the http response
* @throws RuntimeException if any error happened or response code is neither 200 nor 304
*/
public <T> HttpResponse<T> doGet(HttpRequest httpRequest, Class<T> responseType) {
......@@ -67,7 +68,8 @@ public class HttpUtil {
}
throw new RuntimeException(
String.format("Get operation failed for %s, status code - %d", httpRequest.getUrl(), statusCode));
String.format("Get operation failed for %s, status code - %d", httpRequest.getUrl(),
statusCode));
} catch (Throwable ex) {
throw new RuntimeException("Could not complete get operation", ex);
......@@ -75,7 +77,7 @@ public class HttpUtil {
if (is != null) {
try {
is.close();
} catch (IOException e) {
} catch (IOException ex) {
//ignore
}
}
......
......@@ -37,6 +37,9 @@
<requirement>
<role>com.ctrip.apollo.util.http.HttpUtil</role>
</requirement>
<requirement>
<role>com.ctrip.apollo.util.ConfigUtil</role>
</requirement>
</requirements>
</component>
<component>
......
package com.ctrip.apollo;
import com.ctrip.apollo.integration.ConfigIntegrationTest;
import com.ctrip.apollo.internals.DefaultConfigManagerTest;
import com.ctrip.apollo.internals.DefaultConfigTest;
import com.ctrip.apollo.internals.LocalFileConfigRepositoryTest;
......@@ -18,7 +19,8 @@ import org.junit.runners.Suite.SuiteClasses;
@SuiteClasses({
ConfigServiceTest.class, DefaultConfigRegistryTest.class, DefaultConfigFactoryManagerTest.class,
DefaultConfigManagerTest.class, DefaultConfigTest.class, LocalFileConfigRepositoryTest.class,
RemoteConfigRepositoryTest.class, SimpleConfigTest.class, DefaultConfigFactoryTest.class
RemoteConfigRepositoryTest.class, SimpleConfigTest.class, DefaultConfigFactoryTest.class,
ConfigIntegrationTest.class
})
public class AllTests {
......
......@@ -18,6 +18,7 @@ public class ConfigServiceTest extends ComponentTestCase {
@Before
public void setUp() throws Exception {
super.setUp();
//as ConfigService is singleton, so we must manually clear its container
ConfigService.setContainer(getContainer());
}
......
package com.ctrip.apollo.integration;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.google.gson.Gson;
import com.ctrip.apollo.ConfigService;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.core.dto.ServiceDTO;
import com.ctrip.apollo.core.utils.ClassLoaderUtil;
import com.ctrip.apollo.util.ConfigUtil;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.unidal.lookup.ComponentTestCase;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class BaseIntegrationTest extends ComponentTestCase {
private static final int PORT = 5678;
private static final String metaServiceUrl = "http://localhost:" + PORT;
private static final String someAppName = "someAppName";
private static final String someInstanceId = "someInstanceId";
private static final String configServiceURL = "http://localhost:" + PORT;
protected static String someAppId;
protected static String someClusterName;
protected static int refreshInterval;
protected static TimeUnit refreshTimeUnit;
private Server server;
protected Gson gson = new Gson();
@BeforeClass
public static void beforeClass() throws Exception {
File apolloEnvPropertiesFile = new File(ClassLoaderUtil.getClassPath(), "apollo-env.properties");
Files.write("local.meta=" + metaServiceUrl, apolloEnvPropertiesFile, Charsets.UTF_8);
apolloEnvPropertiesFile.deleteOnExit();
}
@Before
public void setUp() throws Exception {
super.setUp();
someAppId = "1003171";
someClusterName = "someClusterName";
refreshInterval = 5;
refreshTimeUnit = TimeUnit.MINUTES;
//as ConfigService is singleton, so we must manually clear its container
ConfigService.setContainer(getContainer());
defineComponent(ConfigUtil.class, MockConfigUtil.class);
}
/**
* init and start a jetty server, remember to call server.stop when the task is finished
* @param handlers
* @throws Exception
*/
protected Server startServerWithHandlers(ContextHandler... handlers) throws Exception {
server = new Server(PORT);
ContextHandlerCollection contexts = new ContextHandlerCollection();
contexts.setHandlers(handlers);
contexts.addHandler(mockMetaServerHandler());
server.setHandler(contexts);
server.start();
return server;
}
@After
public void tearDown() throws Exception {
super.tearDown();
if (server != null && server.isStarted()) {
server.stop();
}
}
private ContextHandler mockMetaServerHandler() {
final ServiceDTO someServiceDTO = new ServiceDTO();
someServiceDTO.setAppName(someAppName);
someServiceDTO.setInstanceId(someInstanceId);
someServiceDTO.setHomepageUrl(configServiceURL);
ContextHandler context = new ContextHandler("/services/config");
context.setHandler(new AbstractHandler() {
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(gson.toJson(Lists.newArrayList(someServiceDTO)));
baseRequest.setHandled(true);
}
});
return context;
}
protected void setRefreshInterval(int refreshInterval) {
BaseIntegrationTest.refreshInterval = refreshInterval;
}
protected void setRefreshTimeUnit(TimeUnit refreshTimeUnit) {
BaseIntegrationTest.refreshTimeUnit = refreshTimeUnit;
}
public static class MockConfigUtil extends ConfigUtil {
@Override
public String getAppId() {
return someAppId;
}
@Override
public String getCluster() {
return someClusterName;
}
@Override
public int getRefreshInterval() {
return refreshInterval;
}
@Override
public TimeUnit getRefreshTimeUnit() {
return refreshTimeUnit;
}
@Override
public Env getApolloEnv() {
return Env.LOCAL;
}
}
}
package com.ctrip.apollo.integration;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ctrip.apollo.Config;
import com.ctrip.apollo.ConfigChangeListener;
import com.ctrip.apollo.ConfigService;
import com.ctrip.apollo.core.ConfigConsts;
import com.ctrip.apollo.core.dto.ApolloConfig;
import com.ctrip.apollo.core.utils.ClassLoaderUtil;
import com.ctrip.apollo.model.ConfigChangeEvent;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class ConfigIntegrationTest extends BaseIntegrationTest {
private long someReleaseId;
private File configDir;
private String someNamespace;
@Before
public void setUp() throws Exception {
super.setUp();
someNamespace = ConfigConsts.NAMESPACE_APPLICATION;
someReleaseId = 1;
configDir = new File(ClassLoaderUtil.getClassPath() + "config-cache");
configDir.mkdirs();
}
@Override
@After
public void tearDown() throws Exception {
super.tearDown();
recursiveDelete(configDir);
}
private void recursiveDelete(File file) {
if (!file.exists()) {
return;
}
if (file.isDirectory()) {
for (File f : file.listFiles()) {
recursiveDelete(f);
}
}
file.delete();
}
@Test
public void testGetConfigWithNoLocalFileButWithRemoteConfig() throws Exception {
String someKey = "someKey";
String someValue = "someValue";
String someNonExistedKey = "someNonExistedKey";
String someDefaultValue = "someDefaultValue";
ApolloConfig apolloConfig = assembleApolloConfig(ImmutableMap.of(someKey, someValue));
ContextHandler handler = mockConfigServerHandler(HttpServletResponse.SC_OK, apolloConfig);
startServerWithHandlers(handler);
Config config = ConfigService.getConfig();
assertEquals(someValue, config.getProperty(someKey, null));
assertEquals(someDefaultValue, config.getProperty(someNonExistedKey, someDefaultValue));
}
@Test
public void testGetConfigWithLocalFileAndWithRemoteConfig() throws Exception {
String someKey = "someKey";
String someValue = "someValue";
String anotherValue = "anotherValue";
Properties properties = new Properties();
properties.put(someKey, someValue);
createLocalCachePropertyFile(properties);
ApolloConfig apolloConfig = assembleApolloConfig(ImmutableMap.of(someKey, anotherValue));
ContextHandler handler = mockConfigServerHandler(HttpServletResponse.SC_OK, apolloConfig);
startServerWithHandlers(handler);
Config config = ConfigService.getConfig();
assertEquals(anotherValue, config.getProperty(someKey, null));
}
@Test
public void testGetConfigWithNoLocalFileAndRemoteConfigError() throws Exception {
ContextHandler
handler =
mockConfigServerHandler(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null);
startServerWithHandlers(handler);
Config config = ConfigService.getConfig();
String someKey = "someKey";
String someDefaultValue = "defaultValue" + Math.random();
assertEquals(someDefaultValue, config.getProperty(someKey, someDefaultValue));
}
@Test
public void testGetConfigWithLocalFileAndRemoteConfigError() throws Exception {
String someKey = "someKey";
String someValue = "someValue";
Properties properties = new Properties();
properties.put(someKey, someValue);
createLocalCachePropertyFile(properties);
ContextHandler
handler =
mockConfigServerHandler(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null);
startServerWithHandlers(handler);
Config config = ConfigService.getConfig();
assertEquals(someValue, config.getProperty(someKey, null));
}
@Test
public void testRefreshConfig() throws Exception {
final String someKey = "someKey";
final String someValue = "someValue";
final String anotherValue = "anotherValue";
int someRefreshInterval = 500;
TimeUnit someRefreshTimeUnit = TimeUnit.MILLISECONDS;
setRefreshInterval(someRefreshInterval);
setRefreshTimeUnit(someRefreshTimeUnit);
Map<String, String> configurations = Maps.newHashMap();
configurations.put(someKey, someValue);
ApolloConfig apolloConfig = assembleApolloConfig(configurations);
ContextHandler handler = mockConfigServerHandler(HttpServletResponse.SC_OK, apolloConfig);
startServerWithHandlers(handler);
Config config = ConfigService.getConfig();
final List<ConfigChangeEvent> changeEvents = Lists.newArrayList();
config.addChangeListener(new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent changeEvent) {
assertEquals(1, changeEvent.getChanges().size());
assertEquals(someValue, changeEvent.getChange(someKey).getOldValue());
assertEquals(anotherValue, changeEvent.getChange(someKey).getNewValue());
//if there is any assertion failed above, this line won't be executed
changeEvents.add(changeEvent);
}
});
apolloConfig.getConfigurations().put(someKey, anotherValue);
Thread.sleep(someRefreshTimeUnit.toMillis(someRefreshInterval * 2));
assertThat(
"Change event's size should equal to one or there must be some assertion failed in change listener",
1, equalTo(changeEvents.size()));
assertEquals(anotherValue, config.getProperty(someKey, null));
}
private ContextHandler mockConfigServerHandler(final int statusCode, final ApolloConfig result) {
ContextHandler context = new ContextHandler("/config/*");
context.setHandler(new AbstractHandler() {
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(statusCode);
response.getWriter().println(gson.toJson(result));
baseRequest.setHandled(true);
}
});
return context;
}
private ApolloConfig assembleApolloConfig(Map<String, String> configurations) {
ApolloConfig apolloConfig =
new ApolloConfig(someAppId, someClusterName, someNamespace, someReleaseId);
apolloConfig.setConfigurations(configurations);
return apolloConfig;
}
private File createLocalCachePropertyFile(Properties properties) throws IOException {
File file = new File(configDir, assembleLocalCacheFileName());
properties.store(new FileOutputStream(file), "Persisted by ConfigIntegrationTest");
return file;
}
private String assembleLocalCacheFileName() {
return String.format("%s-%s-%s.properties", someAppId,
someClusterName, someNamespace);
}
}
......@@ -19,7 +19,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.only;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
......@@ -34,6 +33,8 @@ public class LocalFileConfigRepositoryTest extends ComponentTestCase {
private Properties someProperties;
private static String someAppId = "someApp";
private static String someCluster = "someCluster";
private String defaultKey;
private String defaultValue;
@Before
public void setUp() throws Exception {
......@@ -43,7 +44,9 @@ public class LocalFileConfigRepositoryTest extends ComponentTestCase {
someNamespace = "someName";
someProperties = new Properties();
someProperties.setProperty("defaultKey", "defaultValue");
defaultKey = "defaultKey";
defaultValue = "defaultValue";
someProperties.setProperty(defaultKey, defaultValue);
fallbackRepo = mock(ConfigRepository.class);
when(fallbackRepo.getConfig()).thenReturn(someProperties);
......@@ -52,6 +55,7 @@ public class LocalFileConfigRepositoryTest extends ComponentTestCase {
@After
public void tearDown() throws Exception {
super.tearDown();
recursiveDelete(someBaseDir);
}
......@@ -73,10 +77,6 @@ public class LocalFileConfigRepositoryTest extends ComponentTestCase {
someCluster, someNamespace);
}
@Test
public void testLoadConfig() throws Exception {
}
@Test
public void testLoadConfigWithLocalFile() throws Exception {
......@@ -94,6 +94,24 @@ public class LocalFileConfigRepositoryTest extends ComponentTestCase {
}
@Test
public void testLoadConfigWithLocalFileAndFallbackRepo() throws Exception {
File file = new File(someBaseDir, assembleLocalCacheFileName());
String someValue = "someValue";
Files.write(defaultKey + "=" + someValue, file, Charsets.UTF_8);
LocalFileConfigRepository localRepo = new LocalFileConfigRepository(someBaseDir, someNamespace);
//when fallback is set, it will try to sync from it
localRepo.setFallback(fallbackRepo);
Properties properties = localRepo.getConfig();
assertEquals(defaultValue, properties.getProperty(defaultKey));
}
@Test
public void testLoadConfigWithNoLocalFile() throws Exception {
LocalFileConfigRepository
......
......@@ -92,11 +92,16 @@ public class RemoteConfigRepositoryTest extends ComponentTestCase {
remoteConfigRepository.addChangeListener(someListener);
final ArgumentCaptor<Properties> captor = ArgumentCaptor.forClass(Properties.class);
remoteConfigRepository.loadRemoteConfig();
Map<String, String> newConfigurations = ImmutableMap.of("someKey", "anotherValue");
ApolloConfig newApolloConfig = assembleApolloConfig(newConfigurations);
when(someResponse.getBody()).thenReturn(newApolloConfig);
remoteConfigRepository.sync();
verify(someListener, times(1)).onRepositoryChange(eq(someNamespace), captor.capture());
assertEquals(configurations, captor.getValue());
assertEquals(newConfigurations, captor.getValue());
}
private ApolloConfig assembleApolloConfig(Map<String, String> configurations) {
......
......@@ -2,6 +2,7 @@ package com.ctrip.apollo.internals;
import com.google.common.collect.ImmutableMap;
import com.ctrip.apollo.Config;
import com.ctrip.apollo.ConfigChangeListener;
import com.ctrip.apollo.enums.PropertyChangeType;
import com.ctrip.apollo.model.ConfigChange;
......@@ -50,11 +51,15 @@ public class SimpleConfigTest {
assertEquals(someValue, config.getProperty(someKey, null));
}
@Test(expected = RuntimeException.class)
@Test
public void testLoadConfigFromConfigRepositoryError() throws Exception {
when(configRepository.getConfig()).thenThrow(Throwable.class);
new SimpleConfig(someNamespace, configRepository);
Config config = new SimpleConfig(someNamespace, configRepository);
String someKey = "someKey";
String anyValue = "anyValue" + Math.random();
assertEquals(anyValue, config.getProperty(someKey, anyValue));
}
@Test
......
package com.ctrip.apollo;
public class Apollo {
public static final String VERSION = "java-0.0.1-SNAPSHOT";
private static Env m_env;
public enum Env {
LOCAL, DEV, FWS, FAT, UAT, LPT, PRO, TOOLS
}
public static void initialize(Env env) {
m_env = env;
}
public static Env getEnv() {
return m_env;
}
}
......@@ -4,7 +4,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import com.ctrip.apollo.Apollo.Env;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.core.utils.ResourceUtils;
public class MetaDomainConsts {
......
package com.ctrip.apollo.core.dto;
import com.ctrip.apollo.Apollo.Env;
import com.ctrip.apollo.core.enums.Env;
import java.util.LinkedList;
import java.util.List;
......
package com.ctrip.apollo.constants;
package com.ctrip.apollo.core.enums;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class Constants {
public static final String ENV = "env";
public enum Env {
LOCAL, DEV, FWS, FAT, UAT, LPT, PRO, TOOLS
}
package com.ctrip.apollo.core;
import com.ctrip.apollo.core.enums.Env;
import org.junit.Assert;
import org.junit.Test;
import com.ctrip.apollo.Apollo.Env;
public class MetaDomainTest {
@Test
......
......@@ -36,7 +36,11 @@ public class ApolloConfigDemo implements ConfigChangeListener {
"Apollo Config Demo. Please input key to get the value.");
while (true) {
System.out.print("> ");
String input = (new BufferedReader(new InputStreamReader(System.in)).readLine()).trim();
String input = new BufferedReader(new InputStreamReader(System.in)).readLine();
if (input == null) {
continue;
}
input = input.trim();
if (input.equalsIgnoreCase("quit")) {
System.exit(0);
}
......
......@@ -8,7 +8,7 @@ import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.ctrip.apollo.Apollo.Env;
import com.ctrip.apollo.core.enums.Env;
@Component
public class PortalSettings {
......
package com.ctrip.apollo.portal.api;
import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.core.exception.ServiceException;
import com.ctrip.apollo.portal.service.ServiceLocator;
......@@ -14,7 +14,7 @@ public class API {
protected RestTemplate restTemplate = new RestTemplate();
public String getAdminServiceHost(Apollo.Env env) {
public String getAdminServiceHost(Env env) {
// 本地测试用
// return "http://localhost:8090";
try {
......
package com.ctrip.apollo.portal.api;
import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.core.dto.AppDTO;
import com.ctrip.apollo.core.dto.ClusterDTO;
import com.ctrip.apollo.core.dto.ItemChangeSets;
......@@ -36,11 +36,11 @@ public class AdminServiceAPI {
public static String APP_API = "/apps";
public List<AppDTO> getApps(Apollo.Env env) {
public List<AppDTO> getApps(Env env) {
return Arrays.asList(restTemplate.getForObject(getAdminServiceHost(env) + APP_API, AppDTO[].class));
}
public AppDTO save(Apollo.Env env, AppDTO app) {
public AppDTO save(Env env, AppDTO app) {
return restTemplate.postForEntity(getAdminServiceHost(env) + APP_API, app, AppDTO.class).getBody();
}
}
......@@ -49,7 +49,7 @@ public class AdminServiceAPI {
@Service
public static class NamespaceAPI extends API {
public List<NamespaceDTO> findGroupsByAppAndCluster(String appId, Apollo.Env env,
public List<NamespaceDTO> findGroupsByAppAndCluster(String appId, Env env,
String clusterName) {
if (StringUtils.isContainEmpty(appId, clusterName)) {
return null;
......@@ -60,7 +60,7 @@ public class AdminServiceAPI {
NamespaceDTO[].class));
}
public NamespaceDTO loadNamespace(String appId, Apollo.Env env,
public NamespaceDTO loadNamespace(String appId, Env env,
String clusterName, String namespaceName) {
if (StringUtils.isContainEmpty(appId, clusterName, namespaceName)) {
return null;
......@@ -74,7 +74,7 @@ public class AdminServiceAPI {
@Service
public static class ItemAPI extends API {
public List<ItemDTO> findItems(String appId, Apollo.Env env, String clusterName, String namespace) {
public List<ItemDTO> findItems(String appId, Env env, String clusterName, String namespace) {
if (StringUtils.isContainEmpty(appId, clusterName, namespace)) {
return Collections.EMPTY_LIST;
}
......@@ -85,7 +85,7 @@ public class AdminServiceAPI {
ItemDTO[].class));
}
public void updateItems(String appId, Apollo.Env env, String clusterName, String namespace,
public void updateItems(String appId, Env env, String clusterName, String namespace,
ItemChangeSets changeSets) {
if (StringUtils.isContainEmpty(appId, clusterName, namespace)) {
return;
......@@ -101,7 +101,7 @@ public class AdminServiceAPI {
@Service
public static class ClusterAPI extends API {
public List<ClusterDTO> findClustersByApp(String appId, Apollo.Env env) {
public List<ClusterDTO> findClustersByApp(String appId, Env env) {
if (StringUtils.isContainEmpty(appId)) {
return null;
}
......@@ -115,7 +115,7 @@ public class AdminServiceAPI {
@Service
public static class ReleaseAPI extends API {
public ReleaseDTO loadLatestRelease(String appId, Apollo.Env env, String clusterName, String namespace) {
public ReleaseDTO loadLatestRelease(String appId, Env env, String clusterName, String namespace) {
if (StringUtils.isContainEmpty(appId, clusterName, namespace)) {
return null;
}
......@@ -132,7 +132,7 @@ public class AdminServiceAPI {
}
}
public ReleaseDTO release(String appId, Apollo.Env env, String clusterName, String namespace, String releaseBy,
public ReleaseDTO release(String appId, Env env, String clusterName, String namespace, String releaseBy,
String comment) {
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
parameters.add("name", releaseBy);
......
package com.ctrip.apollo.portal.controller;
import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.core.dto.ReleaseDTO;
import com.ctrip.apollo.core.exception.BadRequestException;
import com.ctrip.apollo.core.utils.StringUtils;
......@@ -34,7 +34,7 @@ public class ConfigController {
throw new BadRequestException("app id and cluster name can not be empty");
}
return configService.findNampspaces(appId, Apollo.Env.valueOf(env), clusterName);
return configService.findNampspaces(appId, Env.valueOf(env), clusterName);
}
@RequestMapping(value = "/apps/{appId}/env/{env}/clusters/{clusterName}/namespaces/{namespaceName}/items", method = RequestMethod.PUT, consumes = {
......
package com.ctrip.apollo.portal.entity;
import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.core.dto.ClusterDTO;
import java.util.LinkedList;
......@@ -19,18 +19,18 @@ public class ClusterNavTree {
}
public static class Node{
private Apollo.Env env;
private Env env;
private List<ClusterDTO> clusters;
public Node(Apollo.Env env){
public Node(Env env){
this.env = env;
}
public Apollo.Env getEnv() {
public Env getEnv() {
return env;
}
public void setEnv(Apollo.Env env) {
public void setEnv(Env env) {
this.env = env;
}
......
package com.ctrip.apollo.portal.entity.form;
import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.core.utils.StringUtils;
public class NamespaceReleaseModel implements FormModel{
......@@ -26,8 +26,8 @@ public class NamespaceReleaseModel implements FormModel{
this.appId = appId;
}
public Apollo.Env getEnv() {
return Apollo.Env.valueOf(env);
public Env getEnv() {
return Env.valueOf(env);
}
public void setEnv(String env) {
......
package com.ctrip.apollo.portal.entity.form;
import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.core.utils.StringUtils;
public class NamespaceTextModel implements FormModel{
......@@ -26,8 +26,8 @@ public class NamespaceTextModel implements FormModel{
this.appId = appId;
}
public Apollo.Env getEnv() {
return Apollo.Env.valueOf(env);
public Env getEnv() {
return Env.valueOf(env);
}
public void setEnv(String env) {
......
......@@ -8,7 +8,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ctrip.apollo.Apollo.Env;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.core.dto.AppDTO;
import com.ctrip.apollo.portal.PortalSettings;
import com.ctrip.apollo.portal.api.AdminServiceAPI;
......
package com.ctrip.apollo.portal.service;
import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.core.dto.ClusterDTO;
import com.ctrip.apollo.portal.api.AdminServiceAPI;
......@@ -15,7 +15,7 @@ public class ClusterService {
@Autowired
private AdminServiceAPI.ClusterAPI clusterAPI;
public List<ClusterDTO> findClusters(Apollo.Env env, String appId){
public List<ClusterDTO> findClusters(Env env, String appId){
return clusterAPI.findClustersByApp(appId, env);
}
......
......@@ -6,7 +6,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.core.dto.ItemChangeSets;
import com.ctrip.apollo.core.dto.ItemDTO;
import com.ctrip.apollo.core.dto.NamespaceDTO;
......@@ -52,7 +52,7 @@ public class ConfigService {
* @param clusterName
* @return
*/
public List<NamespaceVO> findNampspaces(String appId, Apollo.Env env, String clusterName) {
public List<NamespaceVO> findNampspaces(String appId, Env env, String clusterName) {
List<NamespaceDTO> namespaces = groupAPI.findGroupsByAppAndCluster(appId, env, clusterName);
if (namespaces == null || namespaces.size() == 0) {
......@@ -76,7 +76,7 @@ public class ConfigService {
return namespaceVOs;
}
private NamespaceVO parseNamespace(String appId, Apollo.Env env, String clusterName, NamespaceDTO namespace) {
private NamespaceVO parseNamespace(String appId, Env env, String clusterName, NamespaceDTO namespace) {
NamespaceVO namespaceVO = new NamespaceVO();
namespaceVO.setNamespace(namespace);
......@@ -137,7 +137,7 @@ public class ConfigService {
*/
public void updateConfigItemByText(NamespaceTextModel model) {
String appId = model.getAppId();
Apollo.Env env = model.getEnv();
Env env = model.getEnv();
String clusterName = model.getClusterName();
String namespaceName = model.getNamespaceName();
long namespaceId = model.getNamespaceId();
......
......@@ -9,7 +9,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.ctrip.apollo.Apollo.Env;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.core.MetaDomainConsts;
import com.ctrip.apollo.core.dto.ServiceDTO;
import com.ctrip.apollo.core.exception.ServiceException;
......
......@@ -81,6 +81,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<jetty.version>9.3.8.v20160314</jetty.version>
<github.global.server>github</github.global.server>
<github.global.oauth2Token>${env.GITHUB_OAUTH_TOKEN}</github.global.oauth2Token>
</properties>
......@@ -135,6 +136,26 @@
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${jetty.version}</version>
</dependency>
<!--for test -->
<dependency>
<groupId>com.h2database</groupId>
......
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