1 /*
2  * Copyright 2013-2020 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  *      https://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.support;
17
18 import static org.springframework.data.util.Optionals.*;
19
20 import java.util.Map;
21 import java.util.Optional;
22 import java.util.concurrent.ConcurrentHashMap;
23
24 import org.springframework.core.convert.ConversionService;
25 import org.springframework.data.repository.CrudRepository;
26 import org.springframework.data.repository.PagingAndSortingRepository;
27 import org.springframework.data.repository.core.RepositoryInformation;
28 import org.springframework.format.support.DefaultFormattingConversionService;
29 import org.springframework.util.Assert;
30
31 /**
32  * Default implementation of {@link RepositoryInvokerFactory} to inspect the requested repository type and create a
33  * matching {@link RepositoryInvoker} that suits the repository best. That means, the more concrete the base interface
34  * of the repository is, the more concrete will the actual invoker become - which means it will favor concrete method
35  * invocations over reflection ones.
36  *
37  * @author Oliver Gierke
38  * @author Christoph Strobl
39  * @since 1.10
40  */

41 public class DefaultRepositoryInvokerFactory implements RepositoryInvokerFactory {
42
43     private final Repositories repositories;
44     private final ConversionService conversionService;
45     private final Map<Class<?>, RepositoryInvoker> invokers;
46
47     /**
48      * Creates a new {@link DefaultRepositoryInvokerFactory} for the given {@link Repositories}.
49      *
50      * @param repositories must not be {@literal null}.
51      */

52     public DefaultRepositoryInvokerFactory(Repositories repositories) {
53         this(repositories, new DefaultFormattingConversionService());
54     }
55
56     /**
57      * Creates a new {@link DefaultRepositoryInvokerFactory} for the given {@link Repositories} and
58      * {@link ConversionService}.
59      *
60      * @param repositories must not be {@literal null}.
61      * @param conversionService must not be {@literal null}.
62      */

63     public DefaultRepositoryInvokerFactory(Repositories repositories, ConversionService conversionService) {
64
65         Assert.notNull(repositories, "Repositories must not be null!");
66         Assert.notNull(conversionService, "ConversionService must not be null!");
67
68         this.repositories = repositories;
69         this.conversionService = conversionService;
70         this.invokers = new ConcurrentHashMap<>();
71     }
72
73     /*
74      * (non-Javadoc)
75      * @see org.springframework.data.rest.core.invoke.RepositoryInvokerFactory#getInvokerFor(java.lang.Class)
76      */

77     @Override
78     public RepositoryInvoker getInvokerFor(Class<?> domainType) {
79         return invokers.computeIfAbsent(domainType, this::prepareInvokers);
80     }
81
82     /**
83      * Creates a {@link RepositoryInvoker} for the repository managing the given domain type.
84      *
85      * @param domainType
86      * @return
87      */

88     private RepositoryInvoker prepareInvokers(Class<?> domainType) {
89
90         Optional<RepositoryInformation> information = repositories.getRepositoryInformationFor(domainType);
91         Optional<Object> repository = repositories.getRepositoryFor(domainType);
92
93         return mapIfAllPresent(information, repository, this::createInvoker)//
94                 .orElseThrow(
95                         () -> new IllegalArgumentException(String.format("No repository found for domain type: %s", domainType)));
96     }
97
98     @SuppressWarnings("unchecked")
99     protected RepositoryInvoker createInvoker(RepositoryInformation information, Object repository) {
100
101         if (repository instanceof PagingAndSortingRepository) {
102             return new PagingAndSortingRepositoryInvoker((PagingAndSortingRepository<Object, Object>) repository, information,
103                     conversionService);
104         } else if (repository instanceof CrudRepository) {
105             return new CrudRepositoryInvoker((CrudRepository<Object, Object>) repository, information, conversionService);
106         } else {
107             return new ReflectionRepositoryInvoker(repository, information, conversionService);
108         }
109     }
110 }
111