Skip to content

Commit

Permalink
Remove RepositoryMethodReference from RepositoryItemReaderBuilder
Browse files Browse the repository at this point in the history
This class and its usage are confusing and causing
context startup failures.

This commit removes that class to simplify the creation
of a  RepositoryItemReader through its builder.

Issue spring-projects#793
  • Loading branch information
fmbenhassine committed May 17, 2022
1 parent 9315a3b commit 5602a0d
Show file tree
Hide file tree
Showing 4 changed files with 9 additions and 144 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2022 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.
Expand Down Expand Up @@ -72,13 +72,14 @@
*
* @author Michael Minella
* @author Antoine Kapps
* @author Mahmoud Ben Hassine
* @since 2.2
*/
public class RepositoryItemReader<T> extends AbstractItemCountingItemStreamItemReader<T> implements InitializingBean {

protected Log logger = LogFactory.getLog(getClass());

private PagingAndSortingRepository<?, ?> repository;
private PagingAndSortingRepository<T, ?> repository;

private Sort sort;

Expand Down Expand Up @@ -130,8 +131,9 @@ public void setPageSize(int pageSize) {
* implementation used to read input from.
*
* @param repository underlying repository for input to be read from.
* @param <R> type of the repositoryt to use to read items
*/
public void setRepository(PagingAndSortingRepository<?, ?> repository) {
public <R extends PagingAndSortingRepository<T, ?>> void setRepository(R repository) {
this.repository = repository;
}

Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 the original author or authors.
* Copyright 2017-2022 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.
Expand Down Expand Up @@ -43,7 +43,7 @@
*/
public class RepositoryItemReaderBuilder<T> {

private PagingAndSortingRepository<?, ?> repository;
private PagingAndSortingRepository<T, ?> repository;

private Map<String, Sort.Direction> sorts;

Expand All @@ -53,8 +53,6 @@ public class RepositoryItemReaderBuilder<T> {

private String methodName;

private RepositoryMethodReference<?> repositoryMethodReference;

private boolean saveState = true;

private String name;
Expand Down Expand Up @@ -176,7 +174,7 @@ public RepositoryItemReaderBuilder<T> pageSize(int pageSize) {
* @return The current instance of the builder.
* @see RepositoryItemReader#setRepository(PagingAndSortingRepository)
*/
public RepositoryItemReaderBuilder<T> repository(PagingAndSortingRepository<?, ?> repository) {
public RepositoryItemReaderBuilder<T> repository(PagingAndSortingRepository<T, ?> repository) {
this.repository = repository;

return this;
Expand All @@ -196,44 +194,12 @@ public RepositoryItemReaderBuilder<T> methodName(String methodName) {
return this;
}

/**
* Specifies a repository and the type-safe method to call for the reader. The method
* configured via this mechanism must take
* {@link org.springframework.data.domain.Pageable} as the <em>last</em>
* argument. This method can be used in place of {@link #repository(PagingAndSortingRepository)},
* {@link #methodName(String)}, and {@link #arguments(List)}.
*
* Note: The repository that is used by the repositoryMethodReference must be
* non-final.
*
* @param repositoryMethodReference of the used to get a repository and type-safe
* method for use by the reader.
* @return The current instance of the builder.
* @see RepositoryItemReader#setMethodName(String)
* @see RepositoryItemReader#setRepository(PagingAndSortingRepository)
*
*/
public RepositoryItemReaderBuilder<T> repository(RepositoryMethodReference<?> repositoryMethodReference) {
this.repositoryMethodReference = repositoryMethodReference;

return this;
}

/**
* Builds the {@link RepositoryItemReader}.
*
* @return a {@link RepositoryItemReader}
*/
public RepositoryItemReader<T> build() {
if (this.repositoryMethodReference != null) {
this.methodName = this.repositoryMethodReference.getMethodName();
this.repository = this.repositoryMethodReference.getRepository();

if(CollectionUtils.isEmpty(this.arguments)) {
this.arguments = this.repositoryMethodReference.getArguments();
}
}

Assert.notNull(this.sorts, "sorts map is required.");
Assert.notNull(this.repository, "repository is required.");
Assert.hasText(this.methodName, "methodName is required.");
Expand All @@ -254,71 +220,4 @@ public RepositoryItemReader<T> build() {
return reader;
}

/**
* Establishes a proxy that will capture a the Repository and the associated
* methodName that will be used by the reader.
* @param <T> The type of repository that will be used by the reader. The class must
* not be final.
*/
public static class RepositoryMethodReference<T> {
private RepositoryMethodInterceptor repositoryInvocationHandler;

private PagingAndSortingRepository<?, ?> repository;

public RepositoryMethodReference(PagingAndSortingRepository<?, ?> repository) {
this.repository = repository;
this.repositoryInvocationHandler = new RepositoryMethodInterceptor();
}

/**
* The proxy returned prevents actual method execution and is only used to gather,
* information about the method.
* @return T is a proxy of the object passed in in the constructor
*/
@SuppressWarnings("unchecked")
public T methodIs() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.repository.getClass());
enhancer.setCallback(this.repositoryInvocationHandler);
return (T) enhancer.create();
}

PagingAndSortingRepository<?, ?> getRepository() {
return this.repository;
}

String getMethodName() {
return this.repositoryInvocationHandler.getMethodName();
}

List<Object> getArguments() {
return this.repositoryInvocationHandler.getArguments();
}
}

private static class RepositoryMethodInterceptor implements MethodInterceptor {
private String methodName;

private List<Object> arguments;

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
this.methodName = method.getName();
if (objects != null && objects.length > 1) {
arguments = new ArrayList<>(Arrays.asList(objects));
// remove last entry because that will be provided by the
// RepositoryItemReader
arguments.remove(objects.length - 1);
}
return null;
}

String getMethodName() {
return this.methodName;
}

List<Object> getArguments() {
return arguments;
}
}
}
Expand Up @@ -367,7 +367,7 @@ public void testResetOfPage() throws Exception {
assertEquals("3", reader.read());
}

public interface TestRepository extends PagingAndSortingRepository<Map, Long> {
public interface TestRepository extends PagingAndSortingRepository<String, Long> {
Page<String> findFirstNames(Pageable pageable);
}

Expand Down
Expand Up @@ -90,42 +90,6 @@ public void testBasicRead() throws Exception {
assertEquals("page size was not expected value.", 10, this.pageRequestContainer.getValue().getPageSize());
}

@Test
public void testRepositoryMethodReference() throws Exception {
RepositoryItemReaderBuilder.RepositoryMethodReference<TestRepository> repositoryMethodReference =
new RepositoryItemReaderBuilder.RepositoryMethodReference<>(this.repository);
repositoryMethodReference.methodIs().foo(null);
RepositoryItemReader<Object> reader = new RepositoryItemReaderBuilder<>()
.repository(repositoryMethodReference)
.sorts(this.sorts)
.maxItemCount(5)
.name("bar").build();
String result = (String) reader.read();
assertEquals("Result returned from reader was not expected value.", TEST_CONTENT, result);
assertEquals("page size was not expected value.", 10, this.pageRequestContainer.getValue().getPageSize());
}

@Test
public void testRepositoryMethodReferenceWithArgs() throws Exception {
RepositoryItemReaderBuilder.RepositoryMethodReference<TestRepository> repositoryMethodReference =
new RepositoryItemReaderBuilder.RepositoryMethodReference<>(this.repository);
repositoryMethodReference.methodIs().foo(ARG1, ARG2, ARG3, null);
RepositoryItemReader<Object> reader = new RepositoryItemReaderBuilder<>()
.repository(repositoryMethodReference)
.sorts(this.sorts)
.maxItemCount(5)
.name("bar").build();
ArgumentCaptor<String> arg1Captor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> arg2Captor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> arg3Captor = ArgumentCaptor.forClass(String.class);
when(this.repository.foo(arg1Captor.capture(), arg2Captor.capture(), arg3Captor.capture(),
this.pageRequestContainer.capture())).thenReturn(this.page);

String result = (String) reader.read();
assertEquals("Result returned from reader was not expected value.", TEST_CONTENT, result);
verifyMultiArgRead(arg1Captor, arg2Captor, arg3Captor, result);
}

@Test
public void testCurrentItemCount() throws Exception {
RepositoryItemReader<Object> reader = new RepositoryItemReaderBuilder<>().repository(this.repository)
Expand Down

0 comments on commit 5602a0d

Please sign in to comment.