Commit bb68324d by Jason Song

Client side retry and logging to CAT

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