Add example code for Multi-Site Caching Sample.

Resolves gh-80.
This commit is contained in:
John Blum
2020-04-15 22:52:14 -07:00
parent 39d81d339a
commit 0efed96dcd
13 changed files with 779 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
# This file is generated by the 'io.freefair.lombok' Gradle plugin
config.stopBubbling = true

View File

@@ -0,0 +1,27 @@
plugins {
id "io.freefair.lombok" version "5.0.0-rc6"
}
apply plugin: 'io.spring.convention.spring-sample-boot'
description = "Spring Geode Sample for Multi-Site Caching."
dependencies {
compile project(":spring-geode-starter")
compile "org.assertj:assertj-core"
compile "org.projectlombok:lombok"
compile "org.springframework.boot:spring-boot-starter-web"
runtime project(":spring-geode-starter-logging")
testCompile project(":spring-geode-starter-test")
testCompile "org.springframework.boot:spring-boot-starter-test"
}
bootJar {
mainClassName = 'example.app.caching.multisite.client.BootGeodeMultiSiteCachingClientApplication'
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2020 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
*
* https://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 example.app.caching.multisite.client;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.gemfire.config.annotation.ClientCacheApplication;
import org.springframework.data.gemfire.config.annotation.EnableCachingDefinedRegions;
/**
* The {@link BootGeodeMultiSiteCachingClientApplication} class is a Spring Boot, Apache Geode {@link ClientCache}
* Web application that can be configured to connect to 1 of many Apache Geode clusters.
*
* @author John Blum
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.boot.SpringApplication
* @see org.springframework.boot.autoconfigure.SpringBootApplication
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.data.gemfire.config.annotation.ClientCacheApplication
* @since 1.3.0
*/
@SpringBootApplication
@SuppressWarnings("unused")
public class BootGeodeMultiSiteCachingClientApplication {
public static void main(String[] args) {
SpringApplication.run(BootGeodeMultiSiteCachingClientApplication.class, args);
}
@ClientCacheApplication
@EnableCachingDefinedRegions
static class GeodeClientConfiguration { }
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2020 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
*
* https://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 example.app.caching.multisite.client.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.gemfire.mapping.annotation.Region;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
/**
* {@link Customer} is an Abstract Data Type (ADT) modeling a customer in a customer service application.
*
* @author John Blum
* @see org.springframework.data.annotation.Id
* @see org.springframework.data.gemfire.mapping.annotation.Region
* @since 1.3.0
*/
@Region("Customers")
@EqualsAndHashCode
@ToString(of = "name")
@RequiredArgsConstructor(staticName = "newCustomer")
public class Customer {
@Id @Getter @NonNull
private Long id;
@Getter @NonNull
private String name;
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2020 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
*
* https://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 example.app.caching.multisite.client.service;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import example.app.caching.multisite.client.model.Customer;
import example.app.caching.multisite.client.util.ThreadUtils;
/**
* {@link CustomerService} is a Spring application {@link Service} component used to service interactions with
* {@link Customer customers}.
*
* This service class employs the {@literal Look-Aside Caching} Pattern to lookups of {@link Customer}
* by {@link String name} since it is not common for the {@link Customer Customer's} {@link String name}
* to change frequently.
*
* Additionally, the {@link #findBy(String)} {@link CustomerService} operations simulates an expensive,
* or resource-intensive operation by introducing a delay.
*
* @author John Blum
* @see org.springframework.cache.annotation.Cacheable
* @see org.springframework.stereotype.Service
* @see example.app.caching.multisite.client.model.Customer
* @since 1.3.0
*/
@Service
public class CustomerService {
private static final long SLEEP_IN_SECONDS = 5;
private final AtomicBoolean cacheMiss = new AtomicBoolean(false);
private final AtomicLong customerId = new AtomicLong(0L);
private volatile Long sleepInSeconds;
@Cacheable("CustomersByName")
public Customer findBy(String name) {
setCacheMiss();
ThreadUtils.safeSleep(name, Duration.ofSeconds(getSleepInSeconds()));
return Customer.newCustomer(this.customerId.incrementAndGet(), name);
}
public boolean isCacheMiss() {
return this.cacheMiss.compareAndSet(true, false);
}
protected void setCacheMiss() {
this.cacheMiss.set(true);
}
public Long getSleepInSeconds() {
Long sleepInSeconds = this.sleepInSeconds;
return sleepInSeconds != null ? sleepInSeconds : SLEEP_IN_SECONDS;
}
public void setSleepInSeconds(Long seconds) {
this.sleepInSeconds = seconds;
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2020 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
*
* https://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 example.app.caching.multisite.client.util;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import org.springframework.lang.NonNull;
/**
* Abstract utility class for {@link Thread Threading}.
*
* @author John Blum
* @see java.lang.Thread
* @see java.time.Duration
* @since 1.3.0
*/
public abstract class ThreadUtils {
@SuppressWarnings("all")
public static void safeSleep(@NonNull Object lock, @NonNull Duration duration) {
boolean interrupted = false;
long timeout = System.currentTimeMillis() + duration.toMillis();
while (System.currentTimeMillis() < timeout) {
synchronized (lock) {
try {
lock.wait(duration.toMillis(), 0);
}
catch (InterruptedException ignore) {
interrupted = true;
}
}
}
if (interrupted) {
Thread.currentThread().interrupt();
}
}
public static boolean waitFor(@NonNull Duration duration, @NonNull Condition condition) {
return waitFor(duration, duration.toMillis() / 10L, condition);
}
@SuppressWarnings("all")
public static boolean waitFor(Duration duration, long interval, Condition condition) {
long timeout = System.currentTimeMillis() + duration.toMillis();
try {
while (!condition.evaluate() && System.currentTimeMillis() < timeout) {
synchronized (condition) {
TimeUnit.MILLISECONDS.timedWait(condition, interval);
}
}
}
catch (InterruptedException cause) {
Thread.currentThread().interrupt();
}
return condition.evaluate();
}
@FunctionalInterface
public interface Condition {
boolean evaluate();
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2020 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
*
* https://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 example.app.caching.multisite.client.web;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import example.app.caching.multisite.client.model.Customer;
import example.app.caching.multisite.client.service.CustomerService;
/**
* {@link CustomerController} is a Spring Web application {@link RestController} component used to
* service user requests for {@link Customer customers} through a REST API (interface) over HTTP.
*
* @author John Blum
* @see org.springframework.core.env.Environment
* @see org.springframework.web.bind.annotation.GetMapping
* @see org.springframework.web.bind.annotation.RestController
* @see example.app.caching.multisite.client.model.Customer
* @see example.app.caching.multisite.client.service.CustomerService
* @since 1.3.0
*/
@RestController
public class CustomerController {
@Autowired
private CustomerService customerService;
@Autowired
private Environment environment;
@GetMapping("/customers/{name}")
public CustomerHolder searchBy(@PathVariable String name) {
return CustomerHolder.from(this.customerService.findBy(name))
.setCacheMiss(this.customerService.isCacheMiss());
}
@GetMapping("/ping")
public String pingPong() {
return "PONG";
}
@GetMapping("/")
public String home() {
return String.format("%s is running!",
environment.getProperty("spring.application.name", "UNKNOWN"));
}
public static class CustomerHolder {
public static CustomerHolder from(Customer customer) {
return new CustomerHolder(customer);
}
private boolean cacheMiss = true;
private final Customer customer;
protected CustomerHolder(Customer customer) {
Assert.notNull(customer, "Customer must not be null");
this.customer = customer;
}
public CustomerHolder setCacheMiss(boolean cacheMiss) {
this.cacheMiss = cacheMiss;
return this;
}
public boolean isCacheMiss() {
return this.cacheMiss;
}
public Customer getCustomer() {
return customer;
}
}
}

View File

@@ -0,0 +1,199 @@
/*
* Copyright 2020 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
*
* https://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 example.app.caching.multisite.server;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.server.CacheServer;
import org.apache.geode.cache.wan.GatewayReceiver;
import org.apache.geode.cache.wan.GatewaySender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.data.gemfire.PeerRegionFactoryBean;
import org.springframework.data.gemfire.ReplicatedRegionFactoryBean;
import org.springframework.data.gemfire.config.annotation.CacheServerApplication;
import org.springframework.data.gemfire.config.annotation.EnableLocator;
import org.springframework.data.gemfire.config.annotation.EnableManager;
import org.springframework.data.gemfire.config.annotation.RegionConfigurer;
import org.springframework.data.gemfire.util.ArrayUtils;
import org.springframework.data.gemfire.util.RegionUtils;
import org.springframework.data.gemfire.wan.GatewayReceiverFactoryBean;
import org.springframework.data.gemfire.wan.GatewaySenderFactoryBean;
import example.app.caching.multisite.client.model.Customer;
/**
* The {@link BootGeodeMultiSiteCachingServerApplication} class is a Spring Boot, Apache Geode {@literal peer}
* {@link CacheServer} application serving cache clients.
*
* This Apache Geode peer member server data node can also connect to a remote cluster over a WAN using Gateways.
*
* @author John Blum
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.server.CacheServer
* @see org.apache.geode.cache.wan.GatewayReceiver
* @see org.apache.geode.cache.wan.GatewaySender
* @see org.springframework.boot.ApplicationRunner
* @see org.springframework.boot.autoconfigure.SpringBootApplication
* @see org.springframework.boot.builder.SpringApplicationBuilder
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.Profile
* @see org.springframework.data.gemfire.ReplicatedRegionFactoryBean
* @see org.springframework.data.gemfire.config.annotation.CacheServerApplication
* @see org.springframework.data.gemfire.config.annotation.EnableLocator
* @see org.springframework.data.gemfire.config.annotation.EnableManager
* @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
* @see org.springframework.data.gemfire.wan.GatewayReceiverFactoryBean
* @see org.springframework.data.gemfire.wan.GatewaySenderFactoryBean
* @since 1.3.0
*/
@SpringBootApplication
@SuppressWarnings("unused")
public class BootGeodeMultiSiteCachingServerApplication {
private static final boolean PERSISTENT = false;
private static final int GATEWAY_RECEIVER_END_PORT = 29779;
private static final int GATEWAY_RECEIVER_START_PORT = 13339;
private static final String CUSTOMERS_BY_NAME_REGION = "CustomersByName";
private static final String GATEWAY_RECEIVER_HOSTNAME_FOR_SENDERS = "localhost";
public static void main(String[] args) {
new SpringApplicationBuilder(BootGeodeMultiSiteCachingServerApplication.class)
.web(WebApplicationType.NONE)
.build()
.run(args);
}
@CacheServerApplication(name = "BootGeodeMultiSiteCachingServerApplication", port = 0)
static class GeodeServerConfiguration {
@Bean(CUSTOMERS_BY_NAME_REGION)
ReplicatedRegionFactoryBean<String, Customer> customersByNameRegion(Cache cache,
@Autowired(required = false) List<RegionConfigurer> regionConfigurers) {
ReplicatedRegionFactoryBean<String, Customer> customersByName = new ReplicatedRegionFactoryBean<>();
customersByName.setCache(cache);
customersByName.setPersistent(PERSISTENT);
customersByName.setRegionConfigurers(regionConfigurers);
return customersByName;
}
@Bean
ApplicationRunner geodeClusterObjectsBootstrappedAssertionRunner(Environment environment, Cache cache,
Region<?, ?> customersByName, GatewayReceiver gatewayReceiver, GatewaySender gatewaySender) {
return args -> {
assertThat(cache).isNotNull();
assertThat(cache.getName()).startsWith(BootGeodeMultiSiteCachingServerApplication.class.getSimpleName());
assertThat(customersByName).isNotNull();
assertThat(customersByName.getAttributes()).isNotNull();
assertThat(customersByName.getAttributes().getDataPolicy()).isEqualTo(DataPolicy.REPLICATE);
assertThat(customersByName.getAttributes().getGatewaySenderIds()).containsExactly(gatewaySender.getId());
assertThat(customersByName.getName()).isEqualTo(CUSTOMERS_BY_NAME_REGION);
assertThat(customersByName.getRegionService()).isEqualTo(cache);
assertThat(cache.getRegion(RegionUtils.toRegionPath(CUSTOMERS_BY_NAME_REGION))).isEqualTo(customersByName);
assertThat(gatewayReceiver).isNotNull();
assertThat(gatewayReceiver.isRunning()).isTrue();
assertThat(cache.getGatewayReceivers()).containsExactly(gatewayReceiver);
assertThat(gatewaySender).isNotNull();
assertThat(gatewaySender.isRunning()).isTrue();
assertThat(cache.getGatewaySenders().stream().map(GatewaySender::getId).collect(Collectors.toSet()))
.containsExactly(gatewaySender.getId());
System.err.printf("Apache Geode Cluster [%s] configured and bootstrapped successfully!%n",
environment.getProperty("spring.application.name", "UNKNOWN"));
};
}
}
@Configuration
@EnableLocator
@EnableManager(start = true)
@Profile("locator-manager")
static class GeodeLocatorManagerConfiguration { }
@Configuration
@Profile("gateway-receiver")
static class GeodeGatewayReceiverConfiguration {
@Bean
GatewayReceiverFactoryBean gatewayReceiver(Cache cache) {
GatewayReceiverFactoryBean gatewayReceiver = new GatewayReceiverFactoryBean(cache);
gatewayReceiver.setHostnameForSenders(GATEWAY_RECEIVER_HOSTNAME_FOR_SENDERS);
gatewayReceiver.setStartPort(GATEWAY_RECEIVER_START_PORT);
gatewayReceiver.setEndPort(GATEWAY_RECEIVER_END_PORT);
return gatewayReceiver;
}
}
@Configuration
@Profile("gateway-sender")
static class GeodeGatewaySenderConfiguration {
@Bean
GatewaySenderFactoryBean customersByNameGatewaySender(Cache cache,
@Value("${geode.distributed-system.remote.id:1}") int remoteDistributedSystemId) {
GatewaySenderFactoryBean gatewaySender = new GatewaySenderFactoryBean(cache);
gatewaySender.setPersistent(PERSISTENT);
gatewaySender.setRemoteDistributedSystemId(remoteDistributedSystemId);
return gatewaySender;
}
@Bean
RegionConfigurer customersByNameConfigurer(GatewaySender gatewaySender) {
return new RegionConfigurer() {
@Override
public void configure(String beanName, PeerRegionFactoryBean<?, ?> regionBean) {
if (CUSTOMERS_BY_NAME_REGION.equals(beanName)) {
regionBean.setGatewaySenders(ArrayUtils.asArray(gatewaySender));
}
}
};
}
}
}

View File

@@ -0,0 +1,3 @@
server.port=8080
spring.application.name=BootGeodeMultiSiteCachingClientApplication-Site1
spring.data.gemfire.pool.locators=localhost[11235]

View File

@@ -0,0 +1,3 @@
server.port=9090
spring.application.name=BootGeodeMultiSiteCachingClientApplication-Site2
spring.data.gemfire.pool.locators=localhost[12480]

View File

@@ -0,0 +1,7 @@
gemfire.distributed-system-id=10
gemfire.remote-locators=localhost[12480]
geode.distributed-system.remote.id=20
spring.application.name=BootGeodeMultiSiteCachingServerApplication-Site1
spring.profiles.include=locator-manager,gateway-receiver,gateway-sender
spring.data.gemfire.locator.port=11235
spring.data.gemfire.manager.port=1199

View File

@@ -0,0 +1,7 @@
gemfire.distributed-system-id=20
gemfire.remote-locators=localhost[11235]
geode.distributed-system.remote.id=10
spring.application.name=BootGeodeMultiSiteCachingServerApplication-Site2
spring.profiles.include=locator-manager,gateway-receiver,gateway-sender
spring.data.gemfire.locator.port=12480
spring.data.gemfire.manager.port=2299

View File

@@ -0,0 +1,172 @@
/*
* Copyright 2020 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
*
* https://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 example.app.caching.multisite;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.time.Duration;
import java.util.Optional;
import java.util.Properties;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport;
import org.springframework.data.gemfire.tests.process.ProcessWrapper;
import example.app.caching.multisite.client.BootGeodeMultiSiteCachingClientApplication;
import example.app.caching.multisite.client.model.Customer;
import example.app.caching.multisite.client.service.CustomerService;
import example.app.caching.multisite.client.util.ThreadUtils;
import example.app.caching.multisite.server.BootGeodeMultiSiteCachingServerApplication;
/**
* Integration Tests testing the Multi-Site (WAN) Caching Example.
*
* @author John Blum
* @see org.junit.Test
* @see java.util.Properties
* @see org.springframework.boot.SpringApplication
* @see org.springframework.boot.builder.SpringApplicationBuilder
* @see org.springframework.context.ConfigurableApplicationContext
* @see org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport
* @see org.springframework.data.gemfire.tests.process.ProcessWrapper
* @see example.app.caching.multisite.client.BootGeodeMultiSiteCachingClientApplication
* @see example.app.caching.multisite.client.model.Customer
* @see example.app.caching.multisite.client.service.CustomerService
* @see example.app.caching.multisite.server.BootGeodeMultiSiteCachingServerApplication
* @since 1.3.0
*/
public class MultiSiteCachingIntegrationTests extends ForkingClientServerIntegrationTestsSupport {
private static int locatorPortClusterOne;
private static int locatorPortClusterTwo;
private static ProcessWrapper geodeClusterOne;
private static ProcessWrapper geodeClusterTwo;
private static final String HOSTNAME = "localhost";
@BeforeClass
public static void startGeodeClusters() throws IOException {
locatorPortClusterOne = findAndReserveAvailablePort();
locatorPortClusterTwo = findAndReserveAvailablePort();
geodeClusterOne = run(BootGeodeMultiSiteCachingServerApplication.class,
"-Dspring.profiles.active=server-site1",
String.format("-Dspring.data.gemfire.locator.port=%d", locatorPortClusterOne),
"-Dspring.data.gemfire.manager.start=false",
String.format("-Dgemfire.remote-locators=%s[%d]", HOSTNAME, locatorPortClusterTwo));
waitForServerToStart("localhost", locatorPortClusterOne);
geodeClusterTwo = run(BootGeodeMultiSiteCachingServerApplication.class,
"-Dspring.profiles.active=server-site2",
String.format("-Dspring.data.gemfire.locator.port=%d", locatorPortClusterTwo),
"-Dspring.data.gemfire.manager.start=false",
String.format("-Dgemfire.remote-locators=%s[%d]", HOSTNAME, locatorPortClusterOne));
waitForServerToStart("localhost", locatorPortClusterTwo);
}
@AfterClass
public static void stopGeodeClusters() {
stop(geodeClusterOne);
stop(geodeClusterTwo);
}
private ConfigurableApplicationContext newApplicationContext(int locatorPort, String... springProfiles) {
Properties configuration = new Properties();
configuration.setProperty("spring.data.gemfire.pool.locators",
String.format("%s[%d]", HOSTNAME, locatorPort));
System.setProperty("spring.data.gemfire.pool.locators",
configuration.getProperty("spring.data.gemfire.pool.locators"));
SpringApplication springApplication =
new SpringApplicationBuilder(BootGeodeMultiSiteCachingClientApplication.class)
.headless(true)
.profiles(springProfiles)
//.properties(configuration)
.registerShutdownHook(true)
.build();
return springApplication.run();
}
private void close(ConfigurableApplicationContext applicationContext) {
Optional.ofNullable(applicationContext)
.ifPresent(it -> {
it.close();
ThreadUtils.waitFor(Duration.ofSeconds(5), () -> !(it.isActive() || it.isRunning()));
});
}
@Test
public void clientOfClusterTwoResultsInCacheHit() {
ConfigurableApplicationContext applicationContext = null;
try {
applicationContext = newApplicationContext(locatorPortClusterOne, "client-site1");
assertThat(applicationContext).isNotNull();
assertThat(applicationContext.isActive()).isTrue();
CustomerService customerService = applicationContext.getBean(CustomerService.class);
customerService.setSleepInSeconds(2L);
Customer jonDoe = customerService.findBy("Jon Doe");
assertThat(jonDoe).isNotNull();
assertThat(jonDoe.getName()).isEqualTo("Jon Doe");
assertThat(customerService.isCacheMiss()).isTrue();
assertThat(customerService.findBy(jonDoe.getName())).isEqualTo(jonDoe);
assertThat(customerService.isCacheMiss()).isFalse();
close(applicationContext);
applicationContext = newApplicationContext(locatorPortClusterTwo, "client-site2");
assertThat(applicationContext).isNotNull();
assertThat(applicationContext.isActive()).isTrue();
customerService = applicationContext.getBean(CustomerService.class);
customerService.setSleepInSeconds(2L);
Customer cachedJonDoe = customerService.findBy(jonDoe.getName());
assertThat(cachedJonDoe).isNotNull();
assertThat(cachedJonDoe).isNotSameAs(jonDoe);
assertThat(cachedJonDoe).isEqualTo(jonDoe);
assertThat(customerService.isCacheMiss()).isFalse();
}
finally {
close(applicationContext);
}
}
}