Skip to content

Commit 532af1a

Browse files
mp911deodrotbohm
authored andcommitted
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>, 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. Original pull request: #280.
1 parent 0e772a5 commit 532af1a

File tree

2 files changed

+66
-3
lines changed

2 files changed

+66
-3
lines changed

src/main/java/org/springframework/data/repository/core/support/RepositoryFragment.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,27 @@ public String toString() {
155155
}
156156
}
157157

158-
@RequiredArgsConstructor
159158
@EqualsAndHashCode(callSuper = false)
160159
static class ImplementedRepositoryFragment<T> implements RepositoryFragment<T> {
161160

162-
private final @NonNull Optional<Class<T>> interfaceClass;
163-
private final @NonNull T implementation;
161+
private final Optional<Class<T>> interfaceClass;
162+
private final T implementation;
163+
164+
public ImplementedRepositoryFragment(Optional<Class<T>> interfaceClass, T implementation) {
165+
166+
Assert.notNull(interfaceClass, "Interface class must not be null!");
167+
Assert.notNull(implementation, "Implementation object must not be null!");
168+
169+
interfaceClass.ifPresent(it -> {
170+
171+
Assert.isTrue(ClassUtils.isAssignableValue(it, implementation),
172+
() -> String.format("Fragment implementation %s does not implement %s!", ClassUtils.getQualifiedName(it),
173+
ClassUtils.getQualifiedName(implementation.getClass())));
174+
});
175+
176+
this.interfaceClass = interfaceClass;
177+
this.implementation = implementation;
178+
}
164179

165180
/*
166181
* (non-Javadoc)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.repository.core.support;
17+
18+
import static org.assertj.core.api.Assertions.*;
19+
20+
import org.junit.Test;
21+
22+
/**
23+
* Unit tests for {@link RepositoryFragment}.
24+
*
25+
* @author Mark Paluch
26+
*/
27+
public class RepositoryFragmentUnitTests {
28+
29+
@SuppressWarnings("unchecked")
30+
@Test // DATACMNS-1289
31+
public void fragmentCreationFromUnrelatedTypesShouldFail() {
32+
33+
assertThatThrownBy(() -> RepositoryFragment.implemented((Class) CustomFragment.class, new UnrelatedImpl()))
34+
.hasMessageMatching("Fragment implementation .* does not implement .*UnrelatedImpl!")
35+
.isInstanceOf(IllegalArgumentException.class);
36+
}
37+
38+
@Test // DATACMNS-1289
39+
public void fragmentCreationFromRelatedTypesShouldCreateNewFragment() {
40+
assertThat(RepositoryFragment.implemented(CustomFragment.class, new CustomFragmentImpl())).isNotNull();
41+
}
42+
43+
interface CustomFragment {}
44+
45+
private static class CustomFragmentImpl implements CustomFragment {}
46+
47+
private static class UnrelatedImpl {}
48+
}

0 commit comments

Comments
 (0)