diff --git a/docs/src/main/asciidoc/parameter-store.adoc b/docs/src/main/asciidoc/parameter-store.adoc
index beb06202c..125d245f8 100644
--- a/docs/src/main/asciidoc/parameter-store.adoc
+++ b/docs/src/main/asciidoc/parameter-store.adoc
@@ -88,3 +88,19 @@ turn on `DEBUG` logging on `org.springframework.cloud.aws.paramstore.AwsParamSto
logging.level.org.springframework.cloud.aws.paramstore.AwsParamStorePropertySource=debug
----
====
+
+In `spring-cloud` `2020.0.0` (aka Ilford), the bootstrap phase is no longer enabled by default. In order
+enable it you need an additional dependency:
+
+[source,xml,indent=0]
+----
+
+ org.springframework.cloud
+ spring-cloud-starter-bootstrap
+ {spring-cloud-version}
+
+----
+
+However, starting at `spring-cloud-aws` `2.3`, allows import default aws' parameterstore keys
+(`spring.config.import=aws-parameterstore:`) or individual keys
+(`spring.config.import=aws-parameterstore:config-key;other-config-key`)
diff --git a/pom.xml b/pom.xml
index 8cc80276a..7740a31a7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
org.springframework.cloud
spring-cloud-build
- 3.0.0-M4
+ 3.0.0-SNAPSHOT
@@ -48,7 +48,7 @@
1.5.5
2.8.2
1.2.0
- 3.0.0-M4
+ 3.0.0-SNAPSHOT
0.0.25
diff --git a/spring-cloud-aws-dependencies/pom.xml b/spring-cloud-aws-dependencies/pom.xml
index e1e05c0a9..186003c79 100644
--- a/spring-cloud-aws-dependencies/pom.xml
+++ b/spring-cloud-aws-dependencies/pom.xml
@@ -22,7 +22,7 @@
org.springframework.cloud
spring-cloud-dependencies-parent
- 2.3.2.BUILD-SNAPSHOT
+ 3.0.0-SNAPSHOT
spring-cloud-aws-dependencies
diff --git a/spring-cloud-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/paramstore/AwsParamStorePropertySource.java b/spring-cloud-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/paramstore/AwsParamStorePropertySource.java
index 087d147e9..7d2b29d86 100644
--- a/spring-cloud-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/paramstore/AwsParamStorePropertySource.java
+++ b/spring-cloud-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/paramstore/AwsParamStorePropertySource.java
@@ -34,15 +34,16 @@
* from the AWS Parameter Store using the provided SSM client.
*
* @author Joris Kuipers
+ * @author Eddú Meléndez
* @since 2.0.0
*/
public class AwsParamStorePropertySource extends EnumerablePropertySource {
private static final Logger LOGGER = LoggerFactory.getLogger(AwsParamStorePropertySource.class);
- private String context;
+ private final String context;
- private Map properties = new LinkedHashMap<>();
+ private final Map properties = new LinkedHashMap<>();
public AwsParamStorePropertySource(String context, AWSSimpleSystemsManagement ssmClient) {
super(context, ssmClient);
@@ -57,21 +58,21 @@ public void init() {
@Override
public String[] getPropertyNames() {
- Set strings = properties.keySet();
+ Set strings = this.properties.keySet();
return strings.toArray(new String[strings.size()]);
}
@Override
public Object getProperty(String name) {
- return properties.get(name);
+ return this.properties.get(name);
}
private void getParameters(GetParametersByPathRequest paramsRequest) {
- GetParametersByPathResult paramsResult = source.getParametersByPath(paramsRequest);
+ GetParametersByPathResult paramsResult = this.source.getParametersByPath(paramsRequest);
for (Parameter parameter : paramsResult.getParameters()) {
- String key = parameter.getName().replace(context, "").replace('/', '.');
+ String key = parameter.getName().replace(this.context, "").replace('/', '.');
LOGGER.debug("Populating property retrieved from AWS Parameter Store: {}", key);
- properties.put(key, parameter.getValue());
+ this.properties.put(key, parameter.getValue());
}
if (paramsResult.getNextToken() != null) {
getParameters(paramsRequest.withNextToken(paramsResult.getNextToken()));
diff --git a/spring-cloud-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/paramstore/AwsParamStorePropertySourceLocator.java b/spring-cloud-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/paramstore/AwsParamStorePropertySourceLocator.java
index c15d1d58a..acbf42b9d 100644
--- a/spring-cloud-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/paramstore/AwsParamStorePropertySourceLocator.java
+++ b/spring-cloud-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/paramstore/AwsParamStorePropertySourceLocator.java
@@ -31,7 +31,6 @@
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
-import org.springframework.util.ReflectionUtils;
/**
* Builds a {@link CompositePropertySource} with various
@@ -43,6 +42,7 @@
*
* @author Joris Kuipers
* @author Matej Nedic
+ * @author Eddú Meléndez
* @since 2.0.0
*/
public class AwsParamStorePropertySourceLocator implements PropertySourceLocator {
@@ -73,56 +73,22 @@ public PropertySource> locate(Environment environment) {
ConfigurableEnvironment env = (ConfigurableEnvironment) environment;
- String appName = properties.getName();
-
- if (appName == null) {
- appName = env.getProperty("spring.application.name");
- }
+ AwsParamStorePropertySources sources = new AwsParamStorePropertySources(this.properties, this.logger);
List profiles = Arrays.asList(env.getActiveProfiles());
-
- String prefix = this.properties.getPrefix();
-
- String appContext = prefix + "/" + appName;
- addProfiles(this.contexts, appContext, profiles);
- this.contexts.add(appContext + "/");
-
- String defaultContext = prefix + "/" + this.properties.getDefaultContext();
- addProfiles(this.contexts, defaultContext, profiles);
- this.contexts.add(defaultContext + "/");
+ this.contexts.addAll(sources.getAutomaticContexts(profiles));
CompositePropertySource composite = new CompositePropertySource("aws-param-store");
for (String propertySourceContext : this.contexts) {
- try {
- composite.addPropertySource(create(propertySourceContext));
- }
- catch (Exception e) {
- if (this.properties.isFailFast()) {
- logger.error(
- "Fail fast is set and there was an error reading configuration from AWS Parameter Store:\n"
- + e.getMessage());
- ReflectionUtils.rethrowRuntimeException(e);
- }
- else {
- logger.warn("Unable to load AWS config from " + propertySourceContext, e);
- }
+ PropertySource propertySource = sources
+ .createPropertySource(propertySourceContext, !this.properties.isFailFast(), this.ssmClient);
+ if (propertySource != null) {
+ composite.addPropertySource(propertySource);
}
}
return composite;
}
- private AwsParamStorePropertySource create(String context) {
- AwsParamStorePropertySource propertySource = new AwsParamStorePropertySource(context, this.ssmClient);
- propertySource.init();
- return propertySource;
- }
-
- private void addProfiles(Set contexts, String baseContext, List profiles) {
- for (String profile : profiles) {
- contexts.add(baseContext + this.properties.getProfileSeparator() + profile + "/");
- }
- }
-
}
diff --git a/spring-cloud-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/paramstore/AwsParamStorePropertySources.java b/spring-cloud-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/paramstore/AwsParamStorePropertySources.java
new file mode 100644
index 000000000..d994ac538
--- /dev/null
+++ b/spring-cloud-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/paramstore/AwsParamStorePropertySources.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2013-2020 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
+ *
+ * https://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.aws.paramstore;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagement;
+import org.apache.commons.logging.Log;
+
+import org.springframework.util.StringUtils;
+
+/**
+ * @author Eddú Meléndez
+ * @since 2.3
+ */
+public class AwsParamStorePropertySources {
+
+ private final AwsParamStoreProperties properties;
+
+ private final Log log;
+
+ public AwsParamStorePropertySources(AwsParamStoreProperties properties, Log log) {
+ this.properties = properties;
+ this.log = log;
+ }
+
+ public List getAutomaticContexts(List profiles) {
+ List contexts = new ArrayList<>();
+ String prefix = this.properties.getPrefix();
+ String defaultContext = getContext(prefix, this.properties.getDefaultContext());
+
+ String appName = this.properties.getName();
+
+ String appContext = prefix + "/" + appName;
+ addProfiles(contexts, appContext, profiles);
+ contexts.add(appContext + "/");
+
+ addProfiles(contexts, defaultContext, profiles);
+ contexts.add(defaultContext + "/");
+ return contexts;
+ }
+
+ protected String getContext(String prefix, String context) {
+ if (StringUtils.hasLength(prefix)) {
+ return prefix + "/" + context;
+ }
+ return context;
+ }
+
+ private void addProfiles(List contexts, String baseContext, List profiles) {
+ for (String profile : profiles) {
+ contexts.add(baseContext + this.properties.getProfileSeparator() + profile + "/");
+ }
+ }
+
+ /**
+ * Creates property source for given context.
+ * @param context property source context equivalent to the parameter name
+ * @param optional if creating context should fail with exception if parameter cannot
+ * be loaded
+ * @param client System Manager Management client
+ * @return a property source or null if parameter could not be loaded and optional is
+ * set to true
+ */
+ public AwsParamStorePropertySource createPropertySource(String context, boolean optional,
+ AWSSimpleSystemsManagement client) {
+ try {
+ AwsParamStorePropertySource propertySource = new AwsParamStorePropertySource(context, client);
+ propertySource.init();
+ return propertySource;
+ // TODO: howto call close when /refresh
+ }
+ catch (Exception e) {
+ if (!optional) {
+ throw new AwsParameterPropertySourceNotFoundException(e);
+ }
+ else {
+ log.warn("Unable to load AWS parameter from " + context + ". " + e.getMessage());
+ }
+ }
+ return null;
+ }
+
+ static class AwsParameterPropertySourceNotFoundException extends RuntimeException {
+
+ AwsParameterPropertySourceNotFoundException(Exception source) {
+ super(source);
+ }
+
+ }
+
+}
diff --git a/spring-cloud-aws-parameter-store-config/src/test/java/org/springframework/cloud/aws/paramstore/AwsParamStorePropertySourceLocatorTest.java b/spring-cloud-aws-parameter-store-config/src/test/java/org/springframework/cloud/aws/paramstore/AwsParamStorePropertySourceLocatorTest.java
index 7d3087e4a..afc939acc 100644
--- a/spring-cloud-aws-parameter-store-config/src/test/java/org/springframework/cloud/aws/paramstore/AwsParamStorePropertySourceLocatorTest.java
+++ b/spring-cloud-aws-parameter-store-config/src/test/java/org/springframework/cloud/aws/paramstore/AwsParamStorePropertySourceLocatorTest.java
@@ -25,9 +25,12 @@
import com.amazonaws.services.simplesystemsmanagement.model.Parameter;
import org.junit.jupiter.api.Test;
+import org.springframework.cloud.aws.paramstore.AwsParamStorePropertySources.AwsParameterPropertySourceNotFoundException;
+import org.springframework.core.env.CompositePropertySource;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -36,6 +39,7 @@
* Unit test for {@link AwsParamStorePropertySourceLocator}.
*
* @author Matej Nedic
+ * @author Eddú Meléndez
*/
public class AwsParamStorePropertySourceLocatorTest {
@@ -45,48 +49,54 @@ public class AwsParamStorePropertySourceLocatorTest {
@Test
void contextExpectedToHave2Elements() {
- AwsParamStoreProperties properties = new AwsParamStorePropertiesBuilder().withDefaultContext("application")
- .withName("application").build();
+ AwsParamStoreProperties properties = new AwsParamStoreProperties();
+ properties.setPrefix("application");
+ properties.setName("application");
GetParametersByPathResult firstResult = getFirstResult();
GetParametersByPathResult nextResult = getNextResult();
- when(ssmClient.getParametersByPath(any(GetParametersByPathRequest.class))).thenReturn(firstResult, nextResult);
+ when(this.ssmClient.getParametersByPath(any(GetParametersByPathRequest.class))).thenReturn(firstResult,
+ nextResult);
- AwsParamStorePropertySourceLocator locator = new AwsParamStorePropertySourceLocator(ssmClient, properties);
- env.setActiveProfiles("test");
- locator.locate(env);
+ AwsParamStorePropertySourceLocator locator = new AwsParamStorePropertySourceLocator(this.ssmClient, properties);
+ this.env.setActiveProfiles("test");
+ locator.locate(this.env);
assertThat(locator.getContexts()).hasSize(2);
}
@Test
void contextExpectedToHave4Elements() {
- AwsParamStoreProperties properties = new AwsParamStorePropertiesBuilder().withDefaultContext("application")
- .withName("messaging-service").build();
+ AwsParamStoreProperties properties = new AwsParamStoreProperties();
+ properties.setPrefix("application");
+ properties.setName("messaging-service");
GetParametersByPathResult firstResult = getFirstResult();
GetParametersByPathResult nextResult = getNextResult();
- when(ssmClient.getParametersByPath(any(GetParametersByPathRequest.class))).thenReturn(firstResult, nextResult);
+ when(this.ssmClient.getParametersByPath(any(GetParametersByPathRequest.class))).thenReturn(firstResult,
+ nextResult);
- AwsParamStorePropertySourceLocator locator = new AwsParamStorePropertySourceLocator(ssmClient, properties);
- env.setActiveProfiles("test");
- locator.locate(env);
+ AwsParamStorePropertySourceLocator locator = new AwsParamStorePropertySourceLocator(this.ssmClient, properties);
+ this.env.setActiveProfiles("test");
+ locator.locate(this.env);
assertThat(locator.getContexts()).hasSize(4);
}
@Test
void contextSpecificOrderExpected() {
- AwsParamStoreProperties properties = new AwsParamStorePropertiesBuilder().withDefaultContext("application")
- .withName("messaging-service").build();
+ AwsParamStoreProperties properties = new AwsParamStoreProperties();
+ properties.setPrefix("application");
+ properties.setName("messaging-service");
GetParametersByPathResult firstResult = getFirstResult();
GetParametersByPathResult nextResult = getNextResult();
- when(ssmClient.getParametersByPath(any(GetParametersByPathRequest.class))).thenReturn(firstResult, nextResult);
+ when(this.ssmClient.getParametersByPath(any(GetParametersByPathRequest.class))).thenReturn(firstResult,
+ nextResult);
- AwsParamStorePropertySourceLocator locator = new AwsParamStorePropertySourceLocator(ssmClient, properties);
- env.setActiveProfiles("test");
- locator.locate(env);
+ AwsParamStorePropertySourceLocator locator = new AwsParamStorePropertySourceLocator(this.ssmClient, properties);
+ this.env.setActiveProfiles("test");
+ locator.locate(this.env);
List contextToBeTested = new ArrayList<>(locator.getContexts());
@@ -96,6 +106,28 @@ void contextSpecificOrderExpected() {
assertThat(contextToBeTested.get(3)).isEqualTo("application/application/");
}
+ @Test
+ void whenFailFastIsTrueAndParameterDoesNotExistThrowsException() {
+ AwsParamStoreProperties properties = new AwsParamStoreProperties();
+ properties.setFailFast(true);
+
+ AwsParamStorePropertySourceLocator locator = new AwsParamStorePropertySourceLocator(this.ssmClient, properties);
+ assertThatThrownBy(() -> locator.locate(this.env))
+ .isInstanceOf(AwsParameterPropertySourceNotFoundException.class);
+ }
+
+ @Test
+ void whenFailFastIsFalseAndParameterDoesNotExistReturnsEmptyPropertySource() {
+ AwsParamStoreProperties properties = new AwsParamStoreProperties();
+ properties.setFailFast(false);
+
+ AwsParamStorePropertySourceLocator locator = new AwsParamStorePropertySourceLocator(this.ssmClient, properties);
+
+ CompositePropertySource result = (CompositePropertySource) locator.locate(this.env);
+
+ assertThat(result.getPropertySources()).isEmpty();
+ }
+
private static GetParametersByPathResult getNextResult() {
return new GetParametersByPathResult().withParameters(
new Parameter().withName("/config/myservice/key3").withValue("value3"),
@@ -108,27 +140,4 @@ private static GetParametersByPathResult getFirstResult() {
new Parameter().withName("/config/myservice/key4").withValue("value4"));
}
- private static final class AwsParamStorePropertiesBuilder {
-
- private final AwsParamStoreProperties properties = new AwsParamStoreProperties();
-
- private AwsParamStorePropertiesBuilder() {
- }
-
- public AwsParamStorePropertiesBuilder withDefaultContext(String defaultContext) {
- this.properties.setPrefix(defaultContext);
- return this;
- }
-
- public AwsParamStorePropertiesBuilder withName(String name) {
- this.properties.setName(name);
- return this;
- }
-
- public AwsParamStoreProperties build() {
- return this.properties;
- }
-
- }
-
}
diff --git a/spring-cloud-starter-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/autoconfigure/paramstore/AwsParamStoreBootstrapConfiguration.java b/spring-cloud-starter-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/autoconfigure/paramstore/AwsParamStoreBootstrapConfiguration.java
index 68d44e458..1d1487257 100644
--- a/spring-cloud-starter-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/autoconfigure/paramstore/AwsParamStoreBootstrapConfiguration.java
+++ b/spring-cloud-starter-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/autoconfigure/paramstore/AwsParamStoreBootstrapConfiguration.java
@@ -30,6 +30,7 @@
import org.springframework.cloud.aws.paramstore.AwsParamStorePropertySourceLocator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
/**
* Spring Cloud Bootstrap Configuration for setting up an
@@ -46,15 +47,28 @@
@ConditionalOnProperty(prefix = AwsParamStoreProperties.CONFIG_PREFIX, name = "enabled", matchIfMissing = true)
public class AwsParamStoreBootstrapConfiguration {
+ private final Environment environment;
+
+ public AwsParamStoreBootstrapConfiguration(Environment environment) {
+ this.environment = environment;
+ }
+
@Bean
AwsParamStorePropertySourceLocator awsParamStorePropertySourceLocator(AWSSimpleSystemsManagement ssmClient,
AwsParamStoreProperties properties) {
+ if (StringUtils.isNullOrEmpty(properties.getName())) {
+ properties.setName(this.environment.getProperty("spring.application.name"));
+ }
return new AwsParamStorePropertySourceLocator(ssmClient, properties);
}
@Bean
@ConditionalOnMissingBean
AWSSimpleSystemsManagement ssmClient(AwsParamStoreProperties properties) {
+ return createSimpleSystemManagementClient(properties);
+ }
+
+ public static AWSSimpleSystemsManagement createSimpleSystemManagementClient(AwsParamStoreProperties properties) {
AWSSimpleSystemsManagementClientBuilder builder = AWSSimpleSystemsManagementClientBuilder.standard()
.withClientConfiguration(SpringCloudClientConfiguration.getClientConfiguration());
if (!StringUtils.isNullOrEmpty(properties.getRegion())) {
diff --git a/spring-cloud-starter-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/autoconfigure/paramstore/AwsParamStoreConfigDataLoader.java b/spring-cloud-starter-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/autoconfigure/paramstore/AwsParamStoreConfigDataLoader.java
new file mode 100644
index 000000000..b954e0820
--- /dev/null
+++ b/spring-cloud-starter-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/autoconfigure/paramstore/AwsParamStoreConfigDataLoader.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013-2020 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
+ *
+ * https://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.aws.autoconfigure.paramstore;
+
+import java.util.Collections;
+
+import com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagement;
+
+import org.springframework.boot.context.config.ConfigData;
+import org.springframework.boot.context.config.ConfigDataLoader;
+import org.springframework.boot.context.config.ConfigDataLoaderContext;
+import org.springframework.boot.context.config.ConfigDataResourceNotFoundException;
+import org.springframework.cloud.aws.paramstore.AwsParamStorePropertySource;
+
+/**
+ * @author Eddú Meléndez
+ * @since 2.3.0
+ */
+public class AwsParamStoreConfigDataLoader implements ConfigDataLoader {
+
+ @Override
+ public ConfigData load(ConfigDataLoaderContext context, AwsParamStoreConfigDataResource resource) {
+ try {
+ AWSSimpleSystemsManagement ssm = context.getBootstrapContext().get(AWSSimpleSystemsManagement.class);
+ AwsParamStorePropertySource propertySource = resource.getPropertySources()
+ .createPropertySource(resource.getContext(), resource.isOptional(), ssm);
+ if (propertySource != null) {
+ return new ConfigData(Collections.singletonList(propertySource));
+ }
+ else {
+ return null;
+ }
+ }
+ catch (Exception e) {
+ throw new ConfigDataResourceNotFoundException(resource, e);
+ }
+ }
+
+}
diff --git a/spring-cloud-starter-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/autoconfigure/paramstore/AwsParamStoreConfigDataLocationResolver.java b/spring-cloud-starter-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/autoconfigure/paramstore/AwsParamStoreConfigDataLocationResolver.java
new file mode 100644
index 000000000..0661c5061
--- /dev/null
+++ b/spring-cloud-starter-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/autoconfigure/paramstore/AwsParamStoreConfigDataLocationResolver.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2013-2020 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
+ *
+ * https://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.aws.autoconfigure.paramstore;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagement;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.boot.BootstrapContext;
+import org.springframework.boot.BootstrapRegistry;
+import org.springframework.boot.ConfigurableBootstrapContext;
+import org.springframework.boot.context.config.ConfigDataLocation;
+import org.springframework.boot.context.config.ConfigDataLocationNotFoundException;
+import org.springframework.boot.context.config.ConfigDataLocationResolver;
+import org.springframework.boot.context.config.ConfigDataLocationResolverContext;
+import org.springframework.boot.context.config.Profiles;
+import org.springframework.boot.context.properties.bind.Bindable;
+import org.springframework.boot.context.properties.bind.Binder;
+import org.springframework.cloud.aws.paramstore.AwsParamStoreProperties;
+import org.springframework.cloud.aws.paramstore.AwsParamStorePropertySources;
+import org.springframework.util.StringUtils;
+
+/**
+ * @author Eddú Meléndez
+ * @since 2.3.0
+ */
+public class AwsParamStoreConfigDataLocationResolver
+ implements ConfigDataLocationResolver {
+
+ /**
+ * AWS ParameterStore Config Data prefix.
+ */
+ public static final String PREFIX = "aws-parameterstore:";
+
+ private final Log log = LogFactory.getLog(AwsParamStoreConfigDataLocationResolver.class);
+
+ @Override
+ public boolean isResolvable(ConfigDataLocationResolverContext context, ConfigDataLocation location) {
+ if (!location.hasPrefix(PREFIX)) {
+ return false;
+ }
+ return context.getBinder().bind(AwsParamStoreProperties.CONFIG_PREFIX + ".enabled", Boolean.class).orElse(true);
+ }
+
+ @Override
+ public List resolve(ConfigDataLocationResolverContext context,
+ ConfigDataLocation location) throws ConfigDataLocationNotFoundException {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List resolveProfileSpecific(
+ ConfigDataLocationResolverContext resolverContext, ConfigDataLocation location, Profiles profiles)
+ throws ConfigDataLocationNotFoundException {
+ registerBean(resolverContext, AwsParamStoreProperties.class, loadProperties(resolverContext.getBinder()));
+
+ registerAndPromoteBean(resolverContext, AWSSimpleSystemsManagement.class,
+ this::createSimpleSystemManagementClient);
+
+ AwsParamStoreProperties properties = loadConfigProperties(resolverContext.getBinder());
+
+ AwsParamStorePropertySources sources = new AwsParamStorePropertySources(properties, log);
+
+ List contexts = location.getValue().equals(PREFIX)
+ ? sources.getAutomaticContexts(profiles.getAccepted())
+ : getCustomContexts(location.getNonPrefixedValue(PREFIX));
+
+ List locations = new ArrayList<>();
+ contexts.forEach(propertySourceContext -> locations
+ .add(new AwsParamStoreConfigDataResource(propertySourceContext, location.isOptional(), sources)));
+
+ return locations;
+ }
+
+ private List getCustomContexts(String keys) {
+ if (StringUtils.hasLength(keys)) {
+ return Arrays.asList(keys.split(";"));
+ }
+ return Collections.emptyList();
+ }
+
+ protected void registerAndPromoteBean(ConfigDataLocationResolverContext context, Class type,
+ BootstrapRegistry.InstanceSupplier supplier) {
+ registerBean(context, type, supplier);
+ context.getBootstrapContext().addCloseListener(event -> {
+ T instance = event.getBootstrapContext().get(type);
+ event.getApplicationContext().getBeanFactory().registerSingleton("configData" + type.getSimpleName(),
+ instance);
+ });
+ }
+
+ public void registerBean(ConfigDataLocationResolverContext context, Class type, T instance) {
+ context.getBootstrapContext().registerIfAbsent(type, BootstrapRegistry.InstanceSupplier.of(instance));
+ }
+
+ protected void registerBean(ConfigDataLocationResolverContext context, Class type,
+ BootstrapRegistry.InstanceSupplier supplier) {
+ ConfigurableBootstrapContext bootstrapContext = context.getBootstrapContext();
+ bootstrapContext.registerIfAbsent(type, supplier);
+ }
+
+ protected AWSSimpleSystemsManagement createSimpleSystemManagementClient(BootstrapContext context) {
+ AwsParamStoreProperties properties = context.get(AwsParamStoreProperties.class);
+
+ return AwsParamStoreBootstrapConfiguration.createSimpleSystemManagementClient(properties);
+ }
+
+ protected AwsParamStoreProperties loadProperties(Binder binder) {
+ return binder.bind(AwsParamStoreProperties.CONFIG_PREFIX, Bindable.of(AwsParamStoreProperties.class))
+ .orElseGet(AwsParamStoreProperties::new);
+ }
+
+ protected AwsParamStoreProperties loadConfigProperties(Binder binder) {
+ AwsParamStoreProperties properties = binder
+ .bind(AwsParamStoreProperties.CONFIG_PREFIX, Bindable.of(AwsParamStoreProperties.class))
+ .orElse(new AwsParamStoreProperties());
+
+ if (!StringUtils.hasLength(properties.getName())) {
+ properties.setName(binder.bind("spring.application.name", String.class).orElse("application"));
+ }
+
+ return properties;
+ }
+
+}
diff --git a/spring-cloud-starter-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/autoconfigure/paramstore/AwsParamStoreConfigDataResource.java b/spring-cloud-starter-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/autoconfigure/paramstore/AwsParamStoreConfigDataResource.java
new file mode 100644
index 000000000..625526895
--- /dev/null
+++ b/spring-cloud-starter-aws-parameter-store-config/src/main/java/org/springframework/cloud/aws/autoconfigure/paramstore/AwsParamStoreConfigDataResource.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2013-2020 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
+ *
+ * https://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.aws.autoconfigure.paramstore;
+
+import java.util.Objects;
+
+import org.springframework.boot.context.config.ConfigDataResource;
+import org.springframework.cloud.aws.paramstore.AwsParamStorePropertySources;
+import org.springframework.core.style.ToStringCreator;
+
+/**
+ * Config data resource for AWS System Manager Management integration.
+ *
+ * @author Eddú Meléndez
+ * @since 2.3.0
+ */
+public class AwsParamStoreConfigDataResource extends ConfigDataResource {
+
+ private final String context;
+
+ private final boolean optional;
+
+ private final AwsParamStorePropertySources propertySources;
+
+ public AwsParamStoreConfigDataResource(String context, boolean optional,
+ AwsParamStorePropertySources propertySources) {
+ this.context = context;
+ this.optional = optional;
+ this.propertySources = propertySources;
+ }
+
+ /**
+ * Returns context which is equal to Secret Manager secret name.
+ * @return the context
+ */
+ public String getContext() {
+ return this.context;
+ }
+
+ /**
+ * If application startup should fail when secret cannot be loaded or does not exist.
+ * @return is optional
+ */
+ public boolean isOptional() {
+ return this.optional;
+ }
+
+ public AwsParamStorePropertySources getPropertySources() {
+ return this.propertySources;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ AwsParamStoreConfigDataResource that = (AwsParamStoreConfigDataResource) o;
+ return this.optional == that.optional && this.context.equals(that.context);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.optional, this.context);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringCreator(this).append("context", context).append("optional", optional).toString();
+
+ }
+
+}
diff --git a/spring-cloud-starter-aws-parameter-store-config/src/main/resources/META-INF/spring.factories b/spring-cloud-starter-aws-parameter-store-config/src/main/resources/META-INF/spring.factories
index 3d915d8a6..1bec81093 100644
--- a/spring-cloud-starter-aws-parameter-store-config/src/main/resources/META-INF/spring.factories
+++ b/spring-cloud-starter-aws-parameter-store-config/src/main/resources/META-INF/spring.factories
@@ -1,2 +1,10 @@
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.aws.autoconfigure.paramstore.AwsParamStoreBootstrapConfiguration
+
+# ConfigData Location Resolvers
+org.springframework.boot.context.config.ConfigDataLocationResolver=\
+org.springframework.cloud.aws.autoconfigure.paramstore.AwsParamStoreConfigDataLocationResolver
+
+# ConfigData Loaders
+org.springframework.boot.context.config.ConfigDataLoader=\
+org.springframework.cloud.aws.autoconfigure.paramstore.AwsParamStoreConfigDataLoader
diff --git a/spring-cloud-starter-aws-parameter-store-config/src/test/java/org/springframework/cloud/aws/autoconfigure/paramstore/AwsParamStoreConfigDataLocationResolverTest.java b/spring-cloud-starter-aws-parameter-store-config/src/test/java/org/springframework/cloud/aws/autoconfigure/paramstore/AwsParamStoreConfigDataLocationResolverTest.java
new file mode 100644
index 000000000..bf5579c60
--- /dev/null
+++ b/spring-cloud-starter-aws-parameter-store-config/src/test/java/org/springframework/cloud/aws/autoconfigure/paramstore/AwsParamStoreConfigDataLocationResolverTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2013-2020 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
+ *
+ * https://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.aws.autoconfigure.paramstore;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.junit.jupiter.api.Test;
+
+import org.springframework.boot.BootstrapRegistry;
+import org.springframework.boot.context.config.ConfigDataLocation;
+import org.springframework.boot.context.config.ConfigDataLocationResolverContext;
+import org.springframework.boot.context.config.Profiles;
+import org.springframework.boot.context.properties.bind.Binder;
+import org.springframework.mock.env.MockEnvironment;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class AwsParamStoreConfigDataLocationResolverTest {
+
+ @Test
+ void testResolveProfileSpecificWithAutomaticPaths() {
+ String location = "aws-parameterstore:";
+ List locations = testResolveProfileSpecific(location);
+ assertThat(locations).hasSize(4);
+ assertThat(toContexts(locations)).containsExactly("/config/testapp_dev/", "/config/testapp/",
+ "/config/application_dev/", "/config/application/");
+ }
+
+ @Test
+ void testResolveProfileSpecificWithCustomPaths() {
+ String location = "aws-parameterstore:/mypath1;/mypath2;/mypath3";
+ List locations = testResolveProfileSpecific(location);
+ assertThat(locations).hasSize(3);
+ assertThat(toContexts(locations)).containsExactly("/mypath1", "/mypath2", "/mypath3");
+ }
+
+ private List toContexts(List locations) {
+ return locations.stream().map(AwsParamStoreConfigDataResource::getContext).collect(Collectors.toList());
+ }
+
+ private List testResolveProfileSpecific(String location) {
+ AwsParamStoreConfigDataLocationResolver resolver = createResolver();
+ ConfigDataLocationResolverContext context = mock(ConfigDataLocationResolverContext.class);
+ MockEnvironment env = new MockEnvironment();
+ env.setProperty("spring.application.name", "testapp");
+ when(context.getBinder()).thenReturn(Binder.get(env));
+ Profiles profiles = mock(Profiles.class);
+ when(profiles.getAccepted()).thenReturn(Collections.singletonList("dev"));
+ return resolver.resolveProfileSpecific(context, ConfigDataLocation.of(location), profiles);
+ }
+
+ private AwsParamStoreConfigDataLocationResolver createResolver() {
+ return new AwsParamStoreConfigDataLocationResolver() {
+ @Override
+ public void registerBean(ConfigDataLocationResolverContext context, Class type, T instance) {
+ // do nothing
+ }
+
+ @Override
+ protected void registerBean(ConfigDataLocationResolverContext context, Class type,
+ BootstrapRegistry.InstanceSupplier supplier) {
+ // do nothing
+ }
+
+ @Override
+ protected void registerAndPromoteBean(ConfigDataLocationResolverContext context, Class type,
+ BootstrapRegistry.InstanceSupplier supplier) {
+ // do nothing
+ }
+ };
+ }
+
+}