Commit 7a1b0d1c authored by Jason Song's avatar Jason Song

client rewrite using unidal

parent c5774ce5
...@@ -7,7 +7,7 @@ import org.hibernate.annotations.SQLDelete; ...@@ -7,7 +7,7 @@ import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where; import org.hibernate.annotations.Where;
@Entity @Entity
@SQLDelete(sql = "Update App set isDeleted = 'false' where id = ?") @SQLDelete(sql = "Update App set isDeleted = 1 where id = ?")
@Where(clause = "isDeleted = 0") @Where(clause = "isDeleted = 0")
public class App extends BaseEntity { public class App extends BaseEntity {
......
...@@ -7,7 +7,7 @@ import org.hibernate.annotations.SQLDelete; ...@@ -7,7 +7,7 @@ import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where; import org.hibernate.annotations.Where;
@Entity @Entity
@SQLDelete(sql = "Update AppNamespace set isDeleted = 'false' where id = ?") @SQLDelete(sql = "Update AppNamespace set isDeleted = 1 where id = ?")
@Where(clause = "isDeleted = 0") @Where(clause = "isDeleted = 0")
public class AppNamespace extends BaseEntity{ public class AppNamespace extends BaseEntity{
......
...@@ -10,7 +10,7 @@ import org.hibernate.annotations.Where; ...@@ -10,7 +10,7 @@ import org.hibernate.annotations.Where;
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@Entity @Entity
@SQLDelete(sql = "Update Cluster set isDeleted = 'false' where id = ?") @SQLDelete(sql = "Update Cluster set isDeleted = 1 where id = ?")
@Where(clause = "isDeleted = 0") @Where(clause = "isDeleted = 0")
public class Cluster extends BaseEntity { public class Cluster extends BaseEntity {
......
...@@ -7,7 +7,7 @@ import org.hibernate.annotations.SQLDelete; ...@@ -7,7 +7,7 @@ import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where; import org.hibernate.annotations.Where;
@Entity @Entity
@SQLDelete(sql = "Update Item set isDeleted = 'false' where id = ?") @SQLDelete(sql = "Update Item set isDeleted = 1 where id = ?")
@Where(clause = "isDeleted = 0") @Where(clause = "isDeleted = 0")
public class Item extends BaseEntity { public class Item extends BaseEntity {
......
...@@ -7,7 +7,7 @@ import org.hibernate.annotations.SQLDelete; ...@@ -7,7 +7,7 @@ import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where; import org.hibernate.annotations.Where;
@Entity @Entity
@SQLDelete(sql = "Update Namespace set isDeleted = 'false' where id = ?") @SQLDelete(sql = "Update Namespace set isDeleted = 1 where id = ?")
@Where(clause = "isDeleted = 0") @Where(clause = "isDeleted = 0")
public class Namespace extends BaseEntity { public class Namespace extends BaseEntity {
......
...@@ -7,7 +7,7 @@ import org.hibernate.annotations.SQLDelete; ...@@ -7,7 +7,7 @@ import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where; import org.hibernate.annotations.Where;
@Entity @Entity
@SQLDelete(sql = "Update Privilege set isDeleted = 'false' where id = ?") @SQLDelete(sql = "Update Privilege set isDeleted = 1 where id = ?")
@Where(clause = "isDeleted = 0") @Where(clause = "isDeleted = 0")
public class Privilege extends BaseEntity { public class Privilege extends BaseEntity {
......
package com.ctrip.apollo.biz.entity; package com.ctrip.apollo.biz.entity;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Lob; import javax.persistence.Lob;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@Entity @Entity
@SQLDelete(sql = "Update Release set isDeleted = 'false' where id = ?") @SQLDelete(sql = "Update Release set isDeleted = 1 where id = ?")
@Where(clause = "isDeleted = 0") @Where(clause = "isDeleted = 0")
public class Release extends BaseEntity { public class Release extends BaseEntity {
......
...@@ -4,5 +4,12 @@ package com.ctrip.apollo; ...@@ -4,5 +4,12 @@ package com.ctrip.apollo;
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public interface Config { public interface Config {
/**
* 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
*/
public String getProperty(String key, String defaultValue); public String getProperty(String key, String defaultValue);
} }
...@@ -10,6 +10,7 @@ import org.codehaus.plexus.component.repository.exception.ComponentLookupExcepti ...@@ -10,6 +10,7 @@ import org.codehaus.plexus.component.repository.exception.ComponentLookupExcepti
import org.unidal.lookup.ContainerLoader; import org.unidal.lookup.ContainerLoader;
/** /**
* Entry point for client config use
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class ConfigService { public class ConfigService {
...@@ -21,10 +22,19 @@ public class ConfigService { ...@@ -21,10 +22,19 @@ public class ConfigService {
m_container = ContainerLoader.getDefaultContainer(); m_container = ContainerLoader.getDefaultContainer();
} }
/**
* Get the config instance with default namespace
* @return config instance
*/
public static Config getConfig() { public static Config getConfig() {
return getConfig(ConfigConsts.NAMESPACE_APPLICATION); return getConfig(ConfigConsts.NAMESPACE_APPLICATION);
} }
/**
* Get the config instance for the namespace
* @param namespace the namespace of the config
* @return config instance
*/
public static Config getConfig(String namespace) { public static Config getConfig(String namespace) {
return getManager().getConfig(namespace); return getManager().getConfig(namespace);
} }
......
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;
}
}
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;
}
}
package com.ctrip.apollo.client.loader;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.core.dto.ApolloConfig;
/**
* @author Jason Song(songs_ctrip.com)
*/
public interface ConfigLoader {
/**
* Load apollo config
* @param apolloRegistry
* @param previous
* @return
*/
ApolloConfig loadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous);
}
package com.ctrip.apollo.client.loader.impl;
import com.ctrip.apollo.client.loader.ConfigLoader;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.core.dto.ApolloConfig;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public abstract class AbstractConfigLoader implements ConfigLoader {
@Override
public ApolloConfig loadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
try {
return doLoadApolloConfig(apolloRegistry, previous);
} catch (Throwable ex) {
throw new RuntimeException(
String.format("Load Apollo Config failed - %s", apolloRegistry.toString()), ex);
}
}
protected abstract ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry,
ApolloConfig previous);
}
package com.ctrip.apollo.client.loader.impl;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.core.dto.ApolloConfig;
/**
* Load config from local backup file
*
* @author Jason Song(song_s@ctrip.com)
*/
public class LocalFileConfigLoader extends AbstractConfigLoader {
@Override
public ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
throw new IllegalStateException("Not implemented yet!");
}
}
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());
}
}
package com.ctrip.apollo.client.model;
import com.google.common.base.MoreObjects;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class ApolloRegistry {
private String appId;
private String clusterName;
private String namespace;
public ApolloRegistry(String appId, String clusterName, String namespace) {
this.appId = appId;
this.clusterName = clusterName;
this.namespace = namespace;
}
public String getAppId() {
return appId;
}
public String getClusterName() {
return clusterName;
}
public String getNamespace() {
return namespace;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.omitNullValues()
.add("appId", appId)
.add("clusterName", clusterName)
.add("namespace", namespace)
.toString();
}
}
package com.ctrip.apollo.client.model;
import com.google.common.collect.Lists;
import org.springframework.core.env.CompositePropertySource;
import java.util.List;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class PropertySourceReloadResult {
private CompositePropertySource propertySource;
private List<PropertyChange> changes;
public PropertySourceReloadResult(CompositePropertySource propertySource) {
this.propertySource = propertySource;
changes = Lists.newArrayList();
}
public PropertySourceReloadResult(CompositePropertySource propertySource,
List<PropertyChange> changes) {
this.propertySource = propertySource;
this.changes = changes;
}
public CompositePropertySource getPropertySource() {
return propertySource;
}
public void setPropertySource(CompositePropertySource propertySource) {
this.propertySource = propertySource;
}
public List<PropertyChange> getChanges() {
return changes;
}
public void setChanges(List<PropertyChange> changes) {
this.changes = changes;
}
public boolean hasChanges() {
return !changes.isEmpty();
}
}
package com.ctrip.apollo.client.constants; package com.ctrip.apollo.constants;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
......
package com.ctrip.apollo.client.env; package com.ctrip.apollo.env;
import com.ctrip.apollo.Apollo; import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.Apollo.Env; import com.ctrip.apollo.Apollo.Env;
import com.ctrip.apollo.client.constants.Constants; import com.ctrip.apollo.constants.Constants;
import com.ctrip.apollo.core.MetaDomainConsts; import com.ctrip.apollo.core.MetaDomainConsts;
import com.ctrip.apollo.core.utils.ResourceUtils; import com.ctrip.apollo.core.utils.ResourceUtils;
import com.ctrip.apollo.core.utils.StringUtils; import com.ctrip.apollo.core.utils.StringUtils;
......
package com.ctrip.apollo.internals;
import java.util.Properties;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface ConfigLoader {
public Properties loadConfig();
}
package com.ctrip.apollo.client.loader; package com.ctrip.apollo.internals;
import com.ctrip.apollo.client.env.ClientEnvironment;
import com.ctrip.apollo.core.dto.ServiceDTO; import com.ctrip.apollo.core.dto.ServiceDTO;
import com.ctrip.apollo.env.ClientEnvironment;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
......
package com.ctrip.apollo.internals; package com.ctrip.apollo.internals;
import com.ctrip.apollo.Config; import com.ctrip.apollo.Config;
import com.ctrip.apollo.core.utils.ClassLoaderUtil;
import com.dianping.cat.Cat; import com.dianping.cat.Cat;
import java.io.File; import java.io.File;
...@@ -12,22 +13,53 @@ import java.util.Properties; ...@@ -12,22 +13,53 @@ import java.util.Properties;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class DefaultConfig implements Config { public class DefaultConfig implements Config, ConfigLoader {
private final String m_namespace; private final String m_namespace;
private final File m_baseDir;
private Properties m_resourceProperties; private Properties m_resourceProperties;
private Properties m_fileProperties; private Properties m_fileProperties;
private ConfigLoader m_fallbackLoader;
public DefaultConfig(File baseDir, String namespace) { public DefaultConfig(File baseDir, String namespace, ConfigLoader fallbackLoader) {
m_namespace = namespace; m_namespace = namespace;
m_resourceProperties = loadFromResource(namespace); m_baseDir = baseDir;
m_fileProperties = loadFromFile(baseDir, namespace); m_resourceProperties = loadFromResource(m_namespace);
m_fallbackLoader = fallbackLoader;
this.initLocalConfig();
}
@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) {
value = m_fileProperties.getProperty(key);
}
/**
* step 3: check env variable, i.e. PATH=...
* normally system environment variables are in UPPERCASE, however there might be exceptions.
* so the caller should provide the key in the right case
*/
if (value == null) {
value = System.getenv(key);
}
// step 4: check properties file from classpath
if (value == null) {
if (m_resourceProperties != null) {
value = (String) m_resourceProperties.get(key);
}
}
return value == null ? defaultValue : value;
} }
private Properties loadFromResource(String namespace) { private Properties loadFromResource(String namespace) {
String name = String.format("/META-INF/config/%s.properties", namespace); String name = String.format("META-INF/config/%s.properties", namespace);
InputStream in = getClass().getResourceAsStream(name); InputStream in = ClassLoaderUtil.getLoader().getResourceAsStream(name);
Properties properties = null; Properties properties = null;
if (in != null) { if (in != null) {
...@@ -81,30 +113,28 @@ public class DefaultConfig implements Config { ...@@ -81,30 +113,28 @@ public class DefaultConfig implements Config {
return properties; return properties;
} }
@Override void initLocalConfig() {
public String getProperty(String key, String defaultValue) { m_fileProperties = this.loadFromFile(m_baseDir, m_namespace);
// step 1: check system properties, i.e. -Dkey=value //TODO check if local file is expired
String value = System.getProperty(key); if (m_fileProperties == null && m_fallbackLoader != null) {
m_fileProperties = m_fallbackLoader.loadConfig();
// step 2: check local cached properties file
if (value == null) {
if (m_fileProperties != null) {
value = (String) m_fileProperties.get(key);
}
} }
if (m_fileProperties == null) {
// step 3: check env variable, i.e. PATH=... throw new RuntimeException(
if (value == null) { String.format("Init Apollo Local Config failed - namespace: %s",
value = System.getenv(key); // TODO fix naming issues m_namespace));
} }
//TODO persist file
}
// step 4: check properties file from classpath @Override
if (value == null) { public Properties loadConfig() {
if (m_resourceProperties != null) { Properties result = new Properties();
value = (String) m_resourceProperties.get(key); result.putAll(m_fileProperties);
} return result;
} }
return value; public ConfigLoader getFallbackLoader() {
return m_fallbackLoader;
} }
} }
...@@ -14,7 +14,7 @@ import java.util.Map; ...@@ -14,7 +14,7 @@ import java.util.Map;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@Named(type = ConfigManager.class) @Named(type = ConfigManager.class, value = "default")
public class DefaultConfigManager implements ConfigManager { public class DefaultConfigManager implements ConfigManager {
@Inject @Inject
private ConfigFactoryManager m_factoryManager; private ConfigFactoryManager m_factoryManager;
......
package com.ctrip.apollo.client.loader.impl; package com.ctrip.apollo.internals;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.ctrip.apollo.client.loader.ConfigServiceLocator; import com.ctrip.apollo.Config;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig; import com.ctrip.apollo.core.dto.ApolloConfig;
import com.ctrip.apollo.core.dto.ServiceDTO; import com.ctrip.apollo.core.dto.ServiceDTO;
import com.ctrip.apollo.util.ConfigUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -19,28 +18,72 @@ import org.springframework.web.client.RestTemplate; ...@@ -19,28 +18,72 @@ import org.springframework.web.client.RestTemplate;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties;
/** /**
* Load config from remote config server
*
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class RemoteConfigLoader extends AbstractConfigLoader { public class RemoteConfig implements Config, ConfigLoader {
private static final Logger logger = LoggerFactory.getLogger(RemoteConfigLoader.class); private static final Logger logger = LoggerFactory.getLogger(RemoteConfig.class);
private final RestTemplate restTemplate; private RestTemplate m_restTemplate;
private final ConfigServiceLocator serviceLocator; private ConfigServiceLocator m_serviceLocator;
private String m_namespace;
public RemoteConfigLoader(RestTemplate restTemplate,
ConfigServiceLocator locator) { private Properties m_remoteProperties;
this.restTemplate = restTemplate;
this.serviceLocator = locator; public RemoteConfig(RestTemplate restTemplate,
ConfigServiceLocator locator, String namespace) {
this.m_restTemplate = restTemplate;
this.m_serviceLocator = locator;
this.m_namespace = namespace;
this.initialize();
} }
ApolloConfig getRemoteConfig(RestTemplate restTemplate, String uri, @Override
ApolloRegistry apolloRegistry, ApolloConfig previousConfig) { public String getProperty(String key, String defaultValue) {
String appId = apolloRegistry.getAppId(); return this.m_remoteProperties.getProperty(key, defaultValue);
String cluster = apolloRegistry.getClusterName(); }
String namespace = apolloRegistry.getNamespace();
@Override
public Properties loadConfig() {
if (m_remoteProperties == null) {
return null;
}
Properties result = new Properties();
result.putAll(m_remoteProperties);
return result;
}
private void initialize() {
ApolloConfig apolloConfig = this.loadApolloConfig();
m_remoteProperties = new Properties();
m_remoteProperties.putAll(apolloConfig.getConfigurations());
}
private ApolloConfig loadApolloConfig() {
String appId = ConfigUtil.getInstance().getAppId();
String cluster = ConfigUtil.getInstance().getCluster();
try {
ApolloConfig result =
this.getRemoteConfig(m_restTemplate, getConfigServiceUrl(),
appId, cluster,
m_namespace,
null);
if (result == null) {
return null;
}
logger.info("Loaded config: {}", result);
return result;
} catch (Throwable ex) {
throw new RuntimeException(
String.format("Load Apollo Config failed - appId: %s, cluster: %s, namespace: %s", appId,
cluster, m_namespace), ex);
}
}
private ApolloConfig getRemoteConfig(RestTemplate restTemplate, String uri,
String appId, String cluster, String namespace,
ApolloConfig previousConfig) {
logger.info("Loading config from {}, appId={}, cluster={}, namespace={}", uri, appId, cluster, logger.info("Loading config from {}, appId={}, cluster={}, namespace={}", uri, appId, cluster,
namespace); namespace);
...@@ -85,19 +128,12 @@ public class RemoteConfigLoader extends AbstractConfigLoader { ...@@ -85,19 +128,12 @@ public class RemoteConfigLoader extends AbstractConfigLoader {
return result; return result;
} }
@Override
protected ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
ApolloConfig result = this.getRemoteConfig(restTemplate,
getConfigServiceUrl(), apolloRegistry, previous);
//When remote server return 304, we need to return the previous result
return result == null ? previous : result;
}
private String getConfigServiceUrl() { private String getConfigServiceUrl() {
List<ServiceDTO> services = serviceLocator.getConfigServices(); List<ServiceDTO> services = m_serviceLocator.getConfigServices();
if (services.size() == 0) { if (services.size() == 0) {
throw new RuntimeException("No available config service"); throw new RuntimeException("No available config service");
} }
return services.get(0).getHomepageUrl(); return services.get(0).getHomepageUrl();
} }
} }
package com.ctrip.apollo.model;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.Properties;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class ConfigRefreshResult {
private String m_namespace;
private Properties m_properties;
private List<PropertyChange> m_changes;
public ConfigRefreshResult(String namespace, Properties properties) {
this.m_namespace = namespace;
this.m_properties = properties;
m_changes = Lists.newArrayList();
}
public Properties getProperties() {
return m_properties;
}
public String getNamespace() {
return m_namespace;
}
public List<PropertyChange> getChanges() {
return m_changes;
}
public void setChanges(List<PropertyChange> changes) {
this.m_changes = changes;
}
public boolean hasChanges() {
return !m_changes.isEmpty();
}
}
package com.ctrip.apollo.client.model; package com.ctrip.apollo.model;
import com.ctrip.apollo.client.enums.PropertyChangeType;
import com.ctrip.apollo.enums.PropertyChangeType;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
......
package com.ctrip.apollo.spi; package com.ctrip.apollo.spi;
import com.ctrip.apollo.Config; import com.ctrip.apollo.Config;
import com.ctrip.apollo.core.utils.ClassLoaderUtil;
import com.ctrip.apollo.internals.ConfigServiceLocator;
import com.ctrip.apollo.internals.DefaultConfig; import com.ctrip.apollo.internals.DefaultConfig;
import com.ctrip.apollo.internals.RemoteConfig;
import org.springframework.web.client.RestTemplate;
import org.unidal.lookup.annotation.Named; import org.unidal.lookup.annotation.Named;
import java.io.File; import java.io.File;
...@@ -12,10 +16,21 @@ import java.io.File; ...@@ -12,10 +16,21 @@ import java.io.File;
*/ */
@Named(type = ConfigFactory.class, value = "default") @Named(type = ConfigFactory.class, value = "default")
public class DefaultConfigFactory implements ConfigFactory { public class DefaultConfigFactory implements ConfigFactory {
private static final String CONFIG_DIR = "/config-cache";
private File m_baseDir; private File m_baseDir;
public DefaultConfigFactory() {
m_baseDir = new File(ClassLoaderUtil.getClassPath() + CONFIG_DIR);
}
@Override @Override
public Config create(String namespace) { public Config create(String namespace) {
return new DefaultConfig(m_baseDir, namespace); RemoteConfig remoteConfig = this.createRemoteConfig(namespace);
DefaultConfig defaultConfig = new DefaultConfig(m_baseDir, namespace, remoteConfig);
return defaultConfig;
}
public RemoteConfig createRemoteConfig(String namespace) {
return new RemoteConfig(new RestTemplate(), new ConfigServiceLocator(), namespace);
} }
} }
...@@ -12,7 +12,7 @@ import java.util.Map; ...@@ -12,7 +12,7 @@ import java.util.Map;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@Named(type = ConfigFactoryManager.class) @Named(type = ConfigFactoryManager.class, value = "default")
public class DefaultConfigFactoryManager extends ContainerHolder implements ConfigFactoryManager { public class DefaultConfigFactoryManager extends ContainerHolder implements ConfigFactoryManager {
@Inject @Inject
private ConfigRegistry m_registry; private ConfigRegistry m_registry;
...@@ -40,7 +40,6 @@ public class DefaultConfigFactoryManager extends ContainerHolder implements Conf ...@@ -40,7 +40,6 @@ public class DefaultConfigFactoryManager extends ContainerHolder implements Conf
factory = lookup(ConfigFactory.class, namespace); factory = lookup(ConfigFactory.class, namespace);
} catch (LookupException e) { } catch (LookupException e) {
// ignore it // ignore it
e.printStackTrace();
} }
// step 4: check default config factory // step 4: check default config factory
......
...@@ -11,7 +11,7 @@ import java.util.Map; ...@@ -11,7 +11,7 @@ import java.util.Map;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@Named(type = ConfigRegistry.class) @Named(type = ConfigRegistry.class, value = "default")
public class DefaultConfigRegistry implements ConfigRegistry, LogEnabled { public class DefaultConfigRegistry implements ConfigRegistry, LogEnabled {
private Map<String, ConfigFactory> m_instances = Maps.newConcurrentMap(); private Map<String, ConfigFactory> m_instances = Maps.newConcurrentMap();
......
package com.ctrip.apollo.client.util; package com.ctrip.apollo.util;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -13,8 +13,6 @@ import java.util.concurrent.TimeUnit; ...@@ -13,8 +13,6 @@ import java.util.concurrent.TimeUnit;
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class ConfigUtil { public class ConfigUtil {
private static final Logger logger = LoggerFactory.getLogger(ConfigUtil.class);
public static final String APOLLO_PROPERTY = "apollo.properties";
//TODO read from config? //TODO read from config?
private static final int refreshInterval = 5; private static final int refreshInterval = 5;
private static final TimeUnit refreshIntervalTimeUnit = TimeUnit.MINUTES; private static final TimeUnit refreshIntervalTimeUnit = TimeUnit.MINUTES;
...@@ -30,7 +28,7 @@ public class ConfigUtil { ...@@ -30,7 +28,7 @@ public class ConfigUtil {
public String getAppId() { public String getAppId() {
// TODO return the actual app id // TODO return the actual app id
return "101"; return "100003171";
} }
public String getCluster() { public String getCluster() {
......
com.ctrip.apollo.client.manager.impl.RemoteConfigManagerManager
com.ctrip.apollo.client.manager.impl.LocalFileConfigManagerManager
package com.ctrip.apollo;
import com.ctrip.apollo.internals.DefaultConfigManagerTest;
import com.ctrip.apollo.internals.DefaultConfigTest;
import com.ctrip.apollo.internals.RemoteConfigTest;
import com.ctrip.apollo.spi.DefaultConfigFactoryManagerTest;
import com.ctrip.apollo.spi.DefaultConfigFactoryTest;
import com.ctrip.apollo.spi.DefaultConfigRegistryTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
ConfigServiceTest.class, DefaultConfigRegistryTest.class, DefaultConfigFactoryManagerTest.class,
DefaultConfigFactoryTest.class, DefaultConfigManagerTest.class, DefaultConfigTest.class,
RemoteConfigTest.class
})
public class AllTests {
}
package com.ctrip.apollo; package com.ctrip.apollo;
import com.ctrip.apollo.core.ConfigConsts;
import com.ctrip.apollo.spi.ConfigFactory; import com.ctrip.apollo.spi.ConfigFactory;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.unidal.lookup.ComponentTestCase; import org.unidal.lookup.ComponentTestCase;
import static org.junit.Assert.assertEquals;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
...@@ -21,22 +23,37 @@ public class ConfigServiceTest extends ComponentTestCase { ...@@ -21,22 +23,37 @@ public class ConfigServiceTest extends ComponentTestCase {
@Test @Test
public void testHackConfig() { public void testHackConfig() {
ConfigService.setConfig(new MockConfig("hack")); String someNamespace = "hack";
String someKey = "first";
ConfigService.setConfig(new MockConfig(someNamespace));
Config config = ConfigService.getConfig();
assertEquals(someNamespace + ":" + someKey, config.getProperty(someKey, null));
assertEquals(null, config.getProperty("unknown", null));
}
@Test
public void testHackConfigFactory() throws Exception {
String someKey = "someKey";
ConfigService.setConfigFactory(new MockConfigFactory());
Config config = ConfigService.getConfig(); Config config = ConfigService.getConfig();
Assert.assertEquals("hack:first", config.getProperty("first", null)); assertEquals(ConfigConsts.NAMESPACE_APPLICATION + ":" + someKey,
Assert.assertEquals(null, config.getProperty("unknown", null)); config.getProperty(someKey, null));
} }
@Test @Test
public void testMockConfigFactory() throws Exception { public void testMockConfigFactory() throws Exception {
defineComponent(ConfigFactory.class, "mock", MockConfigFactory.class); String someNamespace = "mock";
String someKey = "someKey";
defineComponent(ConfigFactory.class, someNamespace, MockConfigFactory.class);
Config config = ConfigService.getConfig("mock"); Config config = ConfigService.getConfig(someNamespace);
Assert.assertEquals("mock:first", config.getProperty("first", null)); assertEquals(someNamespace + ":" + someKey, config.getProperty(someKey, null));
Assert.assertEquals(null, config.getProperty("unknown", null)); assertEquals(null, config.getProperty("unknown", null));
} }
private static class MockConfig implements Config { private static class MockConfig implements Config {
......
package com.ctrip.apollo.client;
import com.ctrip.apollo.client.loader.impl.RemoteConfigLoaderTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
RemoteConfigLoaderTest.class
})
public class AllTests {
}
package com.ctrip.apollo.internals;
import com.ctrip.apollo.Config;
import com.ctrip.apollo.spi.ConfigFactory;
import com.ctrip.apollo.spi.ConfigFactoryManager;
import org.junit.Before;
import org.junit.Test;
import org.unidal.lookup.ComponentTestCase;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class DefaultConfigManagerTest extends ComponentTestCase {
private DefaultConfigManager defaultConfigManager;
@Before
public void setUp() throws Exception {
super.setUp();
defineComponent(ConfigFactoryManager.class, MockConfigManager.class);
defaultConfigManager = (DefaultConfigManager) lookup(ConfigManager.class, "default");
}
@Test
public void testGetConfig() throws Exception {
String someNamespace = "someName";
String anotherNamespace = "anotherName";
String someKey = "someKey";
Config config = defaultConfigManager.getConfig(someNamespace);
Config anotherConfig = defaultConfigManager.getConfig(anotherNamespace);
assertEquals(someNamespace + ":" + someKey, config.getProperty(someKey, null));
assertEquals(anotherNamespace + ":" + someKey, anotherConfig.getProperty(someKey, null));
}
@Test
public void testGetConfigMultipleTimesWithSameNamespace() throws Exception {
String someNamespace = "someName";
Config config = defaultConfigManager.getConfig(someNamespace);
Config anotherConfig = defaultConfigManager.getConfig(someNamespace);
assertThat(
"Get config multiple times with the same namespace should return the same config instance",
config, equalTo(anotherConfig));
}
public static class MockConfigManager implements ConfigFactoryManager {
@Override
public ConfigFactory getFactory(String namespace) {
return new ConfigFactory() {
@Override
public Config create(final String namespace) {
return new Config() {
@Override
public String getProperty(String key, String defaultValue) {
return namespace + ":" + key;
}
};
}
};
}
}
}
package com.ctrip.apollo.internals;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import com.ctrip.apollo.core.utils.ClassLoaderUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.unidal.lookup.ComponentTestCase;
import java.io.File;
import java.util.Properties;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class DefaultConfigTest extends ComponentTestCase {
private File someBaseDir;
private File someResourceDir;
private String someNamespace;
private ConfigLoader fallbackLoader;
private Properties someProperties;
@Before
public void setUp() throws Exception {
super.setUp();
someBaseDir = new File("src/test/resources/config-cache");
someBaseDir.deleteOnExit();
someBaseDir.mkdir();
someResourceDir = new File(ClassLoaderUtil.getClassPath() + "/META-INF/config");
someResourceDir.deleteOnExit();
someResourceDir.mkdir();
someNamespace = "someName";
someProperties = new Properties();
someProperties.setProperty("defaultKey", "defaultValue");
fallbackLoader = mock(RemoteConfig.class);
when(fallbackLoader.loadConfig()).thenReturn(someProperties);
}
@Override
@After
public void tearDown() throws Exception {
}
@Test
public void testGetPropertyWithLocalFile() throws Exception {
File file = new File(someBaseDir, someNamespace + ".properties");
String someKey = "someKey";
String someValue = "someValue";
Files.write(someKey + "=" + someValue, file, Charsets.UTF_8);
DefaultConfig defaultConfig = new DefaultConfig(someBaseDir, someNamespace, fallbackLoader);
file.delete();
assertEquals(someValue, defaultConfig.getProperty(someKey, null));
}
@Test
public void testGetPropertyWithLocalResource() throws Exception {
File file = new File(someResourceDir, someNamespace + ".properties");
file.deleteOnExit();
Files.createParentDirs(file);
String someKey = "someKey";
String someValue = "someValue";
Files.write(someKey + "=" + someValue, file, Charsets.UTF_8);
DefaultConfig defaultConfig = new DefaultConfig(someBaseDir, someNamespace, fallbackLoader);
file.delete();
assertEquals(someValue, defaultConfig.getProperty(someKey, null));
}
@Test
public void testGetPropertyWithAllPropertyHierarchy() throws Exception {
String someKey = "someKey";
String someSystemPropertyValue = "system-property-value";
String anotherKey = "anotherKey";
String someLocalFileValue = "local-file-value";
String lastKey = "lastKey";
String someResourceValue = "resource-value";
//set up system property
System.setProperty(someKey, someSystemPropertyValue);
//set up local file
File localCacheFile = new File(someBaseDir, someNamespace + ".properties");
Files.write(someKey + "=" + someLocalFileValue, localCacheFile, Charsets.UTF_8);
Files.append(System.getProperty("line.separator"), localCacheFile, Charsets.UTF_8);
Files.append(anotherKey + "=" + someLocalFileValue, localCacheFile, Charsets.UTF_8);
//set up resource file
File resourceFile = new File(someResourceDir, someNamespace + ".properties");
Files.write(someKey + "=" + someResourceValue, resourceFile, Charsets.UTF_8);
Files.append(System.getProperty("line.separator"), resourceFile, Charsets.UTF_8);
Files.append(anotherKey + "=" + someResourceValue, resourceFile, Charsets.UTF_8);
Files.append(System.getProperty("line.separator"), resourceFile, Charsets.UTF_8);
Files.append(lastKey + "=" + someResourceValue, resourceFile, Charsets.UTF_8);
DefaultConfig defaultConfig = new DefaultConfig(someBaseDir, someNamespace, fallbackLoader);
String someKeyValue = defaultConfig.getProperty(someKey, null);
String anotherKeyValue = defaultConfig.getProperty(anotherKey, null);
String lastKeyValue = defaultConfig.getProperty(lastKey, null);
localCacheFile.delete();
resourceFile.delete();
assertEquals(someSystemPropertyValue, someKeyValue);
assertEquals(someLocalFileValue, anotherKeyValue);
assertEquals(someResourceValue, lastKeyValue);
}
@Test
public void testInitLocalConfigWithNoLocalFile() throws Exception {
DefaultConfig defaultConfig = new DefaultConfig(someBaseDir, someNamespace, fallbackLoader);
Properties result = defaultConfig.loadConfig();
assertThat(
"Default config's properties should be the same as fallback loader's when there is no local cache",
result.entrySet(), equalTo(someProperties.entrySet()));
}
}
package com.ctrip.apollo.client.loader.impl; package com.ctrip.apollo.internals;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ctrip.apollo.client.loader.ConfigServiceLocator;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig; import com.ctrip.apollo.core.dto.ApolloConfig;
import com.ctrip.apollo.core.dto.ServiceDTO; import com.ctrip.apollo.core.dto.ServiceDTO;
...@@ -17,131 +17,106 @@ import org.springframework.http.HttpStatus; ...@@ -17,131 +17,106 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import java.util.ArrayList; import java.util.Map;
import java.util.List; import java.util.Properties;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyMap; import static org.mockito.Matchers.anyMap;
import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class RemoteConfigLoaderTest { public class RemoteConfigTest {
private RemoteConfigLoader remoteConfigLoader;
@Mock @Mock
private RestTemplate restTemplate; private RestTemplate restTemplate;
@Mock @Mock
private ConfigUtil configUtil; private ConfigServiceLocator configServiceLocator;
@Mock private String someNamespace;
private ConfigServiceLocator serviceLocater;
@Mock @Mock
private ResponseEntity<ApolloConfig> someResponse; private ResponseEntity<ApolloConfig> someResponse;
@Before @Before
public void setUp() { public void setUp() throws Exception {
remoteConfigLoader = spy(new RemoteConfigLoader(restTemplate, serviceLocater)); someNamespace = "someName";
}
@Test String someServerUrl = "http://someServer";
public void testLoadApolloConfig() throws Exception { mockConfigServiceLocator(someServerUrl);
String someServerUrl = "http://someUrl";
String someCluster = "some cluster";
ApolloConfig apolloConfig = mock(ApolloConfig.class);
ApolloRegistry
apolloRegistry =
assembleSomeApolloRegistry("someAppId", "someCluster", "someNamespace");
ApolloConfig previousConfig = null;
ServiceDTO someService = new ServiceDTO();
someService.setHomepageUrl(someServerUrl);
List<ServiceDTO> someServices = new ArrayList<>();
someServices.add(someService);
when(serviceLocater.getConfigServices()).thenReturn(someServices);
when(configUtil.getCluster()).thenReturn(someCluster);
doReturn(apolloConfig).when(remoteConfigLoader)
.getRemoteConfig(restTemplate, someServerUrl, apolloRegistry, previousConfig);
ApolloConfig result = remoteConfigLoader.loadApolloConfig(apolloRegistry, previousConfig);
assertEquals(apolloConfig, result);
} }
@Test @Test
public void testGetRemoteConfig() throws Exception { public void testGetProperty() throws Exception {
String someServerUrl = "http://someServer"; String someKey = "someKey";
ApolloConfig someApolloConfig = mock(ApolloConfig.class); String someValue = "someValue";
ApolloRegistry String someKeyNotExisted = "key-not-existed";
apolloRegistry = String someDefaultValue = "someDefault";
assembleSomeApolloRegistry("someAppId", "someCluster", "someNamespace"); Map<String, String> configurations = Maps.newHashMap();
ApolloConfig previousConfig = null; configurations.put(someKey, someValue);
ApolloConfig someApolloConfig = assembleApolloConfig(configurations);
when(someResponse.getStatusCode()).thenReturn(HttpStatus.OK); when(someResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(someResponse.getBody()).thenReturn(someApolloConfig); when(someResponse.getBody()).thenReturn(someApolloConfig);
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);
ApolloConfig RemoteConfig remoteConfig = new RemoteConfig(restTemplate, configServiceLocator, someNamespace);
result =
remoteConfigLoader
.getRemoteConfig(restTemplate, someServerUrl, apolloRegistry,
previousConfig);
assertEquals(someApolloConfig, result); assertEquals(someValue, remoteConfig.getProperty(someKey, null));
assertEquals(someDefaultValue, remoteConfig.getProperty(someKeyNotExisted, someDefaultValue));
} }
@Test(expected = RuntimeException.class) @Test(expected = RuntimeException.class)
public void testGetRemoteConfigWithServerError() throws Exception { public void testGetRemoteConfigWithServerError() throws Exception {
String someServerUrl = "http://someServer";
ApolloRegistry
apolloRegistry =
assembleSomeApolloRegistry("someAppId", "someCluster", "someNamespace");
ApolloConfig previousConfig = null;
HttpStatus someErrorCode = HttpStatus.INTERNAL_SERVER_ERROR;
when(someResponse.getStatusCode()).thenReturn(someErrorCode); when(someResponse.getStatusCode()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR);
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, apolloRegistry, RemoteConfig remoteConfig = new RemoteConfig(restTemplate, configServiceLocator, someNamespace);
previousConfig);
} }
@Test @Test
public void testGetRemoteConfigWith304Response() throws Exception { public void testLoadConfig() throws Exception {
String someServerUrl = "http://someServer"; String someKey = "someKey";
ApolloRegistry String someValue = "someValue";
apolloRegistry = Map<String, String> configurations = Maps.newHashMap();
assembleSomeApolloRegistry("someAppId", "someCluster", "someNamespace"); configurations.put(someKey, someValue);
ApolloConfig previousConfig = null; ApolloConfig someApolloConfig = assembleApolloConfig(configurations);
when(someResponse.getStatusCode()).thenReturn(HttpStatus.NOT_MODIFIED); when(someResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(someResponse.getBody()).thenReturn(someApolloConfig);
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);
ApolloConfig
result =
remoteConfigLoader
.getRemoteConfig(restTemplate, someServerUrl, apolloRegistry,
previousConfig);
assertNull(result); RemoteConfig remoteConfig = new RemoteConfig(restTemplate, configServiceLocator, someNamespace);
Properties config = remoteConfig.loadConfig();
assertEquals(configurations, config);
}
private ApolloConfig assembleApolloConfig(Map<String, String> configurations) {
String someAppId = "appId";
String someClusterName = "cluster";
long someReleaseId = 1;
ApolloConfig apolloConfig =
new ApolloConfig(someAppId, someClusterName, someNamespace, someReleaseId);
apolloConfig.setConfigurations(configurations);
return apolloConfig;
} }
private ApolloRegistry assembleSomeApolloRegistry(String someAppId, String someClusterName, private void mockConfigServiceLocator(String serverUrl) {
String someNamespace) { ServiceDTO serviceDTO = mock(ServiceDTO.class);
ApolloRegistry
someApolloRegistry =
new ApolloRegistry(someAppId, someClusterName, someNamespace);
return someApolloRegistry; when(serviceDTO.getHomepageUrl()).thenReturn(serverUrl);
when(configServiceLocator.getConfigServices()).thenReturn(Lists.newArrayList(serviceDTO));
} }
} }
package com.ctrip.apollo.spi;
import com.ctrip.apollo.Config;
import org.junit.Before;
import org.junit.Test;
import org.unidal.lookup.ComponentTestCase;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class DefaultConfigFactoryManagerTest extends ComponentTestCase {
private DefaultConfigFactoryManager defaultConfigFactoryManager;
@Before
public void setUp() throws Exception {
super.setUp();
defineComponent(ConfigRegistry.class, MockConfigRegistry.class);
defaultConfigFactoryManager =
(DefaultConfigFactoryManager) lookup(ConfigFactoryManager.class, "default");
}
@Test
public void testGetFactoryFromRegistry() throws Exception {
ConfigFactory result =
defaultConfigFactoryManager.getFactory(MockConfigRegistry.NAMESPACE_REGISTERED);
assertEquals(MockConfigRegistry.REGISTERED_CONFIGFACTORY, result);
}
@Test
public void testGetFactoryFromNamespace() throws Exception {
String someNamespace = "someName";
defineComponent(ConfigFactory.class, someNamespace, SomeConfigFactory.class);
ConfigFactory result = defaultConfigFactoryManager.getFactory(someNamespace);
assertThat("When namespace is registered, should return the registerd config factory", result,
instanceOf(SomeConfigFactory.class));
}
@Test
public void testGetFactoryFromNamespaceMultipleTimes() throws Exception {
String someNamespace = "someName";
defineComponent(ConfigFactory.class, someNamespace, SomeConfigFactory.class);
ConfigFactory result = defaultConfigFactoryManager.getFactory(someNamespace);
ConfigFactory anotherResult = defaultConfigFactoryManager.getFactory(someNamespace);
assertThat(
"Get config factory with the same namespace multiple times should returnt the same instance",
anotherResult, equalTo(result));
}
@Test
public void testGetFactoryFromDefault() throws Exception {
String someNamespace = "someName";
defineComponent(ConfigFactory.class, "default", AnotherConfigFactory.class);
ConfigFactory result = defaultConfigFactoryManager.getFactory(someNamespace);
assertThat("When namespace is not registered, should return the default config factory", result,
instanceOf(AnotherConfigFactory.class));
}
public static class MockConfigRegistry implements ConfigRegistry {
public static String NAMESPACE_REGISTERED = "some-namespace-registered";
public static ConfigFactory REGISTERED_CONFIGFACTORY = new ConfigFactory() {
@Override
public Config create(String namespace) {
return null;
}
};
@Override
public void register(String namespace, ConfigFactory factory) {
//do nothing
}
@Override
public ConfigFactory getFactory(String namespace) {
if (namespace.equals(NAMESPACE_REGISTERED)) {
return REGISTERED_CONFIGFACTORY;
}
return null;
}
}
public static class SomeConfigFactory implements ConfigFactory {
@Override
public Config create(String namespace) {
return null;
}
}
public static class AnotherConfigFactory implements ConfigFactory {
@Override
public Config create(String namespace) {
return null;
}
}
}
package com.ctrip.apollo.spi;
import com.ctrip.apollo.Config;
import com.ctrip.apollo.internals.DefaultConfig;
import com.ctrip.apollo.internals.RemoteConfig;
import org.junit.Before;
import org.junit.Test;
import org.unidal.lookup.ComponentTestCase;
import java.util.Properties;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.doReturn;
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)
*/
public class DefaultConfigFactoryTest extends ComponentTestCase {
private DefaultConfigFactory defaultConfigFactory;
@Before
public void setUp() throws Exception {
super.setUp();
defaultConfigFactory = spy((DefaultConfigFactory) lookup(ConfigFactory.class, "default"));
}
@Test
public void testCreate() throws Exception {
String someNamespace = "someName";
Properties someProperties = new Properties();
String someKey = "someKey";
String someValue = "someValue";
someProperties.setProperty(someKey, someValue);
RemoteConfig someRemoteConfig = mock(RemoteConfig.class);
when(someRemoteConfig.loadConfig()).thenReturn(someProperties);
doReturn(someRemoteConfig).when(defaultConfigFactory).createRemoteConfig(someNamespace);
Config result = defaultConfigFactory.create(someNamespace);
assertThat("DefaultConfigFactory should create DefaultConfig", result,
is(instanceOf(DefaultConfig.class)));
assertThat("DefaultConfigFactory should set remote config as the fallback loader",
((DefaultConfig) result).getFallbackLoader(), instanceOf(RemoteConfig.class));
}
}
package com.ctrip.apollo.spi;
import com.ctrip.apollo.Config;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
import org.unidal.lookup.ComponentTestCase;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.*;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class DefaultConfigRegistryTest extends ComponentTestCase {
private DefaultConfigRegistry defaultConfigRegistry;
@Before
public void setUp() throws Exception {
super.setUp();
defaultConfigRegistry = (DefaultConfigRegistry) lookup(ConfigRegistry.class, "default");
}
@Test
public void testGetFactory() throws Exception {
String someNamespace = "someName";
ConfigFactory someConfigFactory = new MockConfigFactory();
defaultConfigRegistry.register(someNamespace, someConfigFactory);
assertThat("Should return the registered config factory",
defaultConfigRegistry.getFactory(someNamespace), equalTo(someConfigFactory));
}
@Test
public void testGetFactoryWithNamespaceUnregistered() throws Exception {
String someUnregisteredNamespace = "someName";
assertNull(defaultConfigRegistry.getFactory(someUnregisteredNamespace));
}
public static class MockConfigFactory implements ConfigFactory {
@Override
public Config create(String namespace) {
return null;
}
}
}
...@@ -50,12 +50,12 @@ public class ApolloConfig { ...@@ -50,12 +50,12 @@ public class ApolloConfig {
return releaseId; return releaseId;
} }
public void setConfigurations(Map<String, String> configurations) { public Map<String, String> getConfigurations() {
this.configurations = configurations; return configurations;
} }
public String getProperty(String key) { public void setConfigurations(Map<String, String> configurations) {
return this.configurations.get(key); this.configurations = configurations;
} }
@Override @Override
......
...@@ -3,6 +3,10 @@ package com.ctrip.apollo.core.utils; ...@@ -3,6 +3,10 @@ package com.ctrip.apollo.core.utils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
...@@ -10,15 +14,36 @@ public class ClassLoaderUtil { ...@@ -10,15 +14,36 @@ public class ClassLoaderUtil {
private static final Logger logger = LoggerFactory.getLogger(ClassLoaderUtil.class); private static final Logger logger = LoggerFactory.getLogger(ClassLoaderUtil.class);
private static ClassLoader loader = Thread.currentThread().getContextClassLoader(); private static ClassLoader loader = Thread.currentThread().getContextClassLoader();
private static String classPath = "";
static { static {
if (loader == null) { if (loader == null) {
logger.info("Using system class loader"); logger.info("Using system class loader");
loader = ClassLoader.getSystemClassLoader(); loader = ClassLoader.getSystemClassLoader();
} }
try {
URL url = loader.getResource("");
// get class path
classPath = url.getPath();
classPath = URLDecoder.decode(classPath, "utf-8");
// 如果是jar包内的,则返回当前路径
if (classPath.contains(".jar!")) {
logger.warn("using config file inline jar!");
classPath = System.getProperty("user.dir");
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} }
public static ClassLoader getLoader() { public static ClassLoader getLoader() {
return loader; return loader;
} }
public static String getClassPath() {
return classPath;
}
} }
import com.ctrip.apollo.client.ConfigService;
import com.ctrip.apollo.client.config.Config; import com.ctrip.apollo.Config;
import com.ctrip.apollo.ConfigService;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
...@@ -12,7 +13,7 @@ public class ApolloConfigDemo { ...@@ -12,7 +13,7 @@ public class ApolloConfigDemo {
private Config config; private Config config;
public ApolloConfigDemo() { public ApolloConfigDemo() {
config = ConfigService.getConfig(); config = ConfigService.getConfig("apollo-config-service");
} }
private String getConfig(String key) { private String getConfig(String key) {
......
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