Commit 934e4f54 by Jason Song

init config client implementation and added a demo project using it.

parent 9009c1c0
<?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/maven-v4_0_0.xsd">
<parent>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.0.1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-client</artifactId>
<name>Apollo Client</name>
<dependencies>
<dependency>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-core</artifactId>
</dependency>
</dependencies>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.0.1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-client</artifactId>
<name>Apollo Client</name>
<dependencies>
<!-- apollo -->
<dependency>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-core</artifactId>
</dependency>
<!-- end of apollo -->
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!-- end of spring -->
<!-- util -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<!-- end of util -->
<!-- test -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<scope>test</scope>
</dependency>
<!-- end of test -->
</dependencies>
</project>
package com.ctrip.apollo.client;
import com.ctrip.apollo.client.loader.ConfigLoader;
import com.ctrip.apollo.client.loader.ConfigLoaderFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.MutablePropertySources;
/**
* Client side config
*
* @author Jason Song(song_s@ctrip.com)
*/
public class ApolloConfig implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ApplicationContextAware {
public static final String APOLLO_PROPERTY_SOURCE_NAME = "ApolloConfigProperties";
private ConfigLoader configLoader;
private ConfigurableApplicationContext applicationContext;
public ApolloConfig() {
this.configLoader = ConfigLoaderFactory.getInstance().getMockConfigLoader();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (!(applicationContext instanceof ConfigurableApplicationContext)) {
throw new RuntimeException(
String.format("ApplicationContext must implement ConfigurableApplicationContext, but found: %s", applicationContext.getClass().getName()));
}
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
}
/**
* This is the first method invoked, so we could prepare the property sources here
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
preparePropertySource();
}
/**
* This is executed after postProcessBeanDefinitionRegistry
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 1;
}
void preparePropertySource() {
MutablePropertySources currentPropertySources = applicationContext.getEnvironment().getPropertySources();
if (currentPropertySources.contains(APOLLO_PROPERTY_SOURCE_NAME)) {
currentPropertySources.remove(APOLLO_PROPERTY_SOURCE_NAME);
}
CompositePropertySource composite = new CompositePropertySource(APOLLO_PROPERTY_SOURCE_NAME);
composite.addPropertySource(configLoader.loadPropertySource());
currentPropertySources.addFirst(composite);
}
}
package com.ctrip.apollo.client.loader;
import org.springframework.core.env.CompositePropertySource;
/**
* @author Jason Song(songs_ctrip.com)
*/
public interface ConfigLoader {
/**
* Load property source for client use
* @return property source
*/
CompositePropertySource loadPropertySource();
}
package com.ctrip.apollo.client.loader;
import com.ctrip.apollo.client.loader.impl.MockConfigLoader;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class ConfigLoaderFactory {
private static ConfigLoaderFactory configLoaderFactory = new ConfigLoaderFactory();
private ConfigLoaderFactory() {
}
public static ConfigLoaderFactory getInstance() {
return configLoaderFactory;
}
public ConfigLoader getMockConfigLoader() {
return new MockConfigLoader();
}
}
package com.ctrip.apollo.client.loader.impl;
import com.ctrip.apollo.client.loader.ConfigLoader;
import org.springframework.core.env.CompositePropertySource;
/**
* Load config from local backup file
* @author Jason Song(song_s@ctrip.com)
*/
public class LocalConfigLoader implements ConfigLoader {
private static final String PROPERTY_SOURCE_NAME = "ApolloLocalConfigProperties";
@Override
public CompositePropertySource loadPropertySource() {
return null;
}
}
package com.ctrip.apollo.client.loader.impl;
import com.ctrip.apollo.client.loader.ConfigLoader;
import com.google.common.collect.Maps;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.MapPropertySource;
import java.util.Map;
/**
* Mock config
* @author Jason Song(song_s@ctrip.com)
*/
public class MockConfigLoader implements ConfigLoader {
private static final String PROPERTY_SOURCE_NAME = "ApolloMockConfigProperties";
@Override
public CompositePropertySource loadPropertySource() {
CompositePropertySource composite = new CompositePropertySource(PROPERTY_SOURCE_NAME);
composite.addPropertySource(mock());
return composite;
}
private MapPropertySource mock() {
Map<String, Object> source = Maps.newHashMap();
source.put("apollo.foo" ,"bar");
MapPropertySource propertySource = new MapPropertySource("mock source", source);
return propertySource;
}
}
package com.ctrip.apollo.client.loader.impl;
import com.ctrip.apollo.client.loader.ConfigLoader;
import org.springframework.core.env.CompositePropertySource;
/**
* Load config from remote config server
* @author Jason Song(song_s@ctrip.com)
*/
public class RemoteConfigLoader implements ConfigLoader {
private static final String PROPERTY_SOURCE_NAME = "ApolloRemoteConfigProperties";
@Override
public CompositePropertySource loadPropertySource() {
return null;
}
}
package com.ctrip.apollo.client;
import com.ctrip.apollo.client.loader.ConfigLoader;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.test.util.ReflectionTestUtils;
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;
@Mock
private ConfigLoader configLoader;
@Mock
private ConfigurableApplicationContext applicationContext;
@Mock
private ConfigurableEnvironment env;
@Mock
private MutablePropertySources mutablePropertySources;
@Before
public void setUp() {
apolloConfig = new ApolloConfig();
when(applicationContext.getEnvironment()).thenReturn(env);
when(env.getPropertySources()).thenReturn(mutablePropertySources);
apolloConfig.setApplicationContext(applicationContext);
ReflectionTestUtils.setField(apolloConfig, "configLoader", configLoader);
}
@Test(expected = RuntimeException.class)
public void testInvalidApplicationContext() {
ApplicationContext someInvalidApplication = mock(ApplicationContext.class);
apolloConfig.setApplicationContext(someInvalidApplication);
}
@Test
public void testPreparePropertySourceSuccessful() {
CompositePropertySource somePropertySource = mock(CompositePropertySource.class);
final ArgumentCaptor<CompositePropertySource> captor = ArgumentCaptor.forClass(CompositePropertySource.class);
when(configLoader.loadPropertySource()).thenReturn(somePropertySource);
apolloConfig.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());
assertTrue(insertedPropertySource.getPropertySources().contains(somePropertySource));
}
}
<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/maven-v4_0_0.xsd">
<parent>
<artifactId>apollo</artifactId>
<groupId>com.ctrip.apollo</groupId>
<version>0.0.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-demo</artifactId>
<packaging>war</packaging>
<name>Apollo Demo</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>com.ctrip.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.tuckey</groupId>
<artifactId>urlrewritefilter</artifactId>
<version>4.0.4</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</dependency>
</dependencies>
<build>
<finalName>apollo-demo</finalName>
</build>
</project>
package com.ctrip.apollo.demo;
import com.ctrip.apollo.client.ApolloConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Configuration
@ComponentScan(value = "com.ctrip.apollo.demo")
public class AppConfig {
@Bean
public ApolloConfig apolloConfig() {
return new ApolloConfig();
}
@Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
package com.ctrip.apollo.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RestController
@RequestMapping("/demo")
public class DemoController {
@Autowired
private Environment env;
@Value("${apollo.foo}")
private String foo;
@RequestMapping(value = "/config/{configName:.*}", method = RequestMethod.GET)
public String queryConfig(@PathVariable String configName) {
return env.getProperty(configName, "undefined");
}
}
package com.ctrip.apollo.demo;
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("/");
}
}
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.0//EN" "http://tuckey.org/res/dtds/urlrewrite3.0.dtd">
<urlrewrite default-match-type="wildcard">
<rule>
<from>/favicon.ico</from>
<to>/s/favicon.ico</to>
</rule>
<rule>
<from>/images/**</from>
<to>/s/images/$1</to>
</rule>
<rule>
<from>/scripts/**</from>
<to>/s/scripts/$1</to>
</rule>
<rule>
<from>/styles/**</from>
<to>/s/styles/$1</to>
</rule>
<rule>
<from>/templates/**</from>
<to>/s/templates/$1</to>
</rule>
<rule match-type="regex">
<from>^\/(index\..*)?</from>
<to>/app/</to>
</rule>
<rule>
<from>/**</from>
<to>/app/$1</to>
</rule>
<outbound-rule>
<from>/app/**</from>
<to>/$1</to>
</outbound-rule>
</urlrewrite>
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Apollo demo</display-name>
<!-- Enables clean URLs with JSP views e.g. /welcome instead of /app/welcome -->
<filter>
<filter-name>UrlRewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>UrlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
com.ctrip.apollo.demo.AppConfig
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
......@@ -70,6 +70,7 @@
<module>apollo-configserver</module>
<module>apollo-portal</module>
<module>apollo-assembly</module>
<module>apollo-demo</module>
</modules>
<dependencyManagement>
<dependencies>
......@@ -97,9 +98,9 @@
</dependency>
<!-- declare Spring BOMs in order -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.3.RELEASE</version>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>2.0.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
......
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