From dbc7e938c8cf243e70b4e66eb67dfa95f43f3688 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 17 Apr 2017 11:41:53 -0700 Subject: [PATCH] Restructor and improve Origin support Move the `Origin` and related classes from `o.s.boot.env` to `o.s.boot.orgin` and extend support. The concept of an origin can now be used outside of just the Spring Environment abstraction. Closes gh-9001 --- .../boot/env/OriginCapablePropertySource.java | 39 ------ .../env/OriginTrackedMapPropertySource.java | 11 +- .../env/OriginTrackedPropertiesLoader.java | 7 +- .../boot/env/OriginTrackedYamlLoader.java | 11 +- .../boot/env/PropertySourcesLoader.java | 4 +- .../springframework/boot/origin/Origin.java | 56 +++++++++ .../boot/origin/OriginLookup.java | 59 +++++++++ .../OriginProvider.java} | 21 ++-- .../{env => origin}/OriginTrackedValue.java | 40 ++++-- .../boot/origin/PropertySourceOrigin.java | 81 +++++++++++++ .../TextResourceOrigin.java} | 12 +- .../boot/origin/package-info.java | 21 ++++ .../OriginTrackedMapPropertySourceTests.java | 15 ++- .../OriginTrackedPropertiesLoaderTests.java | 5 +- .../env/OriginTrackedYamlLoaderTests.java | 5 +- .../boot/origin/MockOrigin.java | 60 +++++++++ .../boot/origin/OriginLookupTests.java | 63 ++++++++++ .../boot/origin/OriginTests.java | 90 ++++++++++++++ .../boot/origin/OriginTrackedValueTests.java | 78 ++++++++++++ .../origin/PropertySourceOriginTests.java | 114 ++++++++++++++++++ .../TextResourceOriginTests.java} | 35 +++--- 21 files changed, 718 insertions(+), 109 deletions(-) delete mode 100644 spring-boot/src/main/java/org/springframework/boot/env/OriginCapablePropertySource.java create mode 100644 spring-boot/src/main/java/org/springframework/boot/origin/Origin.java create mode 100644 spring-boot/src/main/java/org/springframework/boot/origin/OriginLookup.java rename spring-boot/src/main/java/org/springframework/boot/{env/PropertyOrigin.java => origin/OriginProvider.java} (60%) rename spring-boot/src/main/java/org/springframework/boot/{env => origin}/OriginTrackedValue.java (68%) create mode 100644 spring-boot/src/main/java/org/springframework/boot/origin/PropertySourceOrigin.java rename spring-boot/src/main/java/org/springframework/boot/{env/TextResourcePropertyOrigin.java => origin/TextResourceOrigin.java} (90%) create mode 100644 spring-boot/src/main/java/org/springframework/boot/origin/package-info.java create mode 100644 spring-boot/src/test/java/org/springframework/boot/origin/MockOrigin.java create mode 100644 spring-boot/src/test/java/org/springframework/boot/origin/OriginLookupTests.java create mode 100644 spring-boot/src/test/java/org/springframework/boot/origin/OriginTests.java create mode 100644 spring-boot/src/test/java/org/springframework/boot/origin/OriginTrackedValueTests.java create mode 100644 spring-boot/src/test/java/org/springframework/boot/origin/PropertySourceOriginTests.java rename spring-boot/src/test/java/org/springframework/boot/{env/TextResourcePropertyOriginTests.java => origin/TextResourceOriginTests.java} (78%) diff --git a/spring-boot/src/main/java/org/springframework/boot/env/OriginCapablePropertySource.java b/spring-boot/src/main/java/org/springframework/boot/env/OriginCapablePropertySource.java deleted file mode 100644 index 34d3032426e..00000000000 --- a/spring-boot/src/main/java/org/springframework/boot/env/OriginCapablePropertySource.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2012-2017 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.boot.env; - -import org.springframework.core.env.PropertySource; - -/** - * An additional interface that may be implemented by a {@link PropertySource} that can - * return origin information. For example a property source that's backed by a file may - * return origin information for line and column numbers. - * - * @author Phillip Webb - * @since 2.0.0 - */ -public interface OriginCapablePropertySource { - - /** - * Return the origin of the given property name or {@code null} if the origin cannot - * be determined. - * @param name the property name - * @return the origin of the property or {@code null} - */ - PropertyOrigin getPropertyOrigin(String name); - -} diff --git a/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedMapPropertySource.java b/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedMapPropertySource.java index 2ffeb97dd1d..5ffb63ff6ad 100644 --- a/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedMapPropertySource.java +++ b/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedMapPropertySource.java @@ -18,18 +18,21 @@ package org.springframework.boot.env; import java.util.Map; +import org.springframework.boot.origin.Origin; +import org.springframework.boot.origin.OriginLookup; +import org.springframework.boot.origin.OriginTrackedValue; import org.springframework.core.env.MapPropertySource; /** - * {@link OriginCapablePropertySource} backed by a {@link Map} containing - * {@link OriginTrackedValue OriginTrackedValues}. + * {@link OriginLookup} backed by a {@link Map} containing {@link OriginTrackedValue + * OriginTrackedValues}. * * @author Madhura Bhave * @author Phillip Webb * @see OriginTrackedValue */ class OriginTrackedMapPropertySource extends MapPropertySource - implements OriginCapablePropertySource { + implements OriginLookup { @SuppressWarnings({ "unchecked", "rawtypes" }) OriginTrackedMapPropertySource(String name, Map source) { @@ -46,7 +49,7 @@ class OriginTrackedMapPropertySource extends MapPropertySource } @Override - public PropertyOrigin getPropertyOrigin(String name) { + public Origin getOrigin(String name) { Object value = super.getProperty(name); if (value instanceof OriginTrackedValue) { return ((OriginTrackedValue) value).getOrigin(); diff --git a/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java b/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java index 42aafdc3268..3e0e06c1e2b 100644 --- a/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java +++ b/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java @@ -23,7 +23,10 @@ import java.io.LineNumberReader; import java.util.LinkedHashMap; import java.util.Map; -import org.springframework.boot.env.TextResourcePropertyOrigin.Location; +import org.springframework.boot.origin.Origin; +import org.springframework.boot.origin.OriginTrackedValue; +import org.springframework.boot.origin.TextResourceOrigin; +import org.springframework.boot.origin.TextResourceOrigin.Location; import org.springframework.core.io.Resource; import org.springframework.util.Assert; @@ -129,7 +132,7 @@ class OriginTrackedPropertiesLoader { buffer.append(reader.getCharacter()); reader.read(); } - PropertyOrigin origin = new TextResourcePropertyOrigin(this.resource, location); + Origin origin = new TextResourceOrigin(this.resource, location); return OriginTrackedValue.of(buffer.toString().trim(), origin); } diff --git a/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedYamlLoader.java b/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedYamlLoader.java index 45a3cd8d458..5e8a3996f3e 100644 --- a/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedYamlLoader.java +++ b/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedYamlLoader.java @@ -37,7 +37,10 @@ import org.yaml.snakeyaml.representer.Representer; import org.yaml.snakeyaml.resolver.Resolver; import org.springframework.beans.factory.config.YamlProcessor; -import org.springframework.boot.env.TextResourcePropertyOrigin.Location; +import org.springframework.boot.origin.Origin; +import org.springframework.boot.origin.OriginTrackedValue; +import org.springframework.boot.origin.TextResourceOrigin; +import org.springframework.boot.origin.TextResourceOrigin.Location; import org.springframework.boot.yaml.SpringProfileDocumentMatcher; import org.springframework.core.io.Resource; @@ -106,14 +109,14 @@ class OriginTrackedYamlLoader extends YamlProcessor { } private Object constructTrackedObject(Node node, Object value) { - PropertyOrigin origin = getOrigin(node); + Origin origin = getOrigin(node); return OriginTrackedValue.of(value, origin); } - private PropertyOrigin getOrigin(Node node) { + private Origin getOrigin(Node node) { Mark mark = node.getStartMark(); Location location = new Location(mark.getLine(), mark.getColumn()); - return new TextResourcePropertyOrigin(OriginTrackedYamlLoader.this.resource, + return new TextResourceOrigin(OriginTrackedYamlLoader.this.resource, location); } diff --git a/spring-boot/src/main/java/org/springframework/boot/env/PropertySourcesLoader.java b/spring-boot/src/main/java/org/springframework/boot/env/PropertySourcesLoader.java index 81057c6cf28..938016368f5 100644 --- a/spring-boot/src/main/java/org/springframework/boot/env/PropertySourcesLoader.java +++ b/spring-boot/src/main/java/org/springframework/boot/env/PropertySourcesLoader.java @@ -33,8 +33,8 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** - * Utility that can be used update {@link MutablePropertySources} using - * {@link PropertySourceLoader}s. + * Utility that can be used to update {@link MutablePropertySources} using + * {@link PropertySourceLoader PropertySourceLoaders}. * * @author Phillip Webb */ diff --git a/spring-boot/src/main/java/org/springframework/boot/origin/Origin.java b/spring-boot/src/main/java/org/springframework/boot/origin/Origin.java new file mode 100644 index 00000000000..b0c3a6c4f6a --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/origin/Origin.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2017 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.boot.origin; + +import java.io.File; + +/** + * Interface that uniquely represents the origin of an item. For example, a item loaded + * from a {@link File} may have an origin made up of the file name along with line/column + * numbers. + *

+ * Implementations must provide sensible {@code hashCode()}, {@code equals(...)} and + * {@code #toString()} implementations. + * + * @author Madhura Bhave + * @author Phillip Webb + * @since 2.0.0 + * @see OriginProvider + */ +public interface Origin { + + /** + * Find the {@link Origin} that an object originated from. Checks if the source object + * is a {@link OriginProvider} and also searches exception stacks. + * @param source the source object or {@code null} + * @return an optional {@link Origin} + */ + static Origin from(Object source) { + if (source instanceof Origin) { + return (Origin) source; + } + Origin origin = null; + if (source != null && source instanceof OriginProvider) { + origin = ((OriginProvider) source).getOrigin(); + } + if (origin == null && source != null && source instanceof Throwable) { + return from(((Throwable) source).getCause()); + } + return origin; + } + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/origin/OriginLookup.java b/spring-boot/src/main/java/org/springframework/boot/origin/OriginLookup.java new file mode 100644 index 00000000000..2859178c0b6 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/origin/OriginLookup.java @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2017 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.boot.origin; + +/** + * An interface that may be implemented by an object that can lookup {@link Origin} + * information from a given key. Can be used to add origin support to existing classes. + * + * @param The lookup key type + * @author Phillip Webb + * @since 2.0.0 + */ +public interface OriginLookup { + + /** + * Return the origin of the given key or {@code null} if the origin cannot be + * determined. + * @param key the key to lookup + * @return the origin of the key or {@code null} + */ + Origin getOrigin(K key); + + /** + * Attempt to lookup the origin from the given source. If the source is not a + * {@link OriginLookup} or if an exception occurs during lookup then {@code null} is + * returned. + * @param source the source object + * @param key the key to lookup + * @param the key type + * @return an {@link Origin} or {@code null} + */ + @SuppressWarnings("unchecked") + static Origin getOrigin(Object source, K key) { + if (!(source instanceof OriginLookup)) { + return null; + } + try { + return ((OriginLookup) source).getOrigin(key); + } + catch (Throwable ex) { + return null; + } + } + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/env/PropertyOrigin.java b/spring-boot/src/main/java/org/springframework/boot/origin/OriginProvider.java similarity index 60% rename from spring-boot/src/main/java/org/springframework/boot/env/PropertyOrigin.java rename to spring-boot/src/main/java/org/springframework/boot/origin/OriginProvider.java index 9d685f9aac5..19306277de4 100644 --- a/spring-boot/src/main/java/org/springframework/boot/env/PropertyOrigin.java +++ b/spring-boot/src/main/java/org/springframework/boot/origin/OriginProvider.java @@ -14,22 +14,21 @@ * limitations under the License. */ -package org.springframework.boot.env; - -import java.io.File; +package org.springframework.boot.origin; /** - * Interface that uniquely represents the origin of a property. For example, a property - * loaded from a {@link File} may have an origin made up of the file name along with - * line/column numbers. - *

- * Implementations must provide sensible {@code hashCode()}, {@code equals(...)} and - * {@code #toString()} implementations. + * Interface to provide access to the origin of an item. * - * @author Madhura Bhave * @author Phillip Webb * @since 2.0.0 + * @see Origin */ -public interface PropertyOrigin { +public interface OriginProvider { + + /** + * Return the source origin or {@code null} if the origin is not known. + * @return the origin or {@code null} + */ + Origin getOrigin(); } diff --git a/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedValue.java b/spring-boot/src/main/java/org/springframework/boot/origin/OriginTrackedValue.java similarity index 68% rename from spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedValue.java rename to spring-boot/src/main/java/org/springframework/boot/origin/OriginTrackedValue.java index cfa9a867b8f..7b814fa2731 100644 --- a/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedValue.java +++ b/spring-boot/src/main/java/org/springframework/boot/origin/OriginTrackedValue.java @@ -14,41 +14,51 @@ * limitations under the License. */ -package org.springframework.boot.env; +package org.springframework.boot.origin; + +import org.springframework.util.ObjectUtils; /** - * Wrapper class for an Object {@code value} and {@link PropertyOrigin origin}. + * An wrapper for a {@link Object} value and {@link Origin}. * * @author Madhura Bhave - * @see OriginTrackedMapPropertySource + * @author Phillip Webb + * @since 2.0.0 + * @see #of(Object) + * @see #of(Object, Origin) */ -class OriginTrackedValue { +public class OriginTrackedValue implements OriginProvider { private final Object value; - private final PropertyOrigin origin; + private final Origin origin; - OriginTrackedValue(Object value, PropertyOrigin origin) { + private OriginTrackedValue(Object value, Origin origin) { this.value = value; this.origin = origin; } + /** + * Return the tracked value. + * @return the tracked value + */ public Object getValue() { return this.value; } - public PropertyOrigin getOrigin() { + @Override + public Origin getOrigin() { return this.origin; } @Override public String toString() { - return this.value.toString(); + return (this.value == null ? null : this.value.toString()); } @Override public int hashCode() { - return this.value.hashCode(); + return ObjectUtils.nullSafeHashCode(this.value); } @Override @@ -56,7 +66,11 @@ class OriginTrackedValue { if (obj == null || obj.getClass() != getClass()) { return false; } - return this.value.equals(((OriginTrackedValue) obj).value); + return ObjectUtils.nullSafeEquals(this.value, ((OriginTrackedValue) obj).value); + } + + public static OriginTrackedValue of(Object value) { + return of(value, null); } /** @@ -65,10 +79,10 @@ class OriginTrackedValue { * the resulting {@link OriginTrackedValue}. * @param value the source value * @param origin the origin - * @return an {@link OriginTrackedValue} or {@code null} if the source value was + * @return a {@link OriginTrackedValue} or {@code null} if the source value was * {@code null}. */ - public static OriginTrackedValue of(Object value, PropertyOrigin origin) { + public static OriginTrackedValue of(Object value, Origin origin) { if (value == null) { return null; } @@ -84,7 +98,7 @@ class OriginTrackedValue { private static class OriginTrackedCharSequence extends OriginTrackedValue implements CharSequence { - OriginTrackedCharSequence(CharSequence value, PropertyOrigin origin) { + OriginTrackedCharSequence(CharSequence value, Origin origin) { super(value, origin); } diff --git a/spring-boot/src/main/java/org/springframework/boot/origin/PropertySourceOrigin.java b/spring-boot/src/main/java/org/springframework/boot/origin/PropertySourceOrigin.java new file mode 100644 index 00000000000..c7a4258726b --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/origin/PropertySourceOrigin.java @@ -0,0 +1,81 @@ +/* + * Copyright 2012-2017 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.boot.origin; + +import org.springframework.core.env.PropertySource; +import org.springframework.util.Assert; + +/** + * {@link Origin} from a {@link PropertySource}. + * + * @author Phillip Webb + * @since 2.0.0 + */ +public class PropertySourceOrigin implements Origin { + + private final PropertySource propertySource; + + private final String propertyName; + + /** + * Create a new {@link PropertySourceOrigin} instance. + * @param propertySource the origin property source + * @param propertyName the origin property name + */ + public PropertySourceOrigin(PropertySource propertySource, String propertyName) { + Assert.notNull(propertySource, "PropertySource must not be null"); + Assert.hasLength(propertyName, "PropertyName must not be empty"); + this.propertySource = propertySource; + this.propertyName = propertyName; + } + + /** + * Return the origin {@link PropertySource}. + * @return the origin property source + */ + public PropertySource getPropertySource() { + return this.propertySource; + } + + /** + * Return the origin property name. + * @return the origin property name + */ + public String getPropertyName() { + return this.propertyName; + } + + @Override + public String toString() { + return "\"" + this.propertyName + "\" from property source \"" + + this.propertySource.getName() + "\""; + } + + /** + * Get a {@link Origin} for the given {@link PropertySource} and {@code propertyName}. + * Will either return an {@link OriginLookup} result or a + * {@link PropertySourceOrigin}. + * @param propertySource the origin property source + * @param name the property name + * @return the property origin + */ + public static Origin get(PropertySource propertySource, String name) { + Origin origin = OriginLookup.getOrigin(propertySource, name); + return (origin != null ? origin : new PropertySourceOrigin(propertySource, name)); + } + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/env/TextResourcePropertyOrigin.java b/spring-boot/src/main/java/org/springframework/boot/origin/TextResourceOrigin.java similarity index 90% rename from spring-boot/src/main/java/org/springframework/boot/env/TextResourcePropertyOrigin.java rename to spring-boot/src/main/java/org/springframework/boot/origin/TextResourceOrigin.java index e5884294819..851f6bb5dd0 100644 --- a/spring-boot/src/main/java/org/springframework/boot/env/TextResourcePropertyOrigin.java +++ b/spring-boot/src/main/java/org/springframework/boot/origin/TextResourceOrigin.java @@ -14,26 +14,26 @@ * limitations under the License. */ -package org.springframework.boot.env; +package org.springframework.boot.origin; import org.springframework.core.io.Resource; import org.springframework.util.ObjectUtils; /** - * {@link PropertyOrigin} for an item loaded from a text resource. Provides access to the + * {@link Origin} for an item loaded from a text resource. Provides access to the * original {@link Resource} that loaded the text and a {@link Location} within it. * * @author Madhura Bhave * @author Phillip Webb * @since 2.0.0 */ -public class TextResourcePropertyOrigin implements PropertyOrigin { +public class TextResourceOrigin implements Origin { private final Resource resource; private final Location location; - public TextResourcePropertyOrigin(Resource resource, Location location) { + public TextResourceOrigin(Resource resource, Location location) { this.resource = resource; this.location = location; } @@ -70,8 +70,8 @@ public class TextResourcePropertyOrigin implements PropertyOrigin { if (obj == null) { return false; } - if (obj instanceof TextResourcePropertyOrigin) { - TextResourcePropertyOrigin other = (TextResourcePropertyOrigin) obj; + if (obj instanceof TextResourceOrigin) { + TextResourceOrigin other = (TextResourceOrigin) obj; boolean result = true; result = result && ObjectUtils.nullSafeEquals(this.resource, other.resource); result = result && ObjectUtils.nullSafeEquals(this.location, other.location); diff --git a/spring-boot/src/main/java/org/springframework/boot/origin/package-info.java b/spring-boot/src/main/java/org/springframework/boot/origin/package-info.java new file mode 100644 index 00000000000..d1c03ed9def --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/origin/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2012-2017 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. + */ + +/** + * Support for item origin tracking. + * @see org.springframework.boot.origin.Origin + */ +package org.springframework.boot.origin; diff --git a/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedMapPropertySourceTests.java b/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedMapPropertySourceTests.java index bf3e33aa3eb..3737726dad2 100644 --- a/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedMapPropertySourceTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedMapPropertySourceTests.java @@ -21,6 +21,9 @@ import java.util.Map; import org.junit.Test; +import org.springframework.boot.origin.Origin; +import org.springframework.boot.origin.OriginTrackedValue; + import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -37,7 +40,7 @@ public class OriginTrackedMapPropertySourceTests { private OriginTrackedMapPropertySource source = new OriginTrackedMapPropertySource( "test", this.map); - private PropertyOrigin origin = mock(PropertyOrigin.class); + private Origin origin = mock(Origin.class); @Test public void getPropertyWhenMissingShouldReturnNull() throws Exception { @@ -52,25 +55,25 @@ public class OriginTrackedMapPropertySourceTests { @Test public void getPropertyWhenTrackedShouldReturnValue() throws Exception { - this.map.put("test", new OriginTrackedValue("foo", this.origin)); + this.map.put("test", OriginTrackedValue.of("foo", this.origin)); assertThat(this.source.getProperty("test")).isEqualTo("foo"); } @Test public void getPropertyOriginWhenMissingShouldReturnNull() throws Exception { - assertThat(this.source.getPropertyOrigin("test")).isNull(); + assertThat(this.source.getOrigin("test")).isNull(); } @Test public void getPropertyOriginWhenNonTrackedShouldReturnNull() throws Exception { this.map.put("test", "foo"); - assertThat(this.source.getPropertyOrigin("test")).isNull(); + assertThat(this.source.getOrigin("test")).isNull(); } @Test public void getPropertyOriginWhenTrackedShouldReturnOrigin() throws Exception { - this.map.put("test", new OriginTrackedValue("foo", this.origin)); - assertThat(this.source.getPropertyOrigin("test")).isEqualTo(this.origin); + this.map.put("test", OriginTrackedValue.of("foo", this.origin)); + assertThat(this.source.getOrigin("test")).isEqualTo(this.origin); } } diff --git a/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedPropertiesLoaderTests.java b/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedPropertiesLoaderTests.java index 3619cac0e83..3617b5ad9c3 100644 --- a/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedPropertiesLoaderTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedPropertiesLoaderTests.java @@ -22,6 +22,8 @@ import java.util.Properties; import org.junit.Before; import org.junit.Test; +import org.springframework.boot.origin.OriginTrackedValue; +import org.springframework.boot.origin.TextResourceOrigin; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.support.PropertiesLoaderUtils; @@ -234,7 +236,8 @@ public class OriginTrackedPropertiesLoaderTests { if (value == null) { return null; } - return ((TextResourcePropertyOrigin) value.getOrigin()).getLocation().toString(); + return ((TextResourceOrigin) value.getOrigin()).getLocation() + .toString(); } } diff --git a/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedYamlLoaderTests.java b/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedYamlLoaderTests.java index ad4b3e1a5bc..094f7a30a0e 100644 --- a/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedYamlLoaderTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedYamlLoaderTests.java @@ -21,6 +21,8 @@ import java.util.Map; import org.junit.Before; import org.junit.Test; +import org.springframework.boot.origin.OriginTrackedValue; +import org.springframework.boot.origin.TextResourceOrigin; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; @@ -120,7 +122,8 @@ public class OriginTrackedYamlLoaderTests { } private String getLocation(OriginTrackedValue value) { - return ((TextResourcePropertyOrigin) value.getOrigin()).getLocation().toString(); + return ((TextResourceOrigin) value.getOrigin()).getLocation() + .toString(); } } diff --git a/spring-boot/src/test/java/org/springframework/boot/origin/MockOrigin.java b/spring-boot/src/test/java/org/springframework/boot/origin/MockOrigin.java new file mode 100644 index 00000000000..7f81298e755 --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/origin/MockOrigin.java @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2017 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.boot.origin; + +import org.springframework.util.Assert; + +/** + * Mock {@link Origin} implementation used for testing. + * + * @author Phillip Webb + */ +public final class MockOrigin implements Origin { + + private final String value; + + private MockOrigin(String value) { + Assert.notNull(value, "Value must not be null"); + this.value = value; + } + + @Override + public int hashCode() { + return this.value.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + return this.value.equals(((MockOrigin) obj).value); + } + + @Override + public String toString() { + return this.value; + } + + public static Origin of(String value) { + return (value == null ? null : new MockOrigin(value)); + } + +} diff --git a/spring-boot/src/test/java/org/springframework/boot/origin/OriginLookupTests.java b/spring-boot/src/test/java/org/springframework/boot/origin/OriginLookupTests.java new file mode 100644 index 00000000000..1413413bb57 --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/origin/OriginLookupTests.java @@ -0,0 +1,63 @@ +/* + * Copyright 2012-2017 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.boot.origin; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.willThrow; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link OriginLookup}. + * + * @author Phillip Webb + */ +public class OriginLookupTests { + + @Test + public void getOriginWhenSourceIsNullShouldReturnNull() throws Exception { + assertThat(OriginLookup.getOrigin(null, "foo")).isNull(); + } + + @Test + public void getOriginWhenSourceIsNotLookupShouldReturnLookupOrigin() + throws Exception { + Object source = new Object(); + assertThat(OriginLookup.getOrigin(source, "foo")).isNull(); + } + + @Test + @SuppressWarnings("unchecked") + public void getOriginWhenSourceIsLookupShouldReturnLookupOrigin() throws Exception { + OriginLookup source = mock(OriginLookup.class); + Origin origin = MockOrigin.of("bar"); + given(source.getOrigin("foo")).willReturn(origin); + assertThat(OriginLookup.getOrigin(source, "foo")).isEqualTo(origin); + } + + @Test + @SuppressWarnings("unchecked") + public void getOriginWhenSourceLookupThrowsAndErrorShouldReturnNull() + throws Exception { + OriginLookup source = mock(OriginLookup.class); + willThrow(RuntimeException.class).given(source).getOrigin("foo"); + assertThat(OriginLookup.getOrigin(source, "foo")).isNull(); + } + +} diff --git a/spring-boot/src/test/java/org/springframework/boot/origin/OriginTests.java b/spring-boot/src/test/java/org/springframework/boot/origin/OriginTests.java new file mode 100644 index 00000000000..bb9b2c56d84 --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/origin/OriginTests.java @@ -0,0 +1,90 @@ +/* + * Copyright 2012-2017 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.boot.origin; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link Origin}. + * + * @author Phillip Webb + */ +public class OriginTests { + + @Test + public void fromWhenSourceIsNullShouldReturnNull() throws Exception { + assertThat(Origin.from(null)).isNull(); + } + + @Test + public void fromWhenSourceIsRegularObjectShouldReturnNull() throws Exception { + Object source = new Object(); + assertThat(Origin.from(source)).isNull(); + } + + @Test + public void fromWhenSourceIsOriginShouldReturnSource() throws Exception { + Origin origin = mock(Origin.class); + assertThat(Origin.from(origin)).isEqualTo(origin); + } + + @Test + public void fromWhenSourceIsOriginProviderShouldReturnProvidedOrigin() + throws Exception { + Origin origin = mock(Origin.class); + OriginProvider originProvider = mock(OriginProvider.class); + given(originProvider.getOrigin()).willReturn(origin); + assertThat(Origin.from(origin)).isEqualTo(origin); + } + + @Test + public void fromWhenSourceIsThrowableShouldUseCause() throws Exception { + Origin origin = mock(Origin.class); + Exception exception = new RuntimeException(new TestException(origin, null)); + assertThat(Origin.from(exception)).isEqualTo(origin); + } + + @Test + public void fromWhenSourceIsThrowableAndOriginProviderThatReturnsNullShouldUseCause() + throws Exception { + Origin origin = mock(Origin.class); + Exception exception = new TestException(null, new TestException(origin, null)); + assertThat(Origin.from(exception)).isEqualTo(origin); + } + + private static class TestException extends RuntimeException + implements OriginProvider { + + private final Origin origin; + + TestException(Origin origin, Throwable cause) { + super(cause); + this.origin = origin; + } + + @Override + public Origin getOrigin() { + return this.origin; + } + + } + +} diff --git a/spring-boot/src/test/java/org/springframework/boot/origin/OriginTrackedValueTests.java b/spring-boot/src/test/java/org/springframework/boot/origin/OriginTrackedValueTests.java new file mode 100644 index 00000000000..0f3ef77b980 --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/origin/OriginTrackedValueTests.java @@ -0,0 +1,78 @@ +/* + * Copyright 2012-2017 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.boot.origin; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link OriginTrackedValue}. + * + * @author Phillip Webb + */ +public class OriginTrackedValueTests { + + @Test + public void getValueShouldReturnValue() throws Exception { + Object value = new Object(); + assertThat(OriginTrackedValue.of(value).getValue()).isEqualTo(value); + } + + @Test + public void getOriginShouldReturnOrigin() throws Exception { + Object value = new Object(); + Origin origin = mock(Origin.class); + assertThat(OriginTrackedValue.of(value, origin).getOrigin()).isEqualTo(origin); + } + + @Test + public void toStringShouldReturnValueToString() throws Exception { + Object value = new Object(); + assertThat(OriginTrackedValue.of(value).toString()).isEqualTo(value.toString()); + } + + @Test + public void hashCodeAndEqualsShouldIgnoreOrigin() throws Exception { + Object value1 = new Object(); + OriginTrackedValue tracked1 = OriginTrackedValue.of(value1); + OriginTrackedValue tracked2 = OriginTrackedValue.of(value1, mock(Origin.class)); + OriginTrackedValue tracked3 = OriginTrackedValue.of(new Object()); + assertThat(tracked1.hashCode()).isEqualTo(tracked2.hashCode()); + assertThat(tracked1).isEqualTo(tracked1).isEqualTo(tracked2) + .isNotEqualTo(tracked3); + } + + @Test + public void ofWhenValueIsNullShouldReturnNull() throws Exception { + assertThat(OriginTrackedValue.of(null)).isNull(); + assertThat(OriginTrackedValue.of(null, mock(Origin.class))).isNull(); + } + + @Test + public void ofWhenValueIsCharSequenceShouldReturnCharSequence() throws Exception { + String value = "foo"; + OriginTrackedValue tracked = OriginTrackedValue.of(value); + assertThat(tracked).isInstanceOf(CharSequence.class); + CharSequence charSequence = (CharSequence) tracked; + assertThat(charSequence.length()).isEqualTo(value.length()); + assertThat(charSequence.charAt(0)).isEqualTo(value.charAt(0)); + assertThat(charSequence.subSequence(0, 1)).isEqualTo(value.subSequence(0, 1)); + } + +} diff --git a/spring-boot/src/test/java/org/springframework/boot/origin/PropertySourceOriginTests.java b/spring-boot/src/test/java/org/springframework/boot/origin/PropertySourceOriginTests.java new file mode 100644 index 00000000000..8d29961b9c8 --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/origin/PropertySourceOriginTests.java @@ -0,0 +1,114 @@ +/* + * Copyright 2012-2017 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.boot.origin; + +import java.util.HashMap; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.withSettings; + +/** + * Tests for {@link PropertySourceOrigin}. + * + * @author Phillip Webb + */ +public class PropertySourceOriginTests { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void createWhenPropertySourceIsNullShouldThrowException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("PropertySource must not be null"); + new PropertySourceOrigin(null, "name"); + } + + @Test + public void createWhenPropertyNameIsNullShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("PropertyName must not be empty"); + new PropertySourceOrigin(mock(PropertySource.class), null); + } + + @Test + public void createWhenPropertyNameIsEmptyShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("PropertyName must not be empty"); + new PropertySourceOrigin(mock(PropertySource.class), ""); + } + + @Test + public void getPropertySourceShouldReturnPropertySource() throws Exception { + MapPropertySource propertySource = new MapPropertySource("test", new HashMap<>()); + PropertySourceOrigin origin = new PropertySourceOrigin(propertySource, "foo"); + assertThat(origin.getPropertySource()).isEqualTo(propertySource); + } + + @Test + public void getPropertyNameShouldReturnPropertyName() throws Exception { + MapPropertySource propertySource = new MapPropertySource("test", new HashMap<>()); + PropertySourceOrigin origin = new PropertySourceOrigin(propertySource, "foo"); + assertThat(origin.getPropertyName()).isEqualTo("foo"); + } + + @Test + public void toStringShouldShowDetails() throws Exception { + MapPropertySource propertySource = new MapPropertySource("test", new HashMap<>()); + PropertySourceOrigin origin = new PropertySourceOrigin(propertySource, "foo"); + assertThat(origin.toString()).isEqualTo("\"foo\" from property source \"test\""); + } + + @Test + @SuppressWarnings("unchecked") + public void getWhenPropertySourceSupportsOriginLookupShouldReturnOrigin() + throws Exception { + Origin origin = mock(Origin.class); + PropertySource propertySource = mock(PropertySource.class, + withSettings().extraInterfaces(OriginLookup.class)); + OriginLookup originCapablePropertySource = (OriginLookup) propertySource; + given(originCapablePropertySource.getOrigin("foo")).willReturn(origin); + assertThat(PropertySourceOrigin.get(propertySource, "foo")).isSameAs(origin); + } + + @Test + public void getWhenPropertySourceSupportsOriginLookupButNoOriginShouldWrap() + throws Exception { + PropertySource propertySource = mock(PropertySource.class, + withSettings().extraInterfaces(OriginLookup.class)); + assertThat(PropertySourceOrigin.get(propertySource, "foo")) + .isInstanceOf(PropertySourceOrigin.class); + } + + @Test + public void getWhenPropertySourceIsNotOriginAwareShouldWrap() throws Exception { + MapPropertySource propertySource = new MapPropertySource("test", new HashMap<>()); + PropertySourceOrigin origin = new PropertySourceOrigin(propertySource, "foo"); + assertThat(origin.getPropertySource()).isEqualTo(propertySource); + assertThat(origin.getPropertyName()).isEqualTo("foo"); + } + +} diff --git a/spring-boot/src/test/java/org/springframework/boot/env/TextResourcePropertyOriginTests.java b/spring-boot/src/test/java/org/springframework/boot/origin/TextResourceOriginTests.java similarity index 78% rename from spring-boot/src/test/java/org/springframework/boot/env/TextResourcePropertyOriginTests.java rename to spring-boot/src/test/java/org/springframework/boot/origin/TextResourceOriginTests.java index 92f6543c65e..6b7d0d1a8da 100644 --- a/spring-boot/src/test/java/org/springframework/boot/env/TextResourcePropertyOriginTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/origin/TextResourceOriginTests.java @@ -14,47 +14,45 @@ * limitations under the License. */ -package org.springframework.boot.env; +package org.springframework.boot.origin; import org.junit.Test; -import org.springframework.boot.env.TextResourcePropertyOrigin.Location; +import org.springframework.boot.origin.TextResourceOrigin.Location; import org.springframework.core.io.ClassPathResource; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link TextResourcePropertyOrigin}. + * Tests for {@link TextResourceOrigin}. * * @author Phillip Webb */ -public class TextResourcePropertyOriginTests { +public class TextResourceOriginTests { @Test public void createWithNullResourceShouldSetNullResource() throws Exception { - TextResourcePropertyOrigin origin = new TextResourcePropertyOrigin(null, null); + TextResourceOrigin origin = new TextResourceOrigin(null, null); assertThat(origin.getResource()).isNull(); } @Test public void createWithNullLocationShouldSetNullLocation() throws Exception { - TextResourcePropertyOrigin origin = new TextResourcePropertyOrigin(null, null); + TextResourceOrigin origin = new TextResourceOrigin(null, null); assertThat(origin.getLocation()).isNull(); } @Test public void getResourceShouldReturnResource() throws Exception { ClassPathResource resource = new ClassPathResource("foo.txt"); - TextResourcePropertyOrigin origin = new TextResourcePropertyOrigin(resource, - null); + TextResourceOrigin origin = new TextResourceOrigin(resource, null); assertThat(origin.getResource()).isEqualTo(resource); } @Test public void getLocationShouldReturnLocation() throws Exception { Location location = new Location(1, 2); - TextResourcePropertyOrigin origin = new TextResourcePropertyOrigin(null, - location); + TextResourceOrigin origin = new TextResourceOrigin(null, location); assertThat(origin.getLocation()).isEqualTo(location); } @@ -81,24 +79,21 @@ public class TextResourcePropertyOriginTests { public void toStringShouldReturnNiceString() throws Exception { ClassPathResource resource = new ClassPathResource("foo.txt"); Location location = new Location(1, 2); - TextResourcePropertyOrigin origin = new TextResourcePropertyOrigin(resource, - location); + TextResourceOrigin origin = new TextResourceOrigin(resource, location); assertThat(origin.toString()).isEqualTo("class path resource [foo.txt]:2:3"); } @Test public void toStringWhenResourceIsNullShouldReturnNiceString() throws Exception { Location location = new Location(1, 2); - TextResourcePropertyOrigin origin = new TextResourcePropertyOrigin(null, - location); + TextResourceOrigin origin = new TextResourceOrigin(null, location); assertThat(origin.toString()).isEqualTo("unknown resource [?]:2:3"); } @Test public void toStringWhenLocationIsNullShouldReturnNiceString() throws Exception { ClassPathResource resource = new ClassPathResource("foo.txt"); - TextResourcePropertyOrigin origin = new TextResourcePropertyOrigin(resource, - null); + TextResourceOrigin origin = new TextResourceOrigin(resource, null); assertThat(origin.toString()).isEqualTo("class path resource [foo.txt]"); } @@ -117,13 +112,13 @@ public class TextResourcePropertyOriginTests { @Test public void equalsAndHashCodeShouldResourceAndLocation() throws Exception { - TextResourcePropertyOrigin origin1 = new TextResourcePropertyOrigin( + TextResourceOrigin origin1 = new TextResourceOrigin( new ClassPathResource("foo.txt"), new Location(1, 2)); - TextResourcePropertyOrigin origin2 = new TextResourcePropertyOrigin( + TextResourceOrigin origin2 = new TextResourceOrigin( new ClassPathResource("foo.txt"), new Location(1, 2)); - TextResourcePropertyOrigin origin3 = new TextResourcePropertyOrigin( + TextResourceOrigin origin3 = new TextResourceOrigin( new ClassPathResource("foo.txt"), new Location(2, 2)); - TextResourcePropertyOrigin origin4 = new TextResourcePropertyOrigin( + TextResourceOrigin origin4 = new TextResourceOrigin( new ClassPathResource("foo2.txt"), new Location(1, 2)); assertThat(origin1.hashCode()).isEqualTo(origin1.hashCode()); assertThat(origin1.hashCode()).isEqualTo(origin2.hashCode());