/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.data.gemfire;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.geode.cache.client.Pool;
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.Qualifier;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.annotation.Id;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.client.PoolFactoryBean;
import org.springframework.data.gemfire.mapping.annotation.Region;
import org.springframework.data.gemfire.process.ProcessExecutor;
import org.springframework.data.gemfire.process.ProcessWrapper;
import org.springframework.data.gemfire.server.CacheServerFactoryBean;
import org.springframework.data.gemfire.support.ConnectionEndpoint;
import org.springframework.data.gemfire.support.ConnectionEndpointList;
import org.springframework.data.gemfire.test.support.ClientServerIntegrationTestsSupport;
import org.springframework.data.gemfire.util.PropertiesBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
/**
* Integrations tests for {@link GemfireTemplate} testing proper function and behavior of executing (OQL) queries
* from a cache client application using the {@link GemfireTemplate} to a cluster of GemFire servers that have
* been grouped according to business function and data access, primarily to distribute the load.
*
* Each GemFire {@link Pool} is configured to target a specific server group. Each group of servers in the cluster
* defines specific {@link Region Regions} to manage data independently and separately from other data that might
* garner high frequency access.
*
* Spring Data GemFire's {@link GemfireTemplate} should intelligently employ the right
* {@link org.apache.geode.cache.query.QueryService} configured with the {@link Region Region's} {@link Pool}
* meta-data when executing the query in order to ensure the right servers containing the {@link Region Region's}
* with the data of interest are targeted.
*
* @author John Blum
* @see org.springframework.data.gemfire.GemfireTemplate
* @see <a href="https://jira.spring.io/browse/SGF-555">Repository queries on client Regions associated with a Pool configured with a specified server group can lead to a RegionNotFoundException.</a>
* @since 1.9.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes =
GemfireTemplateQueriesOnGroupedPooledClientCacheRegionsIntegrationTests.GemFireClientCacheConfiguration.class)
@SuppressWarnings("unused")
public class GemfireTemplateQueriesOnGroupedPooledClientCacheRegionsIntegrationTests
extends ClientServerIntegrationTestsSupport{
private static ProcessWrapper serverOne;
private static ProcessWrapper serverTwo;
@Autowired
@Qualifier("catsTemplate")
private GemfireTemplate catsTemplate;
@Autowired
@Qualifier("dogsTemplate")
private GemfireTemplate dogsTemplate;
@BeforeClass
public static void setupGemFireCluster() throws Exception {
serverOne = ProcessExecutor.launch(createDirectory("serverOne"), GemFireCacheServerOneConfiguration.class);
waitForServerToStart("localhost", 41414);
serverTwo = ProcessExecutor.launch(createDirectory("serverTwo"), GemFireCacheServerTwoConfiguration.class);
waitForServerToStart("localhost", 42424);
}
@AfterClass
public static void shutdownGemFireCluster() {
serverOne.stop();
serverTwo.stop();
}
@Test
public void findsAllCats() {
List<String> catNames = catsTemplate.<String>find("SELECT c.name FROM /Cats c").asList();
assertThat(catNames).isNotNull();
assertThat(catNames.size()).isEqualTo(5);
assertThat(catNames).containsAll(Arrays.asList("Grey", "Patchit", "Tyger", "Molly", "Sammy"));
}
@Test
public void findsAllDogs() {
List<String> dogNames = dogsTemplate.<String>find("SELECT d.name FROM /Dogs d").asList();
assertThat(dogNames).isNotNull();
assertThat(dogNames.size()).isEqualTo(2);
assertThat(dogNames).containsAll(Arrays.asList("Spuds", "Maha"));
}
@Data
@Region("Cats")
@RequiredArgsConstructor(staticName = "newCat")
static class Cat {
@Id @NonNull private String name;
}
@Data
@Region("Dogs")
@RequiredArgsConstructor(staticName = "newDog")
static class Dog {
@Id @NonNull private String name;
}
@Configuration
static class GemFireClientCacheConfiguration {
Properties gemfireProperties() {
return PropertiesBuilder.create()
.setProperty("name", applicationName())
.setProperty("log-level", logLevel())
.build();
}
String applicationName() {
return GemFireClientCacheConfiguration.class.getName();
}
String logLevel() {
return System.getProperty("spring.data.gemfire.log.level", GEMFIRE_LOG_LEVEL);
}
@Bean
ClientCacheFactoryBean gemfireCache() {
ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setPoolName("ServerOnePool");
gemfireCache.setProperties(gemfireProperties());
return gemfireCache;
}
@Bean(name = "ServerOnePool")
PoolFactoryBean serverOnePool() {
PoolFactoryBean serverOnePool = new PoolFactoryBean();
serverOnePool.setMaxConnections(2);
serverOnePool.setPingInterval(TimeUnit.SECONDS.toMillis(5));
serverOnePool.setReadTimeout(Long.valueOf(TimeUnit.SECONDS.toMillis(20)).intValue());
serverOnePool.setRetryAttempts(1);
serverOnePool.setServerGroup("serverOne");
serverOnePool.setLocators(ConnectionEndpointList.from(newConnectionEndpoint("localhost", 11235)));
return serverOnePool;
}
@Bean(name = "ServerTwoPool")
PoolFactoryBean serverTwoPool() {
PoolFactoryBean serverOnePool = new PoolFactoryBean();
serverOnePool.setMaxConnections(2);
serverOnePool.setPingInterval(TimeUnit.SECONDS.toMillis(5));
serverOnePool.setReadTimeout(Long.valueOf(TimeUnit.SECONDS.toMillis(20)).intValue());
serverOnePool.setRetryAttempts(1);
serverOnePool.setServerGroup("serverTwo");
serverOnePool.setLocators(ConnectionEndpointList.from(newConnectionEndpoint("localhost", 11235)));
return serverOnePool;
}
@Bean(name = "Cats")
ClientRegionFactoryBean<String, Cat> catsRegion(GemFireCache gemfireCache,
@Qualifier("ServerOnePool") Pool serverOnePool) {
ClientRegionFactoryBean<String, Cat> catsRegion = new ClientRegionFactoryBean<String, Cat>();
catsRegion.setCache(gemfireCache);
catsRegion.setClose(false);
catsRegion.setPoolName(serverOnePool.getName());
catsRegion.setShortcut(ClientRegionShortcut.PROXY);
return catsRegion;
}
@Bean(name = "Dogs")
ClientRegionFactoryBean<String, Cat> dogsRegion(GemFireCache gemfireCache,
@Qualifier("ServerTwoPool") Pool serverTwoPool) {
ClientRegionFactoryBean<String, Cat> dogsRegion = new ClientRegionFactoryBean<String, Cat>();
dogsRegion.setCache(gemfireCache);
dogsRegion.setClose(false);
dogsRegion.setPoolName(serverTwoPool.getName());
dogsRegion.setShortcut(ClientRegionShortcut.PROXY);
return dogsRegion;
}
@Bean
@DependsOn("Cats")
GemfireTemplate catsTemplate(GemFireCache gemfireCache) {
return new GemfireTemplate(gemfireCache.getRegion("Cats"));
}
@Bean
@DependsOn("Dogs")
GemfireTemplate dogsTemplate(GemFireCache gemfireCache) {
return new GemfireTemplate(gemfireCache.getRegion("Dogs"));
}
ConnectionEndpoint newConnectionEndpoint(String host, int port) {
return new ConnectionEndpoint(host, port);
}
}
static abstract class AbstractGemFireCacheServerConfiguration {
Properties gemfireProperties() {
return PropertiesBuilder.create()
.setProperty("name", applicationName())
.setProperty("mcast-port", "0")
.setProperty("log-level", logLevel())
.setProperty("locators", "localhost[11235]")
.setProperty("enable-cluster-configuration", "false")
.setProperty("groups", groups())
.setProperty("start-locator", startLocator())
.setProperty("use-cluster-configuration", "false")
.build();
}
String applicationName() {
return getClass().getName();
}
abstract String groups();
String logLevel() {
return System.getProperty("spring.data.gemfire.log.level", GEMFIRE_LOG_LEVEL);
}
String startLocator() {
return "";
}
@Bean
CacheFactoryBean gemfireCache() {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
return gemfireCache;
}
@Bean
CacheServerFactoryBean gemfireCacheServer(Cache gemfireCache) {
CacheServerFactoryBean gemfireCacheServer = new CacheServerFactoryBean();
gemfireCacheServer.setAutoStartup(true);
gemfireCacheServer.setCache(gemfireCache);
gemfireCacheServer.setMaxTimeBetweenPings(Long.valueOf(TimeUnit.SECONDS.toMillis(60)).intValue());
gemfireCacheServer.setPort(cacheServerPort());
return gemfireCacheServer;
}
abstract int cacheServerPort();
}
@Configuration
@SuppressWarnings("all")
static class GemFireCacheServerOneConfiguration extends AbstractGemFireCacheServerConfiguration {
@Resource(name = "Cats")
private org.apache.geode.cache.Region<String, Cat> cats;
Cat save(Cat cat) {
cats.put(cat.getName(), cat);
return cat;
}
@PostConstruct
public void postConstruct() {
save(Cat.newCat("Grey"));
save(Cat.newCat("Patchit"));
save(Cat.newCat("Tyger"));
save(Cat.newCat("Molly"));
save(Cat.newCat("Sammy"));
}
@Override
int cacheServerPort() {
return 41414;
}
@Override
String groups() {
return "serverOne";
}
@Override
String startLocator() {
return "localhost[11235]";
}
@Bean(name = "Cats")
LocalRegionFactoryBean catsRegion(Cache gemfireCache) {
LocalRegionFactoryBean catsRegion = new LocalRegionFactoryBean();
catsRegion.setCache(gemfireCache);
catsRegion.setClose(false);
catsRegion.setPersistent(false);
return catsRegion;
}
public static void main(String[] args) {
new AnnotationConfigApplicationContext(GemFireCacheServerOneConfiguration.class)
.registerShutdownHook();
}
}
@Configuration
@SuppressWarnings("all")
static class GemFireCacheServerTwoConfiguration extends AbstractGemFireCacheServerConfiguration {
@Resource(name = "Dogs")
private org.apache.geode.cache.Region<String, Dog> dogs;
Dog save(Dog dog) {
dogs.put(dog.getName(), dog);
return dog;
}
@PostConstruct
public void postConstruct() {
save(Dog.newDog("Spuds"));
save(Dog.newDog("Maha"));
}
@Override
int cacheServerPort() {
return 42424;
}
@Override
String groups() {
return "serverTwo";
}
@Bean(name = "Dogs")
LocalRegionFactoryBean<String, Dog> dogsRegion(Cache gemfireCache) {
LocalRegionFactoryBean<String, Dog> dogsRegion = new LocalRegionFactoryBean<String, Dog>();
dogsRegion.setCache(gemfireCache);
dogsRegion.setClose(false);
dogsRegion.setPersistent(false);
return dogsRegion;
}
public static void main(String[] args) {
new AnnotationConfigApplicationContext(GemFireCacheServerTwoConfiguration.class)
.registerShutdownHook();
}
}
}