Commit bb68324d by Jason Song

Client side retry and logging to CAT

parent 7795a848
...@@ -4,6 +4,7 @@ import com.ctrip.apollo.core.ConfigConsts; ...@@ -4,6 +4,7 @@ import com.ctrip.apollo.core.ConfigConsts;
import com.ctrip.apollo.internals.ConfigManager; import com.ctrip.apollo.internals.ConfigManager;
import com.ctrip.apollo.spi.ConfigFactory; import com.ctrip.apollo.spi.ConfigFactory;
import com.ctrip.apollo.spi.ConfigRegistry; import com.ctrip.apollo.spi.ConfigRegistry;
import com.dianping.cat.Cat;
import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException; import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
...@@ -36,6 +37,7 @@ public class ConfigService { ...@@ -36,6 +37,7 @@ public class ConfigService {
* @return config instance * @return config instance
*/ */
public static Config getConfig(String namespace) { public static Config getConfig(String namespace) {
Cat.logEvent("Apollo.Client.Version", Apollo.VERSION);
return getManager().getConfig(namespace); return getManager().getConfig(namespace);
} }
...@@ -43,6 +45,7 @@ public class ConfigService { ...@@ -43,6 +45,7 @@ public class ConfigService {
try { try {
return s_instance.m_container.lookup(ConfigManager.class); return s_instance.m_container.lookup(ConfigManager.class);
} catch (ComponentLookupException ex) { } catch (ComponentLookupException ex) {
Cat.logError(ex);
throw new IllegalStateException("Unable to load ConfigManager!", ex); throw new IllegalStateException("Unable to load ConfigManager!", ex);
} }
} }
...@@ -51,6 +54,7 @@ public class ConfigService { ...@@ -51,6 +54,7 @@ public class ConfigService {
try { try {
return s_instance.m_container.lookup(ConfigRegistry.class); return s_instance.m_container.lookup(ConfigRegistry.class);
} catch (ComponentLookupException ex) { } catch (ComponentLookupException ex) {
Cat.logError(ex);
throw new IllegalStateException("Unable to load ConfigRegistry!", ex); throw new IllegalStateException("Unable to load ConfigRegistry!", ex);
} }
} }
...@@ -60,7 +64,7 @@ public class ConfigService { ...@@ -60,7 +64,7 @@ public class ConfigService {
} }
/** /**
* Manually set the config for the namespace specified, use with caution! * Manually set the config for the namespace specified, use with caution.
* @param namespace the namespace * @param namespace the namespace
* @param config the config instance * @param config the config instance
*/ */
...@@ -78,7 +82,7 @@ public class ConfigService { ...@@ -78,7 +82,7 @@ public class ConfigService {
} }
/** /**
* Manually set the config factory for the namespace specified, use with caution! * Manually set the config factory for the namespace specified, use with caution.
* @param namespace the namespace * @param namespace the namespace
* @param factory the factory instance * @param factory the factory instance
*/ */
......
...@@ -5,17 +5,5 @@ package com.ctrip.apollo.enums; ...@@ -5,17 +5,5 @@ package com.ctrip.apollo.enums;
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public enum PropertyChangeType { public enum PropertyChangeType {
NEW("New"), NEW, MODIFIED, DELETED
MODIFIED("Modified"),
DELETED("Deleted");
private String type;
PropertyChangeType(String type) {
this.type = type;
}
public String getType() {
return type;
}
} }
...@@ -5,7 +5,7 @@ import com.google.common.base.Strings; ...@@ -5,7 +5,7 @@ import com.google.common.base.Strings;
import com.ctrip.apollo.core.enums.Env; import com.ctrip.apollo.core.enums.Env;
import com.ctrip.framework.foundation.Foundation; import com.ctrip.framework.foundation.Foundation;
public class Apollo { public class ClientEnvironment {
private static Env s_env; private static Env s_env;
private static String s_appId; private static String s_appId;
private static String s_cluster; private static String s_cluster;
...@@ -39,6 +39,7 @@ public class Apollo { ...@@ -39,6 +39,7 @@ public class Apollo {
s_env = Env.LPT; s_env = Env.LPT;
break; break;
case "FAT": case "FAT":
case "FWS":
s_env = Env.FAT; s_env = Env.FAT;
break; break;
case "UAT": case "UAT":
......
...@@ -9,6 +9,7 @@ import com.ctrip.apollo.ConfigChangeListener; ...@@ -9,6 +9,7 @@ import com.ctrip.apollo.ConfigChangeListener;
import com.ctrip.apollo.enums.PropertyChangeType; import com.ctrip.apollo.enums.PropertyChangeType;
import com.ctrip.apollo.model.ConfigChange; import com.ctrip.apollo.model.ConfigChange;
import com.ctrip.apollo.model.ConfigChangeEvent; import com.ctrip.apollo.model.ConfigChangeEvent;
import com.dianping.cat.Cat;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -36,6 +37,7 @@ public abstract class AbstractConfig implements Config { ...@@ -36,6 +37,7 @@ public abstract class AbstractConfig implements Config {
try { try {
listener.onChange(changeEvent); listener.onChange(changeEvent);
} catch (Throwable ex) { } catch (Throwable ex) {
Cat.logError(ex);
logger.error("Failed to invoke config change listener {}", listener.getClass(), ex); logger.error("Failed to invoke config change listener {}", listener.getClass(), ex);
} }
} }
......
...@@ -2,6 +2,8 @@ package com.ctrip.apollo.internals; ...@@ -2,6 +2,8 @@ package com.ctrip.apollo.internals;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.dianping.cat.Cat;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -19,6 +21,7 @@ public abstract class AbstractConfigRepository implements ConfigRepository { ...@@ -19,6 +21,7 @@ public abstract class AbstractConfigRepository implements ConfigRepository {
try { try {
sync(); sync();
} catch (Throwable ex) { } catch (Throwable ex) {
Cat.logError(ex);
logger.error("Sync config failed with repository {}", this.getClass(), ex); logger.error("Sync config failed with repository {}", this.getClass(), ex);
} }
} }
...@@ -42,6 +45,7 @@ public abstract class AbstractConfigRepository implements ConfigRepository { ...@@ -42,6 +45,7 @@ public abstract class AbstractConfigRepository implements ConfigRepository {
try { try {
listener.onRepositoryChange(namespace, newProperties); listener.onRepositoryChange(namespace, newProperties);
} catch (Throwable ex) { } catch (Throwable ex) {
Cat.logError(ex);
logger.error("Failed to invoke repository change listener {}", listener.getClass(), ex); logger.error("Failed to invoke repository change listener {}", listener.getClass(), ex);
} }
} }
......
package com.ctrip.apollo.internals; package com.ctrip.apollo.internals;
import com.google.common.collect.Lists;
import com.google.gson.reflect.TypeToken;
import com.ctrip.apollo.core.dto.ServiceDTO; import com.ctrip.apollo.core.dto.ServiceDTO;
import com.ctrip.apollo.util.ConfigUtil; import com.ctrip.apollo.util.ConfigUtil;
import com.ctrip.apollo.util.http.HttpRequest; import com.ctrip.apollo.util.http.HttpRequest;
import com.ctrip.apollo.util.http.HttpResponse; import com.ctrip.apollo.util.http.HttpResponse;
import com.ctrip.apollo.util.http.HttpUtil; import com.ctrip.apollo.util.http.HttpUtil;
import com.dianping.cat.Cat;
import com.dianping.cat.message.Message;
import com.dianping.cat.message.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.unidal.lookup.annotation.Inject; import org.unidal.lookup.annotation.Inject;
import org.unidal.lookup.annotation.Named; import org.unidal.lookup.annotation.Named;
import java.util.ArrayList; import java.lang.reflect.Type;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@Named(type = ConfigServiceLocator.class) @Named(type = ConfigServiceLocator.class)
public class ConfigServiceLocator { public class ConfigServiceLocator {
private static final Logger logger = LoggerFactory.getLogger(ConfigServiceLocator.class);
@Inject @Inject
private HttpUtil m_httpUtil; private HttpUtil m_httpUtil;
@Inject @Inject
private ConfigUtil m_configUtil; private ConfigUtil m_configUtil;
private List<ServiceDTO> serviceCaches = new ArrayList<>(); private AtomicReference<List<ServiceDTO>> m_configServices;
private Type m_responseType;
public ConfigServiceLocator() {
List<ServiceDTO> initial = Lists.newArrayList();
m_configServices = new AtomicReference<>(initial);
m_responseType = new TypeToken<List<ServiceDTO>>() {
}.getType();
}
/** /**
* Get the config service info from remote meta server. * Get the config service info from remote meta server.
*
* @return the services dto * @return the services dto
*/ */
public List<ServiceDTO> getConfigServices() { public List<ServiceDTO> getConfigServices() {
if (m_configServices.get().isEmpty()) {
updateConfigServices();
}
return m_configServices.get();
}
//TODO periodically update config services
private void updateConfigServices() {
String domainName = m_configUtil.getMetaServerDomainName(); String domainName = m_configUtil.getMetaServerDomainName();
String url = domainName + "/services/config"; String url = domainName + "/services/config";
HttpRequest request = new HttpRequest(url); HttpRequest request = new HttpRequest(url);
int maxRetries = 5;
Throwable exception = null;
for (int i = 0; i < maxRetries; i++) {
Transaction transaction = Cat.newTransaction("Apollo.MetaService", "getConfigService");
transaction.addData("Url", url);
try { try {
HttpResponse<ServiceDTO[]> response = m_httpUtil.doGet(request, ServiceDTO[].class); HttpResponse<List<ServiceDTO>> response = m_httpUtil.doGet(request, m_responseType);
ServiceDTO[] services = response.getBody(); m_configServices.set(response.getBody());
if (services != null && services.length > 0) { Cat.logEvent("Apollo.Config.Services", response.getBody().toString());
serviceCaches.clear(); transaction.setStatus(Message.SUCCESS);
for (ServiceDTO service : services) { return;
serviceCaches.add(service); } catch (Throwable ex) {
Cat.logError(ex);
transaction.setStatus(ex);
exception = ex;
} finally {
transaction.complete();
} }
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
//ignore
} }
} catch (Throwable ex) {
logger.error("Get config services failed", ex);
throw new RuntimeException("Get config services failed", ex);
} }
return serviceCaches; throw new RuntimeException("Get config services failed", exception);
} }
} }
...@@ -50,6 +50,7 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis ...@@ -50,6 +50,7 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis
} catch (Throwable ex) { } catch (Throwable ex) {
String message = String.format("Init Apollo Local Config failed - namespace: %s", String message = String.format("Init Apollo Local Config failed - namespace: %s",
m_namespace); m_namespace);
Cat.logError(ex);
logger.error(message, ex); logger.error(message, ex);
} }
} }
...@@ -161,8 +162,8 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis ...@@ -161,8 +162,8 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis
try { try {
properties.load(in); properties.load(in);
} catch (IOException ex) { } catch (IOException ex) {
logger.error("Load resource config for namespace {} failed", namespace, ex);
Cat.logError(ex); Cat.logError(ex);
logger.error("Load resource config for namespace {} failed", namespace, ex);
} finally { } finally {
try { try {
in.close(); in.close();
......
...@@ -34,6 +34,7 @@ public class LocalFileConfigRepository extends AbstractConfigRepository ...@@ -34,6 +34,7 @@ public class LocalFileConfigRepository extends AbstractConfigRepository
/** /**
* Constructor. * Constructor.
*
* @param baseDir the base dir for this local file config repository * @param baseDir the base dir for this local file config repository
* @param namespace the namespace * @param namespace the namespace
*/ */
...@@ -44,6 +45,7 @@ public class LocalFileConfigRepository extends AbstractConfigRepository ...@@ -44,6 +45,7 @@ public class LocalFileConfigRepository extends AbstractConfigRepository
try { try {
m_configUtil = m_container.lookup(ConfigUtil.class); m_configUtil = m_container.lookup(ConfigUtil.class);
} catch (ComponentLookupException ex) { } catch (ComponentLookupException ex) {
Cat.logError(ex);
throw new IllegalStateException("Unable to load component!", ex); throw new IllegalStateException("Unable to load component!", ex);
} }
this.trySync(); this.trySync();
...@@ -86,6 +88,7 @@ public class LocalFileConfigRepository extends AbstractConfigRepository ...@@ -86,6 +88,7 @@ public class LocalFileConfigRepository extends AbstractConfigRepository
try { try {
m_fileProperties = this.loadFromLocalCacheFile(m_baseDir, m_namespace); m_fileProperties = this.loadFromLocalCacheFile(m_baseDir, m_namespace);
} catch (Throwable ex) { } catch (Throwable ex) {
Cat.logError(ex);
ex.printStackTrace(); ex.printStackTrace();
//ignore //ignore
} }
...@@ -107,9 +110,8 @@ public class LocalFileConfigRepository extends AbstractConfigRepository ...@@ -107,9 +110,8 @@ public class LocalFileConfigRepository extends AbstractConfigRepository
Properties properties = m_fallback.getConfig(); Properties properties = m_fallback.getConfig();
updateFileProperties(properties); updateFileProperties(properties);
} catch (Throwable ex) { } catch (Throwable ex) {
String message = Cat.logError(ex);
String.format("Sync config from fallback repository %s failed", m_fallback.getClass()); logger.warn("Sync config from fallback repository {} failed", m_fallback.getClass(), ex);
logger.warn(message, ex);
} }
} }
...@@ -136,9 +138,9 @@ public class LocalFileConfigRepository extends AbstractConfigRepository ...@@ -136,9 +138,9 @@ public class LocalFileConfigRepository extends AbstractConfigRepository
properties = new Properties(); properties = new Properties();
properties.load(in); properties.load(in);
} catch (IOException ex) { } catch (IOException ex) {
logger.error("Loading config from local cache file {} failed", file.getAbsolutePath(), ex);
Cat.logError(ex); Cat.logError(ex);
throw ex; throw new RuntimeException(String
.format("Loading config from local cache file %s failed", file.getAbsolutePath()), ex);
} finally { } finally {
try { try {
if (in != null) { if (in != null) {
...@@ -149,10 +151,8 @@ public class LocalFileConfigRepository extends AbstractConfigRepository ...@@ -149,10 +151,8 @@ public class LocalFileConfigRepository extends AbstractConfigRepository
} }
} }
} else { } else {
String message = throw new RuntimeException(
String.format("Cannot read from local cache file %s", file.getAbsolutePath()); String.format("Cannot read from local cache file %s", file.getAbsolutePath()));
logger.error(message);
throw new RuntimeException(message);
} }
return properties; return properties;
...@@ -170,8 +170,8 @@ public class LocalFileConfigRepository extends AbstractConfigRepository ...@@ -170,8 +170,8 @@ public class LocalFileConfigRepository extends AbstractConfigRepository
out = new FileOutputStream(file); out = new FileOutputStream(file);
m_fileProperties.store(out, "Persisted by DefaultConfig"); m_fileProperties.store(out, "Persisted by DefaultConfig");
} catch (IOException ex) { } catch (IOException ex) {
logger.error("Persist local cache file {} failed", file.getAbsolutePath(), ex);
Cat.logError(ex); Cat.logError(ex);
logger.error("Persist local cache file {} failed", file.getAbsolutePath(), ex);
} finally { } finally {
if (out != null) { if (out != null) {
try { try {
......
...@@ -10,6 +10,9 @@ import com.ctrip.apollo.util.ConfigUtil; ...@@ -10,6 +10,9 @@ import com.ctrip.apollo.util.ConfigUtil;
import com.ctrip.apollo.util.http.HttpRequest; import com.ctrip.apollo.util.http.HttpRequest;
import com.ctrip.apollo.util.http.HttpResponse; import com.ctrip.apollo.util.http.HttpResponse;
import com.ctrip.apollo.util.http.HttpUtil; import com.ctrip.apollo.util.http.HttpUtil;
import com.dianping.cat.Cat;
import com.dianping.cat.message.Message;
import com.dianping.cat.message.Transaction;
import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException; import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
...@@ -17,10 +20,12 @@ import org.slf4j.Logger; ...@@ -17,10 +20,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.unidal.lookup.ContainerLoader; import org.unidal.lookup.ContainerLoader;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
/** /**
...@@ -38,6 +43,7 @@ public class RemoteConfigRepository extends AbstractConfigRepository { ...@@ -38,6 +43,7 @@ public class RemoteConfigRepository extends AbstractConfigRepository {
/** /**
* Constructor. * Constructor.
*
* @param namespace the namespace * @param namespace the namespace
*/ */
public RemoteConfigRepository(String namespace) { public RemoteConfigRepository(String namespace) {
...@@ -49,6 +55,7 @@ public class RemoteConfigRepository extends AbstractConfigRepository { ...@@ -49,6 +55,7 @@ public class RemoteConfigRepository extends AbstractConfigRepository {
m_httpUtil = m_container.lookup(HttpUtil.class); m_httpUtil = m_container.lookup(HttpUtil.class);
m_serviceLocator = m_container.lookup(ConfigServiceLocator.class); m_serviceLocator = m_container.lookup(ConfigServiceLocator.class);
} catch (ComponentLookupException ex) { } catch (ComponentLookupException ex) {
Cat.logError(ex);
throw new IllegalStateException("Unable to load component!", ex); throw new IllegalStateException("Unable to load component!", ex);
} }
this.m_executorService = Executors.newScheduledThreadPool(1, this.m_executorService = Executors.newScheduledThreadPool(1,
...@@ -110,35 +117,65 @@ public class RemoteConfigRepository extends AbstractConfigRepository { ...@@ -110,35 +117,65 @@ public class RemoteConfigRepository extends AbstractConfigRepository {
private ApolloConfig loadApolloConfig() { private ApolloConfig loadApolloConfig() {
String appId = m_configUtil.getAppId(); String appId = m_configUtil.getAppId();
String cluster = m_configUtil.getCluster(); String cluster = m_configUtil.getCluster();
String Cat.logEvent("Apollo.Client.Config", String.format("%s-%s-%s", appId, cluster, m_namespace));
url = int maxRetries = 2;
assembleUrl(getConfigServiceUrl(), appId, cluster, m_namespace, m_configCache.get()); Throwable exception = null;
logger.info("Loading config from {}", url); List<ServiceDTO> configServices = getConfigServices();
for (int i = 0; i < maxRetries; i++) {
List<ServiceDTO> randomConfigServices = Lists.newArrayList(configServices);
Collections.shuffle(randomConfigServices);
for (ServiceDTO configService : randomConfigServices) {
String url =
assembleUrl(configService.getHomepageUrl(), appId, cluster, m_namespace,
m_configCache.get());
logger.debug("Loading config from {}", url);
HttpRequest request = new HttpRequest(url); HttpRequest request = new HttpRequest(url);
Transaction transaction = Cat.newTransaction("Apollo.ConfigService", "queryConfig");
transaction.addData("Url", url);
try { try {
HttpResponse<ApolloConfig> response = m_httpUtil.doGet(request, ApolloConfig.class); HttpResponse<ApolloConfig> response = m_httpUtil.doGet(request, ApolloConfig.class);
transaction.addData("StatusCode", response.getStatusCode());
transaction.setStatus(Message.SUCCESS);
if (response.getStatusCode() == 304) { if (response.getStatusCode() == 304) {
logger.info("Config server responds with 304 HTTP status code."); logger.debug("Config server responds with 304 HTTP status code.");
return m_configCache.get(); return m_configCache.get();
} }
logger.debug("Loaded config: {}", response.getBody());
logger.info("Loaded config: {}", response.getBody());
return response.getBody(); return response.getBody();
} catch (Throwable ex) { } catch (Throwable ex) {
String message = Cat.logError(ex);
String.format("Load Apollo Config failed - appId: %s, cluster: %s, namespace: %s", appId, transaction.setStatus(ex);
cluster, m_namespace); exception = ex;
logger.error(message, ex); } finally {
throw new RuntimeException(message, ex); transaction.complete();
}
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
//ignore
} }
} }
String message = String.format(
"Load Apollo Config failed - appId: %s, cluster: %s, namespace: %s, services: %s",
appId, cluster, m_namespace, configServices);
logger.error(message, exception);
throw new RuntimeException(message, exception);
}
private String assembleUrl(String uri, String appId, String cluster, String namespace, private String assembleUrl(String uri, String appId, String cluster, String namespace,
ApolloConfig previousConfig) { ApolloConfig previousConfig) {
String path = "/config/%s/%s"; String path = "config/%s/%s";
List<String> params = Lists.newArrayList(appId, cluster); List<String> params = Lists.newArrayList(appId, cluster);
if (!Strings.isNullOrEmpty(namespace)) { if (!Strings.isNullOrEmpty(namespace)) {
...@@ -151,14 +188,18 @@ public class RemoteConfigRepository extends AbstractConfigRepository { ...@@ -151,14 +188,18 @@ public class RemoteConfigRepository extends AbstractConfigRepository {
} }
String pathExpanded = String.format(path, params.toArray()); String pathExpanded = String.format(path, params.toArray());
if (!uri.endsWith("/")) {
uri += "/";
}
return uri + pathExpanded; return uri + pathExpanded;
} }
private String getConfigServiceUrl() { private List<ServiceDTO> getConfigServices() {
List<ServiceDTO> services = m_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;
} }
} }
...@@ -5,6 +5,7 @@ import com.google.common.collect.Maps; ...@@ -5,6 +5,7 @@ import com.google.common.collect.Maps;
import com.ctrip.apollo.model.ConfigChange; import com.ctrip.apollo.model.ConfigChange;
import com.ctrip.apollo.model.ConfigChangeEvent; import com.ctrip.apollo.model.ConfigChangeEvent;
import com.dianping.cat.Cat;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -40,6 +41,7 @@ public class SimpleConfig extends AbstractConfig implements RepositoryChangeList ...@@ -40,6 +41,7 @@ public class SimpleConfig extends AbstractConfig implements RepositoryChangeList
} catch (Throwable ex) { } catch (Throwable ex) {
String message = String.format("Init Apollo Simple Config failed - namespace: %s", String message = String.format("Init Apollo Simple Config failed - namespace: %s",
m_namespace); m_namespace);
Cat.logError(message, ex);
logger.error(message, ex); logger.error(message, ex);
} }
} }
......
...@@ -5,12 +5,15 @@ import com.ctrip.apollo.core.utils.ClassLoaderUtil; ...@@ -5,12 +5,15 @@ import com.ctrip.apollo.core.utils.ClassLoaderUtil;
import com.ctrip.apollo.internals.DefaultConfig; import com.ctrip.apollo.internals.DefaultConfig;
import com.ctrip.apollo.internals.LocalFileConfigRepository; import com.ctrip.apollo.internals.LocalFileConfigRepository;
import com.ctrip.apollo.internals.RemoteConfigRepository; import com.ctrip.apollo.internals.RemoteConfigRepository;
import com.dianping.cat.Cat;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.unidal.lookup.annotation.Named; import org.unidal.lookup.annotation.Named;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
...@@ -26,10 +29,18 @@ public class DefaultConfigFactory implements ConfigFactory { ...@@ -26,10 +29,18 @@ public class DefaultConfigFactory implements ConfigFactory {
*/ */
public DefaultConfigFactory() { public DefaultConfigFactory() {
m_baseDir = new File(ClassLoaderUtil.getClassPath() + CONFIG_DIR); m_baseDir = new File(ClassLoaderUtil.getClassPath() + CONFIG_DIR);
if (!m_baseDir.exists()) { this.checkLocalConfigCacheDir(m_baseDir);
if(!m_baseDir.mkdir()){
logger.error("Creating local cache dir failed.");
} }
private void checkLocalConfigCacheDir(File baseDir) {
if (baseDir.exists()) {
return;
}
try {
Files.createDirectory(baseDir.toPath());
} catch (IOException ex) {
Cat.logError(ex);
logger.error("Unable to create directory: " + baseDir, ex);
} }
} }
......
...@@ -5,7 +5,7 @@ import com.google.common.base.Preconditions; ...@@ -5,7 +5,7 @@ import com.google.common.base.Preconditions;
import com.ctrip.apollo.core.ConfigConsts; import com.ctrip.apollo.core.ConfigConsts;
import com.ctrip.apollo.core.MetaDomainConsts; import com.ctrip.apollo.core.MetaDomainConsts;
import com.ctrip.apollo.core.enums.Env; import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.env.Apollo; import com.ctrip.apollo.env.ClientEnvironment;
import org.unidal.lookup.annotation.Named; import org.unidal.lookup.annotation.Named;
...@@ -28,7 +28,7 @@ public class ConfigUtil { ...@@ -28,7 +28,7 @@ public class ConfigUtil {
* @throws IllegalStateException if app id is not set * @throws IllegalStateException if app id is not set
*/ */
public String getAppId() { public String getAppId() {
String appId = Apollo.getAppId(); String appId = ClientEnvironment.getAppId();
Preconditions.checkState(appId != null, "app.id is not set"); Preconditions.checkState(appId != null, "app.id is not set");
return appId; return appId;
} }
...@@ -38,7 +38,7 @@ public class ConfigUtil { ...@@ -38,7 +38,7 @@ public class ConfigUtil {
* @return the cluster name, or "default" if not specified * @return the cluster name, or "default" if not specified
*/ */
public String getCluster() { public String getCluster() {
String cluster = Apollo.getCluster(); String cluster = ClientEnvironment.getCluster();
if (cluster == null) { if (cluster == null) {
cluster = ConfigConsts.CLUSTER_NAME_DEFAULT; cluster = ConfigConsts.CLUSTER_NAME_DEFAULT;
} }
...@@ -51,7 +51,7 @@ public class ConfigUtil { ...@@ -51,7 +51,7 @@ public class ConfigUtil {
* @throws IllegalStateException if env is set * @throws IllegalStateException if env is set
*/ */
public Env getApolloEnv() { public Env getApolloEnv() {
Env env = Apollo.getEnv(); Env env = ClientEnvironment.getEnv();
Preconditions.checkState(env != null, "env is not set"); Preconditions.checkState(env != null, "env is not set");
return env; return env;
} }
......
package com.ctrip.apollo.util.http; package com.ctrip.apollo.util.http;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.ctrip.apollo.util.ConfigUtil; import com.ctrip.apollo.util.ConfigUtil;
...@@ -11,6 +12,7 @@ import org.unidal.lookup.annotation.Named; ...@@ -11,6 +12,7 @@ import org.unidal.lookup.annotation.Named;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Type;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
...@@ -30,10 +32,43 @@ public class HttpUtil { ...@@ -30,10 +32,43 @@ public class HttpUtil {
/** /**
* Do get operation for the http request. * Do get operation for the http request.
* *
* @return the http response * @param httpRequest the request
* @param responseType the response type
* @return the response
* @throws RuntimeException if any error happened or response code is neither 200 nor 304 * @throws RuntimeException if any error happened or response code is neither 200 nor 304
*/ */
public <T> HttpResponse<T> doGet(HttpRequest httpRequest, Class<T> responseType) { public <T> HttpResponse<T> doGet(HttpRequest httpRequest, final Class<T> responseType) {
Function<String, T> convertResponse = new Function<String, T>() {
@Override
public T apply(String input) {
return gson.fromJson(input, responseType);
}
};
return doGetWithSerializeFunction(httpRequest, convertResponse);
}
/**
* Do get operation for the http request.
*
* @param httpRequest the request
* @param responseType the response type
* @return the response
* @throws RuntimeException if any error happened or response code is neither 200 nor 304
*/
public <T> HttpResponse<T> doGet(HttpRequest httpRequest, final Type responseType) {
Function<String, T> convertResponse = new Function<String, T>() {
@Override
public T apply(String input) {
return gson.fromJson(input, responseType);
}
};
return doGetWithSerializeFunction(httpRequest, convertResponse);
}
private <T> HttpResponse<T> doGetWithSerializeFunction(HttpRequest httpRequest,
Function<String, T> serializeFunction) {
InputStream is = null; InputStream is = null;
try { try {
HttpURLConnection conn = (HttpURLConnection) new URL(httpRequest.getUrl()).openConnection(); HttpURLConnection conn = (HttpURLConnection) new URL(httpRequest.getUrl()).openConnection();
...@@ -60,7 +95,7 @@ public class HttpUtil { ...@@ -60,7 +95,7 @@ public class HttpUtil {
if (statusCode == 200) { if (statusCode == 200) {
is = conn.getInputStream(); is = conn.getInputStream();
String content = Files.IO.INSTANCE.readFrom(is, Charsets.UTF_8.name()); String content = Files.IO.INSTANCE.readFrom(is, Charsets.UTF_8.name());
return new HttpResponse<>(statusCode, gson.fromJson(content, responseType)); return new HttpResponse<>(statusCode, serializeFunction.apply(content));
} }
if (statusCode == 304) { if (statusCode == 304) {
...@@ -82,7 +117,6 @@ public class HttpUtil { ...@@ -82,7 +117,6 @@ public class HttpUtil {
} }
} }
} }
} }
} }
...@@ -23,7 +23,9 @@ import org.unidal.lookup.ComponentTestCase; ...@@ -23,7 +23,9 @@ import org.unidal.lookup.ComponentTestCase;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.ServerSocket;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
...@@ -33,7 +35,7 @@ import javax.servlet.http.HttpServletResponse; ...@@ -33,7 +35,7 @@ import javax.servlet.http.HttpServletResponse;
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class BaseIntegrationTest extends ComponentTestCase { public class BaseIntegrationTest extends ComponentTestCase {
private static final int PORT = 5678; private static final int PORT = findFreePort();
private static final String metaServiceUrl = "http://localhost:" + PORT; private static final String metaServiceUrl = "http://localhost:" + PORT;
private static final String someAppName = "someAppName"; private static final String someAppName = "someAppName";
private static final String someInstanceId = "someInstanceId"; private static final String someInstanceId = "someInstanceId";
...@@ -86,23 +88,33 @@ public class BaseIntegrationTest extends ComponentTestCase { ...@@ -86,23 +88,33 @@ public class BaseIntegrationTest extends ComponentTestCase {
@After @After
public void tearDown() throws Exception { public void tearDown() throws Exception {
super.tearDown();
if (server != null && server.isStarted()) { if (server != null && server.isStarted()) {
server.stop(); server.stop();
} }
super.tearDown();
}
protected ContextHandler mockMetaServerHandler() {
return mockMetaServerHandler(false);
} }
private ContextHandler mockMetaServerHandler() { protected ContextHandler mockMetaServerHandler(final boolean failedAtFirstTime) {
final ServiceDTO someServiceDTO = new ServiceDTO(); final ServiceDTO someServiceDTO = new ServiceDTO();
someServiceDTO.setAppName(someAppName); someServiceDTO.setAppName(someAppName);
someServiceDTO.setInstanceId(someInstanceId); someServiceDTO.setInstanceId(someInstanceId);
someServiceDTO.setHomepageUrl(configServiceURL); someServiceDTO.setHomepageUrl(configServiceURL);
final AtomicInteger counter = new AtomicInteger(0);
ContextHandler context = new ContextHandler("/services/config"); ContextHandler context = new ContextHandler("/services/config");
context.setHandler(new AbstractHandler() { context.setHandler(new AbstractHandler() {
@Override @Override
public void handle(String target, Request baseRequest, HttpServletRequest request, public void handle(String target, Request baseRequest, HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException { HttpServletResponse response) throws IOException, ServletException {
if (failedAtFirstTime && counter.incrementAndGet() == 1) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
baseRequest.setHandled(true);
return;
}
response.setContentType("application/json;charset=UTF-8"); response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_OK); response.setStatus(HttpServletResponse.SC_OK);
...@@ -150,4 +162,37 @@ public class BaseIntegrationTest extends ComponentTestCase { ...@@ -150,4 +162,37 @@ public class BaseIntegrationTest extends ComponentTestCase {
} }
} }
/**
* Returns a free port number on localhost.
*
* Heavily inspired from org.eclipse.jdt.launching.SocketUtil (to avoid a dependency to JDT just because of this).
* Slightly improved with close() missing in JDT. And throws exception instead of returning -1.
*
* @return a free port number on localhost
* @throws IllegalStateException if unable to find a free port
*/
private static int findFreePort() {
ServerSocket socket = null;
try {
socket = new ServerSocket(0);
socket.setReuseAddress(true);
int port = socket.getLocalPort();
try {
socket.close();
} catch (IOException e) {
// Ignore IOException on close()
}
return port;
} catch (IOException e) {
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
}
}
}
throw new IllegalStateException("Could not find a free TCP/IP port to start embedded Jetty HTTP Server on");
}
} }
package com.ctrip.apollo.integration; package com.ctrip.apollo.integration;
import static org.hamcrest.core.IsEqual.equalTo; import com.google.common.collect.ImmutableMap;
import static org.junit.Assert.assertEquals; import com.google.common.collect.Lists;
import static org.junit.Assert.assertThat; 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.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
...@@ -12,28 +27,15 @@ import java.util.List; ...@@ -12,28 +27,15 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request; import static org.hamcrest.core.IsEqual.equalTo;
import org.eclipse.jetty.server.handler.AbstractHandler; import static org.junit.Assert.assertEquals;
import org.eclipse.jetty.server.handler.ContextHandler; import static org.junit.Assert.assertThat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
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 com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
...@@ -142,6 +144,36 @@ public class ConfigIntegrationTest extends BaseIntegrationTest { ...@@ -142,6 +144,36 @@ public class ConfigIntegrationTest extends BaseIntegrationTest {
} }
@Test @Test
public void testGetConfigWithNoLocalFileAndRemoteMetaServiceRetry() throws Exception {
String someKey = "someKey";
String someValue = "someValue";
ApolloConfig apolloConfig = assembleApolloConfig(ImmutableMap.of(someKey, someValue));
ContextHandler configHandler = mockConfigServerHandler(HttpServletResponse.SC_OK, apolloConfig);
boolean failAtFirstTime = true;
ContextHandler metaServerHandler = mockMetaServerHandler(failAtFirstTime);
startServerWithHandlers(metaServerHandler, configHandler);
Config config = ConfigService.getConfig();
assertEquals(someValue, config.getProperty(someKey, null));
}
@Test
public void testGetConfigWithNoLocalFileAndRemoteConfigServiceRetry() throws Exception {
String someKey = "someKey";
String someValue = "someValue";
ApolloConfig apolloConfig = assembleApolloConfig(ImmutableMap.of(someKey, someValue));
boolean failedAtFirstTime = true;
ContextHandler handler =
mockConfigServerHandler(HttpServletResponse.SC_OK, apolloConfig, failedAtFirstTime);
startServerWithHandlers(handler);
Config config = ConfigService.getConfig();
assertEquals(someValue, config.getProperty(someKey, null));
}
@Test
public void testRefreshConfig() throws Exception { public void testRefreshConfig() throws Exception {
final String someKey = "someKey"; final String someKey = "someKey";
final String someValue = "someValue"; final String someValue = "someValue";
...@@ -183,12 +215,21 @@ public class ConfigIntegrationTest extends BaseIntegrationTest { ...@@ -183,12 +215,21 @@ public class ConfigIntegrationTest extends BaseIntegrationTest {
assertEquals(anotherValue, config.getProperty(someKey, null)); assertEquals(anotherValue, config.getProperty(someKey, null));
} }
private ContextHandler mockConfigServerHandler(final int statusCode, final ApolloConfig result) { private ContextHandler mockConfigServerHandler(final int statusCode, final ApolloConfig result,
final boolean failedAtFirstTime) {
ContextHandler context = new ContextHandler("/config/*"); ContextHandler context = new ContextHandler("/config/*");
context.setHandler(new AbstractHandler() { context.setHandler(new AbstractHandler() {
AtomicInteger counter = new AtomicInteger(0);
@Override @Override
public void handle(String target, Request baseRequest, HttpServletRequest request, public void handle(String target, Request baseRequest, HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException { HttpServletResponse response) throws IOException, ServletException {
if (failedAtFirstTime && counter.incrementAndGet() == 1) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
baseRequest.setHandled(true);
return;
}
response.setContentType("application/json;charset=UTF-8"); response.setContentType("application/json;charset=UTF-8");
response.setStatus(statusCode); response.setStatus(statusCode);
...@@ -199,6 +240,12 @@ public class ConfigIntegrationTest extends BaseIntegrationTest { ...@@ -199,6 +240,12 @@ public class ConfigIntegrationTest extends BaseIntegrationTest {
}); });
return context; return context;
}
private ContextHandler mockConfigServerHandler(int statusCode, ApolloConfig result) {
return mockConfigServerHandler(statusCode, result, false);
} }
private ApolloConfig assembleApolloConfig(Map<String, String> configurations) { private ApolloConfig assembleApolloConfig(Map<String, String> configurations) {
......
package com.ctrip.apollo;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class Apollo {
public final static String VERSION = "java-0.0.1-SNAPSHOT";
}
package com.ctrip.apollo.core.dto; package com.ctrip.apollo.core.dto;
import com.google.common.base.MoreObjects;
public class ServiceDTO { public class ServiceDTO {
private String appName; private String appName;
...@@ -31,4 +33,14 @@ public class ServiceDTO { ...@@ -31,4 +33,14 @@ public class ServiceDTO {
public void setInstanceId(String instanceId) { public void setInstanceId(String instanceId) {
this.instanceId = instanceId; this.instanceId = instanceId;
} }
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.omitNullValues()
.add("appName", appName)
.add("instanceId", instanceId)
.add("homepageUrl", homepageUrl)
.toString();
}
} }
import com.google.common.base.Strings;
import com.ctrip.apollo.Config; import com.ctrip.apollo.Config;
import com.ctrip.apollo.ConfigChangeListener; import com.ctrip.apollo.ConfigChangeListener;
import com.ctrip.apollo.ConfigService; import com.ctrip.apollo.ConfigService;
...@@ -37,7 +39,7 @@ public class ApolloConfigDemo implements ConfigChangeListener { ...@@ -37,7 +39,7 @@ public class ApolloConfigDemo implements ConfigChangeListener {
while (true) { while (true) {
System.out.print("> "); System.out.print("> ");
String input = new BufferedReader(new InputStreamReader(System.in)).readLine(); String input = new BufferedReader(new InputStreamReader(System.in)).readLine();
if (input == null) { if (Strings.isNullOrEmpty(input)) {
continue; continue;
} }
input = input.trim(); input = input.trim();
......
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