Skip to content

Commit 8e77434

Browse files
authored
Merge pull request #4 from ericdallo/custom-property-source
[custom-property-source] - Change to use Spring PropertySource Bean
2 parents 2aa05b4 + 34713e7 commit 8e77434

13 files changed

+263
-159
lines changed

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,18 @@ There 2 ways to configure your application to load properties from s3:
3232

3333
**Anotation**
3434

35-
Add this annotation to any spring managed bean
35+
- Adding this annotation to any spring managed bean
3636
```java
3737
@S3PropertiesLocation("my-bucket/my-folder/my-properties.properties")
3838
```
39-
or using a specific profile to only load properties if the app is running with that profile
39+
- Using a specific profile to only load properties if the app is running with that profile
4040
```java
4141
@S3PropertiesLocation(path = "my-bucket/my-folder/my-properties.properties", profiles = "production")
4242
```
43-
43+
- Load from a System env variable
44+
```java
45+
@S3PropertiesLocation(path = "${AWS_S3_LOCATION}", profiles = "production")
46+
```
4447
**Configuration**
4548
```java
4649
@Bean
@@ -55,4 +58,4 @@ S3PropertyPlaceholderConfigurer s3PropertyPlaceholderConfigurer(AmazonS3 amazonS
5558
## Requisites
5659

5760
Official spring aws sdk lib.
58-
See: https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-aws
61+
See: https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-aws

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ dependencies {
6767
testCompile "junit:junit:4.12"
6868
testCompile "org.mockito:mockito-core:1.10.19"
6969
testCompile "org.assertj:assertj-core:1.0.0"
70+
testCompile "org.powermock:powermock-api-mockito:1.6.6"
71+
testCompile "org.powermock:powermock-module-junit4:1.6.6"
7072
}
7173

7274
task sourcesJar(type: Jar) {

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version=1.0.5-SNAPSHOT
1+
version=2.0-SNAPSHOT

src/main/java/com/spring/loader/S3PropertiesLocation.java

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,28 @@
77
import java.lang.annotation.Target;
88

99
import org.springframework.context.annotation.Import;
10-
import org.springframework.core.annotation.AliasFor;
1110

1211
/**
13-
* Allow the auto configuration of the {@link S3PropertyPlaceholderConfigurer} bean.
12+
* Allow the auto configuration of the {@link S3PropertySource} bean.
1413
*
1514
* @author Eric Dallo
1615
* @since 1.0.3
1716
* @see S3PropertiesLocationRegistrar
1817
*/
1918
@Target(ElementType.TYPE)
2019
@Retention(RetentionPolicy.RUNTIME)
21-
@Import({S3ResourseLoaderConfiguration.class, S3PropertiesLocationRegistrar.class})
20+
@Import({ S3ResourseLoaderConfiguration.class, S3PropertiesLocationRegistrar.class })
2221
@Documented
2322
public @interface S3PropertiesLocation {
24-
23+
2524
/**
2625
* The location of the properties in aws s3.
2726
*
28-
* @return the path of aws s3 properties, e.g. my-bucket/my-folder/app.properties
27+
* @return the path of aws s3 properties e.g. "my-bucket/my-folder/app.properties"
28+
* or a enviroment system to the s3 path e.g. "${MY_BUCKET_IN_AWS_S3}"
2929
*/
30-
@AliasFor("path")
31-
String[] value() default {};
32-
33-
@AliasFor("value")
34-
String[] path() default {};
35-
30+
String[] value();
31+
3632
/**
3733
* The profiles to load the properties in aws s3.
3834
*

src/main/java/com/spring/loader/S3PropertiesLocationRegistrar.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,27 @@
1313
import org.springframework.core.type.AnnotationMetadata;
1414

1515
/**
16-
* Creates the {@link S3PropertyPlaceholderConfigurer} bean.
16+
* Creates the {@link S3PropertySource} bean.
1717
* For use with the {@link S3PropertiesLocation} annotation.
1818
*
1919
* @author Eric Dallo
2020
* @since 1.0.3
2121
* @see S3PropertiesLocation
22-
* @see S3PropertyPlaceholderConfigurer
22+
* @see S3PropertySource
2323
*/
2424
class S3PropertiesLocationRegistrar implements EnvironmentAware, ImportBeanDefinitionRegistrar {
2525

2626
private Environment environment;
27+
private SystemPropertyResolver resolver;
28+
29+
public S3PropertiesLocationRegistrar() {
30+
resolver = new SystemPropertyResolver();
31+
}
32+
33+
public S3PropertiesLocationRegistrar(Environment environment, SystemPropertyResolver resolver) {
34+
this.environment = environment;
35+
this.resolver = resolver;
36+
}
2737

2838
@Override
2939
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
@@ -36,12 +46,18 @@ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, B
3646

3747
String[] locations = attributes.getStringArray("value");
3848

39-
BeanDefinition bd = new RootBeanDefinition(S3PropertyPlaceholderConfigurer.class);
49+
String[] formattedLocations = new String[locations.length];
50+
51+
for (int i = 0; i < locations.length; i++) {
52+
formattedLocations[i] = resolver.getFormattedValue(locations[i]);
53+
}
54+
55+
BeanDefinition bd = new RootBeanDefinition(S3PropertiesSourceConfigurer.class);
4056

4157
bd.getPropertyValues().addPropertyValue("s3ResourceLoader", new RuntimeBeanReference("s3ResourceLoader"));
42-
bd.getPropertyValues().add("s3Locations", locations);
58+
bd.getPropertyValues().add("s3Locations", formattedLocations);
4359

44-
String beanName = S3PropertyPlaceholderConfigurer.class.getSimpleName();
60+
String beanName = S3PropertiesSourceConfigurer.class.getSimpleName();
4561
registry.registerBeanDefinition(toLowerCase(beanName.charAt(0)) + beanName.substring(1), bd);
4662
}
4763

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.spring.loader;
2+
3+
import java.io.IOException;
4+
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
import org.springframework.beans.BeansException;
8+
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
9+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
10+
import org.springframework.beans.factory.config.PropertiesFactoryBean;
11+
import org.springframework.context.EnvironmentAware;
12+
import org.springframework.core.Ordered;
13+
import org.springframework.core.PriorityOrdered;
14+
import org.springframework.core.env.ConfigurableEnvironment;
15+
import org.springframework.core.env.Environment;
16+
import org.springframework.core.env.MutablePropertySources;
17+
18+
/**
19+
* Add a new {@link PropertySource} to spring property sources from a S3 bucket
20+
* For use with the {@link S3PropertiesLocation} annotation.
21+
*
22+
* @author Eric Dallo
23+
* @since 2.0
24+
* @see S3PropertiesLocation
25+
* @see S3PropertySource
26+
*/
27+
class S3PropertiesSourceConfigurer implements EnvironmentAware, BeanFactoryPostProcessor, PriorityOrdered {
28+
29+
private static final Logger LOGGER = LoggerFactory.getLogger(S3PropertiesSourceConfigurer.class);
30+
31+
private Environment environment;
32+
private S3ResourceLoader s3ResourceLoader;
33+
private String[] s3Locations;
34+
35+
public void setS3ResourceLoader(S3ResourceLoader s3ResourceLoader) {
36+
this.s3ResourceLoader = s3ResourceLoader;
37+
}
38+
39+
public void setS3Locations(String[] s3Locations) {
40+
this.s3Locations = s3Locations;
41+
}
42+
43+
@Override
44+
public void setEnvironment(Environment environment) {
45+
this.environment = environment;
46+
}
47+
48+
@Override
49+
public int getOrder() {
50+
return Ordered.HIGHEST_PRECEDENCE;
51+
}
52+
53+
@Override
54+
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
55+
if (this.environment instanceof ConfigurableEnvironment) {
56+
57+
PropertiesFactoryBean propertiesFactory = new PropertiesFactoryBean();
58+
MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
59+
60+
for (String s3Location : s3Locations) {
61+
62+
propertiesFactory.setLocation(s3ResourceLoader.getResource(s3Location));
63+
try {
64+
propertiesFactory.afterPropertiesSet();
65+
propertySources.addFirst(new S3PropertySource(propertiesFactory.getObject()));
66+
} catch (IOException e) {
67+
LOGGER.error("Could not read properties from s3Location: " + s3Location, e);
68+
}
69+
}
70+
71+
} else {
72+
LOGGER.warn("Environment is not of type '{}' property source with instance data is not available", ConfigurableEnvironment.class.getName());
73+
}
74+
}
75+
76+
}

src/main/java/com/spring/loader/S3PropertyPlaceholderConfigurer.java

Lines changed: 0 additions & 72 deletions
This file was deleted.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.spring.loader;
2+
3+
import java.util.Properties;
4+
5+
import org.springframework.core.env.PropertySource;
6+
7+
class S3PropertySource extends PropertySource<Object> {
8+
9+
private static final String S3_PROPERTY_SOURCE_NAME = "s3PropertySource";
10+
11+
private final Properties properties;
12+
13+
S3PropertySource (Properties properties) {
14+
super(S3_PROPERTY_SOURCE_NAME);
15+
this.properties = properties;
16+
}
17+
18+
@Override
19+
public Object getProperty(String name) {
20+
return properties.get(name);
21+
}
22+
}

src/main/java/com/spring/loader/S3ResourceLoader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*
1717
* @author Eric Dallo
1818
* @since 1.0.0
19-
* @see S3PropertyPlaceholderConfigurer
19+
* @see S3PropertySource
2020
*/
2121
class S3ResourceLoader implements ResourceLoader {
2222

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.spring.loader;
2+
3+
import static java.lang.String.format;
4+
import static org.springframework.util.StringUtils.isEmpty;
5+
6+
import com.spring.loader.exception.EnviromentPropertyNotFoundException;
7+
import com.spring.loader.exception.InvalidS3LocationException;
8+
9+
/**
10+
* Resolver for properties that will be retrieved from system environment.
11+
*
12+
* @author Eric Dallo
13+
* @since 2.0
14+
*/
15+
public class SystemPropertyResolver {
16+
17+
private static final String SYSTEM_NOTATION_PREFIX = "${";
18+
private static final String SYSTEM_NOTATION_SUFIX = "}";
19+
20+
public String getFormattedValue(String value) {
21+
if (isEmpty(value)) {
22+
throw new InvalidS3LocationException("The location cannot be empty or null");
23+
}
24+
if (value.startsWith(SYSTEM_NOTATION_PREFIX)) {
25+
26+
if (value.endsWith(SYSTEM_NOTATION_SUFIX)) {
27+
String rawProperty = value.substring(SYSTEM_NOTATION_PREFIX.length(), value.length() - SYSTEM_NOTATION_SUFIX.length());
28+
String valueFromEnv = System.getenv(rawProperty);
29+
30+
if (isEmpty(valueFromEnv)) {
31+
throw new EnviromentPropertyNotFoundException(format("Enviroment variable %s not found in system", rawProperty));
32+
}
33+
34+
return valueFromEnv;
35+
}
36+
throw new InvalidS3LocationException("Syntax error for system property: " + value);
37+
}
38+
39+
return value;
40+
}
41+
42+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.spring.loader.exception;
2+
3+
public class EnviromentPropertyNotFoundException extends S3ResourceException {
4+
private static final long serialVersionUID = -3434756303367606716L;
5+
6+
public EnviromentPropertyNotFoundException(String message) {
7+
super(message);
8+
}
9+
10+
}

0 commit comments

Comments
 (0)