提交 854cec2f 编写于 作者: Y Yiming Liu

Merge pull request #16 from nobodyiam/apollo_config_server_merge

Config Server to load configurations from db
......@@ -19,4 +19,7 @@ target
# Idea
.idea
*.iml
\ No newline at end of file
*.iml
# git
*.orig
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>apollo</artifactId>
<groupId>com.ctrip.apollo</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-biz</artifactId>
<name>Apollo BizLogic</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
package com.ctrip.apollo.biz.entity;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Entity
@Where(clause = "isDeleted = 0")
@SQLDelete(sql = "Update Cluster set isDeleted = 1 where id = ?")
public class Cluster {
@Id
@GeneratedValue
private long id;
private String name;
private long appId;
private boolean isDeleted;
public Cluster() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getAppId() {
return appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public boolean isDeleted() {
return isDeleted;
}
public void setDeleted(boolean deleted) {
isDeleted = deleted;
}
}
package com.ctrip.apollo.biz.entity;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Entity
@Where(clause = "isDeleted = 0")
@SQLDelete(sql = "Update Release set isDeleted = 1 where id = ?")
public class Release {
@Id
@GeneratedValue
private long id;
private String name;
private long appId;
private String comment;
private boolean isDeleted;
public Release() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getAppId() {
return appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public boolean isDeleted() {
return isDeleted;
}
public void setDeleted(boolean deleted) {
isDeleted = deleted;
}
}
package com.ctrip.apollo.biz.entity;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Entity
@Where(clause = "isDeleted = 0")
@SQLDelete(sql = "Update ReleaseSnapShot set isDeleted = 1 where id = ?")
public class ReleaseSnapShot {
@Id
@GeneratedValue
private long id;
private long releaseId;
private String clusterName;
private String configurations;
private boolean isDeleted;
public ReleaseSnapShot() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getReleaseId() {
return releaseId;
}
public void setReleaseId(long releaseId) {
this.releaseId = releaseId;
}
public String getClusterName() {
return clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public String getConfigurations() {
return configurations;
}
public void setConfigurations(String configurations) {
this.configurations = configurations;
}
public boolean isDeleted() {
return isDeleted;
}
public void setDeleted(boolean deleted) {
isDeleted = deleted;
}
}
package com.ctrip.apollo.biz.entity;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Entity
@Where(clause = "isDeleted = 0")
@SQLDelete(sql = "Update Version set isDeleted = 1 where id = ?")
public class Version {
@Id
@GeneratedValue
private long id;
private String name;
private long appId;
private long releaseId;
private long parentVersion;
private boolean isDeleted;
public Version() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getAppId() {
return appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public long getReleaseId() {
return releaseId;
}
public void setReleaseId(long releaseId) {
this.releaseId = releaseId;
}
public long getParentVersion() {
return parentVersion;
}
public void setParentVersion(long parentVersion) {
this.parentVersion = parentVersion;
}
public boolean isDeleted() {
return isDeleted;
}
public void setDeleted(boolean deleted) {
isDeleted = deleted;
}
}
package com.ctrip.apollo.biz.repository;
import com.ctrip.apollo.biz.entity.ReleaseSnapShot;
import org.springframework.data.repository.PagingAndSortingRepository;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface ReleaseSnapShotRepository extends PagingAndSortingRepository<ReleaseSnapShot, Long> {
ReleaseSnapShot findByReleaseIdAndClusterName(long releaseId, String clusterName);
}
package com.ctrip.apollo.biz.repository;
import com.ctrip.apollo.biz.entity.Version;
import org.springframework.data.repository.PagingAndSortingRepository;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface VersionRepository extends PagingAndSortingRepository<Version, Long> {
Version findByAppIdAndName(long appId, String name);
}
package com.ctrip.apollo.biz.service;
import com.ctrip.apollo.core.model.ApolloConfig;
/**
* Config Service
* @author Jason Song(song_s@ctrip.com)
*/
public interface ConfigService {
/**
* Load configuration from database
* @param appId
* @param clusterName
* @param versionName
* @return
*/
ApolloConfig loadConfig(long appId, String clusterName, String versionName);
}
package com.ctrip.apollo.biz.service.impl;
import com.ctrip.apollo.biz.entity.ReleaseSnapShot;
import com.ctrip.apollo.biz.entity.Version;
import com.ctrip.apollo.biz.repository.ReleaseSnapShotRepository;
import com.ctrip.apollo.biz.repository.VersionRepository;
import com.ctrip.apollo.biz.service.ConfigService;
import com.ctrip.apollo.core.model.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.io.IOException;
import java.util.Map;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Service("configService")
public class ConfigServiceImpl implements ConfigService {
@Autowired
private VersionRepository versionRepository;
@Autowired
private ReleaseSnapShotRepository releaseSnapShotRepository;
@Autowired
private ObjectMapper objectMapper;
private TypeReference<Map<String, Object>> configurationTypeReference =
new TypeReference<Map<String, Object>>() {
};
@Override
public ApolloConfig loadConfig(long appId, String clusterName, String versionName) {
Version version = versionRepository.findByAppIdAndName(appId, versionName);
if (version == null) {
return null;
}
ReleaseSnapShot releaseSnapShot =
releaseSnapShotRepository.findByReleaseIdAndClusterName(version.getReleaseId(), clusterName);
if (releaseSnapShot == null) {
return null;
}
return assembleConfig(version, releaseSnapShot);
}
private ApolloConfig assembleConfig(Version version, ReleaseSnapShot releaseSnapShot) {
ApolloConfig config =
new ApolloConfig(version.getAppId(), releaseSnapShot.getClusterName(), version.getName(), version.getReleaseId());
config.setConfigurations(transformConfigurationToMap(releaseSnapShot.getConfigurations()));
return config;
}
Map<String, Object> transformConfigurationToMap(String configurations) {
try {
return objectMapper.readValue(configurations, configurationTypeReference);
} catch (IOException e) {
e.printStackTrace();
return Maps.newHashMap();
}
}
}
spring.datasource.url = jdbc:h2:file:~/fxapolloconfigdb;mode=mysql
spring.datasource.username = sa
spring.datasource.password =
spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.EJB3NamingStrategy
INSERT INTO Cluster (AppId, IsDeleted, Name) VALUES (100, 0, 'default');
INSERT INTO Cluster (AppId, IsDeleted, Name) VALUES (101, 0, 'default');
INSERT INTO Version (AppId, IsDeleted, Name, ReleaseId) VALUES (101, 0, '1.0', 1);
INSERT INTO Version (AppId, IsDeleted, Name, ReleaseId) VALUES (102, 0, '1.0', 2);
INSERT INTO RELEASESNAPSHOT (ClusterName, IsDeleted, ReleaseId, Configurations) VALUES ('default', 0, 1, '{"apollo.foo":"bar"}');
INSERT INTO RELEASESNAPSHOT (ClusterName, IsDeleted, ReleaseId, Configurations) VALUES ('default', 0, 2, '{"apollo.bar":"foo"}');
package com.ctrip.apollo.biz;
import com.ctrip.apollo.biz.service.impl.ConfigServiceImplTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
ConfigServiceImplTest.class
})
public class AllTests {
}
package com.ctrip.apollo.biz.service.impl;
import com.ctrip.apollo.biz.entity.ReleaseSnapShot;
import com.ctrip.apollo.biz.entity.Version;
import com.ctrip.apollo.biz.repository.ReleaseSnapShotRepository;
import com.ctrip.apollo.biz.repository.VersionRepository;
import com.ctrip.apollo.core.model.ApolloConfig;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
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.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.*;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith(MockitoJUnitRunner.class)
public class ConfigServiceImplTest {
@Mock
private VersionRepository versionRepository;
@Mock
private ReleaseSnapShotRepository releaseSnapShotRepository;
@Mock
private ObjectMapper objectMapper;
private ConfigServiceImpl configService;
@Before
public void setUp() throws Exception {
configService = new ConfigServiceImpl();
ReflectionTestUtils.setField(configService, "versionRepository", versionRepository);
ReflectionTestUtils.setField(configService, "releaseSnapShotRepository", releaseSnapShotRepository);
ReflectionTestUtils.setField(configService, "objectMapper", objectMapper);
}
@Test
public void testLoadConfig() throws Exception {
long someAppId = 1;
String someClusterName = "someClusterName";
String someVersionName = "someVersionName";
long someReleaseId = 1;
String someValidConfiguration = "{\"apollo.bar\": \"foo\"}";
Version someVersion = assembleVersion(someAppId, someVersionName, someReleaseId);
ReleaseSnapShot someReleaseSnapShot = assembleReleaseSnapShot(someReleaseId, someClusterName, someValidConfiguration);
Map<String, Object> someMap = Maps.newHashMap();
when(versionRepository.findByAppIdAndName(someAppId, someVersionName)).thenReturn(someVersion);
when(releaseSnapShotRepository.findByReleaseIdAndClusterName(someReleaseId, someClusterName)).thenReturn(someReleaseSnapShot);
when(objectMapper.readValue(eq(someValidConfiguration), (TypeReference) anyObject())).thenReturn(someMap);
ApolloConfig result = configService.loadConfig(someAppId, someClusterName, someVersionName);
assertEquals(someAppId, result.getAppId());
assertEquals(someClusterName, result.getCluster());
assertEquals(someVersionName, result.getVersion());
assertEquals(someReleaseId, result.getReleaseId());
assertEquals(someMap, result.getConfigurations());
}
private Version assembleVersion(long appId, String versionName, long releaseId) {
Version version = new Version();
version.setAppId(appId);
version.setName(versionName);
version.setReleaseId(releaseId);
return version;
}
private ReleaseSnapShot assembleReleaseSnapShot(long releaseId, String clusterName, String configurations) {
ReleaseSnapShot releaseSnapShot = new ReleaseSnapShot();
releaseSnapShot.setReleaseId(releaseId);
releaseSnapShot.setClusterName(clusterName);
releaseSnapShot.setConfigurations(configurations);
return releaseSnapShot;
}
@Test
public void testTransformConfigurationToMapSuccessful() throws Exception {
String someValidConfiguration = "{\"apollo.bar\": \"foo\"}";
Map<String, String> someMap = Maps.newHashMap();
when(objectMapper.readValue(eq(someValidConfiguration), (TypeReference) anyObject())).thenReturn(someMap);
Map<String, Object> 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<String, Object> result = configService.transformConfigurationToMap(someInvalidConfiguration);
assertTrue(result.isEmpty());
}
}
......@@ -30,7 +30,6 @@
<artifactId>spring-web</artifactId>
</dependency>
<!-- end of spring -->
<!-- util -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
......
......@@ -20,14 +20,14 @@ import org.springframework.core.env.MutablePropertySources;
*
* @author Jason Song(song_s@ctrip.com)
*/
public class ApolloConfig implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ApplicationContextAware {
public class ApolloConfigManager implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ApplicationContextAware {
public static final String APOLLO_PROPERTY_SOURCE_NAME = "ApolloConfigProperties";
private ConfigLoader configLoader;
private ConfigurableApplicationContext applicationContext;
public ApolloConfig() {
public ApolloConfigManager() {
this.configLoader = ConfigLoaderFactory.getInstance().getRemoteConfigLoader();
}
......
......@@ -3,8 +3,7 @@ package com.ctrip.apollo.client.loader.impl;
import com.ctrip.apollo.client.loader.ConfigLoader;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.environment.Environment;
import com.ctrip.apollo.core.environment.PropertySource;
import com.ctrip.apollo.core.model.ApolloConfig;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -77,54 +76,58 @@ public class RemoteConfigLoader implements ConfigLoader {
}
void doLoadRemoteApolloConfig(List<ApolloRegistry> apolloRegistries, CompositePropertySource compositePropertySource) throws Throwable {
List<Future<CompositePropertySource>> futures = Lists.newArrayList();
List<Future<MapPropertySource>> futures = Lists.newArrayList();
for (final ApolloRegistry apolloRegistry : apolloRegistries) {
futures.add(executorService.submit(new Callable<CompositePropertySource>() {
futures.add(executorService.submit(new Callable<MapPropertySource>() {
@Override
public CompositePropertySource call() throws Exception {
public MapPropertySource call() throws Exception {
return loadSingleApolloConfig(apolloRegistry.getAppId(), apolloRegistry.getVersion());
}
}));
}
for (Future<CompositePropertySource> future : futures) {
for (Future<MapPropertySource> future : futures) {
try {
compositePropertySource.addPropertySource(future.get());
MapPropertySource result = future.get();
if (result == null) {
continue;
}
compositePropertySource.addPropertySource(result);
} catch (ExecutionException e) {
throw e.getCause();
}
}
}
CompositePropertySource loadSingleApolloConfig(String appId, String version) {
CompositePropertySource composite = new CompositePropertySource(appId + "-" + version);
Environment result =
this.getRemoteEnvironment(restTemplate, configUtil.getConfigServerUrl(), appId, configUtil.getCluster(), version);
MapPropertySource loadSingleApolloConfig(long appId, String version) {
ApolloConfig result =
this.getRemoteConfig(restTemplate, configUtil.getConfigServerUrl(), appId, configUtil.getCluster(), version);
if (result == null) {
logger.error("Loaded environment as null...");
return composite;
logger.error("Loaded config null...");
return null;
}
logger.info("Loaded environment: name={}, cluster={}, label={}, version={}", result.getName(), result.getProfiles(), result.getLabel(), result.getVersion());
logger.info("Loaded config: {}", result);
for (PropertySource source : result.getPropertySources()) {
composite.addPropertySource(new MapPropertySource(source.getName(), source.getSource()));
}
return composite;
return new MapPropertySource(assemblePropertySourceName(result), result.getConfigurations());
}
private String assemblePropertySourceName(ApolloConfig apolloConfig) {
return String.format("%d-%s-%s-%d", apolloConfig.getAppId(), apolloConfig.getCluster(), apolloConfig.getVersion(), apolloConfig.getReleaseId());
}
Environment getRemoteEnvironment(RestTemplate restTemplate, String uri, String name, String cluster, String release) {
logger.info("Loading environment from {}, name={}, cluster={}, release={}", uri, name, cluster, release);
String path = "/{name}/{cluster}";
Object[] args = new String[] {name, cluster};
if (StringUtils.hasText(release)) {
args = new String[] {name, cluster, release};
path = path + "/{release}";
ApolloConfig getRemoteConfig(RestTemplate restTemplate, String uri, long appId, String cluster, String version) {
logger.info("Loading config from {}, appId={}, cluster={}, version={}", uri, appId, cluster, version);
String path = "/{appId}/{cluster}";
Object[] args = new String[] {String.valueOf(appId), cluster};
if (StringUtils.hasText(version)) {
args = new String[] {String.valueOf(appId), cluster, version};
path = path + "/{version}";
}
ResponseEntity<Environment> response = null;
ResponseEntity<ApolloConfig> response = null;
try {
// TODO retry
response = restTemplate.exchange(uri
+ path, HttpMethod.GET, new HttpEntity<Void>((Void) null), Environment.class, args);
+ path, HttpMethod.GET, new HttpEntity<Void>((Void) null), ApolloConfig.class, args);
} catch (Exception e) {
throw e;
}
......@@ -132,7 +135,7 @@ public class RemoteConfigLoader implements ConfigLoader {
if (response == null || response.getStatusCode() != HttpStatus.OK) {
return null;
}
Environment result = response.getBody();
ApolloConfig result = response.getBody();
return result;
}
......
......@@ -4,19 +4,19 @@ package com.ctrip.apollo.client.model;
* @author Jason Song(song_s@ctrip.com)
*/
public class ApolloRegistry {
private String appId;
private long appId;
private String version;
public String getAppId() {
public long getAppId() {
return appId;
}
public String getVersion() {
return version;
public void setAppId(long appId) {
this.appId = appId;
}
public void setAppId(String appId) {
this.appId = appId;
public String getVersion() {
return version;
}
public void setVersion(String version) {
......
package com.ctrip.apollo.client.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class ClassLoaderUtil {
private static final Logger logger = LoggerFactory.getLogger(ClassLoaderUtil.class);
private static ClassLoader loader = Thread.currentThread().getContextClassLoader();
static {
if (loader == null) {
logger.info("Using system class loader");
loader = ClassLoader.getSystemClassLoader();
}
}
public static ClassLoader getLoader() {
return loader;
}
}
......@@ -47,7 +47,7 @@ public class ConfigUtil {
public List<ApolloRegistry> loadApolloRegistries() throws IOException {
List<URL> resourceUrls =
Collections.list(Thread.currentThread().getContextClassLoader().getResources(APOLLO_PROPERTY));
Collections.list(ClassLoaderUtil.getLoader().getResources(APOLLO_PROPERTY));
List<ApolloRegistry> registries =
FluentIterable.from(resourceUrls).transform(new Function<URL, ApolloRegistry>() {
@Override
......@@ -57,7 +57,7 @@ public class ConfigUtil {
return null;
}
ApolloRegistry registry = new ApolloRegistry();
registry.setAppId(properties.getProperty(Constants.APP_ID));
registry.setAppId(Long.parseLong(properties.getProperty(Constants.APP_ID)));
registry.setVersion(properties.getProperty(Constants.VERSION, Constants.DEFAULT_VERSION_NAME));
return registry;
}
......
......@@ -8,7 +8,7 @@ import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
ApolloConfigTest.class, RemoteConfigLoaderTest.class, ConfigUtilTest.class
ApolloConfigManagerTest.class, RemoteConfigLoaderTest.class, ConfigUtilTest.class
})
public class AllTests {
......
......@@ -14,15 +14,16 @@ import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.test.util.ReflectionTestUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith(MockitoJUnitRunner.class)
public class ApolloConfigTest {
private ApolloConfig apolloConfig;
public class ApolloConfigManagerTest {
private ApolloConfigManager apolloConfigManager;
@Mock
private ConfigLoader configLoader;
@Mock
......@@ -34,19 +35,19 @@ public class ApolloConfigTest {
@Before
public void setUp() {
apolloConfig = new ApolloConfig();
apolloConfigManager = new ApolloConfigManager();
when(applicationContext.getEnvironment()).thenReturn(env);
when(env.getPropertySources()).thenReturn(mutablePropertySources);
apolloConfig.setApplicationContext(applicationContext);
ReflectionTestUtils.setField(apolloConfig, "configLoader", configLoader);
apolloConfigManager.setApplicationContext(applicationContext);
ReflectionTestUtils.setField(apolloConfigManager, "configLoader", configLoader);
}
@Test(expected = RuntimeException.class)
public void testInvalidApplicationContext() {
ApplicationContext someInvalidApplication = mock(ApplicationContext.class);
apolloConfig.setApplicationContext(someInvalidApplication);
apolloConfigManager.setApplicationContext(someInvalidApplication);
}
@Test
......@@ -56,14 +57,14 @@ public class ApolloConfigTest {
when(configLoader.loadPropertySource()).thenReturn(somePropertySource);
apolloConfig.preparePropertySource();
apolloConfigManager.preparePropertySource();
verify(configLoader, times(1)).loadPropertySource();
verify(mutablePropertySources, times(1)).addFirst(captor.capture());
final CompositePropertySource insertedPropertySource = captor.getValue();
assertEquals(ApolloConfig.APOLLO_PROPERTY_SOURCE_NAME, insertedPropertySource.getName());
assertEquals(ApolloConfigManager.APOLLO_PROPERTY_SOURCE_NAME, insertedPropertySource.getName());
assertTrue(insertedPropertySource.getPropertySources().contains(somePropertySource));
}
......
......@@ -2,10 +2,7 @@ package com.ctrip.apollo.client.loader.impl;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.environment.Environment;
import com.ctrip.apollo.core.environment.PropertySource;
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import com.ctrip.apollo.core.model.ApolloConfig;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.junit.Before;
......@@ -14,9 +11,9 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.MapPropertySource;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
......@@ -41,10 +38,12 @@ public class RemoteConfigLoaderTest {
@Test
public void testLoadPropertySource() throws Exception {
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry("someAppId", "someVersion");
ApolloRegistry anotherApolloRegistry = assembleSomeApolloRegistry("anotherAppId", "anotherVersion");
CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
CompositePropertySource anotherPropertySource = mock(CompositePropertySource.class);
long someAppId = 100;
long anotherAppId = 101;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloRegistry anotherApolloRegistry = assembleSomeApolloRegistry(anotherAppId, "anotherVersion");
MapPropertySource somePropertySource = mock(MapPropertySource.class);
MapPropertySource anotherPropertySource = mock(MapPropertySource.class);
doReturn(Lists.newArrayList(someApolloRegistry, anotherApolloRegistry)).when(configUtil).loadApolloRegistries();
doReturn(somePropertySource).when(remoteConfigLoader).loadSingleApolloConfig(someApolloRegistry.getAppId(), someApolloRegistry.getVersion());
......@@ -68,7 +67,8 @@ public class RemoteConfigLoaderTest {
@Test(expected = RuntimeException.class)
public void testLoadPropertySourceWithError() throws Exception {
Exception someException = mock(Exception.class);
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry("someAppId", "someVersion");
long someAppId = 100;
ApolloRegistry someApolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
doReturn(Lists.newArrayList(someApolloRegistry)).when(configUtil).loadApolloRegistries();
doThrow(someException).when(remoteConfigLoader).loadSingleApolloConfig(someApolloRegistry.getAppId(), someApolloRegistry.getVersion());
......@@ -78,35 +78,19 @@ public class RemoteConfigLoaderTest {
@Test
public void testLoadSingleApolloConfig() throws Exception {
Environment someRemoteEnv = mock(Environment.class);
String someSourceName = "someSource";
String anotherSourceName = "anotherSource";
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
Map<String, Object> someMap = Maps.newHashMap();
PropertySource somePropertySource = mock(PropertySource.class);
PropertySource anotherPropertySource = mock(PropertySource.class);
when(somePropertySource.getSource()).thenReturn(someMap);
when(somePropertySource.getName()).thenReturn(someSourceName);
when(anotherPropertySource.getSource()).thenReturn(someMap);
when(anotherPropertySource.getName()).thenReturn(anotherSourceName);
when(someRemoteEnv.getPropertySources()).thenReturn(Lists.newArrayList(somePropertySource, anotherPropertySource));
doReturn(someRemoteEnv).when(remoteConfigLoader).getRemoteEnvironment(any(RestTemplate.class), anyString(), anyString(), anyString(), anyString());
when(someApolloConfig.getConfigurations()).thenReturn(someMap);
doReturn(someApolloConfig).when(remoteConfigLoader).getRemoteConfig(any(RestTemplate.class), anyString(), anyLong(), anyString(), anyString());
CompositePropertySource result = remoteConfigLoader.loadSingleApolloConfig("someAppId", "someVersion");
long someAppId = 100;
MapPropertySource result = remoteConfigLoader.loadSingleApolloConfig(someAppId, "someVersion");
assertEquals(2, result.getPropertySources().size());
List<String> resultPropertySourceNames = FluentIterable.from(result.getPropertySources()).transform(new Function<org.springframework.core.env.PropertySource<?>, String>() {
@Override
public String apply(org.springframework.core.env.PropertySource<?> input) {
return input.getName();
}
}).toList();
assertTrue(resultPropertySourceNames.containsAll(Lists.newArrayList(someSourceName, anotherSourceName)));
assertEquals(someMap, result.getSource());
}
private ApolloRegistry assembleSomeApolloRegistry(String someAppId, String someVersion) {
private ApolloRegistry assembleSomeApolloRegistry(long someAppId, String someVersion) {
ApolloRegistry someApolloRegistry = new ApolloRegistry();
someApolloRegistry.setAppId(someAppId);
someApolloRegistry.setVersion(someVersion);
......
......@@ -17,7 +17,8 @@ import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
/**
......@@ -41,7 +42,7 @@ public class ConfigUtilTest {
Properties someProperties = mock(Properties.class);
preparePropertiesFromLocalResource(someProperties);
String someAppId = "someApp";
String someAppId = "1";
String someVersionId = "someVersion";
when(someProperties.containsKey(Constants.APP_ID)).thenReturn(true);
......@@ -52,7 +53,7 @@ public class ConfigUtilTest {
ApolloRegistry apolloRegistry = apolloRegistries.get(0);
assertEquals(1, apolloRegistries.size());
assertEquals(someAppId, apolloRegistry.getAppId());
assertEquals(Long.parseLong(someAppId), apolloRegistry.getAppId());
assertEquals(someVersionId, apolloRegistry.getVersion());
}
......
......@@ -10,32 +10,52 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-configserver</artifactId>
<name>Apollo ConfigServer</name>
<packaging>war</packaging>
<dependencies>
<!-- apollo -->
<dependency>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<artifactId>apollo-biz</artifactId>
</dependency>
<!-- end of apollo -->
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!-- end of web -->
<!-- redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<!-- end of redis -->
<!-- eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- end of eureka -->
<!-- jsp -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<!-- end of jsp -->
</dependencies>
<build>
<plugins>
......
package com.ctrip.apollo.configserver;
package com.ctrip.apollo;
import com.jcraft.jsch.JSch;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.config.server.EnableConfigServer;
/**
* Spring boot application entry point
* @author Jason Song(song_s@ctrip.com)
*/
@SpringBootApplication
@EnableConfigServer
@EnableDiscoveryClient
public class ConfigServerApplication {
public static void main(String[] args) {
JSch.setConfig("StrictHostKeyChecking", "no");//for git server key
SpringApplication.run(ConfigServerApplication.class, args);
}
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
package com.ctrip.apollo;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
/**
* Entry point for traditional web app
* @author Jason Song(song_s@ctrip.com)
*/
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(ServerApplication.class);
}
}
package com.ctrip.apollo.server.config;
import org.h2.server.web.WebServlet;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("welcome");
registry.addViewController("/index").setViewName("welcome");
}
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver
= new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("/");
}
@Bean
ServletRegistrationBean h2servletRegistration(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean( new WebServlet());
registrationBean.addUrlMappings("/console/*");
return registrationBean;
}
}
package com.ctrip.apollo.server.controller;
import com.ctrip.apollo.biz.service.ConfigService;
import com.ctrip.apollo.core.model.ApolloConfig;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RestController
public class ConfigController {
@Resource(name = "configService")
private ConfigService configService;
@RequestMapping(value = "/{appId}/{clusterName}/{version:.*}")
public ApolloConfig queryConfig(@PathVariable long appId,
@PathVariable String clusterName,
@PathVariable String version) {
return configService.loadConfig(appId, clusterName, version);
}
}
spring:
application:
name: apollo-configserver
cloud:
config:
server:
git:
uri: ssh://git@10.3.2.56:1022/spring-cloud-config-repo.git
server:
port: 8888
......
spring:
application:
name: apollo-configserver
eureka:
instance:
hostname: localhost
......
<!DOCTYPE html>
<%@page contentType="text/html" pageEncoding="UTF-8" %>
<html>
<meta http-equiv=Content-Type content="text/html;charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Apollo Config Server</title>
<body>
<h2>Welcome to Apollo Config Server!</h2>
</body>
</html>
package com.ctrip.apollo.configserver;
import com.ctrip.apollo.ServerApplication;
import org.junit.runner.RunWith;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ConfigServerApplication.class)
@SpringApplicationConfiguration(classes = ServerApplication.class)
public abstract class AbstractConfigServerTest {
}
package com.ctrip.apollo.server;
import com.ctrip.apollo.server.controller.ConfigControllerTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
ConfigControllerTest.class
})
public class AllTests {
}
package com.ctrip.apollo.server.controller;
import com.ctrip.apollo.biz.service.ConfigService;
import com.ctrip.apollo.core.model.ApolloConfig;
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.test.util.ReflectionTestUtils;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith(MockitoJUnitRunner.class)
public class ConfigControllerTest {
private ConfigController configController;
@Mock
private ConfigService configService;
@Before
public void setUp() throws Exception {
configController = new ConfigController();
ReflectionTestUtils.setField(configController, "configService", configService);
}
@Test
public void testQueryConfig() throws Exception {
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
long someAppId = 1;
String someClusterName = "someClusterName";
String someVersion = "someVersion";
when(configService.loadConfig(someAppId, someClusterName, someVersion)).thenReturn(someApolloConfig);
ApolloConfig result = configController.queryConfig(someAppId, someClusterName, someVersion);
assertEquals(someApolloConfig, result);
verify(configService, times(1)).loadConfig(someAppId, someClusterName, someVersion);
}
}
......@@ -23,5 +23,11 @@
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- end of json -->
<!-- util -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<!-- end of util -->
</dependencies>
</project>
package com.ctrip.apollo.core.environment;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class Environment {
private String name;
private String[] profiles = new String[0];
private String label;
private List<PropertySource> propertySources = new ArrayList<PropertySource>();
private String version;
public Environment(String name, String... profiles) {
this(name, profiles, "master", null);
}
@JsonCreator
public Environment(@JsonProperty("name") String name,
@JsonProperty("profiles") String[] profiles,
@JsonProperty("label") String label,
@JsonProperty("version") String version) {
super();
this.name = name;
this.profiles = profiles;
this.label = label;
this.version = version;
}
public void add(PropertySource propertySource) {
this.propertySources.add(propertySource);
}
public void addFirst(PropertySource propertySource) {
this.propertySources.add(0, propertySource);
}
public List<PropertySource> getPropertySources() {
return propertySources;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String[] getProfiles() {
return profiles;
}
public void setProfiles(String[] profiles) {
this.profiles = profiles;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
@Override
public String toString() {
return "Environment [name=" + name + ", profiles=" + Arrays.asList(profiles) + ", label="
+ label + ", propertySources=" + propertySources + ", version=" + version + "]";
}
}
package com.ctrip.apollo.core.environment;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Map;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class PropertySource {
private String name;
private Map<String, Object> source;
@JsonCreator
public PropertySource(@JsonProperty("name") String name, @JsonProperty("source") Map<String, Object> source) {
this.name = name;
this.source = source;
}
public String getName() {
return name;
}
public Map<String, Object> getSource() {
return source;
}
@Override
public String toString() {
return "PropertySource [name=" + name + "]";
}
}
package com.ctrip.apollo.core.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.MoreObjects;
import java.util.Map;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class ApolloConfig {
private long appId;
private String cluster;
private String version;
private Map<String, Object> configurations;
private long releaseId;
@JsonCreator
public ApolloConfig(@JsonProperty("appId") long appId,
@JsonProperty("cluster") String cluster,
@JsonProperty("version") String version,
@JsonProperty("releaseId") long releaseId) {
super();
this.appId = appId;
this.cluster = cluster;
this.version = version;
this.releaseId = releaseId;
}
public Map<String, Object> getConfigurations() {
return configurations;
}
public void setConfigurations(Map<String, Object> configurations) {
this.configurations = configurations;
}
public long getAppId() {
return appId;
}
public String getCluster() {
return cluster;
}
public String getVersion() {
return version;
}
public long getReleaseId() {
return releaseId;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.omitNullValues()
.add("appId", appId)
.add("cluster", cluster)
.add("version", version)
.add("releaseId", releaseId)
.add("configurations", configurations)
.toString();
}
}
package com.ctrip.apollo.demo;
import com.ctrip.apollo.client.ApolloConfig;
import com.ctrip.apollo.client.ApolloConfigManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
......@@ -13,8 +13,8 @@ import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
@ComponentScan(value = "com.ctrip.apollo.demo")
public class AppConfig {
@Bean
public ApolloConfig apolloConfig() {
return new ApolloConfig();
public ApolloConfigManager apolloConfigManager() {
return new ApolloConfigManager();
}
@Bean
......
appId=hermes
version=master
appId=102
version=1.0
......@@ -71,6 +71,7 @@
<module>apollo-portal</module>
<module>apollo-assembly</module>
<module>apollo-demo</module>
<module>apollo-biz</module>
</modules>
<dependencyManagement>
<dependencies>
......@@ -79,6 +80,11 @@
<artifactId>apollo-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-biz</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.dianping.cat</groupId>
<artifactId>cat-client</artifactId>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册