From f5d513fb0af9646ae9175421e35a97026b153ca8 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Mon, 11 Apr 2016 15:39:13 +0800 Subject: [PATCH] Use HttpURLConnection and gson to remove Spring and Jackson dependencies --- .../apollo/biz/service/ConfigService.java | 26 ++-- .../apollo/biz/service/ConfigServiceTest.java | 20 +-- apollo-client/pom.xml | 41 ++---- .../apollo/build/ComponentConfigurator.java | 6 + .../internals/ConfigServiceLocator.java | 26 ++-- .../ctrip/apollo/internals/DefaultConfig.java | 12 +- .../internals/LocalFileConfigRepository.java | 28 +++- .../internals/RemoteConfigRepository.java | 137 ++++++++---------- .../ctrip/apollo/internals/SimpleConfig.java | 11 +- .../apollo/spi/DefaultConfigFactory.java | 13 +- .../com/ctrip/apollo/util/ConfigUtil.java | 27 ++-- .../ctrip/apollo/util/http/HttpRequest.java | 36 +++++ .../ctrip/apollo/util/http/HttpResponse.java | 22 +++ .../com/ctrip/apollo/util/http/HttpUtil.java | 85 +++++++++++ .../resources/META-INF/plexus/components.xml | 22 +++ .../apollo/internals/DefaultConfigTest.java | 2 +- .../LocalFileConfigRepositoryTest.java | 40 +++-- .../internals/RemoteConfigRepositoryTest.java | 76 ++++++---- apollo-core/pom.xml | 8 +- .../ctrip/apollo/core/dto/ApolloConfig.java | 12 +- 20 files changed, 413 insertions(+), 237 deletions(-) create mode 100644 apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpRequest.java create mode 100644 apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpResponse.java create mode 100644 apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpUtil.java diff --git a/apollo-biz/src/main/java/com/ctrip/apollo/biz/service/ConfigService.java b/apollo-biz/src/main/java/com/ctrip/apollo/biz/service/ConfigService.java index 9333225a4..621e97184 100644 --- a/apollo-biz/src/main/java/com/ctrip/apollo/biz/service/ConfigService.java +++ b/apollo-biz/src/main/java/com/ctrip/apollo/biz/service/ConfigService.java @@ -1,17 +1,18 @@ package com.ctrip.apollo.biz.service; -import java.io.IOException; -import java.util.Map; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; +import com.google.common.collect.Maps; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; import com.ctrip.apollo.biz.entity.Release; import com.ctrip.apollo.biz.repository.ReleaseRepository; import com.ctrip.apollo.core.dto.ApolloConfig; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Maps; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.lang.reflect.Type; +import java.util.Map; /** * Config Service @@ -24,10 +25,9 @@ public class ConfigService { @Autowired private ReleaseRepository releaseRepository; - private ObjectMapper objectMapper = new ObjectMapper(); + private Gson gson = new Gson(); - private TypeReference> configurationTypeReference = - new TypeReference>() {}; + private Type configurationTypeReference = new TypeToken>(){}.getType(); public Release findRelease(String appId, String clusterName, String namespaceName) { Release release = releaseRepository.findLatest(appId, clusterName, namespaceName); @@ -49,8 +49,8 @@ public class ConfigService { Map transformConfigurationToMap(String configurations) { try { - return objectMapper.readValue(configurations, configurationTypeReference); - } catch (IOException e) { + return gson.fromJson(configurations, configurationTypeReference); + } catch (Throwable e) { e.printStackTrace(); return Maps.newHashMap(); } diff --git a/apollo-biz/src/test/java/com/ctrip/apollo/biz/service/ConfigServiceTest.java b/apollo-biz/src/test/java/com/ctrip/apollo/biz/service/ConfigServiceTest.java index f2359d34f..2095f8d7c 100644 --- a/apollo-biz/src/test/java/com/ctrip/apollo/biz/service/ConfigServiceTest.java +++ b/apollo-biz/src/test/java/com/ctrip/apollo/biz/service/ConfigServiceTest.java @@ -1,11 +1,9 @@ package com.ctrip.apollo.biz.service; import com.google.common.collect.Maps; + import com.ctrip.apollo.biz.entity.Release; import com.ctrip.apollo.biz.repository.ReleaseRepository; -import com.ctrip.apollo.biz.service.ConfigService; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Before; import org.junit.Test; @@ -14,16 +12,10 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; -import java.io.IOException; import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.anyObject; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; /** * @author Jason Song(song_s@ctrip.com) @@ -32,8 +24,6 @@ import static org.mockito.Mockito.when; public class ConfigServiceTest { @Mock private ReleaseRepository releaseRepository; - @Mock - private ObjectMapper objectMapper; private ConfigService configService; @Before @@ -41,7 +31,6 @@ public class ConfigServiceTest { configService = new ConfigService(); ReflectionTestUtils .setField(configService, "releaseRepository", releaseRepository); - ReflectionTestUtils.setField(configService, "objectMapper", objectMapper); } // @Test @@ -131,21 +120,16 @@ public class ConfigServiceTest { public void testTransformConfigurationToMapSuccessful() throws Exception { String someValidConfiguration = "{\"apollo.bar\": \"foo\"}"; Map someMap = Maps.newHashMap(); - when(objectMapper.readValue(eq(someValidConfiguration), (TypeReference) anyObject())) - .thenReturn(someMap); + someMap.put("apollo.bar", "foo"); Map result = configService.transformConfigurationToMap(someValidConfiguration); assertEquals(someMap, result); - verify(objectMapper, times(1)) - .readValue(eq(someValidConfiguration), (TypeReference) anyObject()); } @Test public void testTransformConfigurationToMapFailed() throws Exception { String someInvalidConfiguration = "xxx"; - when(objectMapper.readValue(eq(someInvalidConfiguration), (TypeReference) anyObject())) - .thenThrow(IOException.class); Map result = diff --git a/apollo-client/pom.xml b/apollo-client/pom.xml index 9de3ef5bf..812867bc3 100644 --- a/apollo-client/pom.xml +++ b/apollo-client/pom.xml @@ -24,38 +24,29 @@ com.dianping.cat cat-client - - org.springframework - spring-web - - - - com.google.guava - guava - - - - - commons-logging - commons-logging - test + org.slf4j + slf4j-api org.apache.logging.log4j - log4j-core - test - - - org.apache.logging.log4j - log4j-api - test + log4j-slf4j-impl + + + org.slf4j + slf4j-api + + + org.apache.logging.log4j + log4j-api + + + provided org.apache.logging.log4j - log4j-slf4j-impl - test + log4j-core + provided - diff --git a/apollo-client/src/main/java/com/ctrip/apollo/build/ComponentConfigurator.java b/apollo-client/src/main/java/com/ctrip/apollo/build/ComponentConfigurator.java index 7e57c6881..59a8daa23 100644 --- a/apollo-client/src/main/java/com/ctrip/apollo/build/ComponentConfigurator.java +++ b/apollo-client/src/main/java/com/ctrip/apollo/build/ComponentConfigurator.java @@ -1,9 +1,12 @@ package com.ctrip.apollo.build; +import com.ctrip.apollo.internals.ConfigServiceLocator; import com.ctrip.apollo.internals.DefaultConfigManager; import com.ctrip.apollo.spi.DefaultConfigFactory; import com.ctrip.apollo.spi.DefaultConfigFactoryManager; import com.ctrip.apollo.spi.DefaultConfigRegistry; +import com.ctrip.apollo.util.ConfigUtil; +import com.ctrip.apollo.util.http.HttpUtil; import org.unidal.lookup.configuration.AbstractResourceConfigurator; import org.unidal.lookup.configuration.Component; @@ -27,6 +30,9 @@ public class ComponentConfigurator extends AbstractResourceConfigurator { all.add(A(DefaultConfigFactory.class)); all.add(A(DefaultConfigRegistry.class)); all.add(A(DefaultConfigFactoryManager.class)); + all.add(A(ConfigUtil.class)); + all.add(A(ConfigServiceLocator.class)); + all.add(A(HttpUtil.class)); return all; } diff --git a/apollo-client/src/main/java/com/ctrip/apollo/internals/ConfigServiceLocator.java b/apollo-client/src/main/java/com/ctrip/apollo/internals/ConfigServiceLocator.java index 9ae4cadb2..2f7b91f72 100644 --- a/apollo-client/src/main/java/com/ctrip/apollo/internals/ConfigServiceLocator.java +++ b/apollo-client/src/main/java/com/ctrip/apollo/internals/ConfigServiceLocator.java @@ -2,38 +2,46 @@ package com.ctrip.apollo.internals; import com.ctrip.apollo.core.dto.ServiceDTO; import com.ctrip.apollo.env.ClientEnvironment; +import com.ctrip.apollo.util.http.HttpRequest; +import com.ctrip.apollo.util.http.HttpResponse; +import com.ctrip.apollo.util.http.HttpUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.web.client.RestTemplate; +import org.unidal.lookup.annotation.Inject; +import org.unidal.lookup.annotation.Named; -import java.net.URI; import java.util.ArrayList; import java.util.List; +@Named(type = ConfigServiceLocator.class) public class ConfigServiceLocator { - private static final Logger logger = LoggerFactory.getLogger(ConfigServiceLocator.class); - - private RestTemplate restTemplate = new RestTemplate(); - + @Inject + private HttpUtil m_httpUtil; private List serviceCaches = new ArrayList<>(); public List getConfigServices() { ClientEnvironment env = ClientEnvironment.getInstance(); String domainName = env.getMetaServerDomainName(); String url = domainName + "/services/config"; + + HttpRequest request = new HttpRequest(url); + try { - ServiceDTO[] services = restTemplate.getForObject(new URI(url), ServiceDTO[].class); + HttpResponse 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); } } - } catch (Exception ex) { - logger.warn(ex.getMessage()); + } catch (Throwable t) { + logger.error("Get config services failed", t); + throw new RuntimeException("Get config services failed", t); } + return serviceCaches; } } diff --git a/apollo-client/src/main/java/com/ctrip/apollo/internals/DefaultConfig.java b/apollo-client/src/main/java/com/ctrip/apollo/internals/DefaultConfig.java index d962c68af..4067b988b 100644 --- a/apollo-client/src/main/java/com/ctrip/apollo/internals/DefaultConfig.java +++ b/apollo-client/src/main/java/com/ctrip/apollo/internals/DefaultConfig.java @@ -4,6 +4,9 @@ import com.ctrip.apollo.Config; import com.ctrip.apollo.core.utils.ClassLoaderUtil; import com.dianping.cat.Cat; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.io.InputStream; import java.util.Properties; @@ -12,6 +15,7 @@ import java.util.Properties; * @author Jason Song(song_s@ctrip.com) */ public class DefaultConfig implements Config { + private static final Logger logger = LoggerFactory.getLogger(DefaultConfig.class); private final String m_namespace; private Properties m_resourceProperties; private Properties m_configProperties; @@ -28,9 +32,10 @@ public class DefaultConfig implements Config { try { m_configProperties = m_configRepository.loadConfig(); } catch (Throwable ex) { - throw new RuntimeException( - String.format("Init Apollo Local Config failed - namespace: %s", - m_namespace), ex); + String message = String.format("Init Apollo Local Config failed - namespace: %s", + m_namespace); + logger.error(message, ex); + throw new RuntimeException(message, ex); } } @@ -74,6 +79,7 @@ public class DefaultConfig implements Config { try { properties.load(in); } catch (IOException e) { + logger.error("Load resource config for namespace {} failed", namespace, e); Cat.logError(e); } finally { try { diff --git a/apollo-client/src/main/java/com/ctrip/apollo/internals/LocalFileConfigRepository.java b/apollo-client/src/main/java/com/ctrip/apollo/internals/LocalFileConfigRepository.java index bf1ae1852..8ef4a72e5 100644 --- a/apollo-client/src/main/java/com/ctrip/apollo/internals/LocalFileConfigRepository.java +++ b/apollo-client/src/main/java/com/ctrip/apollo/internals/LocalFileConfigRepository.java @@ -5,9 +5,14 @@ import com.google.common.base.Preconditions; import com.ctrip.apollo.util.ConfigUtil; import com.dianping.cat.Cat; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.unidal.lookup.ContainerLoader; + import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -18,16 +23,23 @@ import java.util.Properties; * @author Jason Song(song_s@ctrip.com) */ public class LocalFileConfigRepository implements ConfigRepository { + private static final Logger logger = LoggerFactory.getLogger(LocalFileConfigRepository.class); + private PlexusContainer m_container; private final String m_namespace; private final File m_baseDir; private final ConfigUtil m_configUtil; private Properties m_fileProperties; private ConfigRepository m_fallback; - public LocalFileConfigRepository(File baseDir, String namespace, ConfigUtil configUtil) { + public LocalFileConfigRepository(File baseDir, String namespace) { m_baseDir = baseDir; m_namespace = namespace; - m_configUtil = configUtil; + m_container = ContainerLoader.getDefaultContainer(); + try { + m_configUtil = m_container.lookup(ConfigUtil.class); + } catch (ComponentLookupException e) { + throw new IllegalStateException("Unable to load component!", e); + } } @Override @@ -53,6 +65,7 @@ public class LocalFileConfigRepository implements ConfigRepository { persistLocalCacheFile(m_baseDir, m_namespace); return; } catch (Throwable ex) { + logger.error("Load config from fallback loader failed", ex); Cat.logError(ex); } } @@ -79,6 +92,7 @@ public class LocalFileConfigRepository implements ConfigRepository { properties = new Properties(); properties.load(in); } catch (IOException e) { + logger.error("Loading config from local cache file {} failed", file.getAbsolutePath(), e); Cat.logError(e); throw e; } finally { @@ -91,7 +105,10 @@ public class LocalFileConfigRepository implements ConfigRepository { } } } else { - //TODO error handling + String message = + String.format("Cannot read from local cache file %s", file.getAbsolutePath()); + logger.error(message); + throw new RuntimeException(message); } return properties; @@ -108,9 +125,8 @@ public class LocalFileConfigRepository implements ConfigRepository { try { out = new FileOutputStream(file); m_fileProperties.store(out, "Persisted by DefaultConfig"); - } catch (FileNotFoundException ex) { - Cat.logError(ex); } catch (IOException ex) { + logger.error("Persist local cache file {} failed", file.getAbsolutePath(), ex); Cat.logError(ex); } finally { if (out != null) { diff --git a/apollo-client/src/main/java/com/ctrip/apollo/internals/RemoteConfigRepository.java b/apollo-client/src/main/java/com/ctrip/apollo/internals/RemoteConfigRepository.java index fa5a0846d..37f096c89 100644 --- a/apollo-client/src/main/java/com/ctrip/apollo/internals/RemoteConfigRepository.java +++ b/apollo-client/src/main/java/com/ctrip/apollo/internals/RemoteConfigRepository.java @@ -1,52 +1,56 @@ package com.ctrip.apollo.internals; -import com.google.common.collect.Maps; +import com.google.common.base.Strings; +import com.google.common.collect.Lists; import com.ctrip.apollo.core.dto.ApolloConfig; 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 org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; -import org.springframework.web.client.RestTemplate; +import org.unidal.lookup.ContainerLoader; import java.util.List; -import java.util.Map; import java.util.Properties; +import java.util.concurrent.atomic.AtomicReference; /** * @author Jason Song(song_s@ctrip.com) */ public class RemoteConfigRepository implements ConfigRepository { private static final Logger logger = LoggerFactory.getLogger(RemoteConfigRepository.class); - private RestTemplate m_restTemplate; + private PlexusContainer m_container; private ConfigServiceLocator m_serviceLocator; + private HttpUtil m_httpUtil; private ConfigUtil m_configUtil; - private Properties m_remoteProperties; + private AtomicReference m_configCache; private String m_namespace; - public RemoteConfigRepository(RestTemplate restTemplate, - ConfigServiceLocator serviceLocator, - ConfigUtil configUtil, String namespace) { - m_restTemplate = restTemplate; - m_serviceLocator = serviceLocator; - m_configUtil = configUtil; + public RemoteConfigRepository(String namespace) { m_namespace = namespace; + m_configCache = new AtomicReference<>(); + m_container = ContainerLoader.getDefaultContainer(); + try { + m_configUtil = m_container.lookup(ConfigUtil.class); + m_httpUtil = m_container.lookup(HttpUtil.class); + m_serviceLocator = m_container.lookup(ConfigServiceLocator.class); + } catch (ComponentLookupException e) { + throw new IllegalStateException("Unable to load component!", e); + } } @Override public Properties loadConfig() { - if (m_remoteProperties == null) { + if (m_configCache.get() == null) { initRemoteConfig(); } - Properties result = new Properties(); - result.putAll(m_remoteProperties); - return result; + return transformApolloConfigToProperties(m_configCache.get()); } @Override @@ -55,79 +59,60 @@ public class RemoteConfigRepository implements ConfigRepository { } private void initRemoteConfig() { - ApolloConfig apolloConfig = this.loadApolloConfig(); - m_remoteProperties = new Properties(); - m_remoteProperties.putAll(apolloConfig.getConfigurations()); + m_configCache.set(this.loadApolloConfig()); + } + + private Properties transformApolloConfigToProperties(ApolloConfig apolloConfig) { + Properties result = new Properties(); + result.putAll(apolloConfig.getConfigurations()); + return result; } private ApolloConfig loadApolloConfig() { String appId = m_configUtil.getAppId(); String cluster = m_configUtil.getCluster(); + String uri = getConfigServiceUrl(); + + logger.info("Loading config from {}, appId={}, cluster={}, namespace={}", uri, appId, cluster, + m_namespace); + HttpRequest request = + new HttpRequest(assembleUrl(uri, appId, cluster, m_namespace, m_configCache.get())); + try { - ApolloConfig result = - this.getRemoteConfig(m_restTemplate, getConfigServiceUrl(), - appId, cluster, - m_namespace, - null); - if (result == null) { - return null; + HttpResponse response = m_httpUtil.doGet(request, ApolloConfig.class); + if (response.getStatusCode() == 304) { + return m_configCache.get(); } - logger.info("Loaded config: {}", result); - return result; - } catch (Throwable ex) { - throw new RuntimeException( + + logger.info("Loaded config: {}", response.getBody()); + + return response.getBody(); + } catch (Throwable t) { + String message = String.format("Load Apollo Config failed - appId: %s, cluster: %s, namespace: %s", appId, - cluster, m_namespace), ex); + cluster, m_namespace); + logger.error(message, t); + throw new RuntimeException(message, t); } } + private String assembleUrl(String uri, String appId, String cluster, String namespace, + ApolloConfig previousConfig) { + String path = "/config/%s/%s"; + List params = Lists.newArrayList(appId, cluster); - 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, - namespace); - String path = "/config/{appId}/{cluster}"; - Map paramMap = Maps.newHashMap(); - paramMap.put("appId", appId); - paramMap.put("cluster", cluster); - - if (StringUtils.hasText(namespace)) { - path = path + "/{namespace}"; - paramMap.put("namespace", namespace); + if (!Strings.isNullOrEmpty(namespace)) { + path = path + "/%s"; + params.add(namespace); } if (previousConfig != null) { - path = path + "?releaseId={releaseId}"; - paramMap.put("releaseId", previousConfig.getReleaseId()); + path = path + "?releaseId=%s"; + params.add(String.valueOf(previousConfig.getReleaseId())); } - ResponseEntity response; - - try { - // TODO retry - response = restTemplate.exchange(uri - + path, HttpMethod.GET, new HttpEntity((Void) null), ApolloConfig.class, paramMap); - } catch (Throwable ex) { - throw ex; - } - - if (response == null) { - throw new RuntimeException("Load apollo config failed, response is null"); - } - - if (response.getStatusCode() == HttpStatus.NOT_MODIFIED) { - return null; - } - - if (response.getStatusCode() != HttpStatus.OK) { - throw new RuntimeException( - String.format("Load apollo config failed, response status %s", response.getStatusCode())); - } - - ApolloConfig result = response.getBody(); - return result; + String pathExpanded = String.format(path, params.toArray()); + return uri + pathExpanded; } private String getConfigServiceUrl() { diff --git a/apollo-client/src/main/java/com/ctrip/apollo/internals/SimpleConfig.java b/apollo-client/src/main/java/com/ctrip/apollo/internals/SimpleConfig.java index 2375cf8a1..e5d5c0bb6 100644 --- a/apollo-client/src/main/java/com/ctrip/apollo/internals/SimpleConfig.java +++ b/apollo-client/src/main/java/com/ctrip/apollo/internals/SimpleConfig.java @@ -2,12 +2,16 @@ package com.ctrip.apollo.internals; import com.ctrip.apollo.Config; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.Properties; /** * @author Jason Song(song_s@ctrip.com) */ public class SimpleConfig implements Config { + private static final Logger logger = LoggerFactory.getLogger(SimpleConfig.class); private String m_namespace; private ConfigRepository m_configRepository; private Properties m_configProperties; @@ -22,9 +26,10 @@ public class SimpleConfig implements Config { try { m_configProperties = m_configRepository.loadConfig(); } catch (Throwable ex) { - throw new RuntimeException( - String.format("Init Apollo Remote Config failed - namespace: %s", - m_namespace), ex); + String message = String.format("Init Apollo Remote Config failed - namespace: %s", + m_namespace); + logger.error(message, ex); + throw new RuntimeException(message, ex); } } diff --git a/apollo-client/src/main/java/com/ctrip/apollo/spi/DefaultConfigFactory.java b/apollo-client/src/main/java/com/ctrip/apollo/spi/DefaultConfigFactory.java index d836286aa..a54655819 100644 --- a/apollo-client/src/main/java/com/ctrip/apollo/spi/DefaultConfigFactory.java +++ b/apollo-client/src/main/java/com/ctrip/apollo/spi/DefaultConfigFactory.java @@ -2,11 +2,10 @@ package com.ctrip.apollo.spi; import com.ctrip.apollo.Config; import com.ctrip.apollo.core.utils.ClassLoaderUtil; -import com.ctrip.apollo.internals.*; +import com.ctrip.apollo.internals.DefaultConfig; +import com.ctrip.apollo.internals.LocalFileConfigRepository; import com.ctrip.apollo.internals.RemoteConfigRepository; -import com.ctrip.apollo.util.ConfigUtil; -import org.springframework.web.client.RestTemplate; import org.unidal.lookup.annotation.Named; import java.io.File; @@ -28,20 +27,20 @@ public class DefaultConfigFactory implements ConfigFactory { @Override public Config create(String namespace) { - DefaultConfig defaultConfig = new DefaultConfig(namespace, createLocalConfigRepository(namespace)); + DefaultConfig defaultConfig = + new DefaultConfig(namespace, createLocalConfigRepository(namespace)); return defaultConfig; } LocalFileConfigRepository createLocalConfigRepository(String namespace) { LocalFileConfigRepository localFileConfigLoader = - new LocalFileConfigRepository(m_baseDir, namespace, ConfigUtil.getInstance()); + new LocalFileConfigRepository(m_baseDir, namespace); localFileConfigLoader.setFallback(createRemoteConfigRepository(namespace)); return localFileConfigLoader; } RemoteConfigRepository createRemoteConfigRepository(String namespace) { - return new RemoteConfigRepository(new RestTemplate(), new ConfigServiceLocator(), - ConfigUtil.getInstance(), namespace); + return new RemoteConfigRepository(namespace); } } diff --git a/apollo-client/src/main/java/com/ctrip/apollo/util/ConfigUtil.java b/apollo-client/src/main/java/com/ctrip/apollo/util/ConfigUtil.java index ae3569d17..1abe80aca 100644 --- a/apollo-client/src/main/java/com/ctrip/apollo/util/ConfigUtil.java +++ b/apollo-client/src/main/java/com/ctrip/apollo/util/ConfigUtil.java @@ -1,30 +1,19 @@ package com.ctrip.apollo.util; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.unidal.lookup.annotation.Named; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Properties; import java.util.concurrent.TimeUnit; /** * @author Jason Song(song_s@ctrip.com) */ +@Named(type = ConfigUtil.class) public class ConfigUtil { //TODO read from config? private static final int refreshInterval = 5; private static final TimeUnit refreshIntervalTimeUnit = TimeUnit.MINUTES; - - private static ConfigUtil configUtil = new ConfigUtil(); - - private ConfigUtil() { - } - - public static ConfigUtil getInstance() { - return configUtil; - } + private static final int connectTimeout = 5000; //5 seconds + private static final int readTimeout = 10000; //10 seconds public String getAppId() { // TODO return the actual app id @@ -36,6 +25,14 @@ public class ConfigUtil { return "default"; } + public int getConnectTimeout() { + return connectTimeout; + } + + public int getReadTimeout() { + return readTimeout; + } + public int getRefreshInterval() { return refreshInterval; } diff --git a/apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpRequest.java b/apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpRequest.java new file mode 100644 index 000000000..bc6f195a6 --- /dev/null +++ b/apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpRequest.java @@ -0,0 +1,36 @@ +package com.ctrip.apollo.util.http; + +/** + * @author Jason Song(song_s@ctrip.com) + */ +public class HttpRequest { + private String m_url; + private int m_connectTimeout; + private int m_readTimeout; + + public HttpRequest(String url) { + this.m_url = url; + m_connectTimeout = -1; + m_readTimeout = -1; + } + + public String getUrl() { + return m_url; + } + + public int getConnectTimeout() { + return m_connectTimeout; + } + + public void setConnectTimeout(int connectTimeout) { + this.m_connectTimeout = connectTimeout; + } + + public int getReadTimeout() { + return m_readTimeout; + } + + public void setReadTimeout(int readTimeout) { + this.m_readTimeout = readTimeout; + } +} diff --git a/apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpResponse.java b/apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpResponse.java new file mode 100644 index 000000000..633f365b3 --- /dev/null +++ b/apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpResponse.java @@ -0,0 +1,22 @@ +package com.ctrip.apollo.util.http; + +/** + * @author Jason Song(song_s@ctrip.com) + */ +public class HttpResponse { + private final int m_statusCode; + private final T m_body; + + public HttpResponse(int statusCode, T body) { + this.m_statusCode = statusCode; + this.m_body = body; + } + + public int getStatusCode() { + return m_statusCode; + } + + public T getBody() { + return m_body; + } +} diff --git a/apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpUtil.java b/apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpUtil.java new file mode 100644 index 000000000..fe0661397 --- /dev/null +++ b/apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpUtil.java @@ -0,0 +1,85 @@ +package com.ctrip.apollo.util.http; + +import com.google.common.base.Charsets; +import com.google.gson.Gson; + +import com.ctrip.apollo.util.ConfigUtil; + +import org.unidal.helper.Files; +import org.unidal.lookup.annotation.Inject; +import org.unidal.lookup.annotation.Named; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; + +/** + * @author Jason Song(song_s@ctrip.com) + */ +@Named(type = HttpUtil.class) +public class HttpUtil { + @Inject + private ConfigUtil m_configUtil; + private Gson gson; + + public HttpUtil() { + gson = new Gson(); + } + + /** + * Do get operation for the http request + * + * @throws RuntimeException if any error happened or response code is neither 200 nor 304 + */ + public HttpResponse doGet(HttpRequest httpRequest, Class responseType) { + InputStream is = null; + try { + HttpURLConnection conn = (HttpURLConnection) new URL(httpRequest.getUrl()).openConnection(); + + conn.setRequestMethod("GET"); + + if (httpRequest.getConnectTimeout() < 0) { + conn.setConnectTimeout(m_configUtil.getConnectTimeout()); + } else { + conn.setConnectTimeout(httpRequest.getConnectTimeout()); + } + + if (httpRequest.getReadTimeout() < 0) { + conn.setReadTimeout(m_configUtil.getReadTimeout()); + } else { + conn.setReadTimeout(httpRequest.getReadTimeout()); + } + + conn.connect(); + + int statusCode = conn.getResponseCode(); + + 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)); + } + + if (statusCode == 304) { + return new HttpResponse<>(statusCode, null); + } + + throw new RuntimeException( + String.format("Get operation failed, status code - %d", statusCode)); + + } catch (Throwable ex) { + throw new RuntimeException("Could not complete get operation", ex); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + //ignore + } + } + } + + } + +} diff --git a/apollo-client/src/main/resources/META-INF/plexus/components.xml b/apollo-client/src/main/resources/META-INF/plexus/components.xml index d380cf442..d0a38de9c 100644 --- a/apollo-client/src/main/resources/META-INF/plexus/components.xml +++ b/apollo-client/src/main/resources/META-INF/plexus/components.xml @@ -26,5 +26,27 @@ + + com.ctrip.apollo.util.ConfigUtil + com.ctrip.apollo.util.ConfigUtil + + + com.ctrip.apollo.internals.ConfigServiceLocator + com.ctrip.apollo.internals.ConfigServiceLocator + + + com.ctrip.apollo.util.http.HttpUtil + + + + + com.ctrip.apollo.util.http.HttpUtil + com.ctrip.apollo.util.http.HttpUtil + + + com.ctrip.apollo.util.ConfigUtil + + + diff --git a/apollo-client/src/test/java/com/ctrip/apollo/internals/DefaultConfigTest.java b/apollo-client/src/test/java/com/ctrip/apollo/internals/DefaultConfigTest.java index 1205bc32d..193a396b5 100644 --- a/apollo-client/src/test/java/com/ctrip/apollo/internals/DefaultConfigTest.java +++ b/apollo-client/src/test/java/com/ctrip/apollo/internals/DefaultConfigTest.java @@ -28,7 +28,7 @@ public class DefaultConfigTest { @Before public void setUp() throws Exception { someResourceDir = new File(ClassLoaderUtil.getClassPath() + "/META-INF/config"); - someResourceDir.mkdir(); + someResourceDir.mkdirs(); someNamespace = "someName"; configRepository = mock(ConfigRepository.class); } diff --git a/apollo-client/src/test/java/com/ctrip/apollo/internals/LocalFileConfigRepositoryTest.java b/apollo-client/src/test/java/com/ctrip/apollo/internals/LocalFileConfigRepositoryTest.java index 5392b25d1..075d8bbba 100644 --- a/apollo-client/src/test/java/com/ctrip/apollo/internals/LocalFileConfigRepositoryTest.java +++ b/apollo-client/src/test/java/com/ctrip/apollo/internals/LocalFileConfigRepositoryTest.java @@ -8,6 +8,7 @@ import com.ctrip.apollo.util.ConfigUtil; 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; @@ -21,15 +22,17 @@ import static org.mockito.Mockito.when; /** * Created by Jason on 4/9/16. */ -public class LocalFileConfigRepositoryTest { +public class LocalFileConfigRepositoryTest extends ComponentTestCase { private File someBaseDir; private String someNamespace; private ConfigRepository fallbackRepo; private Properties someProperties; - private ConfigUtil someConfigUtil; + private static String someAppId = "someApp"; + private static String someCluster = "someCluster"; @Before public void setUp() throws Exception { + super.setUp(); someBaseDir = new File("src/test/resources/config-cache"); someBaseDir.mkdir(); @@ -39,12 +42,7 @@ public class LocalFileConfigRepositoryTest { fallbackRepo = mock(ConfigRepository.class); when(fallbackRepo.loadConfig()).thenReturn(someProperties); - String someAppId = "someApp"; - String someCluster = "someCluster"; - someConfigUtil = mock(ConfigUtil.class); - when(someConfigUtil.getAppId()).thenReturn(someAppId); - when(someConfigUtil.getCluster()).thenReturn(someCluster); - + defineComponent(ConfigUtil.class, MockConfigUtil.class); } @After @@ -66,8 +64,8 @@ public class LocalFileConfigRepositoryTest { } private String assembleLocalCacheFileName() { - return String.format("%s-%s-%s.properties", someConfigUtil.getAppId(), - someConfigUtil.getCluster(), someNamespace); + return String.format("%s-%s-%s.properties", someAppId, + someCluster, someNamespace); } @Test @@ -84,7 +82,7 @@ public class LocalFileConfigRepositoryTest { Files.write(someKey + "=" + someValue, file, Charsets.UTF_8); - LocalFileConfigRepository localRepo = new LocalFileConfigRepository(someBaseDir, someNamespace, someConfigUtil); + LocalFileConfigRepository localRepo = new LocalFileConfigRepository(someBaseDir, someNamespace); Properties properties = localRepo.loadConfig(); assertEquals(someValue, properties.getProperty(someKey)); @@ -95,7 +93,7 @@ public class LocalFileConfigRepositoryTest { public void testLoadConfigWithNoLocalFile() throws Exception { LocalFileConfigRepository localFileConfigRepository = - new LocalFileConfigRepository(someBaseDir, someNamespace, someConfigUtil); + new LocalFileConfigRepository(someBaseDir, someNamespace); localFileConfigRepository.setFallback(fallbackRepo); @@ -109,7 +107,7 @@ public class LocalFileConfigRepositoryTest { @Test public void testLoadConfigWithNoLocalFileMultipleTimes() throws Exception { LocalFileConfigRepository localRepo = - new LocalFileConfigRepository(someBaseDir, someNamespace, someConfigUtil); + new LocalFileConfigRepository(someBaseDir, someNamespace); localRepo.setFallback(fallbackRepo); @@ -117,7 +115,7 @@ public class LocalFileConfigRepositoryTest { LocalFileConfigRepository anotherLocalRepoWithNoFallback = - new LocalFileConfigRepository(someBaseDir, someNamespace, someConfigUtil); + new LocalFileConfigRepository(someBaseDir, someNamespace); Properties anotherProperties = anotherLocalRepoWithNoFallback.loadConfig(); @@ -127,4 +125,16 @@ public class LocalFileConfigRepositoryTest { } -} \ No newline at end of file + public static class MockConfigUtil extends ConfigUtil { + @Override + public String getAppId() { + return someAppId; + } + + @Override + public String getCluster() { + return someCluster; + } + } + +} diff --git a/apollo-client/src/test/java/com/ctrip/apollo/internals/RemoteConfigRepositoryTest.java b/apollo-client/src/test/java/com/ctrip/apollo/internals/RemoteConfigRepositoryTest.java index 4456bc5ba..3e8eafa4d 100644 --- a/apollo-client/src/test/java/com/ctrip/apollo/internals/RemoteConfigRepositoryTest.java +++ b/apollo-client/src/test/java/com/ctrip/apollo/internals/RemoteConfigRepositoryTest.java @@ -6,26 +6,22 @@ import com.google.common.collect.Maps; import com.ctrip.apollo.core.dto.ApolloConfig; 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 org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestTemplate; +import org.unidal.lookup.ComponentTestCase; +import java.util.List; import java.util.Map; import java.util.Properties; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyMap; -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -33,27 +29,23 @@ import static org.mockito.Mockito.when; * Created by Jason on 4/9/16. */ @RunWith(MockitoJUnitRunner.class) -public class RemoteConfigRepositoryTest { - @Mock - private RestTemplate restTemplate; +public class RemoteConfigRepositoryTest extends ComponentTestCase { @Mock private ConfigServiceLocator configServiceLocator; private String someNamespace; @Mock - private ResponseEntity someResponse; + private static HttpResponse someResponse; @Mock private ConfigUtil someConfigUtil; @Before public void setUp() throws Exception { + super.setUp(); someNamespace = "someName"; - String someServerUrl = "http://someServer"; - mockConfigServiceLocator(someServerUrl); - String someAppId = "someApp"; - String someCluster = "someCluster"; - when(someConfigUtil.getAppId()).thenReturn(someAppId); - when(someConfigUtil.getCluster()).thenReturn(someCluster); + defineComponent(ConfigUtil.class, MockConfigUtil.class); + defineComponent(ConfigServiceLocator.class, MockConfigServiceLocator.class); + defineComponent(HttpUtil.class, MockHttpUtil.class); } @Test @@ -64,13 +56,10 @@ public class RemoteConfigRepositoryTest { configurations.put(someKey, someValue); ApolloConfig someApolloConfig = assembleApolloConfig(configurations); - when(someResponse.getStatusCode()).thenReturn(HttpStatus.OK); + when(someResponse.getStatusCode()).thenReturn(200); when(someResponse.getBody()).thenReturn(someApolloConfig); - when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), - eq(ApolloConfig.class), anyMap())).thenReturn(someResponse); - - RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(restTemplate, configServiceLocator, someConfigUtil, someNamespace); + RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(someNamespace); Properties config = remoteConfigRepository.loadConfig(); assertEquals(configurations, config); @@ -79,11 +68,9 @@ public class RemoteConfigRepositoryTest { @Test(expected = RuntimeException.class) public void testGetRemoteConfigWithServerError() throws Exception { - when(someResponse.getStatusCode()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR); - when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), - eq(ApolloConfig.class), anyMap())).thenReturn(someResponse); + when(someResponse.getStatusCode()).thenReturn(500); - RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(restTemplate, configServiceLocator, someConfigUtil, someNamespace); + RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(someNamespace); remoteConfigRepository.loadConfig(); } @@ -99,10 +86,35 @@ public class RemoteConfigRepositoryTest { return apolloConfig; } - private void mockConfigServiceLocator(String serverUrl) { - ServiceDTO serviceDTO = mock(ServiceDTO.class); + public static class MockConfigUtil extends ConfigUtil { + @Override + public String getAppId() { + return "someApp"; + } - when(serviceDTO.getHomepageUrl()).thenReturn(serverUrl); - when(configServiceLocator.getConfigServices()).thenReturn(Lists.newArrayList(serviceDTO)); + @Override + public String getCluster() { + return "someCluster"; + } } + + public static class MockConfigServiceLocator extends ConfigServiceLocator { + @Override + public List getConfigServices() { + String someServerUrl = "http://someServer"; + + ServiceDTO serviceDTO = mock(ServiceDTO.class); + + when(serviceDTO.getHomepageUrl()).thenReturn(someServerUrl); + return Lists.newArrayList(serviceDTO); + } + } + + public static class MockHttpUtil extends HttpUtil { + @Override + public HttpResponse doGet(HttpRequest httpRequest, Class responseType) { + return (HttpResponse) someResponse; + } + } + } diff --git a/apollo-core/pom.xml b/apollo-core/pom.xml index 659db5742..6bae25f48 100644 --- a/apollo-core/pom.xml +++ b/apollo-core/pom.xml @@ -15,12 +15,8 @@ - com.fasterxml.jackson.core - jackson-annotations - - - com.fasterxml.jackson.core - jackson-databind + com.google.code.gson + gson diff --git a/apollo-core/src/main/java/com/ctrip/apollo/core/dto/ApolloConfig.java b/apollo-core/src/main/java/com/ctrip/apollo/core/dto/ApolloConfig.java index 2a4413124..ca4f01895 100644 --- a/apollo-core/src/main/java/com/ctrip/apollo/core/dto/ApolloConfig.java +++ b/apollo-core/src/main/java/com/ctrip/apollo/core/dto/ApolloConfig.java @@ -2,9 +2,6 @@ package com.ctrip.apollo.core.dto; import com.google.common.base.MoreObjects; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Map; /** @@ -22,11 +19,10 @@ public class ApolloConfig { private long releaseId; - @JsonCreator - public ApolloConfig(@JsonProperty("appId") String appId, - @JsonProperty("cluster") String cluster, - @JsonProperty("namespace") String namespace, - @JsonProperty("releaseId") long releaseId) { + public ApolloConfig(String appId, + String cluster, + String namespace, + long releaseId) { super(); this.appId = appId; this.cluster = cluster; -- GitLab