From 4e6b12fc8a792b2daf2e25a6c9af75a5b3912e4f Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Thu, 14 Mar 2019 12:48:35 +0100 Subject: [PATCH] DATACMNS-1498 - Improved attribute lookup on RepositoryConfigurationSource. --- ...notationRepositoryConfigurationSource.java | 32 +++++++++++++++++-- .../config/RepositoryConfigurationSource.java | 30 +++++++++++++++-- .../XmlRepositoryConfigurationSource.java | 16 ++++++++-- ...epositoryConfigurationSourceUnitTests.java | 29 +++++++++++++++++ ...epositoryConfigurationSourceUnitTests.java | 3 +- 5 files changed, 102 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSource.java b/src/main/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSource.java index 5755485e8..f9b0054be 100644 --- a/src/main/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSource.java +++ b/src/main/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSource.java @@ -328,9 +328,35 @@ public class AnnotationRepositoryConfigurationSource extends RepositoryConfigura */ @Override public Optional getAttribute(String name) { + return getAttribute(name, String.class); + } - String attribute = attributes.getString(name); - return StringUtils.hasText(attribute) ? Optional.of(attribute) : Optional.empty(); + /* + * (non-Javadoc) + * @see org.springframework.data.repository.config.RepositoryConfigurationSource#getAttribute(java.lang.String, java.lang.Class) + */ + @Override + public Optional getAttribute(String name, Class type) { + + if (!attributes.containsKey(name)) { + throw new IllegalArgumentException(String.format("No attribute named %s found!", name)); + } + + Object value = attributes.get(name); + + if (value == null) { + return Optional.empty(); + } + + Assert.isInstanceOf(type, value, + () -> String.format("Attribute value for %s is of type %s but was expected to be of type %s!", name, + value.getClass(), type)); + + Object result = String.class.isInstance(value) // + ? StringUtils.hasText((String) value) ? value : null // + : value; + + return Optional.ofNullable(type.cast(result)); } /* @@ -342,7 +368,7 @@ public class AnnotationRepositoryConfigurationSource extends RepositoryConfigura return hasExplicitFilters; } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.config.RepositoryConfigurationSource#getBootstrapMode() */ diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSource.java b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSource.java index abc8090b0..3b473ddbf 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSource.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSource.java @@ -24,6 +24,7 @@ import org.springframework.core.type.filter.TypeFilter; import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.util.Streamable; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; /** * Interface containing the configurable options for the Spring Data repository subsystem. @@ -103,6 +104,31 @@ public interface RepositoryConfigurationSource { */ Optional getAttribute(String name); + /** + * Returns the value for the attribute with the given name and type. The name is expected to be handed in camel-case. + * + * @param name must not be {@literal null} or empty. + * @param type the type of the attribute to look up. + * @return the attribute with the given name or {@link Optional#empty()} if not configured or empty. + * @since 2.2 + */ + Optional getAttribute(String name, Class type); + + /** + * Returns the attribute value for the attribute of the given name. + * + * @param name must not be {@literal null} or empty. + * @return the attribute with the given name and type. + * @since 2.2 + */ + default T getRequiredAttribute(String name, Class type) { + + Assert.hasText(name, "Attribute name must not be null or empty!"); + + return getAttribute(name, type) + .orElseThrow(() -> new IllegalArgumentException(String.format("No attribute named %s found!", name))); + } + /** * Returns whether the configuration uses explicit filtering to scan for repository types. * @@ -131,7 +157,7 @@ public interface RepositoryConfigurationSource { /** * Returns the {@link ImplementationDetectionConfiguration} to be used to scan for custom implementations of the * repository instances to be created from this {@link RepositoryConfigurationSource}. - * + * * @param factory * @return will never be {@literal null}. * @since 2.1 @@ -140,7 +166,7 @@ public interface RepositoryConfigurationSource { /** * Defines the repository {@link BootstrapMode} to be used. - * + * * @return * @since 2.1 */ diff --git a/src/main/java/org/springframework/data/repository/config/XmlRepositoryConfigurationSource.java b/src/main/java/org/springframework/data/repository/config/XmlRepositoryConfigurationSource.java index 2e0c75f26..c32e2e3cb 100644 --- a/src/main/java/org/springframework/data/repository/config/XmlRepositoryConfigurationSource.java +++ b/src/main/java/org/springframework/data/repository/config/XmlRepositoryConfigurationSource.java @@ -188,7 +188,6 @@ public class XmlRepositoryConfigurationSource extends RepositoryConfigurationSou */ @Override public boolean shouldConsiderNestedRepositories() { - return getNullDefaultedAttribute(element, CONSIDER_NESTED_REPOSITORIES).map(Boolean::parseBoolean).orElse(false); } @@ -205,6 +204,19 @@ public class XmlRepositoryConfigurationSource extends RepositoryConfigurationSou return StringUtils.hasText(attribute) ? Optional.of(attribute) : Optional.empty(); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.config.RepositoryConfigurationSource#getAttribute(java.lang.String, java.lang.Class) + */ + @SuppressWarnings("unchecked") + @Override + public Optional getAttribute(String name, Class type) { + + Assert.isAssignable(String.class, type, "Only String attribute lookups are allowed for XML namespaces!"); + + return (Optional) getAttribute(name); + } + /* * (non-Javadoc) * @see org.springframework.data.repository.config.RepositoryConfigurationSource#usesExplicitFilters() @@ -214,7 +226,7 @@ public class XmlRepositoryConfigurationSource extends RepositoryConfigurationSou return !(this.includeFilters.isEmpty() && this.excludeFilters.isEmpty()); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.config.RepositoryConfigurationSource#getBootstrapMode() */ diff --git a/src/test/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSourceUnitTests.java b/src/test/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSourceUnitTests.java index 0196030e5..93459c6c7 100755 --- a/src/test/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSourceUnitTests.java +++ b/src/test/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSourceUnitTests.java @@ -33,6 +33,7 @@ import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.StandardAnnotationMetadata; +import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.util.Streamable; /** @@ -138,6 +139,34 @@ public class AnnotationRepositoryConfigurationSourceUnitTests { assertThat(configurationSource.getRepositoryBaseClassName()).isNotPresent(); } + @Test // DATACMNS-1498 + public void allowsLookupOfNonStringAttribute() { + + RepositoryConfigurationSource source = getConfigSource(DefaultConfiguration.class); + + assertThat(source.getAttribute("repositoryBaseClass", Class.class)).hasValue(PagingAndSortingRepository.class); + assertThat(source.getRequiredAttribute("repositoryBaseClass", Class.class)) + .isEqualTo(PagingAndSortingRepository.class); + } + + @Test // DATACMNS-1498 + public void rejectsInvalidAttributeName() { + + RepositoryConfigurationSource source = getConfigSource(DefaultConfiguration.class); + + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> source.getAttribute("fooBar")); + } + + @Test // DATACMNS-1498 + public void lookupOfEmptyStringExposesAbsentValue() { + + RepositoryConfigurationSource source = getConfigSource(DefaultConfiguration.class); + + assertThat(source.getAttribute("namedQueriesLocation", String.class)).isEmpty(); + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> source.getRequiredAttribute("namedQueriesLocation", String.class)); + } + private AnnotationRepositoryConfigurationSource getConfigSource(Class type) { AnnotationMetadata metadata = new StandardAnnotationMetadata(type, true); diff --git a/src/test/java/org/springframework/data/repository/config/XmlRepositoryConfigurationSourceUnitTests.java b/src/test/java/org/springframework/data/repository/config/XmlRepositoryConfigurationSourceUnitTests.java index e00359158..fcd6d878d 100755 --- a/src/test/java/org/springframework/data/repository/config/XmlRepositoryConfigurationSourceUnitTests.java +++ b/src/test/java/org/springframework/data/repository/config/XmlRepositoryConfigurationSourceUnitTests.java @@ -16,6 +16,7 @@ package org.springframework.data.repository.config; import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import org.junit.Test; @@ -40,8 +41,8 @@ public class XmlRepositoryConfigurationSourceUnitTests { RepositoryConfigurationSource source = mock(XmlRepositoryConfigurationSource.class); ReflectionTestUtils.setField(source, "element", element); - when(source.getAttribute(anyString())).thenCallRealMethod(); + when(source.getAttribute(anyString())).thenCallRealMethod(); when(element.getAttribute("some-xml-attribute")).thenReturn("value"); assertThat(source.getAttribute("someXmlAttribute")).hasValue("value");