From 31d5a88279458190e7b7bdf2835b5c767252da73 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 5 Apr 2018 13:39:29 +0200 Subject: [PATCH 1/2] DATACMNS-1289 - Prepare issue branch. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 877ee6a008..f0711f783c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-commons - 2.1.0.BUILD-SNAPSHOT + 2.1.0.DATACMNS-1289-SNAPSHOT Spring Data Core From 0c5e620deb8d0cf07078a8d883a860e0f56a607f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 5 Apr 2018 15:27:50 +0200 Subject: [PATCH 2/2] DATACMNS-1289 - Allow fragment creation only from implementations that implement their declared class. We now check that fragment implementations created via RepositoryFragment.implemented(Class, T) are a subtype of the given class. This assertion raises an exception that prevents errors during runtime. This change addresses an issue with customized intermediate base repositories in combination with ambiguous naming of implementations. Method invocations fail if a repository derives from a customized base repository interface that implements e.g. CrudRepository and there's an implementation matching the base repository name followed by the implementation suffix. We assume in that case, the implementation contains implementations of the methods declared in the customized base interface. --- .../core/support/RepositoryFragment.java | 21 ++++++-- .../support/RepositoryFragmentUnitTests.java | 48 +++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/springframework/data/repository/core/support/RepositoryFragmentUnitTests.java diff --git a/src/main/java/org/springframework/data/repository/core/support/RepositoryFragment.java b/src/main/java/org/springframework/data/repository/core/support/RepositoryFragment.java index 66fd51e98a..51a664e61b 100644 --- a/src/main/java/org/springframework/data/repository/core/support/RepositoryFragment.java +++ b/src/main/java/org/springframework/data/repository/core/support/RepositoryFragment.java @@ -155,12 +155,27 @@ public String toString() { } } - @RequiredArgsConstructor @EqualsAndHashCode(callSuper = false) static class ImplementedRepositoryFragment implements RepositoryFragment { - private final @NonNull Optional> interfaceClass; - private final @NonNull T implementation; + private final Optional> interfaceClass; + private final T implementation; + + public ImplementedRepositoryFragment(Optional> interfaceClass, T implementation) { + + Assert.notNull(interfaceClass, "Interface class must not be null!"); + Assert.notNull(implementation, "Implementation object must not be null!"); + + interfaceClass.ifPresent(it -> { + + Assert.isTrue(ClassUtils.isAssignableValue(it, implementation), + () -> String.format("Fragment implementation %s does not implement %s!", ClassUtils.getQualifiedName(it), + ClassUtils.getQualifiedName(implementation.getClass()))); + }); + + this.interfaceClass = interfaceClass; + this.implementation = implementation; + } /* * (non-Javadoc) diff --git a/src/test/java/org/springframework/data/repository/core/support/RepositoryFragmentUnitTests.java b/src/test/java/org/springframework/data/repository/core/support/RepositoryFragmentUnitTests.java new file mode 100644 index 0000000000..976877e227 --- /dev/null +++ b/src/test/java/org/springframework/data/repository/core/support/RepositoryFragmentUnitTests.java @@ -0,0 +1,48 @@ +/* + * Copyright 2018 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.data.repository.core.support; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.Test; + +/** + * Unit tests for {@link RepositoryFragment}. + * + * @author Mark Paluch + */ +public class RepositoryFragmentUnitTests { + + @SuppressWarnings("unchecked") + @Test // DATACMNS-1289 + public void fragmentCreationFromUnrelatedTypesShouldFail() { + + assertThatThrownBy(() -> RepositoryFragment.implemented((Class) CustomFragment.class, new UnrelatedImpl())) + .hasMessageMatching("Fragment implementation .* does not implement .*UnrelatedImpl!") + .isInstanceOf(IllegalArgumentException.class); + } + + @Test // DATACMNS-1289 + public void fragmentCreationFromRelatedTypesShouldCreateNewFragment() { + assertThat(RepositoryFragment.implemented(CustomFragment.class, new CustomFragmentImpl())).isNotNull(); + } + + interface CustomFragment {} + + private static class CustomFragmentImpl implements CustomFragment {} + + private static class UnrelatedImpl {} +}