DATACMNS-489 - Support for Future<T> as return value wrapper for repository methods.
Added a converter to process a NullableWrapper into a Future by producing an AsyncResult. This is to support repository methods annotated with @Async and returning a Future<T>. With Spring's asynchronous method invocation support activated this will cause the repository method being executed asynchronously. Note, that this currently only works on Spring 4 as Spring 3.2.8 async support is not discovering the introduction on the repository proxy and thus fails to discover the @Async annotation on the repository method. This will be resolved in Spring 3.2.9, see the related tickets. Related tickets: DATACMNS-483, SPR-11725, DATACMNS-499.
This commit is contained in:
@@ -31,6 +31,9 @@ import org.springframework.data.repository.query.RepositoryQuery;
|
||||
|
||||
public class DummyRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
public final MyRepositoryQuery queryOne = mock(MyRepositoryQuery.class);
|
||||
public final RepositoryQuery queryTwo = mock(RepositoryQuery.class);
|
||||
|
||||
private final Object repository;
|
||||
|
||||
public DummyRepositoryFactory(Object repository) {
|
||||
@@ -44,7 +47,6 @@ public class DummyRepositoryFactory extends RepositoryFactorySupport {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T, ID extends Serializable> EntityInformation<T, ID> getEntityInformation(Class<T> domainClass) {
|
||||
|
||||
return mock(EntityInformation.class);
|
||||
}
|
||||
|
||||
@@ -73,10 +75,8 @@ public class DummyRepositoryFactory extends RepositoryFactorySupport {
|
||||
@Override
|
||||
protected QueryLookupStrategy getQueryLookupStrategy(Key key) {
|
||||
|
||||
MyRepositoryQuery queryOne = mock(MyRepositoryQuery.class);
|
||||
RepositoryQuery queryTwo = mock(RepositoryQuery.class);
|
||||
|
||||
QueryLookupStrategy strategy = mock(QueryLookupStrategy.class);
|
||||
|
||||
when(
|
||||
strategy.resolveQuery(Mockito.any(Method.class), Mockito.any(RepositoryMetadata.class),
|
||||
Mockito.any(NamedQueries.class))).thenReturn(queryOne, queryTwo);
|
||||
|
||||
@@ -21,13 +21,19 @@ import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.core.SpringVersion;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@@ -36,6 +42,8 @@ import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.RepositoryDefinition;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -47,7 +55,7 @@ import org.springframework.util.ClassUtils;
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class RepositoryFactorySupportUnitTests {
|
||||
|
||||
RepositoryFactorySupport factory;
|
||||
DummyRepositoryFactory factory;
|
||||
|
||||
@Mock PagingAndSortingRepository<Object, Serializable> backingRepo;
|
||||
@Mock ObjectRepositoryCustom customImplementation;
|
||||
@@ -121,6 +129,44 @@ public class RepositoryFactorySupportUnitTests {
|
||||
assertThat(ReflectionTestUtils.getField(factory, "classLoader"), is((Object) ClassUtils.getDefaultClassLoader()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-489
|
||||
*/
|
||||
@Test
|
||||
public void wrapsExecutionResultIntoFutureIfConfigured() throws Exception {
|
||||
|
||||
// TODO: Remove once Spring 3.2.9 is released.
|
||||
Assume.assumeThat(SpringVersion.getVersion().startsWith("4"), is(true));
|
||||
|
||||
final Object reference = new Object();
|
||||
|
||||
when(factory.queryOne.execute(Mockito.any(Object[].class))).then(new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
Thread.sleep(500);
|
||||
return reference;
|
||||
}
|
||||
});
|
||||
|
||||
AsyncRepository repository = factory.getRepository(AsyncRepository.class);
|
||||
|
||||
AsyncAnnotationBeanPostProcessor processor = new AsyncAnnotationBeanPostProcessor();
|
||||
processor.setBeanFactory(new DefaultListableBeanFactory());
|
||||
repository = (AsyncRepository) processor.postProcessAfterInitialization(repository, null);
|
||||
|
||||
Future<Object> future = repository.findByFirstname("Foo");
|
||||
|
||||
assertThat(future.isDone(), is(false));
|
||||
|
||||
while (!future.isDone()) {
|
||||
Thread.sleep(300);
|
||||
}
|
||||
|
||||
assertThat(future.get(), is(reference));
|
||||
|
||||
verify(factory.queryOne, times(1)).execute(Mockito.any(Object[].class));
|
||||
}
|
||||
|
||||
interface ObjectRepository extends Repository<Object, Serializable>, ObjectRepositoryCustom {
|
||||
|
||||
Object findByClass(Class<?> clazz);
|
||||
@@ -170,4 +216,10 @@ public class RepositoryFactorySupportUnitTests {
|
||||
interface AnnotatedRepository {
|
||||
|
||||
}
|
||||
|
||||
interface AsyncRepository extends Repository<Object, Long> {
|
||||
|
||||
@Async
|
||||
Future<Object> findByFirstname(String firstname);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user