Skip to content

Commit e5540b4

Browse files
mp911deodrotbohm
authored andcommitted
Support reactive repositories in ResourceReaderRepositoryPopulator.
We now support reactive repositories in addition to imperative repositories when populating repositories from a resource reader. Instead of RepositoryInvokerFactory, we now use a RepositoryPersisterFactory to avoid introducing reactive support to RepositoryInvokerFactory.
1 parent f281006 commit e5540b4

File tree

3 files changed

+393
-15
lines changed

3 files changed

+393
-15
lines changed

src/main/java/org/springframework/data/repository/init/ResourceReaderRepositoryPopulator.java

Lines changed: 186 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,35 +15,49 @@
1515
*/
1616
package org.springframework.data.repository.init;
1717

18+
import reactor.core.publisher.Flux;
19+
import reactor.core.publisher.Mono;
20+
1821
import java.io.IOException;
22+
import java.lang.reflect.Method;
1923
import java.util.Arrays;
2024
import java.util.Collection;
2125
import java.util.Collections;
26+
import java.util.HashMap;
27+
import java.util.Map;
2228

2329
import org.apache.commons.logging.Log;
2430
import org.apache.commons.logging.LogFactory;
31+
import org.reactivestreams.Publisher;
32+
2533
import org.springframework.context.ApplicationEventPublisher;
2634
import org.springframework.context.ApplicationEventPublisherAware;
2735
import org.springframework.core.io.Resource;
2836
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
2937
import org.springframework.core.io.support.ResourcePatternResolver;
30-
import org.springframework.data.repository.support.DefaultRepositoryInvokerFactory;
38+
import org.springframework.data.repository.CrudRepository;
39+
import org.springframework.data.repository.core.CrudMethods;
40+
import org.springframework.data.repository.core.RepositoryInformation;
41+
import org.springframework.data.repository.core.RepositoryMetadata;
42+
import org.springframework.data.repository.core.support.DefaultCrudMethods;
43+
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
3144
import org.springframework.data.repository.support.Repositories;
32-
import org.springframework.data.repository.support.RepositoryInvoker;
33-
import org.springframework.data.repository.support.RepositoryInvokerFactory;
45+
import org.springframework.data.repository.util.ReactiveWrapperConverters;
3446
import org.springframework.lang.Nullable;
3547
import org.springframework.util.Assert;
48+
import org.springframework.util.ReflectionUtils;
3649

