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:
Oliver Gierke
2014-04-22 16:19:44 +02:00
parent 57271baf54
commit 02a3c08ee1
4 changed files with 82 additions and 5 deletions

View File

@@ -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);

View File

@@ -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);
}
}