Add example code for Multi-Site Caching Sample.
Resolves gh-80.
This commit is contained in:
2
spring-geode-samples/caching/multi-site/lombok.config
Normal file
2
spring-geode-samples/caching/multi-site/lombok.config
Normal file
@@ -0,0 +1,2 @@
|
||||
# This file is generated by the 'io.freefair.lombok' Gradle plugin
|
||||
config.stopBubbling = true
|
||||
@@ -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'
|
||||
}
|
||||
@@ -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 { }
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
server.port=8080
|
||||
spring.application.name=BootGeodeMultiSiteCachingClientApplication-Site1
|
||||
spring.data.gemfire.pool.locators=localhost[11235]
|
||||
@@ -0,0 +1,3 @@
|
||||
server.port=9090
|
||||
spring.application.name=BootGeodeMultiSiteCachingClientApplication-Site2
|
||||
spring.data.gemfire.pool.locators=localhost[12480]
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user