DATAGEODE-52 - Using SD Repositories and @EnableContinuousQueries causes errors with Spring container initialization.
This commit is contained in:
@@ -19,6 +19,7 @@ package org.springframework.data.gemfire.config.annotation;
|
|||||||
import static java.util.Arrays.stream;
|
import static java.util.Arrays.stream;
|
||||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeMap;
|
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeMap;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -28,7 +29,8 @@ import java.util.concurrent.Executor;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.geode.cache.GemFireCache;
|
import org.apache.geode.cache.GemFireCache;
|
||||||
import org.apache.shiro.util.Assert;
|
import org.springframework.aop.framework.AopProxyUtils;
|
||||||
|
import org.springframework.aop.support.AopUtils;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.ListableBeanFactory;
|
import org.springframework.beans.factory.ListableBeanFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -44,6 +46,7 @@ import org.springframework.data.gemfire.listener.ContinuousQueryListenerContaine
|
|||||||
import org.springframework.data.gemfire.listener.annotation.ContinuousQuery;
|
import org.springframework.data.gemfire.listener.annotation.ContinuousQuery;
|
||||||
import org.springframework.data.gemfire.util.CacheUtils;
|
import org.springframework.data.gemfire.util.CacheUtils;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ErrorHandler;
|
import org.springframework.util.ErrorHandler;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
@@ -63,6 +66,7 @@ import org.springframework.util.StringUtils;
|
|||||||
* @see org.springframework.data.gemfire.listener.ContinuousQueryDefinition
|
* @see org.springframework.data.gemfire.listener.ContinuousQueryDefinition
|
||||||
* @see org.springframework.data.gemfire.listener.ContinuousQueryListener
|
* @see org.springframework.data.gemfire.listener.ContinuousQueryListener
|
||||||
* @see org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer
|
* @see org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer
|
||||||
|
* @see org.springframework.data.gemfire.listener.annotation.ContinuousQuery
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@@ -82,7 +86,7 @@ public class ContinuousQueryConfiguration extends AbstractAnnotationConfigSuppor
|
|||||||
private String taskExecutorBeanName;
|
private String taskExecutorBeanName;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class getAnnotationType() {
|
protected Class<? extends Annotation> getAnnotationType() {
|
||||||
return EnableContinuousQueries.class;
|
return EnableContinuousQueries.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,11 +122,14 @@ public class ContinuousQueryConfiguration extends AbstractAnnotationConfigSuppor
|
|||||||
this.continuousQueryDefinitions.forEach(this.container::addListener);
|
this.continuousQueryDefinitions.forEach(this.container::addListener);
|
||||||
this.continuousQueryDefinitions.clear();
|
this.continuousQueryDefinitions.clear();
|
||||||
}
|
}
|
||||||
else if (isApplicationBean(bean)) {
|
else if (isApplicationBean(bean, beanName)) {
|
||||||
|
|
||||||
List<ContinuousQueryDefinition> definitions = stream(bean.getClass().getMethods())
|
//Object resolvedBean = resolveTargetObject(bean);
|
||||||
|
Object resolvedBean = bean;
|
||||||
|
|
||||||
|
List<ContinuousQueryDefinition> definitions = stream(resolvedBean.getClass().getMethods())
|
||||||
.filter(method -> method.isAnnotationPresent(ContinuousQuery.class))
|
.filter(method -> method.isAnnotationPresent(ContinuousQuery.class))
|
||||||
.map(method -> ContinuousQueryDefinition.from(bean, method))
|
.map(method -> ContinuousQueryDefinition.from(resolvedBean, method))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
Optional.ofNullable(this.container).map(container -> {
|
Optional.ofNullable(this.container).map(container -> {
|
||||||
@@ -140,15 +147,36 @@ public class ContinuousQueryConfiguration extends AbstractAnnotationConfigSuppor
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isApplicationBean(Object bean) {
|
private boolean isApplicationBean(Object bean, String beanName) {
|
||||||
|
|
||||||
return Optional.ofNullable(bean)
|
return Optional.ofNullable(bean)
|
||||||
|
//.filter(this::isNotProxy)
|
||||||
|
//.map(this::resolveTargetObject)
|
||||||
.map(Object::getClass)
|
.map(Object::getClass)
|
||||||
.filter(type -> type.getPackage().getName().startsWith(ORG_SPRINGFRAMEWORK_DATA_GEMFIRE_PACKAGE_NAME)
|
.map(Class::getPackage)
|
||||||
|| !type.getPackage().getName().startsWith(ORG_SPRINGFRAMEWORK_PACKAGE_NAME))
|
.map(Package::getName)
|
||||||
|
.filter(StringUtils::hasText)
|
||||||
|
.filter(packageName -> packageName.startsWith(ORG_SPRINGFRAMEWORK_DATA_GEMFIRE_PACKAGE_NAME)
|
||||||
|
|| !packageName.startsWith(ORG_SPRINGFRAMEWORK_PACKAGE_NAME))
|
||||||
.isPresent();
|
.isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isNotProxy(Object bean) {
|
||||||
|
return !isProxy(bean);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isProxy(Object bean) {
|
||||||
|
return AopUtils.isAopProxy(bean);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object resolveTargetObject(Object bean) {
|
||||||
|
|
||||||
|
return Optional.ofNullable(bean)
|
||||||
|
.filter(this::isProxy)
|
||||||
|
.map(proxy -> AopProxyUtils.getSingletonTarget(proxy))
|
||||||
|
.orElse(bean);
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ContinuousQueryListenerContainer continuousQueryListenerContainer(GemFireCache gemfireCache) {
|
public ContinuousQueryListenerContainer continuousQueryListenerContainer(GemFireCache gemfireCache) {
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,13 @@ import static org.mockito.ArgumentMatchers.eq;
|
|||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.springframework.data.gemfire.util.ArrayUtils.asArray;
|
||||||
|
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
|
||||||
import org.apache.geode.cache.GemFireCache;
|
import org.apache.geode.cache.GemFireCache;
|
||||||
|
import org.apache.geode.cache.Region;
|
||||||
|
import org.apache.geode.cache.RegionAttributes;
|
||||||
import org.apache.geode.cache.client.ClientCache;
|
import org.apache.geode.cache.client.ClientCache;
|
||||||
import org.apache.geode.cache.client.Pool;
|
import org.apache.geode.cache.client.Pool;
|
||||||
import org.apache.geode.cache.query.CqAttributes;
|
import org.apache.geode.cache.query.CqAttributes;
|
||||||
@@ -31,23 +36,49 @@ import org.apache.geode.cache.query.CqEvent;
|
|||||||
import org.apache.geode.cache.query.CqQuery;
|
import org.apache.geode.cache.query.CqQuery;
|
||||||
import org.apache.geode.cache.query.QueryService;
|
import org.apache.geode.cache.query.QueryService;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.springframework.aop.framework.ProxyFactory;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||||
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.data.annotation.Id;
|
||||||
|
import org.springframework.data.gemfire.RegionAttributesFactoryBean;
|
||||||
import org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer;
|
import org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer;
|
||||||
import org.springframework.data.gemfire.listener.annotation.ContinuousQuery;
|
import org.springframework.data.gemfire.listener.annotation.ContinuousQuery;
|
||||||
|
import org.springframework.data.gemfire.mapping.GemfireMappingContext;
|
||||||
|
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;
|
||||||
|
import org.springframework.data.gemfire.repository.support.GemfireRepositoryFactoryBean;
|
||||||
|
import org.springframework.data.gemfire.test.mock.MockGemFireObjectsSupport;
|
||||||
import org.springframework.data.gemfire.test.mock.annotation.EnableGemFireMockObjects;
|
import org.springframework.data.gemfire.test.mock.annotation.EnableGemFireMockObjects;
|
||||||
|
import org.springframework.data.gemfire.test.model.Person;
|
||||||
|
import org.springframework.data.gemfire.test.repo.PersonRepository;
|
||||||
|
import org.springframework.data.gemfire.test.support.IOUtils;
|
||||||
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link EnableContinuousQueries}, {@link ContinuousQueryConfiguration}, {@link ContinuousQuery}
|
* Unit tests for {@link EnableContinuousQueries}, {@link ContinuousQueryConfiguration}, {@link ContinuousQuery}
|
||||||
* and {@link ContinuousQueryListenerContainer}.
|
* and {@link ContinuousQueryListenerContainer}.
|
||||||
*
|
*
|
||||||
* @author John Blum
|
* @author John Blum
|
||||||
|
* @see java.lang.reflect.Proxy
|
||||||
* @see org.junit.Test
|
* @see org.junit.Test
|
||||||
|
* @see org.apache.geode.cache.GemFireCache
|
||||||
|
* @see org.apache.geode.cache.Region
|
||||||
|
* @see org.apache.geode.cache.client.ClientCache
|
||||||
|
* @see org.apache.geode.cache.query.CqQuery
|
||||||
|
* @see org.apache.geode.cache.query.QueryService
|
||||||
|
* @see org.springframework.aop.framework.ProxyFactory
|
||||||
* @see org.springframework.data.gemfire.config.annotation.ContinuousQueryConfiguration
|
* @see org.springframework.data.gemfire.config.annotation.ContinuousQueryConfiguration
|
||||||
* @see org.springframework.data.gemfire.config.annotation.EnableContinuousQueries
|
* @see org.springframework.data.gemfire.config.annotation.EnableContinuousQueries
|
||||||
* @see org.springframework.data.gemfire.listener.annotation.ContinuousQuery
|
* @see org.springframework.data.gemfire.listener.annotation.ContinuousQuery
|
||||||
@@ -59,35 +90,48 @@ public class EnableContinuousQueriesConfigurationUnitTests {
|
|||||||
return new AnnotationConfigApplicationContext(annotatedClasses);
|
return new AnnotationConfigApplicationContext(annotatedClasses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void testRegisterAndExecuteContinuousQuery(Class<?>... annotatedClasses) throws Exception {
|
||||||
|
|
||||||
|
ConfigurableApplicationContext applicationContext = newApplicationContext(annotatedClasses);
|
||||||
|
|
||||||
|
try {
|
||||||
|
assertThat(applicationContext).isNotNull();
|
||||||
|
assertThat(applicationContext.containsBean("DEFAULT")).isTrue();
|
||||||
|
|
||||||
|
GemFireCache gemfireCache = applicationContext.getBean(ClientCache.class);
|
||||||
|
|
||||||
|
assertThat(gemfireCache).isNotNull();
|
||||||
|
|
||||||
|
QueryService mockQueryService = gemfireCache.getQueryService();
|
||||||
|
|
||||||
|
assertThat(mockQueryService).isNotNull();
|
||||||
|
assertThat(mockQueryService.getCqs()).hasSize(1);
|
||||||
|
|
||||||
|
CqQuery mockCqQuery = mockQueryService.getCqs()[0];
|
||||||
|
|
||||||
|
assertThat(mockCqQuery).isNotNull();
|
||||||
|
assertThat(mockCqQuery.getName()).isEqualTo("TestQuery");
|
||||||
|
assertThat(mockCqQuery.getQueryString()).isEqualTo("SELECT * FROM /Example");
|
||||||
|
assertThat(mockCqQuery.isRunning()).isTrue();
|
||||||
|
|
||||||
|
verify(mockQueryService, times(1)).newCq(eq("TestQuery"),
|
||||||
|
eq("SELECT * FROM /Example"), any(CqAttributes.class), eq(false));
|
||||||
|
|
||||||
|
verify(mockCqQuery, times(1)).execute();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
IOUtils.close(applicationContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void registersAndExecutesContinuousQueries() throws Exception {
|
public void registersAndExecutesContinuousQueries() throws Exception {
|
||||||
|
testRegisterAndExecuteContinuousQuery(TestContinuousQueryRegistrationAndExecutionConfiguration.class);
|
||||||
|
}
|
||||||
|
|
||||||
ConfigurableApplicationContext applicationContext =
|
@Test
|
||||||
newApplicationContext(TestContinuousQueryRegistrationAndExecutionConfiguration.class);
|
public void registersAndExecutesContinuousQueriesOnProxiedBean() throws Exception {
|
||||||
|
testRegisterAndExecuteContinuousQuery(TestContinuousQueryRegistrationAndExecutionOnProxiedBeanConfiguration.class);
|
||||||
assertThat(applicationContext).isNotNull();
|
|
||||||
assertThat(applicationContext.containsBean("DEFAULT")).isTrue();
|
|
||||||
|
|
||||||
GemFireCache gemfireCache = applicationContext.getBean(ClientCache.class);
|
|
||||||
|
|
||||||
assertThat(gemfireCache).isNotNull();
|
|
||||||
|
|
||||||
QueryService mockQueryService = gemfireCache.getQueryService();
|
|
||||||
|
|
||||||
assertThat(mockQueryService).isNotNull();
|
|
||||||
assertThat(mockQueryService.getCqs()).hasSize(1);
|
|
||||||
|
|
||||||
CqQuery mockCqQuery = mockQueryService.getCqs()[0];
|
|
||||||
|
|
||||||
assertThat(mockCqQuery).isNotNull();
|
|
||||||
assertThat(mockCqQuery.getName()).isEqualTo("TestQuery");
|
|
||||||
assertThat(mockCqQuery.getQueryString()).isEqualTo("SELECT * FROM /Example");
|
|
||||||
assertThat(mockCqQuery.isRunning()).isTrue();
|
|
||||||
|
|
||||||
verify(mockQueryService, times(1)).newCq(eq("TestQuery"),
|
|
||||||
eq("SELECT * FROM /Example"), any(CqAttributes.class), eq(false));
|
|
||||||
|
|
||||||
verify(mockCqQuery, times(1)).execute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ClientCacheApplication
|
@ClientCacheApplication
|
||||||
@@ -99,10 +143,9 @@ public class EnableContinuousQueriesConfigurationUnitTests {
|
|||||||
@Bean
|
@Bean
|
||||||
BeanFactoryPostProcessor dependsOnBeanFactoryPostProcessor() {
|
BeanFactoryPostProcessor dependsOnBeanFactoryPostProcessor() {
|
||||||
|
|
||||||
return configurableListableBeanFactory -> {
|
return configurableListableBeanFactory ->
|
||||||
configurableListableBeanFactory.getBeanDefinition("continuousQueryListenerContainer")
|
configurableListableBeanFactory.getBeanDefinition("continuousQueryListenerContainer")
|
||||||
.setDependsOn("testComponent");
|
.setDependsOn("continuousQueryComponent");
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean("DEFAULT")
|
@Bean("DEFAULT")
|
||||||
@@ -112,11 +155,108 @@ public class EnableContinuousQueriesConfigurationUnitTests {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
TestContinuousQueryComponent testComponent() {
|
TestContinuousQueryComponent continuousQueryComponent() {
|
||||||
return new TestContinuousQueryComponent();
|
return new TestContinuousQueryComponent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableGemfireRepositories(basePackageClasses = PersonRepository.class)
|
||||||
|
@Import(TestContinuousQueryRegistrationAndExecutionConfiguration.class)
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
static class TestContinuousQueryRegistrationAndExecutionOnProxiedBeanConfiguration {
|
||||||
|
|
||||||
|
/*
|
||||||
|
@Bean
|
||||||
|
BeanPostProcessor proxyTestContinuousQueryComponentPostProcessor() {
|
||||||
|
|
||||||
|
return new BeanPostProcessor() {
|
||||||
|
|
||||||
|
@Nullable @Override
|
||||||
|
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||||
|
|
||||||
|
if ("continuousQueryComponent".equals(beanName)) {
|
||||||
|
return new ProxyFactory(bean).getProxy(bean.getClass().getClassLoader());
|
||||||
|
}
|
||||||
|
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Bean("People")
|
||||||
|
Region<Long, Person> mockPeopleRegion(GemFireCache gemfireCache,
|
||||||
|
@Qualifier("peopleRegionAttributes") RegionAttributes<Long, Person> peopleRegionAttributes) {
|
||||||
|
|
||||||
|
return MockGemFireObjectsSupport.mockRegion(gemfireCache, "People", peopleRegionAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
RegionAttributesFactoryBean peopleRegionAttributes() {
|
||||||
|
|
||||||
|
RegionAttributesFactoryBean peopleRegionAttributes = new RegionAttributesFactoryBean();
|
||||||
|
|
||||||
|
peopleRegionAttributes.setKeyConstraint(Long.class);
|
||||||
|
peopleRegionAttributes.setValueConstraint(Person.class);
|
||||||
|
|
||||||
|
return peopleRegionAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean("Examples")
|
||||||
|
Region<Long, Example> mockExamplesRegion(GemFireCache gemfireCache,
|
||||||
|
@Qualifier("examplesRegionAttributes") RegionAttributes<Long, Example> mockExamplesRegionAttributes) {
|
||||||
|
|
||||||
|
return MockGemFireObjectsSupport.mockRegion(gemfireCache, "Examples",
|
||||||
|
mockExamplesRegionAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
RegionAttributesFactoryBean examplesRegionAttributes() {
|
||||||
|
|
||||||
|
RegionAttributesFactoryBean examplesRegionAttributes = new RegionAttributesFactoryBean();
|
||||||
|
|
||||||
|
examplesRegionAttributes.setKeyConstraint(Long.class);
|
||||||
|
examplesRegionAttributes.setValueConstraint(Example.class);
|
||||||
|
|
||||||
|
return examplesRegionAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ExampleDataAccessObject exampleDao() {
|
||||||
|
|
||||||
|
return (ExampleDataAccessObject) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
|
||||||
|
asArray(ExampleDataAccessObject.class), (proxy, method, args) -> null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
GemfireRepositoryFactoryBean exampleRepository() {
|
||||||
|
|
||||||
|
GemfireRepositoryFactoryBean<ExampleRepository, Example, Long> exampleRepositoryFactory =
|
||||||
|
new GemfireRepositoryFactoryBean<>(ExampleRepository.class);
|
||||||
|
|
||||||
|
exampleRepositoryFactory.setGemfireMappingContext(new GemfireMappingContext());
|
||||||
|
exampleRepositoryFactory.setLazyInit(false);
|
||||||
|
|
||||||
|
return exampleRepositoryFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
Object exampleService(ExampleRepository exampleRepository, ExampleDataAccessObject exampleDao) {
|
||||||
|
assertThat(exampleRepository).isNotNull();
|
||||||
|
assertThat(exampleDao).isNotNull();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
Object personService(PersonRepository personRepository) {
|
||||||
|
assertThat(personRepository).isNotNull();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
static class TestContinuousQueryComponent {
|
static class TestContinuousQueryComponent {
|
||||||
@@ -125,4 +265,16 @@ public class EnableContinuousQueriesConfigurationUnitTests {
|
|||||||
public void handle(CqEvent event) {
|
public void handle(CqEvent event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@org.springframework.data.gemfire.mapping.annotation.Region("Examples")
|
||||||
|
static class Example {
|
||||||
|
@Id Long id;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExampleDataAccessObject {
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExampleRepository extends CrudRepository<Example, Long> {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ public class ContinuousQueryListenerContainerTests {
|
|||||||
}
|
}
|
||||||
catch (IllegalStateException expected) {
|
catch (IllegalStateException expected) {
|
||||||
|
|
||||||
assertThat(expected).hasMessage("QueryService was not properly initialized");
|
assertThat(expected).hasMessage("QueryService is required");
|
||||||
assertThat(expected).hasNoCause();
|
assertThat(expected).hasNoCause();
|
||||||
|
|
||||||
throw expected;
|
throw expected;
|
||||||
|
|||||||
@@ -31,14 +31,21 @@ import org.springframework.util.Assert;
|
|||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Person class...
|
* The {@link Person} class is an Abstract Data Type (ADT) modeling a person.
|
||||||
*
|
*
|
||||||
* @author John Blum
|
* @author John Blum
|
||||||
* @since 1.0.0
|
* @see java.lang.Comparable
|
||||||
|
* @see java.util.Calendar
|
||||||
|
* @see java.util.Date
|
||||||
|
* @see org.springframework.data.annotation.Id
|
||||||
|
* @see org.springframework.data.annotation.PersistenceConstructor
|
||||||
|
* @see org.springframework.data.gemfire.mapping.annotation.Region
|
||||||
|
* @see org.springframework.data.gemfire.test.support.IdentifierSequence
|
||||||
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
@Region("People")
|
@Region("People")
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class Person implements Serializable {
|
public class Person implements Comparable<Person>, Serializable {
|
||||||
|
|
||||||
protected static final String BIRTH_DATE_PATTERN = "yyyy/MM/dd";
|
protected static final String BIRTH_DATE_PATTERN = "yyyy/MM/dd";
|
||||||
|
|
||||||
@@ -53,11 +60,14 @@ public class Person implements Serializable {
|
|||||||
private final String lastName;
|
private final String lastName;
|
||||||
|
|
||||||
public static Date newBirthDate(int year, int month, int dayOfMonth) {
|
public static Date newBirthDate(int year, int month, int dayOfMonth) {
|
||||||
|
|
||||||
Calendar birthDate = Calendar.getInstance();
|
Calendar birthDate = Calendar.getInstance();
|
||||||
|
|
||||||
birthDate.clear();
|
birthDate.clear();
|
||||||
birthDate.set(Calendar.YEAR, year);
|
birthDate.set(Calendar.YEAR, year);
|
||||||
birthDate.set(Calendar.MONTH, month);
|
birthDate.set(Calendar.MONTH, month);
|
||||||
birthDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
birthDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
||||||
|
|
||||||
return birthDate.getTime();
|
return birthDate.getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,8 +81,9 @@ public class Person implements Serializable {
|
|||||||
|
|
||||||
@PersistenceConstructor
|
@PersistenceConstructor
|
||||||
public Person(String firstName, String lastName, Date birthDate, Gender gender) {
|
public Person(String firstName, String lastName, Date birthDate, Gender gender) {
|
||||||
Assert.hasText(firstName, "firstName must be specified");
|
|
||||||
Assert.hasText(lastName, "lastName must be specified");
|
Assert.hasText(firstName, "firstName is required");
|
||||||
|
Assert.hasText(lastName, "lastName is required");
|
||||||
|
|
||||||
this.firstName = firstName;
|
this.firstName = firstName;
|
||||||
this.lastName = lastName;
|
this.lastName = lastName;
|
||||||
@@ -122,8 +133,24 @@ public class Person implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object obj) {
|
public int compareTo(Person that) {
|
||||||
if (obj == this) {
|
|
||||||
|
int result = nullSafeCompareTo(this.getLastName(), that.getLastName());
|
||||||
|
|
||||||
|
result = (result != 0 ? result : nullSafeCompareTo(this.getFirstName(), that.getLastName()));
|
||||||
|
result = (result != 0 ? result : nullSafeCompareTo(this.getBirthDate(), that.getBirthDate()));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends Comparable<T>> int nullSafeCompareTo(T comparableOne, T comparableTwo) {
|
||||||
|
return (comparableOne == null ? 1 : (comparableTwo == null ? -1 : comparableOne.compareTo(comparableTwo)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
|
||||||
|
if (this == obj) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,11 +169,14 @@ public class Person implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
|
|
||||||
int hashValue = 17;
|
int hashValue = 17;
|
||||||
|
|
||||||
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getId());
|
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getId());
|
||||||
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getBirthDate());
|
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getBirthDate());
|
||||||
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getFirstName());
|
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getFirstName());
|
||||||
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getLastName());
|
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getLastName());
|
||||||
|
|
||||||
return hashValue;
|
return hashValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 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.test.repo;
|
||||||
|
|
||||||
|
import org.springframework.data.gemfire.test.model.Person;
|
||||||
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link PersonRepository} interface defines a Spring Data {@link CrudRepository} used by applications
|
||||||
|
* to perform basic CRUD and querying data access operations on {@link Person people}.
|
||||||
|
*
|
||||||
|
* @author John Blum
|
||||||
|
* @see java.lang.Long
|
||||||
|
* @see org.springframework.data.gemfire.test.model.Person
|
||||||
|
* @see org.springframework.data.repository.CrudRepository
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public interface PersonRepository extends CrudRepository<Person, Long> {
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user