3750
/**
3851
* A {@link RepositoryPopulator} using a {@link ResourceReader} to read objects from the configured {@link Resource}s.
3952
*
4053
* @author Oliver Gierke
4154
* @author Christoph Strobl
55+
* @author Mark Paluch
4256
* @since 1.4
4357
*/
4458
public class ResourceReaderRepositoryPopulator implements RepositoryPopulator, ApplicationEventPublisherAware {
4559

46-
private static final Log logger = LogFactory.getLog(ResourceReaderRepositoryPopulator.class);
60+
private static final Log logger = LogFactory.getLog(ResourceReaderRepositoryPopulator.class);
4761

4862
private final ResourceReader reader;
4963
private final @Nullable ClassLoader classLoader;
@@ -114,7 +128,7 @@ public void populate(Repositories repositories) {
114128

115129
Assert.notNull(repositories, "Repositories must not be null!");
116130

117-
RepositoryInvokerFactory invokerFactory = new DefaultRepositoryInvokerFactory(repositories);
131+
RepositoryPersisterFactory persisterFactory = new RepositoryPersisterFactory(repositories);
118132

119133
for (Resource resource : resources) {
120134

@@ -125,13 +139,13 @@ public void populate(Repositories repositories) {
125139
if (result instanceof Collection) {
126140
for (Object element : (Collection<?>) result) {
127141
if (element != null) {
128-
persist(element, invokerFactory);
142+
persist(element, persisterFactory);
129143
} else {
130144
logger.info("Skipping null element found in unmarshal result!");
131145
}
132146
}
133147
} else {
134-
persist(result, invokerFactory);
148+
persist(result, persisterFactory);
135149
}
136150
}
137151

@@ -158,12 +172,172 @@ private Object readObjectFrom(Resource resource) {
158172
* Persists the given {@link Object} using a suitable repository.
159173
*
160174
* @param object must not be {@literal null}.
161-
* @param invokerFactory must not be {@literal null}.
175+
* @param persisterFactory must not be {@literal null}.
176+
*/
177+
private void persist(Object object, RepositoryPersisterFactory persisterFactory) {
178+
179+
RepositoryPersister persister = persisterFactory.getPersisterFor(object.getClass());
180+
logger.debug(String.format("Persisting %s using repository %s", object, persister));
181+
persister.save(object);
182+
}
183+
184+
/**
185+
* Factory to create {@link RepositoryPersister} instances.
186+
*/
187+
static class RepositoryPersisterFactory {
188+
189+
private final Map<Class<?>, RepositoryPersister> persisters = new HashMap<>();
190+
private final Repositories repositories;
191+
192+
public RepositoryPersisterFactory(Repositories repositories) {
193+
this.repositories = repositories;
194+
}
195+
196+
/**
197+
* Obtain a {@link RepositoryPersister}.
198+
*
199+
* @param domainType
200+
* @return
201+
*/
202+
public RepositoryPersister getPersisterFor(Class<?> domainType) {
203+
return persisters.computeIfAbsent(domainType, this::createPersisterFor);
204+
}
205+
206+
private RepositoryPersister createPersisterFor(Class<?> domainType) {
207+
208+
RepositoryInformation repositoryInformation = repositories.getRequiredRepositoryInformation(domainType);
209+
Object repository = repositories.getRepositoryFor(domainType).orElseThrow(
210+
() -> new IllegalArgumentException(String.format("No repository found for domain type: %s", domainType)));
211+
212+
if (repositoryInformation.isReactiveRepository()) {
213+
return repository instanceof ReactiveCrudRepository ? new ReactiveCrudRepositoryPersister(repository)
214+
: new ReflectiveReactivePersister(repositoryInformation, repository);
215+
}
216+
217+
if (repository instanceof CrudRepository) {
218+
return new CrudRepositoryPersister(repository);
219+
}
220+
221+
return new ReflectivePersister(repositoryInformation, repository);
222+
}
223+
}
224+
225+
/**
226+
* Interface defining a save method to persist an object within a repository.
227+
*/
228+
interface RepositoryPersister {
229+
230+
/**
231+
* Saves the {@code object} in an appropriate repository.
232+
*
233+
* @param object
234+
*/
235+
void save(Object object);
236+
}
237+
238+
/**
239+
* Reflection variant of a {@link RepositoryPersister}.
240+
*/
241+
private static class ReflectivePersister implements RepositoryPersister {
242+
243+
private final CrudMethods methods;
244+
private final Object repository;
245+
246+
public ReflectivePersister(RepositoryMetadata metadata, Object repository) {
247+
this.methods = new DefaultCrudMethods(metadata);
248+
this.repository = repository;
249+
}
250+
251+
@Override
252+
public void save(Object object) {
253+
254+
doPersist(object);
255+
}
256+
257+
Object doPersist(Object object) {
258+
Method method = methods.getSaveMethod()//
259+
.orElseThrow(() -> new IllegalStateException("Repository doesn't have a save-method declared!"));
260+
261+
return ReflectionUtils.invokeMethod(method, repository, object);
262+
}
263+
264+
@Override
265+
public String toString() {
266+
return repository.toString();
267+
}
268+
}
269+
270+
/**
271+
* Reactive extension to save objects in a reactive repository.
272+
*/
273+
private static class ReflectiveReactivePersister extends ReflectivePersister {
274+
275+
public ReflectiveReactivePersister(RepositoryMetadata metadata, Object repository) {
276+
super(metadata, repository);
277+
}
278+
279+
@Override
280+
public void save(Object object) {
281+
282+
Object wrapper = doPersist(object);
283+
284+
Publisher<?> publisher = ReactiveWrapperConverters.toWrapper(wrapper, Publisher.class);
285+
286+
if (!(publisher instanceof Mono)) {
287+
publisher = Flux.from(publisher).collectList();
288+
}
289+
290+
Mono.from(publisher).block();
291+
}
292+
}
293+
294+
/**
295+
* {@link RepositoryPersister} to operate with {@link CrudRepository}.
162296
*/
163-
private void persist(Object object, RepositoryInvokerFactory invokerFactory) {
297+
private static class CrudRepositoryPersister implements RepositoryPersister {
164298

165-
RepositoryInvoker invoker = invokerFactory.getInvokerFor(object.getClass());
166-
logger.debug(String.format("Persisting %s using repository %s", object, invoker));
167-
invoker.invokeSave(object);
299+
private final CrudRepository<Object, Object> repository;
300+
301+
@SuppressWarnings("unchecked")
302+
public CrudRepositoryPersister(Object repository) {
303+
304+
Assert.isInstanceOf(CrudRepository.class, repository);
305+
this.repository = (CrudRepository<Object, Object>) repository;
306+
}
307+
308+
@Override
309+
public void save(Object object) {
310+
repository.save(object);
311+
}
312+
313+
@Override
314+
public String toString() {
315+
return repository.toString();
316+
}
317+
}
318+
319+
/**
320+
* {@link RepositoryPersister} to operate with {@link ReactiveCrudRepository}.
321+
*/
322+
private static class ReactiveCrudRepositoryPersister implements RepositoryPersister {
323+
324+
private final ReactiveCrudRepository<Object, Object> repository;
325+
326+
@SuppressWarnings("unchecked")
327+
public ReactiveCrudRepositoryPersister(Object repository) {
328+
329+
Assert.isInstanceOf(ReactiveCrudRepository.class, repository);
330+
this.repository = (ReactiveCrudRepository<Object, Object>) repository;
331+
}
332+
333+
@Override
334+
public void save(Object object) {
335+
repository.save(object).block();
336+
}
337+
338+
@Override
339+
public String toString() {
340+
return repository.toString();
341+
}
168342
}
169343
}

src/test/java/org/springframework/data/repository/core/support/DummyRepositoryFactoryBean.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,13 @@
1717

1818
import static org.mockito.Mockito.*;
1919

20-
import java.io.Serializable;
21-
2220
import org.springframework.data.mapping.context.SampleMappingContext;
2321
import org.springframework.data.repository.Repository;
2422

2523
/**
2624
* @author Oliver Gierke
2725
*/
28-
public class DummyRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable>
26+
public class DummyRepositoryFactoryBean<T extends Repository<S, ID>, S, ID>
2927
extends RepositoryFactoryBeanSupport<T, S, ID> {
3028

3129
private final T repository;

0 commit comments

Comments
 (0)