diff --git a/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/CassandraCnbBindingProcessor.java b/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/CassandraCnbBindingProcessor.java index 2add981..24b20b1 100644 --- a/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/CassandraCnbBindingProcessor.java +++ b/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/CassandraCnbBindingProcessor.java @@ -15,7 +15,7 @@ */ package org.springframework.cloud.cnb.boot; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; import java.util.Map; diff --git a/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/CnbBindingProcessor.java b/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/CnbBindingProcessor.java index 3fffaee..1c4f23e 100644 --- a/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/CnbBindingProcessor.java +++ b/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/CnbBindingProcessor.java @@ -17,7 +17,7 @@ package org.springframework.cloud.cnb.boot; import java.util.Map; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; public interface CnbBindingProcessor { diff --git a/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/CnbBindingsPostProcessor.java b/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/CnbBindingsPostProcessor.java index 427e21c..585032c 100644 --- a/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/CnbBindingsPostProcessor.java +++ b/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/CnbBindingsPostProcessor.java @@ -25,8 +25,8 @@ import org.springframework.boot.context.config.ConfigFileApplicationListener; import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.cloud.cnb.core.CNBBindingsSingleton; -import org.springframework.cloud.cnb.core.Binding; -import org.springframework.cloud.cnb.core.Bindings; +import org.springframework.cloud.cnb.Binding; +import org.springframework.cloud.cnb.Bindings; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.Ordered; diff --git a/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/DataSourceCnbBindingProcessor.java b/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/DataSourceCnbBindingProcessor.java index 4f1595a..cf0de3f 100644 --- a/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/DataSourceCnbBindingProcessor.java +++ b/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/DataSourceCnbBindingProcessor.java @@ -17,7 +17,7 @@ package org.springframework.cloud.cnb.boot; import java.util.Map; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; import org.springframework.cloud.cnb.jdbc.JdbcBinding; diff --git a/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/MongoCnbBindingProcessor.java b/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/MongoCnbBindingProcessor.java index 44b7531..1baae33 100644 --- a/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/MongoCnbBindingProcessor.java +++ b/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/MongoCnbBindingProcessor.java @@ -17,7 +17,7 @@ package org.springframework.cloud.cnb.boot; import java.util.Map; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; public class MongoCnbBindingProcessor implements CnbBindingProcessor { private static final String MONGO_KIND = "mongodb"; diff --git a/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/RedisCnbBindingProcessor.java b/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/RedisCnbBindingProcessor.java index 12445db..616a813 100644 --- a/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/RedisCnbBindingProcessor.java +++ b/cnb-bindings-boot/src/main/java/org/springframework/cloud/cnb/boot/RedisCnbBindingProcessor.java @@ -17,7 +17,7 @@ package org.springframework.cloud.cnb.boot; import java.util.Map; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; public class RedisCnbBindingProcessor implements CnbBindingProcessor { diff --git a/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/CassandraCnbBindingProcessorTests.java b/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/CassandraCnbBindingProcessorTests.java index 2122748..11cdc58 100644 --- a/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/CassandraCnbBindingProcessorTests.java +++ b/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/CassandraCnbBindingProcessorTests.java @@ -20,7 +20,7 @@ import java.util.Map; import org.junit.Test; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; import static org.assertj.core.api.Assertions.assertThat; diff --git a/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/DataSourceCnbBindingProcessorTests.java b/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/DataSourceCnbBindingProcessorTests.java index e4f338a..6dd57a8 100644 --- a/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/DataSourceCnbBindingProcessorTests.java +++ b/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/DataSourceCnbBindingProcessorTests.java @@ -21,7 +21,7 @@ import java.util.Map; import org.junit.Test; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; import static org.assertj.core.api.Assertions.assertThat; diff --git a/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/MongoCnbBindingProcessorTests.java b/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/MongoCnbBindingProcessorTests.java index 662e61f..9e158ba 100644 --- a/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/MongoCnbBindingProcessorTests.java +++ b/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/MongoCnbBindingProcessorTests.java @@ -21,7 +21,7 @@ import java.util.Map; import org.junit.Test; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; import static org.assertj.core.api.Assertions.assertThat; diff --git a/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/RedisCnbBindingProcessorTests.java b/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/RedisCnbBindingProcessorTests.java index e98027f..9f089ca 100644 --- a/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/RedisCnbBindingProcessorTests.java +++ b/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/RedisCnbBindingProcessorTests.java @@ -21,7 +21,7 @@ import java.util.Map; import org.junit.Test; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; import static org.assertj.core.api.Assertions.assertThat; diff --git a/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/test/EnvMock.java b/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/test/EnvMock.java index 5d15abe..69a2cd0 100644 --- a/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/test/EnvMock.java +++ b/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/test/EnvMock.java @@ -21,7 +21,7 @@ import java.util.Map; import mockit.MockUp; -import org.springframework.cloud.cnb.core.Bindings; +import org.springframework.cloud.cnb.Bindings; /** diff --git a/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/test/TestJdbcKind.java b/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/test/TestJdbcKind.java index 6710e3e..a9c61f6 100644 --- a/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/test/TestJdbcKind.java +++ b/cnb-bindings-boot/src/test/java/org/springframework/cloud/cnb/boot/test/TestJdbcKind.java @@ -15,7 +15,7 @@ */ package org.springframework.cloud.cnb.boot.test; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; import org.springframework.cloud.cnb.jdbc.JdbcKind; public class TestJdbcKind implements JdbcKind { diff --git a/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/DB2JdbcKind.java b/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/DB2JdbcKind.java index 999be7d..6c11445 100644 --- a/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/DB2JdbcKind.java +++ b/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/DB2JdbcKind.java @@ -15,7 +15,7 @@ */ package org.springframework.cloud.cnb.jdbc; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; public class DB2JdbcKind implements JdbcKind{ diff --git a/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/JdbcBinding.java b/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/JdbcBinding.java index bc1f6db..718b187 100644 --- a/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/JdbcBinding.java +++ b/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/JdbcBinding.java @@ -17,7 +17,7 @@ package org.springframework.cloud.cnb.jdbc; import java.util.ServiceLoader; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; import org.springframework.cloud.cnb.core.IllegalBindingException; diff --git a/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/JdbcKind.java b/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/JdbcKind.java index 0313141..4104342 100644 --- a/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/JdbcKind.java +++ b/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/JdbcKind.java @@ -15,7 +15,7 @@ */ package org.springframework.cloud.cnb.jdbc; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; public interface JdbcKind { boolean forBinding(Binding binding); diff --git a/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/MysqlJdbcKind.java b/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/MysqlJdbcKind.java index b958a6b..ff9fb96 100644 --- a/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/MysqlJdbcKind.java +++ b/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/MysqlJdbcKind.java @@ -15,7 +15,7 @@ */ package org.springframework.cloud.cnb.jdbc; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; public class MysqlJdbcKind implements JdbcKind{ diff --git a/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/OracleJdbcKind.java b/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/OracleJdbcKind.java index f7e76b5..6a50e89 100644 --- a/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/OracleJdbcKind.java +++ b/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/OracleJdbcKind.java @@ -15,7 +15,7 @@ */ package org.springframework.cloud.cnb.jdbc; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; public class OracleJdbcKind implements JdbcKind{ diff --git a/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/PostgresJdbcKind.java b/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/PostgresJdbcKind.java index cbe7acf..873f57e 100644 --- a/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/PostgresJdbcKind.java +++ b/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/PostgresJdbcKind.java @@ -18,7 +18,7 @@ package org.springframework.cloud.cnb.jdbc; import java.util.Arrays; import java.util.List; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; public class PostgresJdbcKind implements JdbcKind{ diff --git a/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/SqlServerJdbcKind.java b/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/SqlServerJdbcKind.java index 5b56171..70d95a3 100644 --- a/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/SqlServerJdbcKind.java +++ b/cnb-bindings-jdbc/src/main/java/org/springframework/cloud/cnb/jdbc/SqlServerJdbcKind.java @@ -15,7 +15,7 @@ */ package org.springframework.cloud.cnb.jdbc; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; public class SqlServerJdbcKind implements JdbcKind{ diff --git a/cnb-bindings-jdbc/src/test/java/org/springframework/cloud/cnb/jdbc/JdbcBindingTest.java b/cnb-bindings-jdbc/src/test/java/org/springframework/cloud/cnb/jdbc/JdbcBindingTest.java index b1aaa3e..2044002 100644 --- a/cnb-bindings-jdbc/src/test/java/org/springframework/cloud/cnb/jdbc/JdbcBindingTest.java +++ b/cnb-bindings-jdbc/src/test/java/org/springframework/cloud/cnb/jdbc/JdbcBindingTest.java @@ -20,7 +20,7 @@ import java.util.Map; import org.junit.Test; -import org.springframework.cloud.cnb.core.Binding; +import org.springframework.cloud.cnb.Binding; import static org.assertj.core.api.Assertions.assertThat; diff --git a/cnb-bindings/pom.xml b/cnb-bindings/pom.xml index 8c4ceed..e51fad8 100644 --- a/cnb-bindings/pom.xml +++ b/cnb-bindings/pom.xml @@ -1,4 +1,20 @@ + + @@ -20,6 +36,11 @@ annotations provided + + org.springframework.boot + spring-boot-starter + provided + org.springframework.boot diff --git a/cnb-bindings/src/main/java/org/springframework/cloud/cnb/core/Binding.java b/cnb-bindings/src/main/java/org/springframework/cloud/bindings/Binding.java similarity index 98% rename from cnb-bindings/src/main/java/org/springframework/cloud/cnb/core/Binding.java rename to cnb-bindings/src/main/java/org/springframework/cloud/bindings/Binding.java index 20c08b9..a2156df 100644 --- a/cnb-bindings/src/main/java/org/springframework/cloud/cnb/core/Binding.java +++ b/cnb-bindings/src/main/java/org/springframework/cloud/bindings/Binding.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 the original author or authors. + * Copyright 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. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.cloud.cnb.core; +package org.springframework.cloud.bindings; import org.jetbrains.annotations.NotNull; diff --git a/cnb-bindings/src/main/java/org/springframework/cloud/cnb/core/Bindings.java b/cnb-bindings/src/main/java/org/springframework/cloud/bindings/Bindings.java similarity index 77% rename from cnb-bindings/src/main/java/org/springframework/cloud/cnb/core/Bindings.java rename to cnb-bindings/src/main/java/org/springframework/cloud/bindings/Bindings.java index 4d2ba99..c0eb485 100644 --- a/cnb-bindings/src/main/java/org/springframework/cloud/cnb/core/Bindings.java +++ b/cnb-bindings/src/main/java/org/springframework/cloud/bindings/Bindings.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.cloud.cnb.core; +package org.springframework.cloud.bindings; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -23,9 +23,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; @@ -58,35 +58,34 @@ public final class Bindings { * * @param path the path to populate the {@code Bindings} from. */ - public Bindings(@NotNull String path) { + public Bindings(String path) { + if (path == null) { + this.bindings = Collections.emptyList(); + return; + } + Path p = Paths.get(path); if (!Files.exists(p)) { this.bindings = Collections.emptyList(); - } else if (!Files.isDirectory(p)) { + return; + } + + if (!Files.isDirectory(p)) { throw new IllegalArgumentException(String.format("%s is not a directory", p)); - } else { - try { - this.bindings = Files.list(p) - .map(Binding::new) - .collect(Collectors.toList()); - } catch (IOException e) { - throw new IllegalStateException(String.format("unable to list children of '%s'", path), e); - } + } + + try { + this.bindings = Files.list(p) + .map(Binding::new) + .collect(Collectors.toList()); + } catch (IOException e) { + throw new IllegalStateException(String.format("unable to list children of '%s'", path), e); } } - /** - * Indicates whether the {@code $CNB_BINDINGS} is and zero or more bindings will be available. - * - * @return {@code true} if {@code $CNB_BINDINGS} is set, {@code false} otherwise. - */ - public static boolean hasBindings() { - return hasBindings(System.getenv()); - } - - static boolean hasBindings(@NotNull Map environment) { - return environment.containsKey(CNB_BINDINGS); + Bindings(@NotNull Binding... bindings) { + this.bindings = Arrays.asList(bindings); } /** @@ -113,13 +112,13 @@ public final class Bindings { } /** - * Returns zero or more {@link Binding}s with a given kind. Equivalent to {@link #getBindings(String, String)}. + * Returns zero or more {@link Binding}s with a given kind. Equivalent to {@link #filterBindings(String, String)}. * * @param kind the kind of the {@code Binding} to find. * @return the collection of {@code Binding}s with a given kind. */ - public @NotNull List getBindings(@Nullable String kind) { - return getBindings(kind, null); + public @NotNull List filterBindings(@Nullable String kind) { + return filterBindings(kind, null); } /** @@ -130,7 +129,7 @@ public final class Bindings { * @param provider the provider of {@code Binding} to find * @return the collection of {@code Binding}s with a given kind and provider. */ - public @NotNull List getBindings(@Nullable String kind, @Nullable String provider) { + public @NotNull List filterBindings(@Nullable String kind, @Nullable String provider) { List filtered = new ArrayList<>(); for (Binding binding : bindings) { diff --git a/cnb-bindings/src/main/java/org/springframework/cloud/bindings/BindingsEnvironmentPostProcessor.java b/cnb-bindings/src/main/java/org/springframework/cloud/bindings/BindingsEnvironmentPostProcessor.java new file mode 100644 index 0000000..c7e0189 --- /dev/null +++ b/cnb-bindings/src/main/java/org/springframework/cloud/bindings/BindingsEnvironmentPostProcessor.java @@ -0,0 +1,112 @@ +/* + * Copyright 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 + * + * 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.bindings; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jetbrains.annotations.NotNull; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.context.config.ConfigFileApplicationListener; +import org.springframework.boot.env.EnvironmentPostProcessor; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.support.SpringFactoriesLoader; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.springframework.core.env.CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; + +/** + * An implementation of {@link EnvironmentPostProcessor} that delegates properties generation from {@link Bindings}. + * This implementation generates a single instance of {@code Bindings} and then calls all implementations of + * {@link BindingsPropertiesProcessor} registered with {@link SpringFactoriesLoader} allowing them to generate any + * properties from the contents of the {@code Bindings}. + */ +public final class BindingsEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { + + /** + * The name of the {@link PropertySource} created by the {@code BindingsEnvironmentPostProcessor}: {@value}. + */ + public static final String BINDINGS_PROPERTY_SOURCE_NAME = "cnbBindings"; + + private final Log log = LogFactory.getLog(getClass()); + + private final Bindings bindings; + + private final List processors; + + /** + * Creates a new instance of {@code BindingsEnvironmentPostProcessor} using the {@link Bindings} available in the + * environment and the {@link BindingsPropertiesProcessor}s registered with {@link SpringFactoriesLoader}. + */ + public BindingsEnvironmentPostProcessor() { + this.bindings = new Bindings(); + this.processors = SpringFactoriesLoader. + loadFactories(BindingsPropertiesProcessor.class, getClass().getClassLoader()); + AnnotationAwareOrderComparator.sort(this.processors); + } + + BindingsEnvironmentPostProcessor(@NotNull Bindings bindings, + @NotNull BindingsPropertiesProcessor... processors) { + + this.bindings = bindings; + this.processors = Arrays.asList(processors); + } + + @Override + public void postProcessEnvironment(@NotNull ConfigurableEnvironment environment, + @NotNull SpringApplication application) { + + if (bindings.getBindings().isEmpty()) { + log.debug("No CNB Bindings found. Skipping Environment post-processing."); + return; + } + + Map properties = new HashMap<>(); + for (BindingsPropertiesProcessor processor : processors) { + processor.process(bindings, properties); + } + if (properties.isEmpty()) { + log.debug("No properties set from CNB Bindings. Skipping PropertySource creation."); + return; + } + + log.info("Creating PropertySource from CNB Bindings"); + MutablePropertySources propertySources = environment.getPropertySources(); + MapPropertySource propertySource = new MapPropertySource(BINDINGS_PROPERTY_SOURCE_NAME, properties); + + if (propertySources.contains(COMMAND_LINE_PROPERTY_SOURCE_NAME)) { + propertySources.addAfter(COMMAND_LINE_PROPERTY_SOURCE_NAME, propertySource); + } else { + propertySources.addFirst(propertySource); + } + } + + @Override + public int getOrder() { + // Before ConfigFileApplicationListener so values there can use values from {@link Bindings}. + return ConfigFileApplicationListener.DEFAULT_ORDER - 1; + } + +} diff --git a/cnb-bindings/src/main/java/org/springframework/cloud/bindings/BindingsPropertiesProcessor.java b/cnb-bindings/src/main/java/org/springframework/cloud/bindings/BindingsPropertiesProcessor.java new file mode 100644 index 0000000..4c12807 --- /dev/null +++ b/cnb-bindings/src/main/java/org/springframework/cloud/bindings/BindingsPropertiesProcessor.java @@ -0,0 +1,38 @@ +/* + * Copyright 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 + * + * 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.bindings; + +import org.springframework.lang.NonNull; + +import java.util.Map; + +/** + * An interface for types that transform the contents of a {@link Bindings} into properties for injection into a + * {@link org.springframework.core.env.PropertySource}. + */ +@FunctionalInterface +public interface BindingsPropertiesProcessor { + + /** + * Transform the currently accumulated {@link Bindings}-related properties. + * + * @param bindings the {@code Bindings} exposed to the application. + * @param properties the currently accumulated properties. + */ + void process(@NonNull Bindings bindings, @NonNull Map properties); + +} diff --git a/cnb-bindings/src/main/resources/META-INF/spring.factories b/cnb-bindings/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..f368657 --- /dev/null +++ b/cnb-bindings/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.env.EnvironmentPostProcessor=\ + org.springframework.cloud.bindings.BindingsEnvironmentPostProcessor diff --git a/cnb-bindings/src/test/java/org/springframework/cloud/cnb/core/BindingTest.java b/cnb-bindings/src/test/java/org/springframework/cloud/bindings/BindingTest.java similarity index 85% rename from cnb-bindings/src/test/java/org/springframework/cloud/cnb/core/BindingTest.java rename to cnb-bindings/src/test/java/org/springframework/cloud/bindings/BindingTest.java index f4140c7..3d14dbb 100644 --- a/cnb-bindings/src/test/java/org/springframework/cloud/cnb/core/BindingTest.java +++ b/cnb-bindings/src/test/java/org/springframework/cloud/bindings/BindingTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 the original author or authors. + * Copyright 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. @@ -13,18 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.cloud.cnb.core; +package org.springframework.cloud.bindings; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.nio.file.Paths; import static org.assertj.core.api.Assertions.assertThat; +@DisplayName("Binding") final class BindingTest { @Test + @DisplayName("populates content from filesystem") void test() { Binding binding = new Binding(Paths.get("src/test/resources/test-name-1")); diff --git a/cnb-bindings/src/test/java/org/springframework/cloud/bindings/BindingsEnvironmentPostProcessorTest.java b/cnb-bindings/src/test/java/org/springframework/cloud/bindings/BindingsEnvironmentPostProcessorTest.java new file mode 100644 index 0000000..d037065 --- /dev/null +++ b/cnb-bindings/src/test/java/org/springframework/cloud/bindings/BindingsEnvironmentPostProcessorTest.java @@ -0,0 +1,116 @@ +/* + * Copyright 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 + * + * 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.bindings; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.context.config.ConfigFileApplicationListener; +import org.springframework.core.env.PropertySource; +import org.springframework.core.env.SimpleCommandLinePropertySource; +import org.springframework.mock.env.MockEnvironment; + +import java.nio.file.Paths; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.cloud.bindings.BindingsEnvironmentPostProcessor.BINDINGS_PROPERTY_SOURCE_NAME; + +@DisplayName("Bindings EnvironmentPostProcessor") +final class BindingsEnvironmentPostProcessorTest { + + private final SpringApplication application = new SpringApplication(); + + private final MockEnvironment environment = new MockEnvironment(); + + @Test + @DisplayName("does not create PropertySource if no bindings") + void noBindings() { + new BindingsEnvironmentPostProcessor(new Bindings()).postProcessEnvironment(environment, application); + + assertThat(environment.getPropertySources()).hasSize(1); + } + + @Test + @DisplayName("does not create PropertySource if no properties") + void noProperties() { + new BindingsEnvironmentPostProcessor( + new Bindings( + new Binding("test-name", Paths.get("test-path"), + Collections.emptyMap(), Collections.emptyMap()) + ) + ).postProcessEnvironment(environment, application); + + assertThat(environment.getPropertySources()).hasSize(1); + } + + @Test + @DisplayName("creates PropertySource with properties") + void containsProperties() { + new BindingsEnvironmentPostProcessor( + new Bindings( + new Binding("test-name", Paths.get("test-path"), + Collections.emptyMap(), Collections.emptyMap()) + ), + (environment, properties) -> properties.put("test-key", "test-value") + ).postProcessEnvironment(environment, application); + + assertThat(environment.getPropertySources()).hasSize(2); + assertThat(environment.getProperty("test-key")).isEqualTo("test-value"); + } + + @Test + @DisplayName("adds PropertySource after CommandLinePropertySource") + void withCommandLinePropertySource() { + environment.getPropertySources().addFirst(new SimpleCommandLinePropertySource()); + + new BindingsEnvironmentPostProcessor( + new Bindings( + new Binding("test-name", Paths.get("test-path"), + Collections.emptyMap(), Collections.emptyMap()) + ), + (environment, properties) -> properties.put("test-key", "test-value") + ).postProcessEnvironment(environment, application); + + PropertySource propertySource = environment.getPropertySources().get(BINDINGS_PROPERTY_SOURCE_NAME); + assertThat(propertySource).isNotNull(); + assertThat(environment.getPropertySources().precedenceOf(propertySource)).isEqualTo(1); + } + + @Test + @DisplayName("adds PropertySource first") + void withoutCommandLinePropertySource() { + new BindingsEnvironmentPostProcessor( + new Bindings( + new Binding("test-name", Paths.get("test-path"), + Collections.emptyMap(), Collections.emptyMap()) + ), + (environment, properties) -> properties.put("test-key", "test-value") + ).postProcessEnvironment(environment, application); + + PropertySource propertySource = environment.getPropertySources().get(BINDINGS_PROPERTY_SOURCE_NAME); + assertThat(propertySource).isNotNull(); + assertThat(environment.getPropertySources().precedenceOf(propertySource)).isEqualTo(0); + } + + @Test + @DisplayName("has order before ConfigFileApplicationListener") + void order() { + assertThat(new BindingsEnvironmentPostProcessor(new Bindings()).getOrder()) + .isLessThan(ConfigFileApplicationListener.DEFAULT_ORDER); + } +} diff --git a/cnb-bindings/src/test/java/org/springframework/cloud/bindings/BindingsTests.java b/cnb-bindings/src/test/java/org/springframework/cloud/bindings/BindingsTests.java new file mode 100644 index 0000000..e3ed083 --- /dev/null +++ b/cnb-bindings/src/test/java/org/springframework/cloud/bindings/BindingsTests.java @@ -0,0 +1,113 @@ +/* + * Copyright 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 + * + * 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.bindings; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +@DisplayName("Bindings") +final class BindingsTests { + + @Nested + @DisplayName("Constructor") + final class Constructor { + + @Test + @DisplayName("empty if path is null") + void nullPath() { + Bindings b = new Bindings((String) null); + + assertThat(b.getBindings()).isEmpty(); + } + + @Test + @DisplayName("empty if path does not exist") + void nonExistentDirectory() { + String path = "src/test/resources/non-existent"; + Bindings b = new Bindings(path); + + assertThat(b.getBindings()).isEmpty(); + } + + @Test + @DisplayName("throws exception if path is not a directory") + void nonDirectory() throws IOException { + String path = File.createTempFile("bindings", "").getPath(); + + assertThatIllegalArgumentException().isThrownBy(() -> new Bindings(path)); + } + + @Test + @DisplayName("populates content") + void construct() { + String path = "src/test/resources"; + Bindings b = new Bindings(path); + + assertThat(b.getBindings()).hasSize(2); + } + + } + + @Nested + @DisplayName("Content") + final class Content { + + private final Bindings bindings = new Bindings( + new Binding("test-name-1", Paths.get("src/test/resources/test-name-1"), + metadata("test-kind-1", "test-provider-1"), Collections.emptyMap()), + new Binding("test-name-2", Paths.get("src/test/resources/test-name-2"), + metadata("test-kind-2", "test-provider-2"), Collections.emptyMap()) + ); + + private Map metadata(String kind, String provider) { + Map metadata = new HashMap<>(2); + metadata.put("kind", kind); + metadata.put("provider", provider); + return metadata; + } + + @Test + @DisplayName("returns content") + void getBindings() { + assertThat(bindings.getBindings()).hasSize(2); + } + + @Test + @DisplayName("filters bindings by kind") + void filterBindingsByKind() { + assertThat(bindings.filterBindings("test-kind-1", null)).hasSize(1); + } + + @Test + @DisplayName("filters bindings by provider") + void filterBindingsByProvider() { + assertThat(bindings.filterBindings(null, "test-provider-1")).hasSize(1); + } + + } + +} diff --git a/cnb-bindings/src/test/java/org/springframework/cloud/cnb/core/BindingsTests.java b/cnb-bindings/src/test/java/org/springframework/cloud/cnb/core/BindingsTests.java deleted file mode 100644 index 83f4b6a..0000000 --- a/cnb-bindings/src/test/java/org/springframework/cloud/cnb/core/BindingsTests.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 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 - * - * 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.cnb.core; - -import org.junit.jupiter.api.Test; - -import java.io.File; -import java.io.IOException; -import java.util.Collections; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; - -final class BindingsTests { - - @Test - void constructFromNonExistentDirectory() { - String path = "src/test/resources/non-existent"; - Bindings b = new Bindings(path); - - assertThat(b.getBindings()).isEmpty(); - } - - @Test - void constructFromNonDirectory() throws IOException { - String path = File.createTempFile("bindings", "").getPath(); - - assertThatIllegalArgumentException().isThrownBy(() -> new Bindings(path)); - } - - @Test - void construct() { - String path = "src/test/resources"; - Bindings b = new Bindings(path); - - assertThat(b.getBindings()).hasSize(2); - } - - @Test - void hasBindings() { - assertThat(Bindings.hasBindings(Collections.emptyMap())).isFalse(); - assertThat(Bindings.hasBindings(Collections.singletonMap("CNB_BINDINGS", ""))).isTrue(); - } - - @Test - void getBindings() { - String path = "src/test/resources"; - Bindings b = new Bindings(path); - - assertThat(b.getBindings()).hasSize(2); - } - - @Test - void findBindingsByKind() { - String path = "src/test/resources"; - Bindings b = new Bindings(path); - - assertThat(b.getBindings("test-kind-1", null)).hasSize(1); - } - - @Test - void findBindingsByProvider() { - String path = "src/test/resources"; - Bindings b = new Bindings(path); - - assertThat(b.getBindings(null, "test-provider-1")).hasSize(1); - } - -}