Skip to content

Commit 49db258

Browse files
authored
Searchable API uses Page (#482)
1 parent eaf6c42 commit 49db258

File tree

12 files changed

+371
-171
lines changed

12 files changed

+371
-171
lines changed

spring-content-commons/src/main/java/internal/org/springframework/content/commons/repository/factory/StoreMethodInterceptor.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
package internal.org.springframework.content.commons.repository.factory;
22

3-
import internal.org.springframework.content.commons.config.StoreFragment;
4-
import internal.org.springframework.content.commons.config.StoreFragments;
3+
import static java.lang.String.format;
4+
5+
import java.io.Serializable;
6+
import java.lang.reflect.Method;
7+
import java.util.Map;
8+
import java.util.Optional;
9+
510
import org.aopalliance.intercept.MethodInterceptor;
611
import org.aopalliance.intercept.MethodInvocation;
712
import org.apache.commons.logging.Log;
@@ -12,12 +17,8 @@
1217
import org.springframework.util.ConcurrentReferenceHashMap;
1318
import org.springframework.util.ReflectionUtils;
1419

15-
import java.io.Serializable;
16-
import java.lang.reflect.Method;
17-
import java.util.Map;
18-
import java.util.Optional;
19-
20-
import static java.lang.String.format;
20+
import internal.org.springframework.content.commons.config.StoreFragment;
21+
import internal.org.springframework.content.commons.config.StoreFragments;
2122

2223
public class StoreMethodInterceptor implements MethodInterceptor {
2324

@@ -49,6 +50,12 @@ public Object invoke(MethodInvocation invocation) throws Throwable {
4950
.filter(it -> it.hasMethod(invocation.getMethod()))
5051
.findFirst();
5152

53+
if (fragment.isPresent() == false) {
54+
fragment = storeFragments.stream()
55+
.filter(it -> it.hasImplementationMethod(invocation.getMethod()))
56+
.findFirst();
57+
}
58+
5259
fragment.orElseThrow(() -> new IllegalStateException(format("No fragment found for method %s", invocation.getMethod())));
5360

5461
StoreFragment f = fragment.get();

spring-content-commons/src/main/java/org/springframework/content/commons/config/AbstractStoreBeanDefinitionRegistrar.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.springframework.content.commons.config;
22

33
import static java.lang.String.format;
4+
import static java.util.stream.Collectors.toList;
45

56
import java.io.IOException;
67
import java.lang.annotation.Annotation;
@@ -241,6 +242,23 @@ private void registerStoreFragmentImplementation(BeanDefinitionRegistry registry
241242

242243
((AbstractBeanDefinition)fragmentDefinition.getBeanDefinition()).setSource(source);
243244

245+
try {
246+
Class<?> ifaceClass = ClassUtils.forName(fragmentDefinition.getInterfaceName(), ((ConfigurableListableBeanFactory)registry).getBeanClassLoader());
247+
Class<?> implClass = ClassUtils.forName(fragmentDefinition.getBeanDefinition().getBeanClassName(), ((ConfigurableListableBeanFactory)registry).getBeanClassLoader());
248+
Class<?> storeClass = ClassUtils.forName(fragmentDefinition.getStoreInterfaceName(), ((ConfigurableListableBeanFactory)registry).getBeanClassLoader());
249+
250+
Method method = ReflectionUtils.findMethod(implClass, "setGenericArguments", Class[].class);
251+
if (method != null) {
252+
List<TypeInformation<?>> types = ClassTypeInformation.from(storeClass).getSuperTypeInformation(ifaceClass).getTypeArguments();
253+
List<Class<?>> genericArguments = types.stream().map(TypeInformation::getType).collect(toList());
254+
fragmentDefinition.getBeanDefinition().getPropertyValues().add("genericArguments", genericArguments.toArray(new Class[] {}));
255+
}
256+
}
257+
catch (ClassNotFoundException e) {
258+
259+
LOGGER.error("Failed setting fragment generic arguments", e);
260+
}
261+
244262
try {
245263
Class<?> implClass = ClassUtils.forName(fragmentDefinition.getBeanDefinition().getBeanClassName(), ((ConfigurableListableBeanFactory)registry).getBeanClassLoader());
246264
Method method = ReflectionUtils.findMethod(implClass, "setDomainClass", Class.class);
@@ -249,7 +267,8 @@ private void registerStoreFragmentImplementation(BeanDefinitionRegistry registry
249267
}
250268
}
251269
catch (ClassNotFoundException e) {
252-
e.printStackTrace();
270+
271+
LOGGER.error("Failed setting fragment domain class", e);
253272
}
254273

255274
try {
@@ -260,7 +279,8 @@ private void registerStoreFragmentImplementation(BeanDefinitionRegistry registry
260279
}
261280
}
262281
catch (ClassNotFoundException e) {
263-
e.printStackTrace();
282+
283+
LOGGER.error("Failed setting fragment ID class", e);
264284
}
265285

266286
registry.registerBeanDefinition(beanName, fragmentDefinition.getBeanDefinition());
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.springframework.content.commons.fragments;
2+
3+
public interface ParameterTypeAware {
4+
5+
void setGenericArguments(Class<?>[] arguments);
6+
}

spring-content-commons/src/main/java/org/springframework/content/commons/repository/factory/AbstractStoreFactoryBean.java

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
package org.springframework.content.commons.repository.factory;
22

3-
import internal.org.springframework.content.commons.config.StoreFragment;
4-
import internal.org.springframework.content.commons.config.StoreFragments;
5-
import internal.org.springframework.content.commons.repository.factory.StoreImpl;
6-
import internal.org.springframework.content.commons.repository.factory.StoreMethodInterceptor;
3+
import java.io.Serializable;
4+
import java.lang.reflect.Method;
5+
import java.lang.reflect.ParameterizedType;
6+
import java.lang.reflect.Type;
7+
import java.util.Collections;
8+
import java.util.HashMap;
9+
import java.util.Map;
10+
import java.util.Set;
11+
712
import org.apache.commons.logging.Log;
813
import org.apache.commons.logging.LogFactory;
914
import org.springframework.aop.framework.ProxyFactory;
@@ -13,6 +18,7 @@
1318
import org.springframework.beans.factory.FactoryBean;
1419
import org.springframework.beans.factory.InitializingBean;
1520
import org.springframework.beans.factory.annotation.Autowired;
21+
import org.springframework.content.commons.fragments.ParameterTypeAware;
1622
import org.springframework.content.commons.repository.AssociativeStore;
1723
import org.springframework.content.commons.repository.ContentStore;
1824
import org.springframework.content.commons.repository.Store;
@@ -21,14 +27,10 @@
2127
import org.springframework.context.ApplicationEventPublisherAware;
2228
import org.springframework.util.Assert;
2329

24-
import java.io.Serializable;
25-
import java.lang.reflect.Method;
26-
import java.lang.reflect.ParameterizedType;
27-
import java.lang.reflect.Type;
28-
import java.util.Collections;
29-
import java.util.HashMap;
30-
import java.util.Map;
31-
import java.util.Set;
30+
import internal.org.springframework.content.commons.config.StoreFragment;
31+
import internal.org.springframework.content.commons.config.StoreFragments;
32+
import internal.org.springframework.content.commons.repository.factory.StoreImpl;
33+
import internal.org.springframework.content.commons.repository.factory.StoreMethodInterceptor;
3234

3335
public abstract class AbstractStoreFactoryBean
3436
implements BeanFactoryAware, InitializingBean, FactoryBean<Store<? extends Serializable>>,
@@ -61,28 +63,30 @@ public void setStoreFragments(StoreFragments storeFragments) {
6163

6264
/*
6365
* (non-Javadoc)
64-
*
66+
*
6567
* @see org.springframework.content.commons.repository.factory.ContentStoreFactory#
6668
* getContentStoreInterface()
6769
*/
68-
public Class<?> getStoreInterface() {
70+
@Override
71+
public Class<?> getStoreInterface() {
6972
return this.storeInterface;
7073
}
7174

7275
/*
7376
* (non-Javadoc)
74-
*
77+
*
7578
* @see org.springframework.content.commons.repository.factory.ContentStoreFactory#
7679
* getContentStore()
7780
*/
78-
@SuppressWarnings("unchecked")
81+
@Override
82+
@SuppressWarnings("unchecked")
7983
public Store<Serializable> getStore() {
8084
return (Store<Serializable>) getObject();
8185
}
8286

8387
/*
8488
* (non-Javadoc)
85-
*
89+
*
8690
* @see
8791
* org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang
8892
* .ClassLoader)
@@ -94,7 +98,7 @@ public void setBeanClassLoader(ClassLoader classLoader) {
9498

9599
/*
96100
* (non-Javadoc)
97-
*
101+
*
98102
* @see org.springframework.context.ApplicationEventPublisherAware#
99103
* setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher)
100104
*/
@@ -106,36 +110,39 @@ public void setApplicationEventPublisher(
106110

107111
/*
108112
* (non-Javadoc)
109-
*
113+
*
110114
* @see org.springframework.beans.factory.FactoryBean#getObject()
111115
*/
112-
public Store<? extends Serializable> getObject() {
116+
@Override
117+
public Store<? extends Serializable> getObject() {
113118
return initAndReturn();
114119
}
115120

116121
/*
117122
* (non-Javadoc)
118-
*
123+
*
119124
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
120125
*/
121-
@SuppressWarnings("unchecked")
126+
@Override
127+
@SuppressWarnings("unchecked")
122128
public Class<? extends Store<? extends Serializable>> getObjectType() {
123129
return (Class<? extends Store<? extends Serializable>>) (null == storeInterface
124130
? Store.class : storeInterface);
125131
}
126132

127133
/*
128134
* (non-Javadoc)
129-
*
135+
*
130136
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
131137
*/
132-
public boolean isSingleton() {
138+
@Override
139+
public boolean isSingleton() {
133140
return true;
134141
}
135142

136143
/*
137144
* (non-Javadoc)
138-
*
145+
*
139146
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
140147
*/
141148
@Override
@@ -157,7 +164,7 @@ protected Store<? extends Serializable> createContentStore() {
157164
// Create proxy
158165
ProxyFactory result = new ProxyFactory();
159166
result.setTarget(target);
160-
result.setInterfaces(new Class[] { storeInterface, Store.class, ContentStore.class });
167+
result.setInterfaces(new Class[] { storeInterface, Store.class, ContentStore.class, ParameterTypeAware.class });
161168

162169
Map<Method, StoreExtension> extensionsMap = new HashMap<>();
163170
try {
@@ -233,7 +240,8 @@ else if (pt.getRawType().getTypeName()
233240
* (non-Javadoc)
234241
* @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory)
235242
*/
236-
public void setBeanFactory(BeanFactory beanFactory) {
243+
@Override
244+
public void setBeanFactory(BeanFactory beanFactory) {
237245
this.beanFactory = beanFactory;
238246
}
239247

spring-content-commons/src/main/java/org/springframework/content/commons/search/Searchable.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
package org.springframework.content.commons.search;
22

3-
import java.util.List;
4-
3+
import org.springframework.data.domain.Page;
54
import org.springframework.data.domain.Pageable;
65

76
public interface Searchable<T> {
87

98
Iterable<T> search(String queryString);
109

11-
List<T> search(String queryString, Pageable pageable);
12-
13-
List<T> search(String queryString, Pageable pageable, Class<? extends T> searchType);
10+
Page<T> search(String queryString, Pageable pageable);
1411

1512
@Deprecated
1613
Iterable<T> findKeyword(String query);

spring-content-elasticsearch/src/main/java/internal/org/springframework/content/fragments/SearchableImpl.java

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
import org.springframework.core.convert.ConversionService;
3737
import org.springframework.core.convert.TypeDescriptor;
3838
import org.springframework.core.convert.support.DefaultConversionService;
39+
import org.springframework.data.domain.Page;
40+
import org.springframework.data.domain.PageImpl;
3941
import org.springframework.data.domain.Pageable;
4042

4143
import internal.org.springframework.content.elasticsearch.ElasticsearchIndexer;
@@ -52,6 +54,7 @@ public class SearchableImpl implements Searchable<Object> {
5254

5355
private Class<?> domainClass;
5456
private Class<?> idClass;
57+
private Class<?>[] genericArguments;
5558

5659
public SearchableImpl() {
5760
client = null;
@@ -82,18 +85,21 @@ public void setIdClass(Class<?> idClass) {
8285
this.idClass = idClass;
8386
}
8487

88+
public void setGenericArguments(Class<?>[] genericArguments) {
89+
this.genericArguments = genericArguments;
90+
}
91+
8592
@Override
8693
public Iterable<Object> search(String queryStr) {
87-
return search(queryStr, null, idClass);
94+
return search(queryStr, null, genericArguments[0], ArrayList.class);
8895
}
8996

9097
@Override
91-
public List<Object> search(String queryStr, Pageable pageable) {
92-
return search(queryStr, pageable, idClass);
98+
public Page<Object> search(String queryStr, Pageable pageable) {
99+
return search(queryStr, pageable, genericArguments[0], Page.class);
93100
}
94101

95-
@Override
96-
public List<Object> search(String queryString, Pageable pageable, Class<? extends Object> searchType) {
102+
private <R> R search(String queryString, Pageable pageable, Class<? extends Object> searchType, Class<R> returnType) {
97103

98104
SearchRequest searchRequest = new SearchRequest(manager.indexName(domainClass));
99105
searchRequest.types(domainClass.getName());
@@ -149,7 +155,7 @@ public List<Object> search(String queryString, Pageable pageable, Class<? extend
149155
throw new StoreAccessException(format("Error searching indexed content for '%s'", queryString), e);
150156
}
151157

152-
return getResults(res.getHits(), searchType);
158+
return getResults(res.getHits(), pageable, searchType, returnType);
153159
}
154160

155161
@Override
@@ -216,12 +222,12 @@ private List<Object> getIDs(SearchHits result) {
216222
return contents;
217223
}
218224

219-
private List<Object> getResults(SearchHits result, Class<?> resultType) {
225+
private <R> R getResults(SearchHits result, Pageable pageable, Class<?> resultType, Class<R> returnType) {
220226

221227
List<Object> contents = new ArrayList<>();
222228

223229
if (result == null || result.getTotalHits().value == 0) {
224-
return contents;
230+
return wrapResult(returnType, contents, pageable, 0);
225231
}
226232

227233
for (SearchHit hit : result.getHits()) {
@@ -258,6 +264,20 @@ private List<Object> getResults(SearchHits result, Class<?> resultType) {
258264
}
259265
}
260266

261-
return contents;
267+
return wrapResult(returnType, contents, pageable, result.getTotalHits().value);
268+
}
269+
270+
@SuppressWarnings("unchecked")
271+
private <R> R wrapResult(Class<R> returnType, List<Object> content, Pageable pageable, long total) {
272+
273+
R rc = null;
274+
if (Page.class.isAssignableFrom(returnType)) {
275+
LOGGER.debug("Wrapping result in Page");
276+
rc = (R) new PageImpl<Object>(content, pageable, total);
277+
} else {
278+
LOGGER.debug("Returning result as-is");
279+
rc = (R) content;
280+
}
281+
return rc;
262282
}
263283
}

0 commit comments

Comments
 (0)