SGF-706 - Fix race condition between ContinuousQuery registration and EnableClusterConfiguration Region creation.
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -243,6 +243,8 @@
|
||||
<systemProperties>
|
||||
<gemfire.disableShutdownHook>true</gemfire.disableShutdownHook>
|
||||
<javax.net.ssl.keyStore>${basedir}/src/test/resources/trusted.keystore</javax.net.ssl.keyStore>
|
||||
<logback.log.level>error</logback.log.level>
|
||||
<spring.profiles.active>apache-geode</spring.profiles.active>
|
||||
</systemProperties>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.gemfire.config.annotation;
|
||||
|
||||
import static java.util.stream.StreamSupport.stream;
|
||||
import static org.springframework.data.gemfire.util.CacheUtils.isClient;
|
||||
import static org.springframework.data.gemfire.util.CacheUtils.isPeer;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Optional;
|
||||
@@ -27,17 +28,12 @@ import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.RegionShortcut;
|
||||
import org.apache.geode.cache.client.ClientCache;
|
||||
import org.apache.geode.cache.query.Index;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.context.event.ApplicationContextEvent;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.gemfire.GemfireUtils;
|
||||
import org.springframework.data.gemfire.config.admin.GemfireAdminOperations;
|
||||
import org.springframework.data.gemfire.config.admin.remote.FunctionGemfireAdminTemplate;
|
||||
import org.springframework.data.gemfire.config.admin.remote.RestHttpGemfireAdminTemplate;
|
||||
@@ -51,6 +47,9 @@ import org.springframework.data.gemfire.config.schema.support.ComposableSchemaOb
|
||||
import org.springframework.data.gemfire.config.schema.support.IndexCollector;
|
||||
import org.springframework.data.gemfire.config.schema.support.IndexDefiner;
|
||||
import org.springframework.data.gemfire.config.schema.support.RegionDefiner;
|
||||
import org.springframework.data.gemfire.config.support.AbstractSmartLifecycle;
|
||||
import org.springframework.data.gemfire.util.CacheUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Spring {@link Configuration @Configuration} class defining Spring beans that will record the creation of
|
||||
@@ -162,70 +161,22 @@ public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigS
|
||||
}
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void gemfireClusterSchemaCreationHandler(ContextRefreshedEvent event) {
|
||||
@Bean
|
||||
public ClusterSchemaObjectInitializer gemfireClusterSchemaObjectInitializer(GemFireCache gemfireCache) {
|
||||
|
||||
GemFireCache gemfireCache = resolveGemFireCache(event);
|
||||
return Optional.ofNullable(gemfireCache)
|
||||
.filter(CacheUtils::isClient)
|
||||
.map(clientCache -> {
|
||||
|
||||
if (isClient(gemfireCache)) {
|
||||
SchemaObjectContext schemaObjectContext = SchemaObjectContext.from(gemfireCache)
|
||||
.with(newGemfireAdminOperations((ClientCache) clientCache))
|
||||
.with(newSchemaObjectCollector())
|
||||
.with(newSchemaObjectDefiner());
|
||||
|
||||
GemfireAdminOperations gemfireAdminOperations = newGemfireAdminOperations((ClientCache) gemfireCache);
|
||||
return new ClusterSchemaObjectInitializer(schemaObjectContext);
|
||||
|
||||
SchemaObjectDefiner schemaObjectDefiner = newSchemaObjectDefiner();
|
||||
|
||||
Iterable<?> schemaObjects = newSchemaObjectCollector().collectFrom(resolveApplicationContext(event));
|
||||
|
||||
stream(schemaObjects.spliterator(), false)
|
||||
.map(schemaObjectDefiner::define)
|
||||
.sorted(OrderComparator.INSTANCE)
|
||||
.forEach(schemaObjectDefinition ->
|
||||
schemaObjectDefinition.ifPresent(it -> it.create(gemfireAdminOperations)));
|
||||
}
|
||||
/*
|
||||
else if (isPeer(gemfireCache)) {
|
||||
|
||||
GemfireFunctionUtils.registerFunctionForPojoMethod(new CreateRegionFunction(),
|
||||
CreateRegionFunction.CREATE_REGION_FUNCTION_ID);
|
||||
|
||||
GemfireFunctionUtils.registerFunctionForPojoMethod(new CreateIndexFunction(),
|
||||
CreateIndexFunction.CREATE_INDEX_FUNCTION_ID);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a reference to the Spring {@link ApplicationContext} from the given {@link ApplicationContextEvent}.
|
||||
*
|
||||
* @param event {@link ApplicationContextEvent} from which to resolve the Spring {@link ApplicationContext}.
|
||||
* @return the resolved Spring {@link ApplicationContext}.
|
||||
* @see org.springframework.context.event.ApplicationContextEvent
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
*/
|
||||
private ApplicationContext resolveApplicationContext(ApplicationContextEvent event) {
|
||||
return event.getApplicationContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to resolve the {@link GemFireCache} from the Spring {@link ApplicationContext}.
|
||||
* The {@link GemFireCache} will be resolvable from the Spring {@link ApplicationContext} if the cache
|
||||
* was registered a managed bean in the Spring container.
|
||||
*
|
||||
* If the {@link GemFireCache} cannot be resolved from the {@link ApplicationContext}, this method will attempt
|
||||
* to resolve the cache reference from GemFire's global context using the GemFire API.
|
||||
*
|
||||
* @param event Spring {@link ApplicationContextEvent} encapsulating the details of the Spring container event.
|
||||
* @return the resolved {@link GemFireCache} if available.
|
||||
* @see org.springframework.context.event.ApplicationContextEvent
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
*/
|
||||
private GemFireCache resolveGemFireCache(ApplicationContextEvent event) {
|
||||
|
||||
try {
|
||||
return resolveApplicationContext(event).getBean(GemFireCache.class);
|
||||
}
|
||||
catch (BeansException ignore) {
|
||||
return GemfireUtils.resolveGemFireCache();
|
||||
}
|
||||
})
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,4 +234,147 @@ public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigS
|
||||
new IndexDefiner()
|
||||
);
|
||||
}
|
||||
|
||||
public static class ClusterSchemaObjectInitializer extends AbstractSmartLifecycle {
|
||||
|
||||
private final SchemaObjectContext schemaObjectContext;
|
||||
|
||||
protected ClusterSchemaObjectInitializer(SchemaObjectContext schemaObjectContext) {
|
||||
Assert.notNull(schemaObjectContext, "SchemaObjectContext is required");
|
||||
this.schemaObjectContext = schemaObjectContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
protected SchemaObjectContext getSchemaObjectContext() {
|
||||
return this.schemaObjectContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
|
||||
SchemaObjectContext schemaObjectContext = getSchemaObjectContext();
|
||||
|
||||
if (schemaObjectContext.isClientCache()) {
|
||||
|
||||
Iterable<?> schemaObjects = schemaObjectContext.getSchemaObjectCollector()
|
||||
.collectFrom(requireApplicationContext());
|
||||
|
||||
stream(schemaObjects.spliterator(), false)
|
||||
.map(schemaObjectContext.getSchemaObjectDefiner()::define)
|
||||
.sorted(OrderComparator.INSTANCE)
|
||||
.forEach(schemaObjectDefinition -> schemaObjectDefinition
|
||||
.ifPresent(it -> it.create(schemaObjectContext.getGemfireAdminOperations())));
|
||||
|
||||
setRunning(true);
|
||||
|
||||
}
|
||||
/*
|
||||
else if (schemaObjectContext.isPeerCache()) {
|
||||
|
||||
GemfireFunctionUtils.registerFunctionForPojoMethod(new CreateRegionFunction(),
|
||||
CreateRegionFunction.CREATE_REGION_FUNCTION_ID);
|
||||
|
||||
GemfireFunctionUtils.registerFunctionForPojoMethod(new CreateIndexFunction(),
|
||||
CreateIndexFunction.CREATE_INDEX_FUNCTION_ID);
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
setRunning(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Runnable callback) {
|
||||
setRunning(false);
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SchemaObjectContext {
|
||||
|
||||
private final GemFireCache gemfireCache;
|
||||
|
||||
private GemfireAdminOperations gemfireAdminOperations;
|
||||
|
||||
private SchemaObjectCollector<?> schemaObjectCollector;
|
||||
|
||||
private SchemaObjectDefiner schemaObjectDefiner;
|
||||
|
||||
protected static SchemaObjectContext from(GemFireCache gemfireCache) {
|
||||
return new SchemaObjectContext(gemfireCache);
|
||||
}
|
||||
|
||||
private SchemaObjectContext(GemFireCache gemfireCache) {
|
||||
Assert.notNull(gemfireCache, "GemFireCache is required");
|
||||
this.gemfireCache = gemfireCache;
|
||||
}
|
||||
|
||||
public GemfireAdminOperations getGemfireAdminOperations() {
|
||||
|
||||
Assert.state(this.gemfireAdminOperations != null,
|
||||
"GemfireAdminOperations was not initialized");
|
||||
|
||||
return this.gemfireAdminOperations;
|
||||
}
|
||||
|
||||
public boolean isClientCache() {
|
||||
return isClient(getGemfireCache());
|
||||
}
|
||||
|
||||
public boolean isPeerCache() {
|
||||
return isPeer(getGemfireCache());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends GemFireCache> T getGemfireCache() {
|
||||
return (T) this.gemfireCache;
|
||||
}
|
||||
|
||||
public SchemaObjectCollector<?> getSchemaObjectCollector() {
|
||||
|
||||
Assert.state(this.schemaObjectCollector != null,
|
||||
"SchemaObjectCollector was not initialized");
|
||||
|
||||
return this.schemaObjectCollector;
|
||||
}
|
||||
|
||||
|
||||
public SchemaObjectDefiner getSchemaObjectDefiner() {
|
||||
|
||||
Assert.state(this.schemaObjectDefiner != null,
|
||||
"SchemaObjectDefiner was not initialized");
|
||||
|
||||
return this.schemaObjectDefiner;
|
||||
}
|
||||
|
||||
protected SchemaObjectContext with(GemfireAdminOperations gemfireAdminOperations) {
|
||||
Assert.notNull(gemfireAdminOperations, "GemfireAdminOperations are required");
|
||||
this.gemfireAdminOperations = gemfireAdminOperations;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected SchemaObjectContext with(SchemaObjectCollector schemaObjectCollector) {
|
||||
Assert.notNull(schemaObjectCollector, "SchemaObjectCollector is required");
|
||||
this.schemaObjectCollector = schemaObjectCollector;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected SchemaObjectContext with(SchemaObjectDefiner schemaObjectDefiner) {
|
||||
Assert.notNull(schemaObjectDefiner, "SchemaObjectDefiner is required");
|
||||
this.schemaObjectDefiner = schemaObjectDefiner;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.config.support;
|
||||
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
|
||||
/**
|
||||
* {@link AbstractSmartLifecycle} is an abstract base class implementing the Spring{@link SmartLifecycle} interface
|
||||
* to support custom implementations.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.context.SmartLifecycle
|
||||
* @since 2.0.2
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class AbstractSmartLifecycle implements ApplicationContextAware, SmartLifecycle {
|
||||
|
||||
protected static final int DEFAULT_PHASE = 0;
|
||||
|
||||
private volatile boolean running = false;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
protected void setRunning(boolean running) {
|
||||
this.running = running;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
protected Optional<ApplicationContext> getApplicationContext() {
|
||||
return Optional.ofNullable(this.applicationContext);
|
||||
}
|
||||
|
||||
protected ApplicationContext requireApplicationContext() {
|
||||
|
||||
return getApplicationContext()
|
||||
.orElseThrow(() -> newIllegalStateException("ApplicationContext could not be resolved"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return DEFAULT_PHASE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Runnable callback) {
|
||||
}
|
||||
}
|
||||
@@ -142,30 +142,24 @@ public class EnableContinuousQueriesConfigurationIntegrationTests extends Client
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableContinuousQueries(poolName = "DEFAULT")
|
||||
@EnableContinuousQueries
|
||||
@Import(GemFireClientConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
TemperatureReadingQueryListeners temperatureReadingQueryListeners() {
|
||||
return new TemperatureReadingQueryListeners();
|
||||
}
|
||||
}
|
||||
|
||||
static class TemperatureReadingQueryListeners {
|
||||
|
||||
@ContinuousQuery(name = "BoilingTemperatures", query = "SELECT * FROM /TemperatureReadings r WHERE r.temperature >= 212")
|
||||
@ContinuousQuery(name = "BoilingTemperatures",
|
||||
query = "SELECT * FROM /TemperatureReadings r WHERE r.temperature >= 212")
|
||||
public void boilingTemperatures(CqEvent event) {
|
||||
boilingTemperatureReadingsCounter.incrementAndGet();
|
||||
}
|
||||
|
||||
@ContinuousQuery(name = "FreezingTemperatures", query = "SELECT * FROM /TemperatureReadings r WHERE r.temperature <= 32")
|
||||
@ContinuousQuery(name = "FreezingTemperatures",
|
||||
query = "SELECT * FROM /TemperatureReadings r WHERE r.temperature <= 32")
|
||||
public void freezingTemperatures(CqEvent event) {
|
||||
freezingTemperatureReadingsCounter.incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
@ClientCacheApplication(logLevel = "warning", subscriptionEnabled = true)
|
||||
@ClientCacheApplication(logLevel = "error", subscriptionEnabled = true)
|
||||
static class GemFireClientConfiguration {
|
||||
|
||||
@Bean
|
||||
@@ -204,7 +198,7 @@ public class EnableContinuousQueriesConfigurationIntegrationTests extends Client
|
||||
}
|
||||
}
|
||||
|
||||
@CacheServerApplication(name = "EnableContinuousQueriesConfigurationIntegrationTests", logLevel = "warning")
|
||||
@CacheServerApplication(name = "EnableContinuousQueriesConfigurationIntegrationTests", logLevel = "error")
|
||||
static class GemFireServerConfiguration {
|
||||
|
||||
public static void main(String[] args) {
|
||||
@@ -215,13 +209,6 @@ public class EnableContinuousQueriesConfigurationIntegrationTests extends Client
|
||||
applicationContext.registerShutdownHook();
|
||||
}
|
||||
|
||||
@Bean
|
||||
CacheServerConfigurer cacheServerPortConfigurer(
|
||||
@Value("${" + GEMFIRE_CACHE_SERVER_PORT_PROPERTY + ":40404}") int port) {
|
||||
|
||||
return (bean, cacheServerFactoryBean) -> cacheServerFactoryBean.setPort(port);
|
||||
}
|
||||
|
||||
@Bean(name = "TemperatureReadings")
|
||||
PartitionedRegionFactoryBean<Long, TemperatureReading> temperatureReadingsRegion(GemFireCache gemfireCache) {
|
||||
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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.config.annotation;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.geode.cache.query.CqEvent;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.gemfire.listener.annotation.ContinuousQuery;
|
||||
import org.springframework.data.gemfire.process.ProcessWrapper;
|
||||
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;
|
||||
import org.springframework.data.gemfire.support.ConnectionEndpoint;
|
||||
import org.springframework.data.gemfire.test.model.Gender;
|
||||
import org.springframework.data.gemfire.test.model.Person;
|
||||
import org.springframework.data.gemfire.test.repo.PersonRepository;
|
||||
import org.springframework.data.gemfire.test.support.ClientServerIntegrationTestsSupport;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* Integration tests testing the combination of {@link EnableContinuousQueries} with {@link EnableClusterConfiguration}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.apache.geode.cache.query.CqEvent
|
||||
* @see org.springframework.data.gemfire.config.annotation.EnableClusterConfiguration
|
||||
* @see org.springframework.data.gemfire.config.annotation.EnableContinuousQueries
|
||||
* @see org.springframework.data.gemfire.listener.annotation.ContinuousQuery
|
||||
* @see org.springframework.test.context.ContextConfiguration
|
||||
* @see org.springframework.test.context.junit4.SpringRunner
|
||||
* @see <a href="https://jira.spring.io/browse/DATAGEODE-73">Fix race condition between ContinuousQuery registration and EnableClusterConfiguration Region creation.</a>
|
||||
* @since 2.0.3
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = EnableContinuousQueriesWithClusterConfigurationIntegrationTests.TestConfiguration.class)
|
||||
@SuppressWarnings("unused")
|
||||
public class EnableContinuousQueriesWithClusterConfigurationIntegrationTests
|
||||
extends ClientServerIntegrationTestsSupport {
|
||||
|
||||
private static final BlockingQueue<Person> events = new ArrayBlockingQueue<>(2);
|
||||
|
||||
private static ProcessWrapper gemfireServer;
|
||||
|
||||
@BeforeClass
|
||||
public static void startGemFireServer() throws Exception {
|
||||
|
||||
int availablePort = findAvailablePort();
|
||||
|
||||
gemfireServer = run(EnableContinuousQueriesWithClusterConfigurationIntegrationTests.GemFireServerConfiguration.class,
|
||||
String.format("-D%s=%d", GEMFIRE_CACHE_SERVER_PORT_PROPERTY, availablePort));
|
||||
|
||||
waitForServerToStart("localhost", availablePort);
|
||||
|
||||
System.setProperty(GEMFIRE_CACHE_SERVER_PORT_PROPERTY, String.valueOf(availablePort));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopGemFireServer() {
|
||||
System.clearProperty(GEMFIRE_CACHE_SERVER_PORT_PROPERTY);
|
||||
stop(gemfireServer);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private PersonRepository personRepository;
|
||||
|
||||
@Test
|
||||
public void personEventsFired() throws Exception {
|
||||
|
||||
Person jonDoe = Person.newPerson(1L, "Jon", "Doe", null, Gender.MALE);
|
||||
|
||||
jonDoe = this.personRepository.save(jonDoe);
|
||||
|
||||
assertThat(this.personRepository.findById(jonDoe.getId()).orElse(null)).isEqualTo(jonDoe);
|
||||
assertThat(events.poll(5L, TimeUnit.SECONDS)).isEqualTo(jonDoe);
|
||||
|
||||
Person janeDoe = Person.newPerson(2L, "Jane", "Doe", null, Gender.FEMALE);
|
||||
|
||||
janeDoe = this.personRepository.save(janeDoe);
|
||||
|
||||
assertThat(this.personRepository.findById(janeDoe.getId()).orElse(null)).isEqualTo(janeDoe);
|
||||
assertThat(events.poll(5L, TimeUnit.SECONDS)).isEqualTo(janeDoe);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableContinuousQueries
|
||||
@Import(GemFireClientConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@ContinuousQuery(name = "PersonEvents", query = "SELECT * FROM /People")
|
||||
public void peopleEventHandler(CqEvent event) {
|
||||
|
||||
Optional.ofNullable(event)
|
||||
.map(CqEvent::getNewValue)
|
||||
.filter(newValue -> newValue instanceof Person)
|
||||
.map(newValue -> (Person) newValue)
|
||||
.ifPresent(events::offer);
|
||||
}
|
||||
}
|
||||
|
||||
@ClientCacheApplication(logLevel = "error", subscriptionEnabled = true)
|
||||
@EnableClusterConfiguration
|
||||
@EnableEntityDefinedRegions(basePackageClasses = Person.class)
|
||||
@EnableGemfireRepositories(basePackageClasses = PersonRepository.class)
|
||||
static class GemFireClientConfiguration {
|
||||
|
||||
@Bean
|
||||
ClientCacheConfigurer clientCachePoolPortConfigurer(
|
||||
@Value("${" + GEMFIRE_CACHE_SERVER_PORT_PROPERTY + ":40404}") int port) {
|
||||
|
||||
return (bean, clientCacheFactoryBean) -> clientCacheFactoryBean.setServers(
|
||||
Collections.singletonList(new ConnectionEndpoint("localhost", port)));
|
||||
}
|
||||
}
|
||||
|
||||
@CacheServerApplication(name = "EnableContinuousQueriesWithClusterConfigurationIntegrationTests", logLevel = "error")
|
||||
static class GemFireServerConfiguration {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
AnnotationConfigApplicationContext applicationContext =
|
||||
new AnnotationConfigApplicationContext(GemFireServerConfiguration.class);
|
||||
|
||||
applicationContext.registerShutdownHook();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<configuration debug="false">
|
||||
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
@@ -13,14 +13,16 @@
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework" level="${logback.log.level:-error}"/>
|
||||
<logger name="ch.qos.logback" level="${logback.log.level:-ERROR}"/>
|
||||
|
||||
<logger name="org.springframework" level="${logback.log.level:-ERROR}"/>
|
||||
|
||||
<logger name="org.springframework.data.gemfire.config.annotation.support.RegionDataAccessTracingAspect" level="trace">
|
||||
<appender-ref ref="testAppender"/>
|
||||
</logger>
|
||||
|
||||
<root level="${logback.log.level:-error}">
|
||||
<appender-ref ref="console" />
|
||||
<root level="${logback.log.level:-ERROR}">
|
||||
<appender-ref ref="console"/>
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
|
||||
Reference in New Issue
Block a user