Skip to content

Commit

Permalink
MongoDB based EnvironmentRepository implementation. spring-cloud#203.
Browse files Browse the repository at this point in the history
  • Loading branch information
venilnoronha committed Nov 27, 2015
1 parent 1c69c59 commit 811a58a
Show file tree
Hide file tree
Showing 5 changed files with 305 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ cache:
directories:
- $HOME/.m2
language: java
services:
- mongodb
before_install:
- git config user.name "$GIT_NAME"
- git config user.email "$GIT_EMAIL"
Expand Down
4 changes: 4 additions & 0 deletions spring-cloud-config-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
<artifactId>svnkit</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,20 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
import org.springframework.cloud.config.server.environment.MongoEnvironmentRepository;
import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentRepository;
import org.springframework.cloud.config.server.environment.NativeEnvironmentRepository;
import org.springframework.cloud.config.server.environment.SearchPathLocator;
import org.springframework.cloud.config.server.environment.SvnKitEnvironmentRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.ConfigurableEnvironment;

/**
* @author Dave Syer
*
* @author Venil Noronha
*/
@Configuration
@ConditionalOnMissingBean(EnvironmentRepository.class)
Expand Down Expand Up @@ -95,5 +98,25 @@ public EnvironmentRepository environmentRepository() {
return repository;
}
}

@Configuration
@Profile("mongodb")
protected static class MongoRepositoryConfiguration {

@Autowired
private ConfigurableEnvironment environment;

@Bean
public SearchPathLocator searchPathLocator() {
return new NativeEnvironmentRepository(environment);
}

@Bean
@Primary
public EnvironmentRepository environmentRepository() {
return new MongoEnvironmentRepository();
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cloud.config.server.environment;

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.mongodb.MongoClient;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;

/**
* Simple implementation of {@link EnvironmentRepository} that is backed by
* MongoDB. The resulting Environment is composed of property sources located
* using the application name as the MongoDB collection while MongoDB document's
* `profile` and `label` key values represent the Spring profile and label
* respectively.
*
* @author Venil Noronha
*/
@ConfigurationProperties("spring.cloud.config.server.mongodb")
public class MongoEnvironmentRepository implements EnvironmentRepository, InitializingBean, DisposableBean {

private static final String DEFAULT_HOST = "127.0.0.1";
private static final int DEFAULT_PORT = 27017;
private static final String DEFAULT_PROFILE = "default";

/**
* The host.
*/
private String host = DEFAULT_HOST;

/**
* The port.
*/
private int port = DEFAULT_PORT;

/**
* The database name.
*/
private String database;

/**
* The username.
*/
private String username;

/**
* The password.
*/
private String password;

public String getHost() {
return host;
}

public void setHost(String host) {
this.host = host;
}

public int getPort() {
return port;
}

public void setPort(int port) {
this.port = port;
}

public String getDatabase() {
return database;
}

public void setDatabase(String database) {
this.database = database;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

private MongoClient mongoClient;
private MongoOperations mongoOps;

@Override
public void afterPropertiesSet() throws Exception {
Assert.state(getDatabase() != null, "You need to configure mongodb database name");
ServerAddress seed = new ServerAddress(host, port);
if (username != null && password != null) {
MongoCredential cred = MongoCredential.createCredential(username, database, password.toCharArray());
mongoClient = new MongoClient(Collections.singletonList(seed), Collections.singletonList(cred));
}
else {
mongoClient = new MongoClient(Collections.singletonList(seed));
}
mongoOps = new MongoTemplate(mongoClient, database);
}

@Override
public void destroy() throws Exception {
mongoClient.close();
}

@Override
public Environment findOne(String name, String profile, String label) {
String[] profilesArr = StringUtils.commaDelimitedListToStringArray(profile);
Environment environment = new Environment(name, profilesArr, label, null);
final List<String> profiles = Arrays.asList(profilesArr.clone());
for (int i = 0; i < profiles.size(); i ++) {
String currProfile = profiles.get(i);
if (DEFAULT_PROFILE.equals(currProfile)) {
profiles.set(i, null); // `null` profile value in MongoDB is considered default
break;
}
}
Query query = new Query().addCriteria(Criteria.where("profile").in(profiles.toArray()));
if (label != null) {
query.addCriteria(Criteria.where("label").is(label));
}
List<MongoPropertySource> sources = mongoOps.find(query, MongoPropertySource.class, name);
Collections.sort(sources, new Comparator<MongoPropertySource>() {
@Override
public int compare(MongoPropertySource s1, MongoPropertySource s2) {
Object p1 = s1.get("profile");
Object p2 = s2.get("profile");
int i1 = profiles.indexOf(p1 != null ? p1 : DEFAULT_PROFILE);
int i2 = profiles.indexOf(p2 != null ? p2 : DEFAULT_PROFILE);
return Double.compare(i1, i2);
}
});
for (MongoPropertySource source : sources) {
String sourceName = String.format("%s-%s-%s", name, source.get("profile"), source.get("label"));
PropertySource propSource = new PropertySource(sourceName, source);
environment.add(propSource);
}
return environment;
}

public static class MongoPropertySource extends HashMap<Object, Object> {

private static final long serialVersionUID = -902368693790845431L;

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cloud.config.server.environment;

import static org.junit.Assert.assertEquals;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.server.config.EnvironmentRepositoryConfiguration;
import org.springframework.cloud.config.server.environment.MongoEnvironmentRepository.MongoPropertySource;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.mongodb.core.MongoTemplate;

import com.mongodb.MongoClient;
import com.mongodb.ServerAddress;

/**
* @author Venil Noronha
*/
public class MongoEnvironmentRepositoryIntegrationTests {

private ConfigurableApplicationContext context;
private MongoClient mongoClient;
private MongoTemplate mongoOps;

@Before
public void init() throws Exception {
ServerAddress seed = new ServerAddress("localhost", 27017);
mongoClient = new MongoClient(Collections.singletonList(seed));
mongoOps = new MongoTemplate(mongoClient, "testdb");
}

@After
public void close() {
mongoClient.close();
if (this.context != null) {
this.context.close();
}
}

@Test
public void defaultRepo() throws IOException {
// Setup
mongoOps.dropCollection("testapp");
MongoPropertySource ps = new MongoPropertySource();
ps.put("testkey", "testval");
mongoOps.save(ps, "testapp");
// Test
Map<String, Object> props = new HashMap<>();
props.put("spring.profiles.active", "mongodb");
props.put("spring.cloud.config.server.mongodb.database", "testdb");
context = new SpringApplicationBuilder(TestConfiguration.class).web(false).properties(props).run();
EnvironmentRepository repository = this.context.getBean(EnvironmentRepository.class);
Environment environment = repository.findOne("testapp", "default", null);
assertEquals(1, environment.getPropertySources().size());
}


@Configuration
@Import({ PropertyPlaceholderAutoConfiguration.class, EnvironmentRepositoryConfiguration.class })
protected static class TestConfiguration {

}

}

0 comments on commit 811a58a

Please sign in to comment.