SGF-402 - Add support for Lucene query result projections.
(cherry picked from commit 5113152b8f85c74c799926f53fefea1f80833745) Signed-off-by: John Blum <jblum@pivotal.io>
This commit is contained in:
@@ -106,6 +106,7 @@ public interface LuceneOperations {
|
||||
* to execute as well as de/serialize to distribute across the cluster.
|
||||
* @param projectionFields array of {@link String} values specifying the query projection.
|
||||
* @return a {@link List} of {@link LuceneResultStruct} containing the query results.
|
||||
* @see org.apache.geode.cache.lucene.LuceneQueryProvider
|
||||
* @see org.apache.geode.cache.lucene.LuceneResultStruct
|
||||
* @see #query(LuceneQueryProvider, int, String...)
|
||||
* @see java.util.List
|
||||
@@ -126,6 +127,7 @@ public interface LuceneOperations {
|
||||
* @param resultLimit limit on the number of query results to return.
|
||||
* @param projectionFields array of {@link String} values specifying the query projection.
|
||||
* @return a {@link List} of {@link LuceneResultStruct} containing the query results.
|
||||
* @see org.apache.geode.cache.lucene.LuceneQueryProvider
|
||||
* @see org.apache.geode.cache.lucene.LuceneResultStruct
|
||||
* @see java.util.List
|
||||
*/
|
||||
@@ -144,6 +146,7 @@ public interface LuceneOperations {
|
||||
* @param pageSize number of results per page.
|
||||
* @param projectionFields array of {@link String} values specifying the query projection.
|
||||
* @return a {@link PageableLuceneQueryResults} data structure containing the results of the Lucene query.
|
||||
* @see org.apache.geode.cache.lucene.LuceneQueryProvider
|
||||
* @see org.apache.geode.cache.lucene.PageableLuceneQueryResults
|
||||
*/
|
||||
<K, V> PageableLuceneQueryResults<K, V> query(LuceneQueryProvider queryProvider,
|
||||
@@ -192,6 +195,7 @@ public interface LuceneOperations {
|
||||
* @param queryProvider {@link LuceneQueryProvider} is a provider implementation supplying the Lucene query
|
||||
* to execute as well as de/serialize to distribute across the cluster.
|
||||
* @return a {@link Collection} of keys matching the Lucene query clause (predicate).
|
||||
* @see org.apache.geode.cache.lucene.LuceneQueryProvider
|
||||
* @see #queryForKeys(String, String, int)
|
||||
* @see java.util.Collection
|
||||
*/
|
||||
@@ -208,6 +212,7 @@ public interface LuceneOperations {
|
||||
* to execute as well as de/serialize to distribute across the cluster.
|
||||
* @param resultLimit limit on the number of keys returned.
|
||||
* @return a {@link Collection} of keys matching the Lucene query clause (predicate).
|
||||
* @see org.apache.geode.cache.lucene.LuceneQueryProvider
|
||||
* @see #queryForKeys(String, String, int)
|
||||
* @see java.util.Collection
|
||||
*/
|
||||
@@ -256,6 +261,7 @@ public interface LuceneOperations {
|
||||
* @param queryProvider {@link LuceneQueryProvider} is a provider implementation supplying the Lucene query
|
||||
* to execute as well as de/serialize to distribute across the cluster.
|
||||
* @return a {@link Collection} of values matching Lucene query clause (predicate).
|
||||
* @see org.apache.geode.cache.lucene.LuceneQueryProvider
|
||||
* @see #queryForValues(String, String, int)
|
||||
* @see java.util.Collection
|
||||
*/
|
||||
@@ -272,6 +278,7 @@ public interface LuceneOperations {
|
||||
* to execute as well as de/serialize to distribute across the cluster.
|
||||
* @param resultLimit limit on the number of values returned.
|
||||
* @return a {@link Collection} of values matching Lucene query clause (predicate).
|
||||
* @see org.apache.geode.cache.lucene.LuceneQueryProvider
|
||||
* @see #queryForValues(String, String, int)
|
||||
* @see java.util.Collection
|
||||
*/
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.search.lucene;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.geode.cache.lucene.LuceneQueryProvider;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
/**
|
||||
* The {@link MappingLuceneOperations} interface defines a contract for implementing classes to execute
|
||||
* Lucene data access operations and mapping the results to entity domain {@link Class types}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.domain.Page
|
||||
* @see org.springframework.data.gemfire.search.lucene.LuceneOperations
|
||||
* @see org.apache.geode.cache.lucene.LuceneQueryProvider
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public interface MappingLuceneOperations extends LuceneOperations {
|
||||
|
||||
default <T> List<T> query(Class<T> entityType, String query, String defaultField,
|
||||
String... projectionFields) {
|
||||
|
||||
return query(entityType, query, defaultField, DEFAULT_RESULT_LIMIT, projectionFields);
|
||||
}
|
||||
|
||||
<T> List<T> query(Class<T> entityType, String query, String defaultField,
|
||||
int resultLimit, String... projectionFields);
|
||||
|
||||
<T> Page<T> query(Class<T> entityType, String query, String defaultField,
|
||||
int resultLimit, int pageSize, String... projectionFields);
|
||||
|
||||
default <T> List<T> query(Class<T> entityType, LuceneQueryProvider queryProvider,
|
||||
String... projectionFields) {
|
||||
|
||||
return query(entityType, queryProvider, DEFAULT_RESULT_LIMIT, projectionFields);
|
||||
}
|
||||
|
||||
<T> List<T> query(Class<T> entityType, LuceneQueryProvider queryProvider,
|
||||
int resultLimit, String... projectionFields);
|
||||
|
||||
<T> Page<T> query(Class<T> entityType, LuceneQueryProvider queryProvider,
|
||||
int resultLimit, int pageSize, String... projectionFields);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.search.lucene;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.lucene.LuceneIndex;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.data.gemfire.search.lucene.support.PdxInstanceMethodInterceptorFactory;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
|
||||
/**
|
||||
* {@link ProjectingLuceneAccessor} is an abstract class supporting implementations of
|
||||
* the {@link ProjectingLuceneOperations} interface encapsulating common functionality
|
||||
* necessary to execute Lucene queries and work with application domain object views.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.lang.ClassLoader
|
||||
* @see org.springframework.beans.factory.BeanClassLoaderAware
|
||||
* @see org.springframework.beans.factory.BeanFactory
|
||||
* @see org.springframework.beans.factory.BeanFactoryAware
|
||||
* @see org.springframework.data.gemfire.search.lucene.ProjectingLuceneOperations
|
||||
* @see org.springframework.data.gemfire.search.lucene.support.PdxInstanceMethodInterceptorFactory
|
||||
* @see org.springframework.data.projection.ProjectionFactory
|
||||
* @see org.springframework.data.projection.SpelAwareProxyProjectionFactory
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see org.apache.geode.cache.lucene.LuceneIndex
|
||||
* @see org.apache.geode.cache.lucene.LuceneQuery
|
||||
* @see org.apache.geode.cache.lucene.LuceneQueryFactory
|
||||
* @see org.apache.geode.cache.lucene.LuceneService
|
||||
* @see org.apache.geode.cache.lucene.LuceneServiceProvider
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public abstract class ProjectingLuceneAccessor extends LuceneTemplate
|
||||
implements BeanClassLoaderAware, BeanFactoryAware, ProjectingLuceneOperations {
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private ClassLoader beanClassLoader;
|
||||
|
||||
private ProjectionFactory projectionFactory;
|
||||
|
||||
/**
|
||||
* Constructs a default, uninitialized instance of the {@link ProjectingLuceneAccessor}.
|
||||
*/
|
||||
public ProjectingLuceneAccessor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of the {@link ProjectingLuceneAccessor} initialized with the given {@link LuceneIndex}
|
||||
* used to perform Lucene queries (searches).
|
||||
*
|
||||
* @param luceneIndex {@link LuceneIndex} used in Lucene queries.
|
||||
* @see org.apache.geode.cache.lucene.LuceneIndex
|
||||
*/
|
||||
public ProjectingLuceneAccessor(LuceneIndex luceneIndex) {
|
||||
super(luceneIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of the {@link ProjectingLuceneAccessor} initialized with the given Lucene index name
|
||||
* and {@link Region} reference upon which Lucene queries are executed.
|
||||
*
|
||||
* @param indexName {@link String} containing the name of the {@link LuceneIndex} used in Lucene queries.
|
||||
* @param region {@link Region} on which Lucene queries are executed.
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
public ProjectingLuceneAccessor(String indexName, Region<?, ?> region) {
|
||||
super(indexName, region);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of the {@link ProjectingLuceneAccessor} initialized with the given Lucene index name
|
||||
* and {@link Region} reference upon which Lucene queries are executed.
|
||||
*
|
||||
* @param indexName {@link String} containing the name of the {@link LuceneIndex} used in Lucene queries.
|
||||
* @param regionPath {@link String} containing the name of the {@link Region} on which Lucene queries are executed.
|
||||
*/
|
||||
public ProjectingLuceneAccessor(String indexName, String regionPath) {
|
||||
super(indexName, regionPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @see #resolveProjectionFactory()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
super.afterPropertiesSet();
|
||||
this.projectionFactory = resolveProjectionFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe method to resolve the Spring Data {@link ProjectionFactory} used to create projections
|
||||
* out of the Lucene query results.
|
||||
*
|
||||
* @return a resolved instance of the Spring Data {@link ProjectionFactory} used to create projections
|
||||
* out of the Lucene query results.
|
||||
* @see org.springframework.data.projection.ProjectionFactory
|
||||
* @see org.springframework.data.projection.SpelAwareProxyProjectionFactory
|
||||
* @see #afterPropertiesSet()
|
||||
*/
|
||||
protected ProjectionFactory resolveProjectionFactory() {
|
||||
return Optional.ofNullable(getProjectionFactory()).orElseGet(() -> {
|
||||
SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory();
|
||||
projectionFactory.setBeanClassLoader(getBeanClassLoader());
|
||||
projectionFactory.setBeanFactory(getBeanFactory());
|
||||
projectionFactory.registerMethodInvokerFactory(PdxInstanceMethodInterceptorFactory.INSTANCE);
|
||||
return setThenGetProjectionFactory(projectionFactory);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader beanClassLoader) {
|
||||
this.beanClassLoader = beanClassLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the {@link ClassLoader} used by the Spring {@link BeanFactory container}
|
||||
* to load bean class definitions.
|
||||
*
|
||||
* @return a reference to the {@link ClassLoader} used by the Spring {@link BeanFactory container}
|
||||
* to load bean class definitions.
|
||||
* @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(ClassLoader)
|
||||
* @see java.lang.ClassLoader
|
||||
*/
|
||||
protected ClassLoader getBeanClassLoader() {
|
||||
return this.beanClassLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the Spring {@link BeanFactory container}.
|
||||
*
|
||||
* @return a reference to the Spring {@link BeanFactory container}.
|
||||
* @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(BeanFactory)
|
||||
* @see org.springframework.beans.factory.BeanFactory
|
||||
*/
|
||||
protected BeanFactory getBeanFactory() {
|
||||
return this.beanFactory;
|
||||
}
|
||||
|
||||
protected ProjectionFactory setThenGetProjectionFactory(ProjectionFactory projectionFactory) {
|
||||
setProjectionFactory(projectionFactory);
|
||||
return getProjectionFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Spring Data {@link ProjectionFactory} used to create projections out of query results.
|
||||
*
|
||||
* @param projectionFactory Spring Data {@link ProjectionFactory} used to created projects out of query results.
|
||||
* @see org.springframework.data.projection.ProjectionFactory
|
||||
*/
|
||||
public void setProjectionFactory(ProjectionFactory projectionFactory) {
|
||||
this.projectionFactory = projectionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Spring Data {@link ProjectionFactory} used to create projections out of query results.
|
||||
*
|
||||
* @return the Spring Data {@link ProjectionFactory} used to created projects out of query results.
|
||||
* @see org.springframework.data.projection.ProjectionFactory
|
||||
*/
|
||||
protected ProjectionFactory getProjectionFactory() {
|
||||
return this.projectionFactory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.search.lucene;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.geode.cache.lucene.LuceneQueryProvider;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
/**
|
||||
* The {@link ProjectingLuceneOperations} interface defines a contract for implementing classes to execute
|
||||
* Lucene data access operations and mapping the results to entity domain {@link Class types}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.util.List
|
||||
* @see org.springframework.data.domain.Page
|
||||
* @see org.springframework.data.gemfire.search.lucene.LuceneOperations
|
||||
* @see org.apache.geode.cache.lucene.LuceneQueryProvider
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public interface ProjectingLuceneOperations extends LuceneOperations {
|
||||
|
||||
/**
|
||||
* Executes the given {@link String query} with the results projected as instances of
|
||||
* the {@link Class projectionType}.
|
||||
*
|
||||
* @param <T> {@link Class} type of the projection.
|
||||
* @param query Lucene {@link String query} to execute.
|
||||
* @param defaultField {@link String} specifying the default field used in Lucene queries when a field
|
||||
* is not explicitly defined in the Lucene query clause.
|
||||
* @param projectionType {@link Class} type of the individual elements in the query results.
|
||||
* @return a {@link List} of Lucene query results projected as instances of {@link Class projectionType}.
|
||||
* @see #query(String, String, int, Class)
|
||||
* @see java.util.List
|
||||
*/
|
||||
default <T> List<T> query(String query, String defaultField, Class<T> projectionType) {
|
||||
return query(query, defaultField, DEFAULT_RESULT_LIMIT, projectionType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given {@link String query} with the limited results projected as instances of
|
||||
* the {@link Class projectionType}.
|
||||
*
|
||||
* @param <T> {@link Class} type of the projection.
|
||||
* @param query Lucene {@link String query} to execute.
|
||||
* @param defaultField {@link String} specifying the default field used in Lucene queries when a field
|
||||
* is not explicitly defined in the Lucene query clause.
|
||||
* @param resultLimit limit on the number of query results to return.
|
||||
* @param projectionType {@link Class} type of the individual elements in the query results.
|
||||
* @return a {@link List} of Lucene query results projected as instances of {@link Class projectionType}.
|
||||
* @see #query(String, String, int, Class)
|
||||
* @see java.util.List
|
||||
*/
|
||||
<T> List<T> query(String query, String defaultField, int resultLimit, Class<T> projectionType);
|
||||
|
||||
/**
|
||||
* Executes the given {@link String query} with the limited results projected as instances of
|
||||
* the {@link Class projectionType}.
|
||||
*
|
||||
* @param <T> {@link Class} type of the projection.
|
||||
* @param query Lucene {@link String query} to execute.
|
||||
* @param defaultField {@link String} specifying the default field used in Lucene queries when a field
|
||||
* is not explicitly defined in the Lucene query clause.
|
||||
* @param resultLimit limit on the number of query results to return.
|
||||
* @param pageSize number of results on a {@link Page}.
|
||||
* @param projectionType {@link Class} type of the individual elements in the query results.
|
||||
* @return the first {@link Page} of results returned from the Lucene query.
|
||||
* @see org.springframework.data.domain.Page
|
||||
*/
|
||||
<T> Page<T> query(String query, String defaultField, int resultLimit, int pageSize, Class<T> projectionType);
|
||||
|
||||
/**
|
||||
* Executes the provided {@link String query} with the results projected as instances of
|
||||
* the {@link Class projectionType}.
|
||||
*
|
||||
* @param <T> {@link Class} type of the projection.
|
||||
* @param queryProvider {@link LuceneQueryProvider} providing the Lucene {@link String query} to execute.
|
||||
* @param projectionType {@link Class} type of the individual elements in the query results.
|
||||
* @return a {@link List} of Lucene query results projected as instances of {@link Class projectionType}.
|
||||
* @see org.apache.geode.cache.lucene.LuceneQueryProvider
|
||||
* @see #query(LuceneQueryProvider, int, Class)
|
||||
* @see java.util.List
|
||||
*/
|
||||
default <T> List<T> query(LuceneQueryProvider queryProvider, Class<T> projectionType) {
|
||||
return query(queryProvider, DEFAULT_RESULT_LIMIT, projectionType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the provided {@link String query} with the limited results projected as instances of
|
||||
* the {@link Class projectionType}.
|
||||
*
|
||||
* @param <T> {@link Class} type of the projection.
|
||||
* @param queryProvider {@link LuceneQueryProvider} providing the Lucene {@link String query} to execute.
|
||||
* @param resultLimit limit on the number of query results to return.
|
||||
* @param projectionType {@link Class} type of the individual elements in the query results.
|
||||
* @return a {@link List} of Lucene query results projected as instances of {@link Class projectionType}.
|
||||
* @see org.apache.geode.cache.lucene.LuceneQueryProvider
|
||||
* @see java.util.List
|
||||
*/
|
||||
<T> List<T> query(LuceneQueryProvider queryProvider, int resultLimit, Class<T> projectionType);
|
||||
|
||||
/**
|
||||
* Executes the provided {@link String query} with the limited results projected as instances of
|
||||
* the {@link Class projectionType}.
|
||||
*
|
||||
* @param <T> {@link Class} type of the projection.
|
||||
* @param queryProvider {@link LuceneQueryProvider} providing the Lucene {@link String query} to execute.
|
||||
* @param resultLimit limit on the number of query results to return.
|
||||
* @param pageSize number of results on a {@link Page}.
|
||||
* @param projectionType {@link Class} type of the individual elements in the query results.
|
||||
* @return the first {@link Page} of results returned from the Lucene query.
|
||||
* @see org.apache.geode.cache.lucene.LuceneQueryProvider
|
||||
* @see org.springframework.data.domain.Page
|
||||
*/
|
||||
<T> Page<T> query(LuceneQueryProvider queryProvider, int resultLimit, int pageSize, Class<T> projectionType);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.search.lucene;
|
||||
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newUnsupportedOperationException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.lucene.LuceneIndex;
|
||||
import org.apache.geode.cache.lucene.LuceneQueryProvider;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
/**
|
||||
* {@link ProjectingLuceneTemplate} is a Lucene data access operations class encapsulating functionality
|
||||
* for performing Lucene queries and other Lucene data access operations and returning the query results
|
||||
* as application-specific domain object views.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.search.lucene.ProjectingLuceneAccessor
|
||||
* @see org.springframework.data.gemfire.search.lucene.ProjectingLuceneOperations
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see org.apache.geode.cache.lucene.LuceneIndex
|
||||
* @see org.apache.geode.cache.lucene.LuceneQuery
|
||||
* @see org.apache.geode.cache.lucene.LuceneQueryFactory
|
||||
* @see org.apache.geode.cache.lucene.LuceneQueryProvider
|
||||
* @see org.apache.geode.cache.lucene.LuceneResultStruct
|
||||
* @see org.apache.geode.cache.lucene.PageableLuceneQueryResults
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ProjectingLuceneTemplate extends ProjectingLuceneAccessor {
|
||||
|
||||
/**
|
||||
* Constructs a default, uninitialized instance of the {@link ProjectingLuceneTemplate}.
|
||||
*/
|
||||
public ProjectingLuceneTemplate() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of the {@link ProjectingLuceneTemplate} initialized with the given {@link LuceneIndex}
|
||||
* used to perform Lucene queries (searches).
|
||||
*
|
||||
* @param luceneIndex {@link LuceneIndex} used in Lucene queries.
|
||||
* @see org.apache.geode.cache.lucene.LuceneIndex
|
||||
*/
|
||||
public ProjectingLuceneTemplate(LuceneIndex luceneIndex) {
|
||||
super(luceneIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of the {@link ProjectingLuceneTemplate} initialized with the given Lucene index name
|
||||
* and {@link Region} reference upon which Lucene queries are executed.
|
||||
*
|
||||
* @param indexName {@link String} containing the name of the {@link LuceneIndex} used in Lucene queries.
|
||||
* @param region {@link Region} on which Lucene queries are executed.
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
public ProjectingLuceneTemplate(String indexName, Region<?, ?> region) {
|
||||
super(indexName, region);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of the {@link ProjectingLuceneTemplate} initialized with the given Lucene index name
|
||||
* and {@link Region} reference upon which Lucene queries are executed.
|
||||
*
|
||||
* @param indexName {@link String} containing the name of the {@link LuceneIndex} used in Lucene queries.
|
||||
* @param regionPath {@link String} containing the name of the {@link Region} on which Lucene queries are executed.
|
||||
*/
|
||||
public ProjectingLuceneTemplate(String indexName, String regionPath) {
|
||||
super(indexName, regionPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public <T> List<T> query(String query, String defaultField, int resultLimit, Class<T> projectionType) {
|
||||
return query(query, defaultField, resultLimit).stream()
|
||||
.map(luceneResultStruct -> getProjectionFactory().createProjection(projectionType, luceneResultStruct.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public <T> Page<T> query(String query, String defaultField, int resultLimit, int pageSize,
|
||||
Class<T> projectionType) {
|
||||
|
||||
throw newUnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public <T> List<T> query(LuceneQueryProvider queryProvider, int resultLimit, Class<T> projectionType) {
|
||||
return query(queryProvider, resultLimit).stream()
|
||||
.map(luceneResultStruct -> getProjectionFactory().createProjection(projectionType, luceneResultStruct.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public <T> Page<T> query(LuceneQueryProvider queryProvider, int resultLimit, int pageSize,
|
||||
Class<T> projectionType) {
|
||||
|
||||
throw newUnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.search.lucene.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.geode.pdx.PdxInstance;
|
||||
import org.apache.geode.pdx.WritablePdxInstance;
|
||||
import org.springframework.data.projection.Accessor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* The {@link PdxInstanceMethodInterceptor} class is a {@link MethodInterceptor} wrapping a {@link PdxInstance}
|
||||
* to back a proxy during intercepted method invocations.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.aopalliance.intercept.MethodInterceptor
|
||||
* @see org.apache.geode.pdx.PdxInstance
|
||||
* @see org.apache.geode.pdx.WritablePdxInstance
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class PdxInstanceMethodInterceptor implements MethodInterceptor {
|
||||
|
||||
private PdxInstance source;
|
||||
|
||||
/**
|
||||
* Factory method used to construct an instance of {@link PdxInstanceMethodInterceptor} initialized with
|
||||
* the given {@link Object source}.
|
||||
*
|
||||
* @param source {@link Object} serving as the source to back the proxy in method invocations.
|
||||
* Source must be an instance of {@link PdxInstance}.
|
||||
* @return a new instance of {@link PdxInstanceMethodInterceptor} initialized with the given {@link Object source}.
|
||||
* @throws IllegalArgumentException if {@link Object source} is not an instance of {@link PdxInstance}.
|
||||
* @see #newPdxInstanceMethodInterceptor(PdxInstance)
|
||||
*/
|
||||
public static PdxInstanceMethodInterceptor newPdxInstanceMethodInterceptor(Object source) {
|
||||
Assert.isInstanceOf(PdxInstance.class, source, () -> String.format("Source [%1$s] is not an instance of [%2$s]",
|
||||
ObjectUtils.nullSafeClassName(source), PdxInstance.class.getName()));
|
||||
|
||||
return newPdxInstanceMethodInterceptor((PdxInstance) source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method used to construct an instance of {@link PdxInstanceMethodInterceptor} initialized
|
||||
* with the given {@link PdxInstance}.
|
||||
*
|
||||
* @param source {@link PdxInstance} serving as the source to back the proxy in method invocations.
|
||||
* Source must not be {@literal null}.
|
||||
* @return a new instance of {@link PdxInstanceMethodInterceptor} initialized with
|
||||
* the given {@link PdxInstance source}.
|
||||
* @throws IllegalArgumentException if {@link PdxInstance source} is {@literal null}.
|
||||
* @see #PdxInstanceMethodInterceptor(PdxInstance)
|
||||
* @see org.apache.geode.pdx.PdxInstance
|
||||
*/
|
||||
public static PdxInstanceMethodInterceptor newPdxInstanceMethodInterceptor(PdxInstance source) {
|
||||
return new PdxInstanceMethodInterceptor(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of {@link PdxInstanceMethodInterceptor} initialized with
|
||||
* the given {@link PdxInstance source}.
|
||||
*
|
||||
* @param source {@link PdxInstance} used as the source to back the proxy in method invocations.
|
||||
* @throws IllegalArgumentException if {@link PdxInstance source} is {@literal null}.
|
||||
* @see org.apache.geode.pdx.PdxInstance
|
||||
*/
|
||||
public PdxInstanceMethodInterceptor(PdxInstance source) {
|
||||
Assert.notNull(source, "Source must not be null");
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link PdxInstance source} backing the proxy for intercepted method invocations.
|
||||
*
|
||||
* @return the {@link PdxInstance source} backing the proxy for intercepted method invocations.
|
||||
* @see org.apache.geode.pdx.PdxInstance
|
||||
*/
|
||||
protected PdxInstance getSource() {
|
||||
return this.source;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
Method method = invocation.getMethod();
|
||||
|
||||
if (ReflectionUtils.isObjectMethod(method)) {
|
||||
return invocation.proceed();
|
||||
}
|
||||
else {
|
||||
Accessor methodAccessor = new Accessor(method);
|
||||
PdxInstance pdxInstance = getSource();
|
||||
String propertyName = methodAccessor.getPropertyName();
|
||||
|
||||
Assert.state(pdxInstance.hasField(propertyName), () -> String.format(
|
||||
"Source [%1$s] does not contain field with name [%2$s]", pdxInstance, propertyName));
|
||||
|
||||
if (methodAccessor.isGetter()) {
|
||||
return pdxInstance.getField(propertyName);
|
||||
}
|
||||
else { // is setter
|
||||
Assert.isTrue(invocation.getArguments().length == 1, () ->
|
||||
String.format("Invoked setter method [%1$s] must expect exactly 1 argument; Arguments were [%2$s]",
|
||||
method.getName(), Arrays.toString(invocation.getArguments())));
|
||||
|
||||
Object value = invocation.getArguments()[0];
|
||||
|
||||
WritablePdxInstance writablePdxInstance = pdxInstance.createWriter();
|
||||
|
||||
Assert.state(writablePdxInstance != null, () -> String.format(
|
||||
"No writer for PdxInstance [%1$s] was found for setting field [%2$s] to value [%3$s]",
|
||||
pdxInstance, propertyName, value));
|
||||
|
||||
writablePdxInstance.setField(propertyName, value);
|
||||
|
||||
this.source = writablePdxInstance;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.search.lucene.support;
|
||||
|
||||
import static org.springframework.data.gemfire.search.lucene.support.PdxInstanceMethodInterceptor.newPdxInstanceMethodInterceptor;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.apache.geode.pdx.PdxInstance;
|
||||
import org.springframework.data.projection.MethodInterceptorFactory;
|
||||
|
||||
/**
|
||||
* The {@link PdxInstanceMethodInterceptorFactory} class is a Spring Data {@link MethodInterceptorFactory} used to
|
||||
* identify {@link PdxInstance} types and instantiates an instance of the {@link PdxInstanceMethodInterceptor}
|
||||
* in order to intercept and handle invocations on the {@link PdxInstance} for the proxied projection.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.aopalliance.intercept.MethodInterceptor
|
||||
* @see org.apache.geode.pdx.PdxInstance
|
||||
* @see org.springframework.data.gemfire.search.lucene.support.PdxInstanceMethodInterceptor
|
||||
* @see org.springframework.data.projection.MethodInterceptorFactory
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public enum PdxInstanceMethodInterceptorFactory implements MethodInterceptorFactory {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public MethodInterceptor createMethodInterceptor(Object source, Class<?> targetType) {
|
||||
return newPdxInstanceMethodInterceptor(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Object source, Class<?> targetType) {
|
||||
return PdxInstance.class.isInstance(source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.search.lucene.support;
|
||||
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newUnsupportedOperationException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.geode.cache.lucene.LuceneQueryProvider;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.gemfire.search.lucene.ProjectingLuceneAccessor;
|
||||
import org.springframework.data.gemfire.util.RuntimeExceptionFactory;
|
||||
|
||||
/**
|
||||
* {@link ProjectingLuceneAccessorSupport} is a {@link ProjectingLuceneAccessor} class implementation providing support
|
||||
* for extending classes.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.search.lucene.ProjectingLuceneAccessor
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class ProjectingLuceneAccessorSupport extends ProjectingLuceneAccessor {
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public <T> List<T> query(String query, String defaultField, int resultLimit, Class<T> projectionType) {
|
||||
throw newUnsupportedOperationException(RuntimeExceptionFactory.NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public <T> Page<T> query(String query, String defaultField, int resultLimit, int pageSize,
|
||||
Class<T> projectionType) {
|
||||
|
||||
throw newUnsupportedOperationException(RuntimeExceptionFactory.NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public <T> List<T> query(LuceneQueryProvider queryProvider, int resultLimit, Class<T> projectionType) {
|
||||
throw newUnsupportedOperationException(RuntimeExceptionFactory.NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public <T> Page<T> query(LuceneQueryProvider queryProvider, int resultLimit, int pageSize,
|
||||
Class<T> projectionType) {
|
||||
|
||||
throw newUnsupportedOperationException(RuntimeExceptionFactory.NOT_IMPLEMENTED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.search.lucene.support;
|
||||
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newUnsupportedOperationException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.geode.cache.lucene.LuceneQueryProvider;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.gemfire.search.lucene.ProjectingLuceneOperations;
|
||||
import org.springframework.data.gemfire.util.RuntimeExceptionFactory;
|
||||
|
||||
/**
|
||||
* {@link ProjectingLuceneOperationsSupport} is a abstract supporting class for implementations
|
||||
* of the {@link ProjectingLuceneOperations} interface.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.search.lucene.ProjectingLuceneOperations
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class ProjectingLuceneOperationsSupport extends LuceneOperationsSupport
|
||||
implements ProjectingLuceneOperations {
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public <T> List<T> query(String query, String defaultField, int resultLimit, Class<T> projectionType) {
|
||||
throw newUnsupportedOperationException(RuntimeExceptionFactory.NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public <T> Page<T> query(String query, String defaultField, int resultLimit, int pageSize,
|
||||
Class<T> projectionType) {
|
||||
|
||||
throw newUnsupportedOperationException(RuntimeExceptionFactory.NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public <T> List<T> query(LuceneQueryProvider queryProvider, int resultLimit, Class<T> projectionType) {
|
||||
throw newUnsupportedOperationException(RuntimeExceptionFactory.NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public <T> Page<T> query(LuceneQueryProvider queryProvider, int resultLimit, int pageSize,
|
||||
Class<T> projectionType) {
|
||||
|
||||
throw newUnsupportedOperationException(RuntimeExceptionFactory.NOT_IMPLEMENTED);
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,11 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
* @see org.mockito.Spy
|
||||
* @see org.mockito.junit.MockitoJUnitRunner
|
||||
* @see org.springframework.data.gemfire.search.lucene.LuceneTemplate
|
||||
* @see org.apache.geode.cache.lucene.LuceneQuery
|
||||
* @see org.apache.geode.cache.lucene.LuceneQueryFactory
|
||||
* @see org.apache.geode.cache.lucene.LuceneQueryProvider
|
||||
* @see org.apache.geode.cache.lucene.LuceneResultStruct
|
||||
* @see org.apache.geode.cache.lucene.PageableLuceneQueryResults
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.search.lucene;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.lucene.LuceneIndex;
|
||||
import org.apache.geode.cache.lucene.LuceneService;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.data.gemfire.search.lucene.support.ProjectingLuceneAccessorSupport;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ProjectingLuceneAccessor}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.junit.runner.RunWith
|
||||
* @see org.mockito.Mock
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.mockito.Spy
|
||||
* @see org.mockito.junit.MockitoJUnitRunner
|
||||
* @see org.springframework.data.gemfire.search.lucene.ProjectingLuceneAccessor
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ProjectingLuceneAccessorUnitTests {
|
||||
|
||||
@Mock
|
||||
private BeanFactory mockBeanFactory;
|
||||
|
||||
@Mock
|
||||
private GemFireCache mockCache;
|
||||
|
||||
@Mock
|
||||
private ClassLoader mockClassLoader;
|
||||
|
||||
@Mock
|
||||
private LuceneIndex mockLuceneIndex;
|
||||
|
||||
@Mock
|
||||
private LuceneService mockLuceneService;
|
||||
|
||||
// SUT
|
||||
private ProjectingLuceneAccessor projectingLuceneAccessor;
|
||||
|
||||
@Mock
|
||||
private ProjectionFactory mockProjectionFactory;
|
||||
|
||||
@Mock
|
||||
private Region<?, ?> mockRegion;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
projectingLuceneAccessor = spy(new ProjectingLuceneAccessorSupport() {});
|
||||
|
||||
doReturn(mockCache).when(projectingLuceneAccessor).resolveCache();
|
||||
doReturn(mockLuceneService).when(projectingLuceneAccessor).resolveLuceneService();
|
||||
doReturn("MockLuceneIndex").when(projectingLuceneAccessor).resolveIndexName();
|
||||
doReturn("/Example").when(projectingLuceneAccessor).resolveRegionPath();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterPropertiesSetInitializesTheProjectingLuceneAccessorCorrectly() throws Exception {
|
||||
doReturn(mockProjectionFactory).when(projectingLuceneAccessor).resolveProjectionFactory();
|
||||
|
||||
projectingLuceneAccessor.afterPropertiesSet();
|
||||
|
||||
assertThat(projectingLuceneAccessor.getProjectionFactory()).isSameAs(mockProjectionFactory);
|
||||
|
||||
verify(projectingLuceneAccessor, times(1)).resolveProjectionFactory();
|
||||
verifyZeroInteractions(mockProjectionFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void projectingLuceneAccessorIsInitializedCorrectly() {
|
||||
assertThat(projectingLuceneAccessor.getBeanClassLoader()).isNull();
|
||||
assertThat(projectingLuceneAccessor.getBeanFactory()).isNull();
|
||||
assertThat(projectingLuceneAccessor.getCache()).isNull();
|
||||
assertThat(projectingLuceneAccessor.getIndexName()).isNull();
|
||||
assertThat(projectingLuceneAccessor.getLuceneIndex()).isNull();
|
||||
assertThat(projectingLuceneAccessor.getLuceneService()).isNull();
|
||||
assertThat(projectingLuceneAccessor.getProjectionFactory()).isNull();
|
||||
assertThat(projectingLuceneAccessor.getRegion()).isNull();
|
||||
assertThat(projectingLuceneAccessor.getRegionPath()).isNull();
|
||||
|
||||
projectingLuceneAccessor.setBeanClassLoader(mockClassLoader);
|
||||
projectingLuceneAccessor.setBeanFactory(mockBeanFactory);
|
||||
projectingLuceneAccessor.setCache(mockCache);
|
||||
projectingLuceneAccessor.setIndexName("TestLuceneIdx");
|
||||
projectingLuceneAccessor.setLuceneIndex(mockLuceneIndex);
|
||||
projectingLuceneAccessor.setLuceneService(mockLuceneService);
|
||||
projectingLuceneAccessor.setProjectionFactory(mockProjectionFactory);
|
||||
projectingLuceneAccessor.setRegion(mockRegion);
|
||||
projectingLuceneAccessor.setRegionPath("/Example");
|
||||
|
||||
assertThat(projectingLuceneAccessor.getBeanClassLoader()).isEqualTo(mockClassLoader);
|
||||
assertThat(projectingLuceneAccessor.getBeanFactory()).isEqualTo(mockBeanFactory);
|
||||
assertThat(projectingLuceneAccessor.getCache()).isEqualTo(mockCache);
|
||||
assertThat(projectingLuceneAccessor.getIndexName()).isEqualTo("TestLuceneIdx");
|
||||
assertThat(projectingLuceneAccessor.getLuceneIndex()).isEqualTo(mockLuceneIndex);
|
||||
assertThat(projectingLuceneAccessor.getLuceneService()).isEqualTo(mockLuceneService);
|
||||
assertThat(projectingLuceneAccessor.getProjectionFactory()).isEqualTo(mockProjectionFactory);
|
||||
assertThat(projectingLuceneAccessor.getRegion()).isEqualTo(mockRegion);
|
||||
assertThat(projectingLuceneAccessor.getRegionPath()).isEqualTo("/Example");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setThenGetProjectionFactoryIsCorrect() {
|
||||
assertThat(projectingLuceneAccessor.getProjectionFactory()).isNull();
|
||||
assertThat(projectingLuceneAccessor.setThenGetProjectionFactory(mockProjectionFactory))
|
||||
.isSameAs(mockProjectionFactory);
|
||||
assertThat(projectingLuceneAccessor.getProjectionFactory()).isSameAs(mockProjectionFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveProjectFactoryReturnsProvidedProjectFactory() {
|
||||
assertThat(projectingLuceneAccessor.getProjectionFactory()).isNull();
|
||||
|
||||
projectingLuceneAccessor.setProjectionFactory(mockProjectionFactory);
|
||||
|
||||
assertThat(projectingLuceneAccessor.getProjectionFactory()).isSameAs(mockProjectionFactory);
|
||||
assertThat(projectingLuceneAccessor.resolveProjectionFactory()).isSameAs(mockProjectionFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveProjectionFactoryCreatesNewProjectionFactory() {
|
||||
assertThat(projectingLuceneAccessor.getProjectionFactory()).isNull();
|
||||
|
||||
ProjectionFactory projectionFactory = projectingLuceneAccessor.resolveProjectionFactory();
|
||||
|
||||
assertThat(projectionFactory).isInstanceOf(SpelAwareProxyProjectionFactory.class);
|
||||
assertThat(projectingLuceneAccessor.getProjectionFactory()).isSameAs(projectionFactory);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.search.lucene;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.apache.geode.cache.lucene.LuceneQueryProvider;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ProjectingLuceneOperations}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.junit.runner.RunWith
|
||||
* @see org.mockito.Mock
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.mockito.junit.MockitoJUnitRunner
|
||||
* @see org.springframework.data.gemfire.search.lucene.ProjectingLuceneOperations
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ProjectingLuceneOperationsUnitTests {
|
||||
|
||||
@Mock
|
||||
private LuceneQueryProvider mockQueryProvider;
|
||||
|
||||
@Mock
|
||||
private TestProjectingLuceneOperations projectingLuceneOperations;
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void stringQueryCallsQueryWithResultLimit() {
|
||||
when(projectingLuceneOperations.query(anyString(), anyString(), any(Class.class))).thenCallRealMethod();
|
||||
|
||||
projectingLuceneOperations.query("title : Up Shit Creek Without A Paddle",
|
||||
"title", Book.class);
|
||||
|
||||
verify(projectingLuceneOperations, times(1))
|
||||
.query(eq("title : Up Shit Creek Without A Paddle"), eq("title"),
|
||||
eq(ProjectingLuceneOperations.DEFAULT_RESULT_LIMIT), eq(Book.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void queryProviderQueryCallsQueryWithResultLimit() {
|
||||
when(projectingLuceneOperations.query(any(LuceneQueryProvider.class), any(Class.class)))
|
||||
.thenCallRealMethod();
|
||||
|
||||
projectingLuceneOperations.query(mockQueryProvider, Book.class);
|
||||
|
||||
verify(projectingLuceneOperations, times(1))
|
||||
.query(eq(mockQueryProvider), eq(ProjectingLuceneOperations.DEFAULT_RESULT_LIMIT), eq(Book.class));
|
||||
}
|
||||
|
||||
static class Book {}
|
||||
|
||||
abstract class TestProjectingLuceneOperations implements ProjectingLuceneOperations {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.search.lucene;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.assertj.core.api.Java6Assertions.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.geode.cache.lucene.LuceneQueryProvider;
|
||||
import org.apache.geode.cache.lucene.LuceneResultStruct;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.gemfire.mapping.annotation.Region;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ProjectingLuceneTemplate}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.junit.runner.RunWith
|
||||
* @see org.mockito.Mock
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.mockito.Spy
|
||||
* @see org.mockito.junit.MockitoJUnitRunner
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ProjectingLuceneTemplateUnitTests {
|
||||
|
||||
@Mock
|
||||
private LuceneQueryProvider mockQueryProvider;
|
||||
|
||||
// SUT
|
||||
private ProjectingLuceneTemplate luceneTemplate;
|
||||
|
||||
@Mock
|
||||
private ProjectionFactory mockProjectionFactory;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
luceneTemplate = spy(new ProjectingLuceneTemplate());
|
||||
luceneTemplate.setProjectionFactory(mockProjectionFactory);
|
||||
}
|
||||
|
||||
protected LuceneResultStruct<Long, String> mockLuceneResultStruct(Book book) {
|
||||
return mockLuceneResultStruct(book.getIsbn(), book.getTitle());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <K, V> LuceneResultStruct<K, V> mockLuceneResultStruct(K key, V value) {
|
||||
LuceneResultStruct<K, V> mockLuceneResultStruct =
|
||||
mock(LuceneResultStruct.class, String.format("MockLuceneResultStruct%s", key));
|
||||
|
||||
when(mockLuceneResultStruct.getKey()).thenReturn(key);
|
||||
when(mockLuceneResultStruct.getValue()).thenReturn(value);
|
||||
|
||||
return mockLuceneResultStruct;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryWithString() {
|
||||
List<Book> books = asList(
|
||||
Book.newBook(4L, "Star Wars - Episode IV New Hope"),
|
||||
Book.newBook(5L, "Star Wars - Episode V Empire Strikes Back"),
|
||||
Book.newBook(6L, "Star Wars - Episode VI Return of the Jedi")
|
||||
);
|
||||
|
||||
List<LuceneResultStruct<Long, String>> queryResults = asList(
|
||||
mockLuceneResultStruct(books.get(0)),
|
||||
mockLuceneResultStruct(books.get(1)),
|
||||
mockLuceneResultStruct(books.get(2))
|
||||
);
|
||||
|
||||
doReturn(queryResults).when(luceneTemplate).query(anyString(), anyString(), anyInt());
|
||||
|
||||
when(mockProjectionFactory.createProjection(eq(Book.class), anyString())).thenAnswer( invocationOnMock ->
|
||||
books.stream().filter(book ->
|
||||
book.getTitle().equals(invocationOnMock.getArgument(1)))
|
||||
.findFirst().orElse(null)
|
||||
);
|
||||
|
||||
assertThat(luceneTemplate.query("title : Star Wars Episode *V*", "title", Book.class))
|
||||
.containsAll(books);
|
||||
|
||||
verify(luceneTemplate, times(1))
|
||||
.query(eq("title : Star Wars Episode *V*"), eq("title"),
|
||||
eq(ProjectingLuceneTemplate.DEFAULT_RESULT_LIMIT), eq(Book.class));
|
||||
verify(mockProjectionFactory, times(1))
|
||||
.createProjection(eq(Book.class), eq(books.get(0).getTitle()));
|
||||
verify(mockProjectionFactory, times(1))
|
||||
.createProjection(eq(Book.class), eq(books.get(1).getTitle()));
|
||||
verify(mockProjectionFactory, times(1))
|
||||
.createProjection(eq(Book.class), eq(books.get(2).getTitle()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryWithQueryProvider() {
|
||||
List<Book> books = asList(
|
||||
Book.newBook(1L, "Star Wars - Episode I Phantom Menace"),
|
||||
Book.newBook(2L, "Star Wars - Episode II Attack of the Clones"),
|
||||
Book.newBook(3L, "Star Wars - Episode III Revenge of the Sith")
|
||||
);
|
||||
|
||||
List<LuceneResultStruct<Long, String>> queryResults = asList(
|
||||
mockLuceneResultStruct(books.get(0)),
|
||||
mockLuceneResultStruct(books.get(1)),
|
||||
mockLuceneResultStruct(books.get(2))
|
||||
);
|
||||
|
||||
doReturn(queryResults).when(luceneTemplate).query(any(LuceneQueryProvider.class), anyInt());
|
||||
|
||||
when(mockProjectionFactory.createProjection(eq(Book.class), anyString())).thenAnswer( invocationOnMock ->
|
||||
books.stream().filter(book ->
|
||||
book.getTitle().equals(invocationOnMock.getArgument(1)))
|
||||
.findFirst().orElse(null)
|
||||
);
|
||||
|
||||
assertThat(luceneTemplate.query(mockQueryProvider, Book.class)).containsAll(books);
|
||||
|
||||
verify(luceneTemplate, times(1))
|
||||
.query(eq(mockQueryProvider), eq(ProjectingLuceneTemplate.DEFAULT_RESULT_LIMIT), eq(Book.class));
|
||||
verify(mockProjectionFactory, times(1))
|
||||
.createProjection(eq(Book.class), eq(books.get(0).getTitle()));
|
||||
verify(mockProjectionFactory, times(1))
|
||||
.createProjection(eq(Book.class), eq(books.get(1).getTitle()));
|
||||
verify(mockProjectionFactory, times(1))
|
||||
.createProjection(eq(Book.class), eq(books.get(2).getTitle()));
|
||||
}
|
||||
|
||||
@Data
|
||||
@Region("Books")
|
||||
@RequiredArgsConstructor(staticName = "newBook")
|
||||
static class Book {
|
||||
@NonNull @Id Long isbn;
|
||||
@NonNull String title;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.search.lucene.support;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.apache.geode.pdx.PdxInstance;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link PdxInstanceMethodInterceptorFactory}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.junit.runner.RunWith
|
||||
* @see org.mockito.Mock
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.mockito.junit.MockitoJUnitRunner
|
||||
* @see org.aopalliance.intercept.MethodInterceptor
|
||||
* @see org.apache.geode.pdx.PdxInstance
|
||||
* @see org.springframework.data.gemfire.search.lucene.support.PdxInstanceMethodInterceptorFactory
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PdxInstanceMethodInterceptorFactoryUnitTests {
|
||||
|
||||
@Mock
|
||||
private PdxInstance mockSource;
|
||||
|
||||
@Test
|
||||
public void createMethodInterceptorIsSuccessful() {
|
||||
MethodInterceptor methodInterceptor =
|
||||
PdxInstanceMethodInterceptorFactory.INSTANCE.createMethodInterceptor(mockSource, Object.class);
|
||||
|
||||
assertThat(methodInterceptor).isInstanceOf(PdxInstanceMethodInterceptor.class);
|
||||
assertThat(((PdxInstanceMethodInterceptor) methodInterceptor).getSource()).isSameAs(mockSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsPdxInstances() {
|
||||
assertThat(PdxInstanceMethodInterceptorFactory.INSTANCE.supports(mockSource, Object.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotSupportNonPdxInstances() {
|
||||
assertThat(PdxInstanceMethodInterceptorFactory.INSTANCE.supports(new Object(), Object.class)).isFalse();
|
||||
assertThat(PdxInstanceMethodInterceptorFactory.INSTANCE.supports(Boolean.TRUE, Object.class)).isFalse();
|
||||
assertThat(PdxInstanceMethodInterceptorFactory.INSTANCE.supports('X', Object.class)).isFalse();
|
||||
assertThat(PdxInstanceMethodInterceptorFactory.INSTANCE.supports(2, Object.class)).isFalse();
|
||||
assertThat(PdxInstanceMethodInterceptorFactory.INSTANCE.supports(Math.PI, Object.class)).isFalse();
|
||||
assertThat(PdxInstanceMethodInterceptorFactory.INSTANCE.supports("test", Object.class)).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.search.lucene.support;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.data.gemfire.search.lucene.support.PdxInstanceMethodInterceptor.newPdxInstanceMethodInterceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.geode.pdx.PdxInstance;
|
||||
import org.apache.geode.pdx.WritablePdxInstance;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link PdxInstanceMethodInterceptor}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.junit.runner.RunWith
|
||||
* @see org.mockito.Mock
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.mockito.junit.MockitoJUnitRunner
|
||||
* @see org.springframework.data.gemfire.search.lucene.support.PdxInstanceMethodInterceptor
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PdxInstanceMethodInterceptorUnitTests {
|
||||
|
||||
@Mock
|
||||
private MethodInvocation mockMethodInvocation;
|
||||
|
||||
@Mock
|
||||
private PdxInstance mockSource;
|
||||
|
||||
@Mock
|
||||
private WritablePdxInstance mockNewSource;
|
||||
|
||||
@SafeVarargs
|
||||
protected static <T> T[] asArray(T... array) {
|
||||
return array;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newPdxInstanceMethodInterceptorWithValidObjectSourceIsSuccessful() {
|
||||
PdxInstanceMethodInterceptor methodInterceptor = newPdxInstanceMethodInterceptor((Object) mockSource);
|
||||
|
||||
assertThat(methodInterceptor).isNotNull();
|
||||
assertThat(methodInterceptor.getSource()).isSameAs(mockSource);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void newPdxInstanceMethodInterceptorWithInvalidObjectSourceThrowsIllegalArgumentException() {
|
||||
try {
|
||||
newPdxInstanceMethodInterceptor(new Object());
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageStartingWith(String.format(
|
||||
"Source [java.lang.Object] is not an instance of [%s]", PdxInstance.class.getName()));
|
||||
assertThat(expected).hasNoCause();
|
||||
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newPdxInstanceMethodInterceptorWithPdxInstanceIsSuccessful() {
|
||||
PdxInstanceMethodInterceptor methodInterceptor = newPdxInstanceMethodInterceptor(mockSource);
|
||||
|
||||
assertThat(methodInterceptor).isNotNull();
|
||||
assertThat(methodInterceptor.getSource()).isSameAs(mockSource);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructPdxInstanceMethodInterceptorWithNullThrowsIllegalArgumentException() {
|
||||
try {
|
||||
new PdxInstanceMethodInterceptor(null);
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessage("Source must not be null");
|
||||
assertThat(expected).hasNoCause();
|
||||
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeObjectMethodIsHandled() throws Throwable {
|
||||
Person jonDoe = Person.newPerson("Jon", "Doe");
|
||||
Method toString = jonDoe.getClass().getMethod("toString");
|
||||
|
||||
when(mockMethodInvocation.getMethod()).thenReturn(toString);
|
||||
when(mockMethodInvocation.proceed()).thenAnswer(invocationOnMock -> toString.invoke(jonDoe));
|
||||
|
||||
assertThat(newPdxInstanceMethodInterceptor(mockSource).invoke(mockMethodInvocation)).isEqualTo("Jon Doe");
|
||||
|
||||
verify(mockMethodInvocation, times(1)).getMethod();
|
||||
verify(mockMethodInvocation, times(1)).proceed();
|
||||
verifyZeroInteractions(mockSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeGetterOnSourceIsHandled() throws Throwable {
|
||||
Person jonDoe = Person.newPerson("Jon", "Doe");
|
||||
Method getFirstName = jonDoe.getClass().getMethod("getFirstName");
|
||||
|
||||
when(mockMethodInvocation.getMethod()).thenReturn(getFirstName);
|
||||
when(mockSource.hasField(eq("firstName"))).thenReturn(true);
|
||||
when(mockSource.getField(eq("firstName"))).thenReturn(jonDoe.getFirstName());
|
||||
|
||||
assertThat(newPdxInstanceMethodInterceptor(mockSource).invoke(mockMethodInvocation)).isEqualTo("Jon");
|
||||
|
||||
verify(mockMethodInvocation, never()).proceed();
|
||||
verify(mockSource, times(1)).hasField(eq("firstName"));
|
||||
verify(mockSource, times(1)).getField(eq("firstName"));
|
||||
verifyNoMoreInteractions(mockSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeSetterOnSourceIsHandled() throws Throwable {
|
||||
Person jonDoe = Person.newPerson("Jon", "Doe");
|
||||
Method setLastName = jonDoe.getClass().getMethod("setLastName", String.class);
|
||||
|
||||
when(mockMethodInvocation.getMethod()).thenReturn(setLastName);
|
||||
when(mockMethodInvocation.getArguments()).thenReturn(asArray("Smith"));
|
||||
when(mockSource.hasField(eq("lastName"))).thenReturn(true);
|
||||
when(mockSource.createWriter()).thenReturn(mockNewSource);
|
||||
|
||||
PdxInstanceMethodInterceptor methodInterceptor = newPdxInstanceMethodInterceptor(mockSource);
|
||||
|
||||
assertThat(methodInterceptor.getSource()).isSameAs(mockSource);
|
||||
assertThat(methodInterceptor.invoke(mockMethodInvocation)).isNull();
|
||||
assertThat(methodInterceptor.getSource()).isEqualTo(mockNewSource);
|
||||
|
||||
verify(mockMethodInvocation, times(1)).getMethod();
|
||||
verify(mockMethodInvocation, times(2)).getArguments();
|
||||
verify(mockSource, times(1)).hasField(eq("lastName"));
|
||||
verify(mockNewSource, times(1)).setField(eq("lastName"), eq("Smith"));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void invokeThrowsIllegalStateExceptionWhenPdxInstanceDoesNotHaveField() throws Throwable {
|
||||
Method getGender = Person.class.getMethod("getGender");
|
||||
|
||||
when(mockMethodInvocation.getMethod()).thenReturn(getGender);
|
||||
when(mockSource.hasField(anyString())).thenReturn(false);
|
||||
|
||||
try {
|
||||
newPdxInstanceMethodInterceptor(mockSource).invoke(mockMethodInvocation);
|
||||
}
|
||||
catch (IllegalStateException expected) {
|
||||
assertThat(expected).hasMessage("Source [%s] does not contain field with name [gender]", mockSource);
|
||||
assertThat(expected).hasNoCause();
|
||||
|
||||
throw expected;
|
||||
}
|
||||
finally {
|
||||
verify(mockMethodInvocation, times(1)).getMethod();
|
||||
verify(mockSource, times(1)).hasField(eq("gender"));
|
||||
verifyNoMoreInteractions(mockSource);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void invokeThrowsIllegalArgumentExceptionWhenMethodInvocationHasIncorrectNumberOfArguments()
|
||||
throws Throwable {
|
||||
|
||||
Method setGender = Person.class.getMethod("setGender", Gender.class);
|
||||
|
||||
when(mockMethodInvocation.getMethod()).thenReturn(setGender);
|
||||
when(mockSource.hasField(eq("gender"))).thenReturn(true);
|
||||
when(mockMethodInvocation.getArguments()).thenReturn(asArray(Gender.FEMALE, Gender.MALE));
|
||||
|
||||
try {
|
||||
newPdxInstanceMethodInterceptor(mockSource).invoke(mockMethodInvocation);
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessage(
|
||||
"Invoked setter method [setGender] must expect exactly 1 argument; Arguments were [[FEMALE, MALE]]");
|
||||
assertThat(expected).hasNoCause();
|
||||
|
||||
throw expected;
|
||||
}
|
||||
finally {
|
||||
verify(mockMethodInvocation, times(1)).getMethod();
|
||||
verify(mockMethodInvocation, times(2)).getArguments();
|
||||
verify(mockSource, times(1)).hasField(eq("gender"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void invokeThrowsIllegalArgumentExceptionWhenPdxInstanceHasNoWriter() throws Throwable {
|
||||
Method setGender = Person.class.getMethod("setGender", Gender.class);
|
||||
|
||||
when(mockMethodInvocation.getMethod()).thenReturn(setGender);
|
||||
when(mockMethodInvocation.getArguments()).thenReturn(asArray(Gender.FEMALE));
|
||||
when(mockSource.hasField(eq("gender"))).thenReturn(true);
|
||||
when(mockSource.createWriter()).thenReturn(null);
|
||||
|
||||
try {
|
||||
newPdxInstanceMethodInterceptor(mockSource).invoke(mockMethodInvocation);
|
||||
}
|
||||
catch (IllegalStateException expected) {
|
||||
assertThat(expected).hasMessage(
|
||||
"No writer for PdxInstance [%s] was found for setting field [gender] to value [FEMALE]", mockSource);
|
||||
assertThat(expected).hasNoCause();
|
||||
|
||||
throw expected;
|
||||
}
|
||||
finally {
|
||||
verify(mockMethodInvocation, times(1)).getMethod();
|
||||
verify(mockMethodInvocation, times(2)).getArguments();
|
||||
verify(mockSource, times(1)).hasField(eq("gender"));
|
||||
verify(mockSource, times(1)).createWriter();
|
||||
}
|
||||
}
|
||||
|
||||
enum Gender {
|
||||
FEMALE,
|
||||
MALE
|
||||
}
|
||||
|
||||
@Data
|
||||
@RequiredArgsConstructor(staticName = "newPerson")
|
||||
@SuppressWarnings("unused")
|
||||
static class Person {
|
||||
|
||||
Gender gender;
|
||||
|
||||
@NonNull String firstName;
|
||||
@NonNull String lastName;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%1$s %2$s", getFirstName(), getLastName());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user