Rename gemfire-spring-boot-starter module to gemfire-spring-boot.

Rename geode-spring-boot-starter module to geode-spring-boot.
This commit is contained in:
John Blum
2018-05-30 01:32:03 -07:00
parent 65004813b5
commit 65ae4cc59d
71 changed files with 1 additions and 1 deletions

View File

@@ -0,0 +1,38 @@
apply plugin: 'io.spring.convention.spring-module'
apply from: IDE_GRADLE
description = "Spring Boot for Apache Geode"
repositories {
maven { url "https://repo.spring.io/libs-snapshot" }
}
dependencies {
compile "org.springframework:spring-context-support"
compile "org.springframework:spring-jcl"
compile "org.springframework.data:spring-data-geode:$springDataGeodeVersion"
compile("org.springframework.boot:spring-boot-starter") {
exclude group: "org.springframework.boot", module: "spring-boot-starter-logging";
}
testCompile "org.assertj:assertj-core"
testCompile "junit:junit"
testCompile "org.mockito:mockito-core"
testCompile "org.projectlombok:lombok"
testCompile "edu.umd.cs.mtc:multithreadedtc"
testCompile("org.springframework.boot:spring-boot-starter-test") {
exclude group: "org.springframework.boot", module: "spring-boot-starter-logging";
}
testCompile slf4jDependencies
testCompile "org.springframework.data:spring-test-data-geode"
testRuntime "javax.cache:cache-api"
// integrationTestRuntime "org.springframework.shell:spring-shell"
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2018 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.function.config;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
import java.util.Optional;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
/**
* The AbstractFunctionExecutionAutoConfigurationExtension class...
*
* @author John Blum
* @since 1.0.0
*/
// TODO replace this class once SD Lovelace is GA and SBDG is rebased on SD Lovelace
public abstract class AbstractFunctionExecutionAutoConfigurationExtension
extends FunctionExecutionBeanDefinitionRegistrar implements BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
registerBeanDefinitions(newAnnotationBasedFunctionExecutionConfigurationSource(annotationMetadata), registry);
}
@SuppressWarnings("unused")
private AbstractFunctionExecutionConfigurationSource newAnnotationBasedFunctionExecutionConfigurationSource(
AnnotationMetadata annotationMetadata) {
StandardAnnotationMetadata metadata =
new StandardAnnotationMetadata(getConfiguration(), true);
return new AnnotationFunctionExecutionConfigurationSource(metadata) {
@Override
public Iterable<String> getBasePackages() {
return AutoConfigurationPackages.get(getBeanFactory());
}
};
}
@Override
@SuppressWarnings("all")
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@SuppressWarnings("all")
protected BeanFactory getBeanFactory() {
return Optional.ofNullable(this.beanFactory)
.orElseThrow(() -> newIllegalStateException("BeanFactory was not properly configured"));
}
protected abstract Class<?> getConfiguration();
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2018 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.function.config;
/**
* The GemFireFunctionExecutionAutoConfigurationRegistrar class...
*
* @author John Blum
* @since 1.0.0
*/
public class GemFireFunctionExecutionAutoConfigurationRegistrar
extends AbstractFunctionExecutionAutoConfigurationExtension {
@Override
protected Class<?> getConfiguration() {
return EnableGemfireFunctionExecutionsConfiguration.class;
}
@EnableGemfireFunctionExecutions
private static class EnableGemfireFunctionExecutionsConfiguration { }
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
import java.util.Optional;
import javax.annotation.PostConstruct;
import org.apache.geode.cache.GemFireCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizers;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.gemfire.cache.GemfireCacheManager;
import org.springframework.data.gemfire.cache.config.EnableGemfireCaching;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} for Spring's Cache Abstraction
* using Apache Geode as the caching provider.
*
* @author John Blum
* @see javax.annotation.PostConstruct
* @see org.apache.geode.cache.GemFireCache
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.boot.autoconfigure.cache.CacheManagerCustomizers
* @see org.springframework.boot.autoconfigure.cache.CacheProperties
* @see org.springframework.cache.CacheManager
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.data.gemfire.cache.GemfireCacheManager
* @see org.springframework.data.gemfire.cache.config.EnableGemfireCaching
* @see org.springframework.geode.boot.autoconfigure.ClientCacheAutoConfiguration
* @since 1.0.0
*/
@Configuration
@AutoConfigureAfter(ClientCacheAutoConfiguration.class)
@ConditionalOnBean(GemFireCache.class)
@ConditionalOnClass({ GemfireCacheManager.class, GemFireCache.class })
@ConditionalOnMissingBean(CacheManager.class)
@EnableGemfireCaching
@SuppressWarnings("all")
public class CachingProviderAutoConfiguration {
private final CacheManagerCustomizers cacheManagerCustomizers;
private final CacheProperties cacheProperties;
@Autowired
private GemfireCacheManager cacheManager;
CachingProviderAutoConfiguration(
@Autowired(required = false) CacheProperties cacheProperties,
@Autowired(required = false) CacheManagerCustomizers cacheManagerCustomizers) {
this.cacheProperties = cacheProperties;
this.cacheManagerCustomizers = cacheManagerCustomizers;
}
GemfireCacheManager getCacheManager() {
return Optional.ofNullable(this.cacheManager)
.orElseThrow(() -> newIllegalStateException("GemfireCacheManager was not properly configured"));
}
Optional<CacheManagerCustomizers> getCacheManagerCustomizers() {
return Optional.ofNullable(this.cacheManagerCustomizers);
}
Optional<CacheProperties> getCacheProperties() {
return Optional.ofNullable(this.cacheProperties);
}
@PostConstruct
public void onGeodeCachingInitialization() {
getCacheManagerCustomizers()
.ifPresent(cacheManagerCustomizers -> cacheManagerCustomizers.customize(getCacheManager()));
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.config.annotation.ClientCacheApplication;
import org.springframework.data.gemfire.config.annotation.EnablePdx;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} for bootstrapping an Apache Geode {@link ClientCache}
* instance constructed, configured and initialized with Spring Data for Apache Geode.
*
* Additionally, this configuration automatically enables Apache Geode PDX serialization to serialize data sent
* between the client and server(s) in the cluster when using the client/server topology.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.data.gemfire.client.ClientCacheFactoryBean
* @see org.springframework.data.gemfire.config.annotation.ClientCacheApplication
* @see org.springframework.data.gemfire.config.annotation.EnablePdx
* @since 1.0.0
*/
@Configuration
@ConditionalOnClass({ ClientCacheFactoryBean.class, ClientCache.class })
@ConditionalOnMissingBean(GemFireCache.class)
@ClientCacheApplication
@EnablePdx
public class ClientCacheAutoConfiguration {
}

View File

@@ -0,0 +1,197 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure;
import java.util.Optional;
import java.util.Properties;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.config.annotation.EnableSecurity;
import org.springframework.data.gemfire.config.annotation.support.AutoConfiguredAuthenticationInitializer;
import org.springframework.geode.core.env.VcapPropertySource;
import org.springframework.geode.core.env.support.CloudCacheService;
import org.springframework.geode.core.env.support.Service;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} enabling Apache Geode's Security functionality,
* and specifically Authentication between a client and server using Spring Data Geode Security annotations.
*
* @author John Blum
* @see java.util.Properties
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.boot.SpringApplication
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.boot.cloud.CloudPlatform
* @see org.springframework.boot.env.EnvironmentPostProcessor
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.core.env.ConfigurableEnvironment
* @see org.springframework.core.env.Environment
* @see org.springframework.core.env.PropertiesPropertySource
* @see org.springframework.data.gemfire.client.ClientCacheFactoryBean
* @see org.springframework.data.gemfire.config.annotation.EnableSecurity
* @see org.springframework.data.gemfire.config.annotation.support.AutoConfiguredAuthenticationInitializer
* @see org.springframework.geode.boot.autoconfigure.ClientCacheAutoConfiguration
* @see org.springframework.geode.core.env.VcapPropertySource
* @see org.springframework.geode.core.env.support.CloudCacheService
* @see org.springframework.geode.core.env.support.Service
* @since 1.0.0
*/
@Configuration
@AutoConfigureBefore(ClientCacheAutoConfiguration.class)
@Conditional(ClientSecurityAutoConfiguration.EnableSecurityCondition.class)
@ConditionalOnClass({ ClientCacheFactoryBean.class, ClientCache.class })
@ConditionalOnMissingBean(GemFireCache.class)
@EnableSecurity
@SuppressWarnings("unused")
public class ClientSecurityAutoConfiguration {
public static final String SECURITY_CLOUD_ENVIRONMENT_POST_PROCESSOR_DISABLED_PROPERTY =
"spring.boot.data.gemfire.security.auth.environment.post-processor.disabled";
private static final String CLOUD_CACHE_PROPERTY_SOURCE_NAME = "cloudcache-configuration";
private static final String MANAGEMENT_HTTP_HOST_PROPERTY = "spring.data.gemfire.management.http.host";
private static final String MANAGEMENT_HTTP_PORT_PROPERTY = "spring.data.gemfire.management.http.port";
private static final String MANAGEMENT_USE_HTTP_PROPERTY = "spring.data.gemfire.management.use-http";
private static final String POOL_LOCATORS_PROPERTY = "spring.data.gemfire.pool.locators";
private static final String SECURITY_USERNAME_PROPERTY =
AutoConfiguredAuthenticationInitializer.SDG_SECURITY_USERNAME_PROPERTY;
private static final String SECURITY_PASSWORD_PROPERTY =
AutoConfiguredAuthenticationInitializer.SDG_SECURITY_PASSWORD_PROPERTY;
private static final String VCAP_PROPERTY_SOURCE_NAME = "vcap";
static class AutoConfiguredCloudSecurityEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Optional.of(environment)
.filter(this::isEnabled)
.filter(this::isCloudFoundryEnvironment)
.ifPresent(env -> {
VcapPropertySource propertySource = VcapPropertySource.from(env);
Properties cloudCacheProperties = new Properties();
CloudCacheService cloudCache = propertySource.findFirstCloudCacheService();
configureAuthentication(env, cloudCacheProperties, propertySource, cloudCache);
configureLocators(env, cloudCacheProperties, propertySource, cloudCache);
configureManagementRestApiAccess(env, cloudCacheProperties, propertySource, cloudCache);
environment.getPropertySources()
.addFirst(new PropertiesPropertySource(CLOUD_CACHE_PROPERTY_SOURCE_NAME, cloudCacheProperties));
});
}
private boolean isCloudFoundryEnvironment(Environment environment) {
return Optional.ofNullable(environment).filter(CloudPlatform.CLOUD_FOUNDRY::isActive).isPresent();
}
private boolean isDisabled(Environment environment) {
return Boolean.getBoolean(SECURITY_CLOUD_ENVIRONMENT_POST_PROCESSOR_DISABLED_PROPERTY);
}
private boolean isEnabled(Environment environment) {
return !isDisabled(environment);
}
private boolean isSecurityPropertiesSet(Environment environment) {
return environment.containsProperty(SECURITY_USERNAME_PROPERTY)
&& environment.containsProperty(SECURITY_PASSWORD_PROPERTY);
}
private boolean isSecurityPropertiesNotSet(Environment environment) {
return !isSecurityPropertiesSet(environment);
}
private void configureAuthentication(Environment environment, Properties cloudCacheProperties,
VcapPropertySource propertySource, Service cloudCache) {
propertySource.findFirstUserByRoleClusterOperator(cloudCache)
.filter(user -> isSecurityPropertiesNotSet(environment))
.ifPresent(user -> {
cloudCacheProperties.setProperty(SECURITY_USERNAME_PROPERTY, user.getName());
user.getPassword().ifPresent(password ->
cloudCacheProperties.setProperty(SECURITY_PASSWORD_PROPERTY, password));
});
}
private void configureLocators(Environment environment, Properties cloudCacheProperties,
VcapPropertySource propertySource, CloudCacheService cloudCache) {
cloudCache.getLocators().ifPresent(locators ->
cloudCacheProperties.setProperty(POOL_LOCATORS_PROPERTY, locators));
}
private void configureManagementRestApiAccess(Environment environment, Properties cloudCacheProperties,
VcapPropertySource propertySource, CloudCacheService cloudCache) {
cloudCache.getGfshUrl().ifPresent(url -> {
cloudCacheProperties.setProperty(MANAGEMENT_USE_HTTP_PROPERTY, Boolean.TRUE.toString());
cloudCacheProperties.setProperty(MANAGEMENT_HTTP_HOST_PROPERTY, url.getHost());
cloudCacheProperties.setProperty(MANAGEMENT_HTTP_PORT_PROPERTY, String.valueOf(url.getPort()));
});
}
}
static class EnableSecurityCondition extends AnyNestedCondition {
public EnableSecurityCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
static class CloudSecurityContextCondition { }
@ConditionalOnProperty({
"spring.data.gemfire.security.username",
"spring.data.gemfire.security.password",
})
static class SpringDataGeodeSecurityContextCondition { }
@ConditionalOnProperty({
"gemfire.security-username",
"gemfire.security-password",
})
static class StandaloneApacheGeodeSecurityContextCondition { }
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer;
import org.springframework.data.gemfire.config.annotation.EnableContinuousQueries;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} enabling Apache Geode's Continuous Query (CQ)
* functionality in a {@link ClientCache} application.
*
* @author John Blum
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.data.gemfire.client.ClientCacheFactoryBean
* @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
* @see org.springframework.data.gemfire.config.annotation.EnableContinuousQueries
* @since 1.0.0
*/
@Configuration
@ConditionalOnBean(ClientCacheFactoryBean.class)
@ConditionalOnMissingBean(name = "continuousQueryBeanPostProcessor")
@EnableContinuousQueries
@SuppressWarnings("unused")
public class ContinuousQueryAutoConfiguration {
@Bean
public ClientCacheConfigurer enableSubscriptionClientCacheConfigurer() {
return (beanName, clientCacheFactoryBean) -> clientCacheFactoryBean.setSubscriptionEnabled(true);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure;
import org.apache.geode.cache.GemFireCache;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.gemfire.function.config.EnableGemfireFunctions;
import org.springframework.data.gemfire.function.config.GemFireFunctionExecutionAutoConfigurationRegistrar;
import org.springframework.data.gemfire.function.execution.GemfireFunctionOperations;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} enabling Apache Geode's Function Execution
* functionality in a {@link GemFireCache} application.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.Import
* @see org.springframework.data.gemfire.function.config.EnableGemfireFunctions
* @see org.springframework.data.gemfire.function.config.EnableGemfireFunctionExecutions
* @see org.springframework.data.gemfire.function.config.GemFireFunctionExecutionAutoConfigurationRegistrar
* @see org.springframework.data.gemfire.function.execution.GemfireFunctionOperations
* @see org.springframework.geode.boot.autoconfigure.ClientCacheAutoConfiguration
* @since 1.0.0
*/
@Configuration
@AutoConfigureAfter(ClientCacheAutoConfiguration.class)
@ConditionalOnBean(GemFireCache.class)
@ConditionalOnClass({ GemfireFunctionOperations.class, GemFireCache.class })
@EnableGemfireFunctions
@Import(GemFireFunctionExecutionAutoConfigurationRegistrar.class)
@SuppressWarnings("unused")
public class FunctionExecutionAutoConfiguration {
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure;
import java.lang.annotation.Annotation;
import org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;
import org.springframework.data.gemfire.repository.config.GemfireRepositoryConfigurationExtension;
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
/**
* Spring {@link ImportBeanDefinitionRegistrar} used to auto-configure Spring Data Geode Repositories.
*
* @author John Blum
* @see org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport
* @see org.springframework.data.gemfire.repository.config.EnableGemfireRepositories
* @see org.springframework.data.gemfire.repository.config.GemfireRepositoryConfigurationExtension
* @see org.springframework.geode.boot.autoconfigure.RepositoriesAutoConfiguration
* @since 1.0.0
*/
public class GemFireRepositoriesAutoConfigurationRegistrar extends AbstractRepositoryConfigurationSourceSupport {
@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableGemfireRepositories.class;
}
@Override
protected Class<?> getConfiguration() {
return EnableGemFireRepositoriesConfiguration.class;
}
@Override
protected RepositoryConfigurationExtension getRepositoryConfigurationExtension() {
return new GemfireRepositoryConfigurationExtension();
}
@EnableGemfireRepositories
private static class EnableGemFireRepositoriesConfiguration { }
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.config.annotation.ApacheShiroSecurityConfiguration;
import org.springframework.data.gemfire.config.annotation.EnableBeanFactoryLocator;
import org.springframework.data.gemfire.config.annotation.EnableSecurity;
import org.springframework.data.gemfire.config.annotation.GeodeIntegratedSecurityConfiguration;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} enabling Apache Geode's Security functionality,
* and specifically Authentication between a client and server using Spring Data Geode Security annotations.
*
* @author John Blum
* @see org.apache.geode.security.SecurityManager
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.data.gemfire.CacheFactoryBean
* @see org.springframework.data.gemfire.client.ClientCacheFactoryBean
* @see org.springframework.data.gemfire.config.annotation.ApacheShiroSecurityConfiguration
* @see org.springframework.data.gemfire.config.annotation.EnableBeanFactoryLocator
* @see org.springframework.data.gemfire.config.annotation.EnableSecurity
* @see org.springframework.data.gemfire.config.annotation.GeodeIntegratedSecurityConfiguration
* @see org.springframework.geode.security.support.SecurityManagerProxy
* @since 1.0.0
*/
@Configuration
@ConditionalOnBean(org.apache.geode.security.SecurityManager.class)
@ConditionalOnMissingBean({
ClientCacheFactoryBean.class,
ApacheShiroSecurityConfiguration.class,
GeodeIntegratedSecurityConfiguration.class
})
@EnableBeanFactoryLocator
@EnableSecurity(securityManagerClassName = "org.springframework.geode.security.support.SecurityManagerProxy")
@SuppressWarnings("unused")
public class PeerSecurityAutoConfiguration {
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.gemfire.repository.GemfireRepository;
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;
import org.springframework.data.gemfire.repository.config.GemfireRepositoryConfigurationExtension;
import org.springframework.data.gemfire.repository.support.GemfireRepositoryFactoryBean;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} for Spring Data Geode
* and Spring Data GemFire Repositories.
*
* Activates when there is a bean of type {@link Cache} or {@link ClientCache} configured in the Spring context,
* the Spring Data Geode {@link GemfireRepository} type is on the classpath, and no other existing
* {@link GemfireRepository GemfireRepositories} are configured.
*
* Once in effect, the auto-configuration is the equivalent of enabling Geode Repositories using the
* {@link EnableGemfireRepositories} annotation.
*
* @author John Blum
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.Import
* @see org.springframework.data.gemfire.repository.GemfireRepository
* @see org.springframework.data.gemfire.repository.config.EnableGemfireRepositories
* @see org.springframework.data.gemfire.repository.config.GemfireRepositoryConfigurationExtension
* @see org.springframework.data.gemfire.repository.support.GemfireRepositoryFactoryBean
* @see org.springframework.geode.boot.autoconfigure.ClientCacheAutoConfiguration
* @see org.springframework.geode.boot.autoconfigure.GemFireRepositoriesAutoConfigurationRegistrar
* @since 1.0.0
*/
@Configuration
@AutoConfigureAfter(ClientCacheAutoConfiguration.class)
@ConditionalOnBean(GemFireCache.class)
@ConditionalOnClass(GemfireRepository.class)
@ConditionalOnMissingBean({ GemfireRepositoryConfigurationExtension.class, GemfireRepositoryFactoryBean.class })
@ConditionalOnProperty(prefix = "spring.data.gemfire.repositories", name = "enabled", havingValue = "true",
matchIfMissing = true)
@Import(GemFireRepositoriesAutoConfigurationRegistrar.class)
public class RepositoriesAutoConfiguration {
}

View File

@@ -0,0 +1,330 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure;
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Optional;
import java.util.Properties;
import org.apache.geode.cache.GemFireCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.data.gemfire.config.annotation.EnableSsl;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} enabling Apache Geode's SSL transport
* between client and servers when using the client/server topology.
*
* @author John Blum
* @see java.io.File
* @see java.net.URL
* @see java.util.Properties
* @see org.apache.geode.cache.GemFireCache
* @see org.springframework.boot.SpringApplication
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.geode.boot.autoconfigure.ClientCacheAutoConfiguration
* @see org.springframework.boot.env.EnvironmentPostProcessor
* @see org.springframework.context.annotation.Condition
* @see org.springframework.context.annotation.Conditional
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.core.env.ConfigurableEnvironment
* @see org.springframework.core.env.Environment
* @see org.springframework.core.env.PropertiesPropertySource
* @see org.springframework.core.io.Resource
* @see org.springframework.data.gemfire.CacheFactoryBean
* @see org.springframework.data.gemfire.config.annotation.EnableSsl
* @since 1.0.0
*/
@Configuration
@AutoConfigureBefore(ClientCacheAutoConfiguration.class)
@Conditional(SslAutoConfiguration.EnableSslCondition.class)
@ConditionalOnClass({ CacheFactoryBean.class, GemFireCache.class })
@EnableSsl
@SuppressWarnings("unused")
public class SslAutoConfiguration {
public static final String SECURITY_SSL_ENVIRONMENT_POST_PROCESSOR_DISABLED_PROPERTY =
"spring.boot.data.gemfire.security.ssl.environment.post-processor.disabled";
private static final String CURRENT_WORKING_DIRECTORY = System.getProperty("user.dir");
private static final String GEMFIRE_SSL_KEYSTORE_PROPERTY = "gemfire.ssl-keystore";
private static final String GEMFIRE_SSL_PROPERTY_SOURCE_NAME = "gemfire-ssl";
private static final String GEMFIRE_SSL_TRUSTSTORE_PROPERTY = "gemfire.ssl-truststore";
private static final String SECURITY_SSL_KEYSTORE_PROPERTY = "spring.data.gemfire.security.ssl.keystore";
private static final String SECURITY_SSL_TRUSTSTORE_PROPERTY = "spring.data.gemfire.security.ssl.truststore";
private static final String SSL_KEYSTORE_PROPERTY = "ssl-keystore";
private static final String SSL_TRUSTSTORE_PROPERTY = "ssl-truststore";
private static final String TRUSTED_KEYSTORE_FILENAME = "trusted.keystore";
private static final String TRUSTED_KEYSTORE_FILENAME_PROPERTY = "spring.boot.data.gemfire.security.ssl.keystore.name";
private static final String USER_HOME_DIRECTORY = System.getProperty("user.home");
private static final Logger logger = LoggerFactory.getLogger(SslAutoConfiguration.class);
private static boolean isSslConfigured(Environment environment) {
return (environment.containsProperty(SECURITY_SSL_KEYSTORE_PROPERTY)
&& environment.containsProperty(SECURITY_SSL_TRUSTSTORE_PROPERTY))
|| (environment.containsProperty(GEMFIRE_SSL_KEYSTORE_PROPERTY)
&& environment.containsProperty(GEMFIRE_SSL_TRUSTSTORE_PROPERTY))
|| (environment.containsProperty(SSL_KEYSTORE_PROPERTY)
&& environment.containsProperty(SSL_TRUSTSTORE_PROPERTY));
}
private static boolean isSslNotConfigured(Environment environment) {
return !isSslConfigured(environment);
}
private static String resolveTrustedKeyStore(Environment environment) {
return locateKeyStoreInFileSystem(environment)
.map(File::getAbsolutePath)
.orElseGet(() -> locateKeyStoreInUserHome(environment)
.map(File::getAbsolutePath)
.orElseGet(() -> resolveKeyStoreFromClassPathAsPathname(environment)
.orElse(null)));
}
private static String resolveTrustedKeystoreName(Environment environment) {
return Optional.ofNullable(environment)
.filter(it -> environment.containsProperty(TRUSTED_KEYSTORE_FILENAME_PROPERTY))
.map(it -> environment.getProperty(TRUSTED_KEYSTORE_FILENAME_PROPERTY))
.orElse(TRUSTED_KEYSTORE_FILENAME);
}
private static Optional<String> resolveKeyStoreFromClassPathAsPathname(Environment environment) {
return resolveKeyStoreFromClassPath(environment)
.filter(File::isFile)
.map(File::getAbsolutePath)
.filter(StringUtils::hasText);
}
private static Optional<File> resolveKeyStoreFromClassPath(Environment environment) {
/*
System.err.printf("KEYSTORE LOCATION [%s]%n", ObjectUtils.doOperationSafely(() ->
new File(new ClassPathResource(keystoreName).getURL().toURI())).getAbsolutePath());
*/
return locateKeyStoreInClassPath(environment)
.map(resource -> {
File trustedKeyStore = null;
try {
URL url = resource.getURL();
if (ResourceUtils.isFileURL(url)) {
trustedKeyStore = new File(url.toURI());
}
else if (ResourceUtils.isJarURL(url)) {
trustedKeyStore = new File(CURRENT_WORKING_DIRECTORY, resolveTrustedKeystoreName(environment));
FileCopyUtils.copy(url.openStream(), new FileOutputStream(trustedKeyStore));
}
}
catch (IOException | URISyntaxException cause) {
if (logger.isWarnEnabled()) {
logger.warn("Trusted KeyStore {} found in Class Path but is not resolvable as a File: {}",
resource, cause.getMessage());
if (logger.isTraceEnabled()) {
logger.trace("Caused by:", cause);
}
}
}
return trustedKeyStore;
});
}
private static Optional<ClassPathResource> locateKeyStoreInClassPath(Environment environment) {
return locateKeyStoreInClassPath(resolveTrustedKeystoreName(environment));
}
@SuppressWarnings("all")
private static Optional<ClassPathResource> locateKeyStoreInClassPath(String keystoreName) {
return Optional.of(new ClassPathResource(keystoreName))
.filter(Resource::exists);
}
private static Optional<File> locateKeyStoreInFileSystem(Environment environment) {
return locateKeyStoreInFileSystem(environment, new File(CURRENT_WORKING_DIRECTORY));
}
private static Optional<File> locateKeyStoreInFileSystem(Environment environment, File directory) {
return locateKeyStoreInFileSystem(directory, resolveTrustedKeystoreName(environment));
}
private static Optional<File> locateKeyStoreInFileSystem(String keystoreName) {
return locateKeyStoreInFileSystem(new File(CURRENT_WORKING_DIRECTORY), keystoreName);
}
@SuppressWarnings("all")
private static Optional<File> locateKeyStoreInFileSystem(File directory, String keystoreFilename) {
assertDirectory(directory);
//System.err.printf("Searching [%s]...%n", directory);
for (File file : nullSafeListFiles(directory)) {
//System.err.printf("Testing [%s]...%n", file);
if (isDirectory(file)) {
Optional<File> theFile = locateKeyStoreInFileSystem(file, keystoreFilename);
if (theFile.isPresent()) {
return theFile;
}
else {
continue;
}
}
if (file.getName().equals(keystoreFilename)) {
return Optional.of(file);
}
}
return Optional.empty();
}
private static Optional<File> locateKeyStoreInUserHome(Environment environment) {
return locateKeyStoreInUserHome(resolveTrustedKeystoreName(environment));
}
private static Optional<File> locateKeyStoreInUserHome(String keystoreFilename) {
return Optional.of(new File(USER_HOME_DIRECTORY, keystoreFilename))
.filter(File::isFile);
}
private static void assertDirectory(File path) {
Assert.isTrue(isDirectory(path), String.format("[%s] is not a valid directory", path));
}
private static boolean isDirectory(File path) {
return path != null && path.isDirectory();
}
private static File[] nullSafeListFiles(File directory) {
return nullSafeArray(directory.listFiles(), File.class);
}
@SuppressWarnings("unused")
static class EnableSslCondition extends AnyNestedCondition {
public EnableSslCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@Conditional(TrustedKeyStoreIsPresentCondition.class)
static class TrustedKeyStoreCondition {}
@ConditionalOnProperty(prefix = "spring.data.gemfire.security.ssl", name = { "keystore", "truststore", })
static class SpringDataGeodeSslContextCondition {}
// TODO: ;-)
@ConditionalOnProperty({
GEMFIRE_SSL_KEYSTORE_PROPERTY,
GEMFIRE_SSL_TRUSTSTORE_PROPERTY,
SSL_KEYSTORE_PROPERTY,
SSL_TRUSTSTORE_PROPERTY,
})
static class StandaloneApacheGeodeSslContextCondition {}
}
static class SslEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Optional.of(environment)
.filter(this::isEnabled)
.filter(SslAutoConfiguration::isSslNotConfigured)
.map(SslAutoConfiguration::resolveTrustedKeyStore)
.filter(StringUtils::hasText)
.ifPresent(trustedKeyStore -> {
Properties gemfireSslProperties = new Properties();
gemfireSslProperties.setProperty(SECURITY_SSL_KEYSTORE_PROPERTY, trustedKeyStore);
gemfireSslProperties.setProperty(SECURITY_SSL_TRUSTSTORE_PROPERTY, trustedKeyStore);
environment.getPropertySources()
.addFirst(new PropertiesPropertySource(GEMFIRE_SSL_PROPERTY_SOURCE_NAME, gemfireSslProperties));
});
}
private boolean isEnabled(Environment environment) {
return !isDisabled(environment);
}
private boolean isDisabled(Environment environment) {
return Boolean.getBoolean(SECURITY_SSL_ENVIRONMENT_POST_PROCESSOR_DISABLED_PROPERTY);
}
}
static class TrustedKeyStoreIsPresentCondition implements Condition {
@Override
@SuppressWarnings("all")
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
return locateKeyStoreInClassPath(environment).isPresent()
|| locateKeyStoreInUserHome(environment).isPresent()
|| locateKeyStoreInFileSystem(environment).isPresent();
}
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2018 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.
*/
/**
* Spring Boot auto-configuration for Apache Geode & Pivotal GemFire.
*/
package org.springframework.geode.boot.autoconfigure;

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2018 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.geode.cache.support;
import org.apache.geode.cache.CacheLoader;
/**
* The {@link CacheLoaderSupport} interface is an extension of {@link CacheLoader} and a {@link FunctionalInterface}
* useful in Lambda expressions.
*
* @author John Blum
* @see org.apache.geode.cache.CacheLoader
* @since 1.0.0
*/
@FunctionalInterface
public interface CacheLoaderSupport<K, V> extends CacheLoader<K, V> {
/**
* Closes any resources opened and used by this {@link CacheLoader}.
*
* @see org.apache.geode.cache.CacheLoader#close()
*/
@Override
default void close() {}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2018 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.geode.config.annotation;
import java.lang.annotation.Annotation;
import java.util.Optional;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer;
import org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer;
import org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport;
import org.springframework.util.StringUtils;
/**
* The {@link MemberNameConfiguration} class is a Spring {@link Configuration} class used to set
* an Apache Geode or Pivotal GemFire's name in the distributed system, whether the member
* is a {@link ClientCache client} in the client/server topology or a {@link Cache peer} member
* of the cluster.
*
* @author John Blum
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.ImportAware
* @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
* @see org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer
* @see org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport
* @see org.springframework.geode.config.annotation.UseMemberName
* @since 1.0.0
*/
@Configuration
@SuppressWarnings("unused")
public class MemberNameConfiguration extends AbstractAnnotationConfigSupport implements ImportAware {
private static final String GEMFIRE_NAME_PROPERTY = "name";
private String memberName;
@Override
protected Class<? extends Annotation> getAnnotationType() {
return UseMemberName.class;
}
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
if (isAnnotationPresent(importMetadata)) {
AnnotationAttributes memberNameAttributes = getAnnotationAttributes(importMetadata);
setMemberNameIfNotSet(memberNameAttributes.containsKey("name")
? memberNameAttributes.getString("name") : null);
setMemberNameIfNotSet(memberNameAttributes.containsKey("value")
? memberNameAttributes.getString("value") : null);
}
}
protected void setMemberName(String memberName) {
this.memberName = memberName;
}
protected void setMemberNameIfNotSet(String memberName) {
setMemberName(this.memberName != null ? this.memberName : memberName);
}
protected Optional<String> getMemberName() {
return Optional.ofNullable(this.memberName)
.filter(StringUtils::hasText);
}
@Bean
ClientCacheConfigurer clientCacheMemberNameConfigurer() {
return (beaName, clientCacheFactoryBean) -> configureMemberName(clientCacheFactoryBean);
}
@Bean
PeerCacheConfigurer peerCacheMemberNameConfigurer() {
return (beaName, peerCacheFactoryBean) -> configureMemberName(peerCacheFactoryBean);
}
private void configureMemberName(CacheFactoryBean cacheFactoryBean) {
getMemberName().ifPresent(memberName ->
cacheFactoryBean.getProperties().setProperty(GEMFIRE_NAME_PROPERTY, memberName));
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2018 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.geode.config.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.AliasFor;
/**
* The {@link UseMemberName} annotation configures the {@literal name} of the member in the Apache Geode
* or Pivotal GemFire distributed system, whether the member is a {@link ClientCache client} in
* the client/server topology or a {@link Cache peer} member of the cluster.
*
* @author John Blum
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.context.annotation.Import
* @see org.springframework.geode.config.annotation.MemberNameConfiguration
* @since 1.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(MemberNameConfiguration.class)
public @interface UseMemberName {
/**
* {@link String Name} used for the Apache Geode/Pivotal GemFire distributed system member.
* @see #name()
*/
@AliasFor("name")
String value() default "";
/**
* Alias for the {@link String name} of the Apache Geode/Pivotal GemFire distributed system member.
* @see #value()
*/
@AliasFor("value")
String name() default "";
}

View File

@@ -0,0 +1,239 @@
/*
* Copyright 2018 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.geode.core.env;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.geode.core.env.support.CloudCacheService;
import org.springframework.geode.core.env.support.Service;
import org.springframework.geode.core.env.support.User;
import org.springframework.geode.core.util.ObjectUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
/**
* The {@link VcapPropertySource} class is a Spring {@link PropertySource} to process
* {@literal VCAP} environment properties in Pivotal CloudFoundry.
*
* @author John Blum
* @see java.lang.Iterable
* @see java.net.URL
* @see java.util.Properties
* @see java.util.function.Predicate
* @see org.springframework.core.env.EnumerablePropertySource
* @see org.springframework.core.env.Environment
* @see org.springframework.core.env.PropertiesPropertySource
* @see org.springframework.core.env.PropertySource
* @see org.springframework.geode.core.env.support.CloudCacheService
* @see org.springframework.geode.core.env.support.Service
* @see org.springframework.geode.core.env.support.User
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class VcapPropertySource extends PropertySource<EnumerablePropertySource<?>> implements Iterable<String> {
private static final String CLOUD_CACHE_TAG_NAME = "cloudcache";
private static final String GEMFIRE_TAG_NAME = "gemfire";
private static final String THIS_PROPERTY_SOURCE_NAME = "boot.data.gemfire.vcap";
private static final String VCAP_APPLICATION_PROPERTY = "vcap.application.";
private static final String VCAP_APPLICATION_NAME_PROPERTY = VCAP_APPLICATION_PROPERTY + "name";
private static final String VCAP_APPLICATION_URIS_PROPERTY = VCAP_APPLICATION_PROPERTY + "uris";
private static final String VCAP_PROPERTY_SOURCE_NAME = "vcap";
private static final String VCAP_SERVICES_PROPERTY = "vcap.services.";
private static final String VCAP_SERVICES_SERVICE_NAME_GFSH_URL_PROPERTY = "vcap.services.%s.credentials.urls.gfsh";
private static final String VCAP_SERVICES_SERVICE_NAME_LOCATORS_PROPERTY = "vcap.services.%s.credentials.locators";
private static final String VCAP_SERVICES_SERVICE_NAME_USERS_PROPERTY = "vcap.services.%s.credentials.users[%d]";
private static final Predicate<Object> CLOUD_CACHE_SERVICE_PREDICATE =
propertyValue -> String.valueOf(propertyValue).toLowerCase().contains(CLOUD_CACHE_TAG_NAME);
private static final Predicate<Object> GEMFIRE_SERVICE_PREDICATE =
propertyValue -> String.valueOf(propertyValue).toLowerCase().contains(GEMFIRE_TAG_NAME);
private static final Predicate<Object> CLOUD_CACHE_AND_GEMFIRE_SERVICE_PREDICATE =
CLOUD_CACHE_SERVICE_PREDICATE.and(GEMFIRE_SERVICE_PREDICATE);
private static final Predicate<String> VCAP_APPLICATION_PROPERTIES_PREDICATE =
propertyName -> String.valueOf(propertyName).trim().toLowerCase().startsWith(VCAP_APPLICATION_PROPERTY);
private static final Predicate<PropertySource> VCAP_REQUIRED_PROPERTIES_PREDICATE =
propertySource -> propertySource.containsProperty(VCAP_APPLICATION_NAME_PROPERTY)
&& propertySource.containsProperty(VCAP_APPLICATION_URIS_PROPERTY);
private static final Predicate<String> VCAP_SERVICES_PROPERTIES_PREDICATE =
propertyName -> String.valueOf(propertyName).trim().toLowerCase().startsWith(VCAP_SERVICES_PROPERTY);
public static VcapPropertySource from(Environment environment) {
return Optional.ofNullable(environment)
.filter(env -> env instanceof ConfigurableEnvironment)
.map(env -> ((ConfigurableEnvironment) env).getPropertySources())
.map(propertySources -> propertySources.get(VCAP_PROPERTY_SOURCE_NAME))
.map(VcapPropertySource::from)
.orElseThrow(() -> newIllegalArgumentException(
"Environment was not configurable or does not contain an enumerable [%s] PropertySource",
VCAP_PROPERTY_SOURCE_NAME));
}
public static VcapPropertySource from(Properties properties) {
return Optional.ofNullable(properties)
.map(it -> new PropertiesPropertySource(THIS_PROPERTY_SOURCE_NAME, properties))
.filter(VCAP_REQUIRED_PROPERTIES_PREDICATE)
.map(VcapPropertySource::new)
.orElseThrow(() -> newIllegalArgumentException("Properties are required"));
}
public static VcapPropertySource from(PropertySource<?> propertySource) {
return Optional.ofNullable(propertySource)
.filter(it -> VCAP_PROPERTY_SOURCE_NAME.equals(it.getName()))
.filter(it -> it instanceof EnumerablePropertySource)
.filter(VCAP_REQUIRED_PROPERTIES_PREDICATE)
.map(it -> (EnumerablePropertySource) it)
.map(VcapPropertySource::new)
.orElseThrow(() -> newIllegalArgumentException(
"A valid EnumerablePropertySource named [%s] with VCAP properties is required",
VCAP_PROPERTY_SOURCE_NAME));
}
private VcapPropertySource(EnumerablePropertySource<?> propertySource) {
super(THIS_PROPERTY_SOURCE_NAME, propertySource);
}
protected Set<String> findAllPropertiesByNameMatching(Predicate<String> predicate) {
return findAllPropertiesByNameMatching(this, predicate);
}
protected Set<String> findAllPropertiesByNameMatching(Iterable<String> properties, Predicate<String> predicate) {
return StreamSupport.stream(properties.spliterator(), false)
.filter(predicate)
.collect(Collectors.toSet());
}
protected Set<String> findAllPropertiesByValueMatching(Predicate<Object> predicate) {
return findAllPropertiesByValueMatching(this, predicate);
}
protected Set<String> findAllPropertiesByValueMatching(Iterable<String> properties, Predicate<Object> predicate) {
return StreamSupport.stream(properties.spliterator(), false)
.filter(propertyName -> predicate.test(getProperty(propertyName)))
.collect(Collectors.toSet());
}
public Set<String> findAllVcapApplicationProperties() {
return findAllPropertiesByNameMatching(VCAP_APPLICATION_PROPERTIES_PREDICATE);
}
public Set<String> findAllVcapServicesProperties() {
return findAllPropertiesByNameMatching(VCAP_SERVICES_PROPERTIES_PREDICATE);
}
public CloudCacheService findFirstCloudCacheService() {
String serviceName = findFirstCloudCacheServiceName();
CloudCacheService service = CloudCacheService.with(serviceName);
Optional.ofNullable(getProperty(String.format(VCAP_SERVICES_SERVICE_NAME_LOCATORS_PROPERTY, serviceName)))
.map(String::valueOf)
.ifPresent(service::withLocators);
Optional.ofNullable(getProperty(String.format(VCAP_SERVICES_SERVICE_NAME_GFSH_URL_PROPERTY, service)))
.map(String::valueOf)
.map(urlString -> ObjectUtils.doOperationSafely(() -> new URL(urlString)))
.ifPresent(service::withGfshUrl);
return service;
}
public String findFirstCloudCacheServiceName() {
Iterable<String> vcapServicesProperties = findAllVcapServicesProperties();
return findAllPropertiesByValueMatching(vcapServicesProperties, CLOUD_CACHE_AND_GEMFIRE_SERVICE_PREDICATE)
.stream()
.filter(propertyName -> propertyName.endsWith(".tags"))
.map(propertyName -> propertyName.substring(VCAP_SERVICES_PROPERTY.length()))
.map(propertyName -> propertyName.substring(0, propertyName.indexOf(".")))
.filter(StringUtils::hasText)
.sorted(String.CASE_INSENSITIVE_ORDER)
.findFirst()
.orElseThrow(() ->
newIllegalStateException("No service with tags [%1$s, %2$s] was found",
CLOUD_CACHE_TAG_NAME, GEMFIRE_TAG_NAME));
}
public Optional<User> findFirstUserByRoleClusterOperator(Service service) {
String serviceName = service.getName();
String userPropertyName = String.format(VCAP_SERVICES_SERVICE_NAME_USERS_PROPERTY, serviceName, 0);
for (int index = 1; containsProperty(userPropertyName+".roles"); index++) {
String roles = String.valueOf(getProperty(userPropertyName+".roles"));
if (roles.contains(User.Role.CLUSTER_OPERATOR.name().toLowerCase())) {
break;
}
userPropertyName = String.format(VCAP_SERVICES_SERVICE_NAME_USERS_PROPERTY, serviceName, index);
}
if (containsProperty(userPropertyName+".username")) {
String username = String.valueOf(getProperty(userPropertyName+".username"));
String password = String.valueOf(getProperty(userPropertyName+".password"));
return Optional.of(User.with(username).withPassword(password).withRole(User.Role.CLUSTER_OPERATOR));
}
return Optional.empty();
}
@Nullable
@Override
@SuppressWarnings("all")
public Object getProperty(String name) {
return getSource().getProperty(name);
}
@Override
@SuppressWarnings("all")
public Iterator<String> iterator() {
return Collections.unmodifiableList(Arrays.asList(getSource().getPropertyNames())).iterator();
}
}

View File

@@ -0,0 +1,345 @@
/*
* Copyright 2018 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.geode.core.env.support;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.data.gemfire.GemfireUtils;
import org.springframework.geode.core.util.ObjectUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* The {@link CloudCacheService} class is an Abstract Data Type (ADT) modeling the Pivotal Cloud Cache service
* in Pivotal CloudFoundry (PCF).
*
* @author John Blum
* @see java.net.URL
* @see org.springframework.geode.core.env.support.Service
* @since 1.0.0
*/
public class CloudCacheService extends Service {
/**
* Factory method used to construct a new {@link CloudCacheService} initialized with the given {@link String name}.
*
* @param name {@link String} containing the name of the {@link CloudCacheService}.
* @throws IllegalArgumentException if the {@link String name} is {@literal null} or empty.
* @return the new {@link CloudCacheService} with the given {@link String name}.
* @see #CloudCacheService(String)
*/
public static CloudCacheService with(String name) {
return new CloudCacheService(name);
}
private String locators;
private URL gfshUrl;
/**
* Construct a new instance of {@link CloudCacheService} initialized with the given {@link String name}.
*
* @param name {@link String} containing the name of the {@link CloudCacheService}.
* @throws IllegalArgumentException if the {@link String name} is {@literal null} or empty.
*/
private CloudCacheService(String name) {
super(name);
}
/**
* Returns an {@link Optional} Gfsh {@link URL}, if configured, used to connect to Pivotal GemFire's
* Management REST API (service).
*
* @return an {@link Optional} Gfsh {@link URL} used to connect to Pivotal GemFire's Management REST API (service).
* @see #withGfshUrl(URL)
* @see java.util.Optional
* @see java.net.URL
*/
public Optional<URL> getGfshUrl() {
return Optional.ofNullable(this.gfshUrl);
}
/**
* Returns an {@link Optional} {@link String} containing the list of Pivotal GemFire Locator network endpoints.
*
* The format of the {@link String}, if present, is {@literal host1[port1],host2[port2], ...,hostN[portN]}.
*
* @return an {@link Optional} {@link String} containing the list of Pivotal GemFire Locator network endpoints.
* @see #withLocators(String)
*/
public Optional<String> getLocators() {
return Optional.ofNullable(this.locators).filter(StringUtils::hasText);
}
/**
* Returns a {@link List} of Pivotal GemFire Locator network endpoints.
*
* Returns an {@link Collections#emptyList() empty List} if no Locators were configured.
*
* @return a {@link List} of Pivotal GemFire Locator network endpoints.
* @see #getLocators()
*/
public List<Locator> getLocatorList() {
return getLocators()
.map(Locator::parseLocators)
.orElseGet(Collections::emptyList);
}
/**
* Builder method used to configure the Gfsh {@link URL} to connect to the Pivotal GemFire
* Management REST API (service).
*
* @param gfshUrl {@link URL} used to connect to the Pivotal GemFire Management REST API (service).
* @return this {@link CloudCacheService}.
* @see #getGfshUrl()
*/
public CloudCacheService withGfshUrl(URL gfshUrl) {
this.gfshUrl = gfshUrl;
return this;
}
/**
* Builder method used to configure the {@link String list of Locator} network endpoints.
*
* @param locators {@link String} containing a comma-delimited list of Locator network endpoints
* of the format: {@literal host1[port1],host2[port2], ...,hostN[portN]}.
* @return this {@link CloudCacheService}.
* @see #getLocators()
*/
public CloudCacheService withLocators(String locators) {
this.locators = locators;
return this;
}
public static class Locator implements Comparable<Locator> {
static final int DEFAULT_LOCATOR_PORT = GemfireUtils.DEFAULT_LOCATOR_PORT;
static final String DEFAULT_LOCATOR_HOST = "localhost";
private Integer port;
private String host;
/**
* Factory method used to construct a new {@link Locator} on the default {@link String host}
* and {@link Integer port}.
*
* @return a new, default {@link Locator}.
* @see #newLocator(String, int)
*/
public static Locator newLocator() {
return newLocator(DEFAULT_LOCATOR_HOST, DEFAULT_LOCATOR_PORT);
}
/**
* Factory method used to construct a new {@link Locator} running on the default {@link String host}
* and configured to listen on the given {@link Integer port}.
*
* @param port {@link Integer} containing the port number on which the {@link Locator} is listening.
* @return a new {@link Locator} running on the default {@link String host},
* listening on the given {@link Integer port}.
* @throws IllegalArgumentException if the {@link Integer port} is less than {@literal 0}.
* @see #newLocator(String, int)
*/
public static Locator newLocator(int port) {
return newLocator(DEFAULT_LOCATOR_HOST, port);
}
/**
* Factory method used to construct a new {@link Locator} configured to run on the given {@link String host}
* and listening on the default {@link Integer port}.
*
* @param host {@link String} containing the name of the host on which the {@link Locator} is running.
* @return a new {@link Locator} running on the configured {@link String host},
* listening on the default {@link Integer port}.
* @throws IllegalArgumentException if {@link String host} is {@literal null} or empty.
* @see #newLocator(String, int)
*/
public static Locator newLocator(String host) {
return newLocator(host, DEFAULT_LOCATOR_PORT);
}
/**
* Factory method used to construct a new {@link Locator} running on the configured {@link String host}
* and listening on the configured {@link Integer port}.
*
* @param host {@link String} containing the name of the host on which the {@link Locator} is running.
* @param port {@link Integer} containing the port number on which the {@link Locator} is listening.
* @throws IllegalArgumentException if {@link String host} is {@literal null} or empty,
* or the {@link Integer port} is less than {@literal 0}.
* @return a new {@link Locator} on the configured {@link String host} and {@link Integer port}.
*/
public static Locator newLocator(String host, int port) {
Assert.hasText(host, String.format("Host [%s] is required", host));
Assert.isTrue(port > -1, String.format("Port [%d] must be greater than equal to 0", port));
return new Locator(host, port);
}
/**
* Factory method used to parse a {@link String comma-delimited list of Locator network endpoints}
* into a {@link List} of {@link Locator} objects.
*
* The {@link String comma-delimited list of Locators} must be formatted as
* {@literal host1[port1],host2[port2], ...,hostN[portN]}.
*
* @param locators {@link String} containing a comma-delimited list of Locator network endpoints.
* @return a new {@link List} of {@link Locator} objects or an empty {@link List}
* if no Locators were specified.
* @throws IllegalArgumentException if an individual Locator {@link String host[port]} is not valid.
* @see #parse(String)
*/
public static List<Locator> parseLocators(String locators) {
return Arrays.stream(String.valueOf(locators).split(","))
.filter(StringUtils::hasText)
.map(Locator::parse)
.collect(Collectors.toList());
}
/**
* Factory method used to parse an individual {@link String host[port]} network endpoint for a Locator.
*
* @param hostPort {@link String} containing the Locator host and port to parse.
* @return a new {@link Locator} configured from the given {@link String}.
* @throws IllegalArgumentException if the {@link String hostPort} are not valid.
* @see #parseHost(String)
* @see #parsePort(String)
* @see #newLocator(String, int)
*/
public static Locator parse(String hostPort) {
return Optional.ofNullable(hostPort)
.filter(StringUtils::hasText)
.map(it -> {
String host = parseHost(it);
int port = parsePort(it);
return newLocator(host, port);
})
.orElseThrow(() -> newIllegalArgumentException("Locator host/port [%s] is not valid", hostPort));
}
private static String parseHost(String value) {
int index = String.valueOf(value).trim().indexOf("[");
return index > 0 ? value.trim().substring(0, index).trim()
: (index != 0 && StringUtils.hasText(value) ? value.trim() : DEFAULT_LOCATOR_HOST);
}
private static int parsePort(String value) {
StringBuilder digits = new StringBuilder();
for (char chr : String.valueOf(value).toCharArray()) {
if (Character.isDigit(chr)) {
digits.append(chr);
}
}
return digits.length() > 0 ? Integer.valueOf(digits.toString()) : DEFAULT_LOCATOR_PORT;
}
/**
* Construct a new {@link Locator} initialized with the {@link String host} and {@link Integer port}
* on which this {@link Locator} is running and listening for connections.
*
* @param host {@link String} containing the name of the host on which this {@link Locator} is running.
* @param port {@link Integer} specifying the port number on which this {@link Locator} is listening.
*/
private Locator(String host, Integer port) {
this.host = host;
this.port = port;
}
/**
* Return the {@link String name} of the host on which this {@link Locator} is running.
*
* Defaults to {@literal localhost}.
*
* @return the {@link String name} of the host on which this {@link Locator} is running.
*/
public String getHost() {
return Optional.ofNullable(this.host).filter(StringUtils::hasText).orElse(DEFAULT_LOCATOR_HOST);
}
/**
* Returns the {@link Integer port} on which this {@link Locator} is listening.
*
* Defaults to {@literal 10334}.
*
* @return the {@link Integer port} on which this {@link Locator} is listening.
*/
public int getPort() {
return Optional.ofNullable(this.port).orElse(DEFAULT_LOCATOR_PORT);
}
@Override
@SuppressWarnings("all")
public int compareTo(Locator other) {
int result = this.getHost().compareTo(other.getHost());
return result != 0 ? result : (this.getPort() - other.getPort());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Locator)) {
return false;
}
Locator that = (Locator) obj;
return this.getHost().equals(that.getHost())
&& this.getPort() == that.getPort();
}
@Override
public int hashCode() {
int hashValue = 17;
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getHost());
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getPort());
return hashValue;
}
@Override
public String toString() {
return String.format("%s[%d]", getHost(), getPort());
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2018 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.geode.core.env.support;
import org.springframework.util.Assert;
/**
* The {@link Service} class is an Abstract Data Type (ADT) modeling a Pivotal CloudFoundry Service.
*
* @author John Blum
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class Service {
/**
* Factory method to construct a new {@link Service} initialized with a {@link String name}.
*
* @param name {@link String} containing the name of the {@link Service}.
* @return a new {@link Service} configured with the given {@link String name}.
* @throws IllegalArgumentException if the {@link String name} is {@literal null} or empty.
* @see #Service(String)
*/
public static Service with(String name) {
return new Service(name);
}
private final String name;
/**
* Constructs a new {@link Service} initialized with a {@link String name}.
*
* @param name {@link String} containing the name of the {@link Service}.
* @throws IllegalArgumentException if the {@link String name} is {@literal null} or empty.
*/
Service(String name) {
Assert.hasText(name, String.format("Service name [%s] is required", name));
this.name = name;
}
/**
* Returns the {@link String name} of this {@link Service}.
*
* @return this {@link Service Service's} {@link String name}.
*/
public String getName() {
return this.name;
}
@Override
public String toString() {
return getName();
}
}

View File

@@ -0,0 +1,182 @@
/*
* Copyright 2018 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.geode.core.env.support;
import java.util.Arrays;
import java.util.Optional;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* The {@link User} class is an Abstract Data Type (ADT) modeling a user in Pivotal CloudFoundry (PCF).
*
* @author John Blum
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class User implements Comparable<User> {
private Role role;
private final String name;
private String password;
/**
* Factory method used to construct a new {@link User} initialized with the given {@link String name}.
*
* @param name {@link String} containing the name of the {@link User}.
* @return a new {@link User} initialized witht he given {@link String name}.
* @throws IllegalArgumentException if {@link String name} is {@literal null} or empty.
* @see #User(String)
*/
public static User with(String name) {
return new User(name);
}
/**
* Constructs a new {@link User} initialized with the given {@link String name}.
*
* @param name {@link String} containing the name of the {@link User}.
* @throws IllegalArgumentException if {@link String name} is {@literal null} or empty.
*/
private User(String name) {
Assert.hasText(name, String.format("User name [%s] is required", name));
this.name = name;
}
/**
* Returns the {@link String name} of this {@link User}.
*
* @return a {@link String} containing the {@link User User's} name.
*/
public String getName() {
return this.name;
}
/**
* Returns an {@link Optional} {@link String} containing {@link User User's} password.
*
* @return an {@link Optional} {@link String} containing {@link User User's} password.
* @see java.util.Optional
*/
public Optional<String> getPassword() {
return Optional.ofNullable(this.password).filter(StringUtils::hasText);
}
/**
* Returns an {@link Optional} {@link Role} for this {@link User}.
*
* @return an {@link Optional} {@link Role} for this {@link User}.
* @see org.springframework.geode.core.env.support.User.Role
* @see java.util.Optional
*/
public Optional<Role> getRole() {
return Optional.ofNullable(this.role);
}
/**
* Builder method used to set this {@link User User's} {@link String password}.
*
* @param password {@link String} containing this {@link User User's} password.
* @return this {@link User}.
*/
public User withPassword(String password) {
this.password = password;
return this;
}
/**
* Builder method used to set this {@link User User's} {@link Role}.
*
* @param role assigned {@link Role} of this {@link User}.
* @return this {@link User}.
* @see org.springframework.geode.core.env.support.User
*/
public User withRole(Role role) {
this.role = role;
return this;
}
@Override
@SuppressWarnings("all")
public int compareTo(User other) {
return this.getName().compareTo(other.getName());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof User)) {
return false;
}
User that = (User) obj;
return this.getName().equals(that.getName());
}
@Override
public int hashCode() {
int hashValue = 17;
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getName());
return hashValue;
}
@Override
public String toString() {
return getName();
}
public enum Role {
CLUSTER_OPERATOR,
DEVELOPER;
public static Role of(String name) {
return Arrays.stream(values())
.filter(role -> role.name().equalsIgnoreCase(String.valueOf(name).trim()))
.findFirst()
.orElse(null);
}
public boolean isClusterOperator() {
return CLUSTER_OPERATOR.equals(this);
}
public boolean isDeveloper() {
return DEVELOPER.equals(this);
}
@Override
public String toString() {
return name().toLowerCase();
}
}
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright 2018 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.geode.core.util;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
/**
* The {@link ObjectUtils} class is an abstract utility class with operations for {@link Object objects}.
*
* @author John Blum
* @see java.lang.Object
* @since 1.0.0
*/
@SuppressWarnings("all")
public abstract class ObjectUtils extends org.springframework.util.ObjectUtils {
private static final Logger logger = LoggerFactory.getLogger(ObjectUtils.class);
/**
* Executes the given {@link ExceptionThrowingOperation} handling any checked {@link Exception} thrown during
* the normal execution of the operation by rethrowing an {@link IllegalStateException} wrapping
* the checked {@link Exception}.
*
* @param <T> {@link Class type} of the operation result.
* @param operation {@link ExceptionThrowingOperation} to execute.
* @return the result of the given {@link ExceptionThrowingOperation} or throw an {@link IllegalStateException}
* wrapping the checked {@link Exception} thrown by the operation.
* @see org.springframework.geode.core.util.ObjectUtils.ExceptionThrowingOperation
* @see #doOperationSafely(ExceptionThrowingOperation, Object)
*/
@Nullable
public static <T> T doOperationSafely(ExceptionThrowingOperation<T> operation) {
return doOperationSafely(operation, null);
}
/**
* Executes the given {@link ExceptionThrowingOperation} handling any checked {@link Exception} thrown
* during the normal execution of the operation, returning the {@link Object default value} in its place
* or throwing a {@link RuntimeException} if the {@link Object default value} is {@literal null}.
*
* @param <T> {@link Class type} of the operation result as well as the {@link Object default value}.
* @param operation {@link ExceptionThrowingOperation} to execute.
* @param defaultValue {@link Object value} to return if the operation results in a checked {@link Exception}.
* @return the result of the given {@link ExceptionThrowingOperation}, returning the {@link Object default value}
* if the operation throws a checked {@link Exception} or throws an {@link IllegalStateException} wrapping
* the checked {@link Exception} if the {@link Object default value} is {@literal null}.
* @throws IllegalStateException if the {@link ExceptionThrowingOperation} throws a checked {@link Exception}
* and {@link Object default value} is {@literal null}.
* @see org.springframework.geode.core.util.ObjectUtils.ExceptionThrowingOperation
* @see #returnValueThrowOnNull(Object, RuntimeException)
*/
@Nullable
public static <T> T doOperationSafely(ExceptionThrowingOperation<T> operation, T defaultValue) {
try {
return operation.doExceptionThrowingOperation();
}
catch (Exception cause) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("Failed to execute operation [%s]", operation), cause);
}
return returnValueThrowOnNull(defaultValue,
newIllegalStateException(cause, "Failed to execute operation"));
}
}
/**
* Returns the given {@link Object value} or throws an {@link IllegalArgumentException}
* if {@link Object value} is {@literal null}.
*
* @param <T> {@link Class type} of the {@link Object value}.
* @param value {@link Object} to return.
* @return the {@link Object value} or throw an {@link IllegalArgumentException}
* if {@link Object value} is {@literal null}.
* @see #returnValueThrowOnNull(Object, RuntimeException)
*/
public static <T> T returnValueThrowOnNull(T value) {
return returnValueThrowOnNull(value, newIllegalArgumentException("Value must not be null"));
}
/**
* Returns the given {@link Object value} or throws the given {@link RuntimeException}
* if {@link Object value} is {@literal null}.
*
* @param <T> {@link Class type} of the {@link Object value}.
* @param value {@link Object} to return.
* @param exception {@link RuntimeException} to throw if {@link Object value} is {@literal null}.
* @return the {@link Object value} or throw the given {@link RuntimeException}
* if {@link Object value} is {@literal null}.
*/
public static <T> T returnValueThrowOnNull(T value, RuntimeException exception) {
if (value == null) {
throw exception;
}
return value;
}
@FunctionalInterface
public interface ExceptionThrowingOperation<T> {
T doExceptionThrowingOperation() throws Exception;
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2018 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.geode.function.support;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.geode.cache.execute.FunctionException;
import org.apache.geode.cache.execute.ResultCollector;
/**
* The {@link AbstractResultCollector} class is an abstract base implementation of the {@link ResultCollector} interface
* encapsulating common functionality for collecting results from a Function execution.
*
* @author John Blum
* @see org.apache.geode.cache.execute.ResultCollector
* @since 1.0.0
*/
@SuppressWarnings("unused")
public abstract class AbstractResultCollector<T, S> implements ResultCollector<T, S> {
protected static final String NOT_IMPLEMENTED = "Not Implemented";
protected static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.MILLISECONDS;
private AtomicBoolean resultsEnded = new AtomicBoolean(false);
private S result = null;
@Override
public synchronized S getResult() throws FunctionException {
return this.result;
}
@Override
public S getResult(long duration, TimeUnit unit) throws FunctionException, InterruptedException {
unit = resolveTimeUnit(unit);
long durationInMilliseconds = unit.toMillis(duration);
long timeout = System.currentTimeMillis() + unit.toMillis(duration);
long waitInMilliseconds = Math.max(50, Math.min(durationInMilliseconds / 5, durationInMilliseconds));
synchronized (this) {
while (getResult() == null && System.currentTimeMillis() < timeout) {
unit.timedWait(this, waitInMilliseconds);
}
}
return getResult();
}
protected synchronized void setResult(S result) {
this.result = result;
}
protected TimeUnit resolveTimeUnit(TimeUnit unit) {
return unit != null ? unit : DEFAULT_TIME_UNIT;
}
@Override
public void clearResults() {
setResult(null);
}
@Override
public void endResults() {
this.resultsEnded.set(true);
}
protected boolean hasResultsEnded() {
return this.resultsEnded.get();
}
protected boolean hasResultsNotEnded() {
return !this.resultsEnded.get();
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2018 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.geode.function.support;
import java.util.Collections;
import java.util.Iterator;
import java.util.Optional;
import org.apache.geode.cache.execute.ResultCollector;
import org.apache.geode.distributed.DistributedMember;
/**
* The {@link SingleResultReturningCollector} class is an implementation of the {@link ResultCollector} interface
* which returns a single {@link Object result}.
*
* @author John Blum
* @see org.apache.geode.cache.execute.ResultCollector
* @see org.springframework.geode.function.support.AbstractResultCollector
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class SingleResultReturningCollector<T> extends AbstractResultCollector<T, T> {
@Override
public void addResult(DistributedMember memberID, T resultOfSingleExecution) {
setResult(extractSingleResult(resultOfSingleExecution));
}
@SuppressWarnings("unchecked")
private <T> T extractSingleResult(Object result) {
return (T) Optional.ofNullable(result)
.filter(this::isInstanceOfIterableOrIterator)
.map(this::toIterator)
.filter(Iterator::hasNext)
.map(Iterator::next)
.map(this::extractSingleResult)
.orElseGet(() -> isInstanceOfIterableOrIterator(result) ? null : result);
}
private boolean isInstanceOfIterableOrIterator(Object obj) {
return obj instanceof Iterable || obj instanceof Iterator;
}
@SuppressWarnings("unchecked")
private <T> Iterator<T> toIterator(Object obj) {
return obj instanceof Iterator ? (Iterator<T>) obj : toIterator((Iterable<T>) obj);
}
private <T> Iterator<T> toIterator(Iterable<T> iterable) {
return iterable != null ? iterable.iterator() : Collections.emptyIterator();
}
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright 2018 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.geode.security.support;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
import java.util.Optional;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.geode.security.AuthenticationFailedException;
import org.apache.geode.security.ResourcePermission;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.gemfire.support.LazyWiringDeclarableSupport;
import org.springframework.util.Assert;
/**
* The {@link SecurityManagerProxy} class is an Apache Geode {@link org.apache.geode.security.SecurityManager}
* proxy implementation delegating to a backing {@link org.apache.geode.security.SecurityManager} implementation
* which is registered as a managed bean in a Spring context.
*
* The idea behind this {@link org.apache.geode.security.SecurityManager} is to enable users to be able to configure
* and manage the {@code SecurityManager} as a Spring bean. However, Apache Geode/Pivotal GemFire require
* the {@link org.apache.geode.security.SecurityManager} to be configured using a System property when launching
* Apache Geode Servers with Gfsh, which makes it difficult to "manage" the {@code SecurityManager} instance.
*
* Therefore, this implementation allows a developer to set the Apache Geode System property using this proxy...
*
* <code>
* gemfire.security-manager=org.springframework.geode.security.support.SecurityManagerProxy
* </code>
*
* And then declare and define a bean in the Spring context implementing the
* {@link org.apache.geode.security.SecurityManager} interface...
*
* <code>
* Configuration
* class MyApplicationConfiguration {
*
* Bean
* ExampleSecurityManager exampleSecurityManager(Environment environment) {
* return new ExampleSecurityManager(environment);
* }
*
* ...
* }
* </code>
*
* @author John Blum
* @see org.apache.geode.security.ResourcePermission
* @see org.apache.geode.security.SecurityManager
* @see org.springframework.beans.factory.annotation.Autowired
* @see org.springframework.data.gemfire.support.LazyWiringDeclarableSupport
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class SecurityManagerProxy extends LazyWiringDeclarableSupport
implements org.apache.geode.security.SecurityManager {
private static final AtomicReference<SecurityManagerProxy> INSTANCE = new AtomicReference<>();
private org.apache.geode.security.SecurityManager securityManager;
/**
* Returns a reference to the single {@link SecurityManagerProxy} instance configured by
* Apache Geode/Pivotal GemFire in startup.
*
* @return a reference to the single {@link SecurityManagerProxy} instance.
*/
public static SecurityManagerProxy getInstance() {
return Optional.ofNullable(INSTANCE.get())
.orElseThrow(() -> newIllegalStateException("SecurityManagerProxy was not configured"));
}
/**
* Constructs a new instance of {@link SecurityManagerProxy}, which will delegate all Apache Geode
* security operations to a Spring managed {@link org.apache.geode.security.SecurityManager} bean.
*/
public SecurityManagerProxy() {
// TODO remove init() call when GEODE-2083 (https://issues.apache.org/jira/browse/GEODE-2083) is resolved!
// NOTE: the init(:Properties) call in the constructor is less than ideal since...
// 1) it allows the *this* reference to escape, and...
// 2) it is Geode's responsibility to identify Geode Declarable objects and invoke their init(:Properties) method
// However, the init(:Properties) method invocation in the constructor is necessary to enable this Proxy to be
// identified and auto-wired in a Spring context.
INSTANCE.compareAndSet(null, this);
init(new Properties());
}
/**
* Configures a reference to the Apache Geode {@link org.apache.geode.security.SecurityManager} instance
* delegated to by this {@link SecurityManagerProxy}.
*
* @param securityManager reference to the underlying Apache Geode {@link org.apache.geode.security.SecurityManager}
* instance delegated to by this {@link SecurityManagerProxy}.
* @throws IllegalArgumentException if the {@link org.apache.geode.security.SecurityManager} reference
* is {@literal null}.
* @see org.apache.geode.security.SecurityManager
*/
@Autowired
public void setSecurityManager(org.apache.geode.security.SecurityManager securityManager) {
Assert.notNull(securityManager, "SecurityManager must not be null");
this.securityManager = securityManager;
}
/**
* Returns a reference to the Apache Geode {@link org.apache.geode.security.SecurityManager} instance
* delegated to by this {@link SecurityManagerProxy}.
*
* @return a reference to the underlying {@link org.apache.geode.security.SecurityManager} instance
* delegated to by this {@link SecurityManagerProxy}.
* @throws IllegalStateException if the configured {@link org.apache.geode.security.SecurityManager}
* was not properly configured.
* @see org.apache.geode.security.SecurityManager
*/
protected org.apache.geode.security.SecurityManager getSecurityManager() {
Assert.state(this.securityManager != null, "No SecurityManager configured");
return this.securityManager;
}
@Override
public Object authenticate(Properties properties) throws AuthenticationFailedException {
return getSecurityManager().authenticate(properties);
}
@Override
public boolean authorize(Object principal, ResourcePermission permission) {
return getSecurityManager().authorize(principal, permission);
}
@Override
public void close() {
getSecurityManager().close();
}
}

View File

@@ -0,0 +1,15 @@
# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.geode.boot.autoconfigure.ClientCacheAutoConfiguration,\
org.springframework.geode.boot.autoconfigure.CachingProviderAutoConfiguration,\
org.springframework.geode.boot.autoconfigure.ClientSecurityAutoConfiguration,\
org.springframework.geode.boot.autoconfigure.ContinuousQueryAutoConfiguration,\
org.springframework.geode.boot.autoconfigure.FunctionExecutionAutoConfiguration,\
org.springframework.geode.boot.autoconfigure.PeerSecurityAutoConfiguration,\
org.springframework.geode.boot.autoconfigure.RepositoriesAutoConfiguration,\
org.springframework.geode.boot.autoconfigure.SslAutoConfiguration
# Environment Post Processing
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.geode.boot.autoconfigure.ClientSecurityAutoConfiguration.AutoConfiguredCloudSecurityEnvironmentPostProcessor,\
org.springframework.geode.boot.autoconfigure.SslAutoConfiguration.SslEnvironmentPostProcessor

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2018 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 example.app;
/**
* The {@link NonBeanType} class is a non-Spring bean, placeholder reference type for classpath component scanning.
*
* @author John Blum
* @since 1.0.0
*/
public class NonBeanType {
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2018 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 example.app.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.gemfire.mapping.annotation.Region;
import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
/**
* The Author class...
*
* @author John Blum
* @since 1.0.0
*/
@Data
@Region("Authors")
@RequiredArgsConstructor(staticName = "newAuthor")
@SuppressWarnings("unused")
public class Author {
@Id
private Long id;
@NonNull
private String name;
public boolean isNew() {
return getId() == null;
}
public Author identifiedBy(Long id) {
setId(id);
return this;
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2018 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 example.app.model;
import java.time.LocalDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.gemfire.mapping.annotation.Region;
import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
/**
* The {@link Book} class is an Abstract Data Type (ADT) modeling a book.
*
* @author John Blum
* @see lombok
* @see org.springframework.data.annotation.Id
* @see org.springframework.data.gemfire.mapping.annotation.Region
* @since 1.0.0
*/
@Region("Books")
@Data
@RequiredArgsConstructor(staticName = "newBook")
public class Book {
private Author author;
@Id
private ISBN isbn;
private LocalDate publishedDate;
@NonNull
private String title;
public boolean isNew() {
return getIsbn() == null;
}
public Book identifiedBy(ISBN isbn) {
setIsbn(isbn);
return this;
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2018 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 example.app.model;
import java.util.UUID;
import org.apache.shiro.util.Assert;
/**
* The {@link ISBN} class is a Abstract Data Type (ADT) modeling either a {@link Book} ISBN-10 or ISBN-13 number.
*
* @author John Blum
* @see java.lang.Comparable
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class ISBN implements Comparable<ISBN> {
public static ISBN autoGenerated() {
return of(UUID.randomUUID().toString());
}
public static ISBN of(String number) {
Assert.hasText(number, String.format("Number [%s] is required", number));
return new ISBN(number);
}
private String number;
private ISBN(String number) {
this.number = number;
}
public String getNumber() {
return number;
}
@Override
@SuppressWarnings("all")
public int compareTo(ISBN other) {
return this.getNumber().compareTo(other.getNumber());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ISBN)) {
return false;
}
ISBN that = (ISBN) obj;
return this.getNumber().equals(that.getNumber());
}
@Override
public int hashCode() {
int hashValue = 17;
hashValue = 37 * hashValue + getNumber().hashCode();
return hashValue;
}
@Override
public String toString() {
return getNumber();
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2018 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 example.app.repo;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
import example.app.model.Author;
import example.app.model.Book;
import example.app.model.ISBN;
/**
* The {@link BookRepository} interface is a Spring Data {@link CrudRepository} defining basic CRUD
* and simple query data access operations on {@link Book} objects to the backing data store.
*
* @author John Blum
* @see example.app.model.Book
* @see example.app.model.ISBN
* @see org.springframework.data.repository.CrudRepository
* @since 1.0.0
*/
@SuppressWarnings("unused")
public interface BookRepository extends CrudRepository<Book, ISBN> {
List<Book> findByAuthorOrderByAuthorNameAscTitleAsc(Author author);
Book findByIsbn(ISBN isbn);
Book findByTitle(String title);
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2018 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 example.app.service;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import example.app.model.Author;
import example.app.model.Book;
import example.app.model.ISBN;
import example.app.repo.BookRepository;
/**
* The {@link BookService} class is an application {@link Service service} class for managing {@link Book Books}.
*
* @author John Blum
* @see example.app.model.Author
* @see example.app.model.Book
* @see example.app.model.ISBN
* @see example.app.repo.BookRepository
* @see org.springframework.stereotype.Service
* @since 1.0.0
*/
@Service
@SuppressWarnings("unused")
public class BookService {
private final BookRepository bookRepository;
public BookService(@Autowired(required = false) BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
protected BookRepository getBookRepository() {
return Optional.ofNullable(this.bookRepository)
.orElseThrow(() -> newIllegalStateException("BookRepository was not properly configured"));
}
public List<Book> findByAuthor(Author author) {
return getBookRepository().findByAuthorOrderByAuthorNameAscTitleAsc(author);
}
public Book findByIsbn(ISBN isbn) {
return getBookRepository().findByIsbn(isbn);
}
public Book findByTitle(String title) {
return getBookRepository().findByTitle(title);
}
public Book stock(Book book) {
if (book.isNew()) {
book.identifiedBy(ISBN.autoGenerated());
}
return getBookRepository().save(book);
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2018 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 example.app.service.support;
import java.util.concurrent.atomic.AtomicBoolean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import example.app.model.Book;
import example.app.model.ISBN;
import example.app.repo.BookRepository;
import example.app.service.BookService;
/**
* The {@link CachingBookService} class is an implementation and extension of {@link BookService}
* with caching capabilities.
*
* @author John Blum
* @see example.app.model.Book
* @see example.app.service.BookService
* @see org.springframework.cache.annotation.Cacheable
* @see org.springframework.stereotype.Service
* @since 1.0.0
*/
@Service
public class CachingBookService extends BookService {
private final AtomicBoolean cacheMiss = new AtomicBoolean(false);
public CachingBookService(@Autowired(required = false) BookRepository bookRepository) {
super(bookRepository);
}
public boolean isCacheMiss() {
return this.cacheMiss.getAndSet(false);
}
@Override
@Cacheable(value = "CachedBooks")
public Book findByTitle(String title) {
this.cacheMiss.set(true);
return Book.newBook(title).identifiedBy(ISBN.autoGenerated());
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2018 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 example.echo.config;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.util.RegionUtils;
/**
* The {@link EchoClientConfiguration} class is a Spring {@link Configuration} class used to configure
* a {@link ClientCache} {@link Region} for echo messages.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.data.gemfire.GemfireTemplate
* @see org.springframework.data.gemfire.client.ClientRegionFactoryBean
* @since 1.0.0
*/
@Configuration
@SuppressWarnings("unused")
public class EchoClientConfiguration {
protected static final String REGION_NAME = "Echo";
@Bean(REGION_NAME)
public ClientRegionFactoryBean<String, String> echoRegion(GemFireCache gemfireCache) {
ClientRegionFactoryBean<String, String> echoRegion = new ClientRegionFactoryBean<>();
echoRegion.setCache(gemfireCache);
echoRegion.setClose(false);
echoRegion.setShortcut(ClientRegionShortcut.PROXY);
return echoRegion;
}
@Bean
public GemfireTemplate echoTemplate(GemFireCache gemfireCache) {
return new GemfireTemplate(gemfireCache.getRegion(RegionUtils.toRegionPath(REGION_NAME)));
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2018 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 example.echo.config;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.data.gemfire.PartitionedRegionFactoryBean;
import org.springframework.data.gemfire.util.RegionUtils;
import example.geode.cache.EchoCacheLoader;
/**
* The {@link EchoClientConfiguration} class is a Spring {@link Configuration} class used to configure
* a peer {@link Cache} {@link Region} for echo messages.
*
* @author John Blum
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.data.gemfire.GemfireTemplate
* @see org.springframework.data.gemfire.PartitionedRegionFactoryBean
* @see example.geode.cache.EchoCacheLoader
* @since 1.0.0
*/
@Configuration
@SuppressWarnings("unused")
public class EchoServerConfiguration {
@Bean(EchoClientConfiguration.REGION_NAME)
public PartitionedRegionFactoryBean<String, String> echoRegion(GemFireCache gemfireCache) {
PartitionedRegionFactoryBean<String, String> echoRegion = new PartitionedRegionFactoryBean<>();
echoRegion.setCache(gemfireCache);
echoRegion.setCacheLoader(EchoCacheLoader.INSTANCE);
echoRegion.setClose(false);
echoRegion.setPersistent(false);
return echoRegion;
}
@Bean
public GemfireTemplate echoTemplate(GemFireCache gemfireCache) {
return new GemfireTemplate(gemfireCache.getRegion(
RegionUtils.toRegionPath(EchoClientConfiguration.REGION_NAME)));
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2018 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 example.geode.cache;
import org.apache.geode.cache.CacheLoader;
import org.apache.geode.cache.CacheLoaderException;
import org.apache.geode.cache.LoaderHelper;
import org.springframework.geode.cache.support.CacheLoaderSupport;
/**
* The {@link EchoCacheLoader} class is an implementation of {@link CacheLoader} that echos the key as the value.
*
* @author John Blum
* @see org.springframework.geode.cache.support.CacheLoaderSupport
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class EchoCacheLoader implements CacheLoaderSupport<String, String> {
public static final EchoCacheLoader INSTANCE = new EchoCacheLoader();
@Override
public String load(LoaderHelper<String, String> helper) throws CacheLoaderException {
return helper.getKey();
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2018 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 example.geode.query.cq.event;
import java.time.LocalDateTime;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
/**
* The {@link TemperatureReading} class is an Abstract Data Type (ADT) modeling a temperature event,
* tracking the recorded temperature, unit and timestamp of the event.
*
* @author John Blum
* @see example.geode.query.cq.event.TemperatureUnit
* @since 1.0.0
*/
@Data
@EqualsAndHashCode(of = { "temperature", "temperatureUnit" })
@RequiredArgsConstructor(staticName = "of")
public class TemperatureReading {
@NonNull
private Integer temperature;
private LocalDateTime timestamp = LocalDateTime.now();
private TemperatureUnit temperatureUnit = TemperatureUnit.defaultTemperatureUnit();
public TemperatureReading at(LocalDateTime timestamp) {
setTimestamp(timestamp);
return this;
}
public TemperatureReading in(TemperatureUnit temperatureUnit) {
setTemperatureUnit(temperatureUnit);
return this;
}
@Override
public String toString() {
return String.format("%d %s", getTemperature(), getTemperatureUnit());
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2018 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 example.geode.query.cq.event;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.apache.geode.cache.query.CqEvent;
import org.springframework.data.gemfire.listener.annotation.ContinuousQuery;
/**
* The TemperatureReadingsContinuousQueriesHandler class...
*
* @author John Blum
* @since 1.0.0
*/
@SuppressWarnings("unused")
public final class TemperatureReadingsContinuousQueriesHandler {
private final AtomicInteger temperatureReadingsCounter = new AtomicInteger(0);
private final List<TemperatureReading> boilingTemperatureReadings = new CopyOnWriteArrayList<>();
private final List<TemperatureReading> freezingTemperatureReadings = new CopyOnWriteArrayList<>();
public List<TemperatureReading> getBoilingTemperatureReadings() {
return Collections.unmodifiableList(this.boilingTemperatureReadings);
}
public List<Integer> getBoilingTemperatures() {
return getBoilingTemperatureReadings().stream()
.map(TemperatureReading::getTemperature)
.collect(Collectors.toList());
}
public List<TemperatureReading> getFreezingTemperatureReadings() {
return Collections.unmodifiableList(this.freezingTemperatureReadings);
}
public List<Integer> getFreezingTemperatures() {
return getFreezingTemperatureReadings().stream()
.map(TemperatureReading::getTemperature)
.collect(Collectors.toList());
}
public int getTemperatureReadingCount() {
return this.temperatureReadingsCounter.get();
}
@ContinuousQuery(name = "BoilingTemperatures",
query = "SELECT * FROM /TemperatureReadings r WHERE r.temperature >= 212")
public void boilingTemperatures(CqEvent event) {
TemperatureReading temperatureReading = (TemperatureReading) event.getNewValue();
this.boilingTemperatureReadings.add(temperatureReading);
this.temperatureReadingsCounter.incrementAndGet();
}
@ContinuousQuery(name = "FreezingTemperatures",
query = "SELECT * FROM /TemperatureReadings r WHERE r.temperature <= 32")
public void freezingTemperatures(CqEvent event) {
TemperatureReading temperatureReading = (TemperatureReading) event.getNewValue();
this.freezingTemperatureReadings.add(temperatureReading);
this.temperatureReadingsCounter.incrementAndGet();
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2018 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 example.geode.query.cq.event;
import java.util.Locale;
import java.util.Optional;
/**
* The {@link TemperatureUnit} enum is an enumeration of different temperature units
* as defined by International System of Units (SI).
*
* @author John Blum
* @since 1.0.0
*/
@SuppressWarnings("unused")
public enum TemperatureUnit {
CELSIUS("°C"),
FAHRENHEIT("°F"),
KELVIN("K");
public static TemperatureUnit defaultTemperatureUnit() {
return Optional.of(Locale.getDefault())
.map(Locale::getISO3Country)
.filter(Locale.US.getISO3Country()::equalsIgnoreCase)
.map(it -> TemperatureUnit.FAHRENHEIT)
.orElse(TemperatureUnit.CELSIUS);
}
private final String symbol;
TemperatureUnit(String symbol) {
this.symbol = symbol;
}
public String getSymbol() {
return this.symbol;
}
@Override
public String toString() {
return getSymbol();
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2018 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 example.java.net;
import java.net.URL;
import org.springframework.core.io.ClassPathResource;
/**
* The {@link UrlRevealed} class...
*
* @author John Blum
* @see java.net.URL
* @since 1.0.0
*/
public class UrlRevealed {
public static void main(String[] args) throws Exception {
URL url = new URL("jar:file:///www.foo.com/bar/jar.jar!/baz/entry.txt");
System.out.printf("URL [%s] {%n \tfile [%s],%n \tpath [%s],%n \tport [%s],%n \tprotocol [%s],%n \tquery [%s]%n}%n%n",
url, url.getFile(), url.getPath(), url.getPort(), url.getProtocol(), url.getQuery());
System.out.printf("URI [%s]%n", new ClassPathResource("trusted.keystore").getURL().toURI());
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2018 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.support;
import org.springframework.beans.factory.BeanFactory;
/**
* The {@link GemfireBeanFactoryLocatorProxy} class is an extension of {@link GemfireBeanFactoryLocator}
* used to clean up all Spring {@link BeanFactory} references, which are stored throughout the runtime
* of an application for different purposes, like configuration of non-Spring managed components.
*
* @author John Blum
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.data.gemfire.support.GemfireBeanFactoryLocator
* @since 1.0.0
*/
// TODO: remove this class once and refactor the SDG GemfireBeanFactoryLocator!
public class GemfireBeanFactoryLocatorProxy extends GemfireBeanFactoryLocator {
public static void clear() {
BEAN_FACTORIES.clear();
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure.cache.client;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
import java.util.Optional;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.config.annotation.EnableLogging;
import org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport;
import org.springframework.data.gemfire.util.RegionUtils;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Integration test testing the auto-configuration of an Apache Geode {@link ClientCache} instance.
*
* @author John Blum
* @see org.junit.Test
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.boot.autoconfigure.SpringBootApplication
* @see org.springframework.boot.test.context.SpringBootTest
* @see org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport
* @see org.springframework.geode.boot.autoconfigure.ClientCacheAutoConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @since 1.0.0
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@SuppressWarnings("unused")
public class SpringBootApacheGeodeClientCacheApplicationIntegrationTests extends IntegrationTestsSupport {
private static final String GEMFIRE_LOG_LEVEL = "error";
@Autowired
private ClientCache clientCache;
@Test
public void clientCacheAndClientRegionAreAvailable() {
Optional.ofNullable(this.clientCache)
.filter(it -> it instanceof GemFireCacheImpl)
.map(it -> (GemFireCacheImpl) it)
.map(it -> assertThat(it.isClient()).isTrue())
.orElseThrow(() -> newIllegalStateException("ClientCache was null"));
Region<Object, Object> example = this.clientCache.getRegion("Example");
assertThat(example).isNotNull();
assertThat(example.getName()).isEqualTo("Example");
assertThat(example.getFullPath()).isEqualTo(RegionUtils.toRegionPath("Example"));
example.put(1, "test");
assertThat(example.get(1)).isEqualTo("test");
}
@SpringBootApplication
@EnableLogging(logLevel = GEMFIRE_LOG_LEVEL)
static class TestConfiguration {
@Bean("Example")
public ClientRegionFactoryBean<Object, Object> exampleRegion(GemFireCache gemfireCache) {
ClientRegionFactoryBean<Object, Object> clientRegion = new ClientRegionFactoryBean<>();
clientRegion.setCache(gemfireCache);
clientRegion.setClose(false);
clientRegion.setShortcut(ClientRegionShortcut.LOCAL);
return clientRegion;
}
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure.cache.peer;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
import java.util.Optional;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.data.gemfire.LocalRegionFactoryBean;
import org.springframework.data.gemfire.config.annotation.PeerCacheApplication;
import org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport;
import org.springframework.data.gemfire.util.RegionUtils;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Integration test testing the auto-configuration of an Apache Geode peer {@link Cache} instance, overriding
* the default {@link ClientCache} instance.
*
* @author John Blum
* @see org.junit.Test
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.springframework.boot.autoconfigure.SpringBootApplication
* @see org.springframework.boot.test.context.SpringBootTest
* @see org.springframework.data.gemfire.config.annotation.PeerCacheApplication
* @see org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport
* @see org.springframework.test.context.junit4.SpringRunner
* @since 1.0.0
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@SuppressWarnings("unused")
public class SpringBootApacheGeodePeerCacheApplicationIntegrationTests extends IntegrationTestsSupport {
private static final String GEMFIRE_LOG_LEVEL = "error";
@Autowired
private GemFireCache peerCache;
@Test
public void peerCacheWithPeerLocalRegionAreAvailable() {
Optional.ofNullable(this.peerCache)
.filter(it -> it instanceof GemFireCacheImpl)
.map(it -> (GemFireCacheImpl) it)
.map(it -> assertThat(it.isClient()).isFalse())
.orElseThrow(() -> newIllegalStateException("Peer cache was null"));
Region<Object, Object> example = peerCache.getRegion("/Example");
assertThat(example).isNotNull();
assertThat(example.getName()).isEqualTo("Example");
assertThat(example.getFullPath()).isEqualTo(RegionUtils.toRegionPath("Example"));
example.put(1, "test");
assertThat(example.get(1)).isEqualTo("test");
}
@SpringBootApplication
@PeerCacheApplication(logLevel = GEMFIRE_LOG_LEVEL)
static class TestConfiguration {
@Bean("Example")
public LocalRegionFactoryBean<Object, Object> exampleRegion(GemFireCache gemfireCache) {
LocalRegionFactoryBean<Object, Object> exampleRegion = new LocalRegionFactoryBean<>();
exampleRegion.setCache(gemfireCache);
exampleRegion.setClose(false);
exampleRegion.setPersistent(false);
return exampleRegion;
}
}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure.caching;
import static org.assertj.core.api.Assertions.assertThat;
import javax.annotation.Resource;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.gemfire.config.annotation.EnableCachingDefinedRegions;
import org.springframework.data.gemfire.config.annotation.EnableLogging;
import org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport;
import org.springframework.data.gemfire.util.RegionUtils;
import org.springframework.test.context.junit4.SpringRunner;
import example.app.NonBeanType;
import example.app.model.Book;
import example.app.service.support.CachingBookService;
/**
* Integration tests testing the auto-configuration of Spring's Cache Abstraction with Apache Geode
* or Pivotal GemFire as the caching provider.
*
* @author John Blum
* @see org.junit.Test
* @see org.apache.geode.cache.Region
* @see org.springframework.boot.autoconfigure.SpringBootApplication
* @see org.springframework.boot.test.context.SpringBootTest
* @see org.springframework.data.gemfire.config.annotation.EnableCachingDefinedRegions
* @see org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport
* @see org.springframework.geode.boot.autoconfigure.CachingProviderAutoConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @since 1.0.0
*/
@RunWith(SpringRunner.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@SuppressWarnings("unused")
public class AutoConfiguredCachingIntegrationTests extends IntegrationTestsSupport {
private static final String GEMFIRE_LOG_LEVEL = "error";
@Autowired
private CachingBookService bookService;
@Resource(name = "CachedBooks")
private Region<String, Book> cachedBooks;
private void assertBook(Book book, String title) {
assertThat(book).isNotNull();
assertThat(book.isNew()).isFalse();
assertThat(book.getTitle()).isEqualTo(title);
}
@Test
public void bookServiceWasConfiguredCorrectly() {
assertThat(this.bookService).isNotNull();
assertThat(this.bookService.isCacheMiss()).isFalse();
}
@Test
public void cachedBooksRegionWasConfiguredCorrectly() {
assertThat(this.cachedBooks).isNotNull();
assertThat(this.cachedBooks.getName()).isEqualTo("CachedBooks");
assertThat(this.cachedBooks.getFullPath()).isEqualTo(RegionUtils.toRegionPath("CachedBooks"));
assertThat(this.cachedBooks).isEmpty();
}
@Test
public void geodeAsTheCachingProviderWasAutoConfiguredCorrectly() {
assertThat(this.cachedBooks).isEmpty();
Book bookOne = this.bookService.findByTitle("Star Wars 3 - Revenge of the Sith");
assertBook(bookOne, "Star Wars 3 - Revenge of the Sith");
assertThat(this.bookService.isCacheMiss()).isTrue();
assertThat(this.cachedBooks).hasSize(1);
assertThat(this.cachedBooks.get(bookOne.getTitle())).isEqualTo(bookOne);
Book bookOneAgain = this.bookService.findByTitle(bookOne.getTitle());
assertThat(bookOneAgain).isEqualTo(bookOne);
assertThat(this.bookService.isCacheMiss()).isFalse();
assertThat(this.cachedBooks).hasSize(1);
assertThat(this.cachedBooks.get(bookOne.getTitle())).isEqualTo(bookOne);
Book bookTwo = this.bookService.findByTitle("Star Wars 6 - Return of the Jedi");
assertBook(bookTwo, "Star Wars 6 - Return of the Jedi");
assertThat(this.bookService.isCacheMiss()).isTrue();
assertThat(this.cachedBooks).hasSize(2);
assertThat(this.cachedBooks.get(bookOne.getTitle())).isEqualTo(bookOne);
assertThat(this.cachedBooks.get(bookTwo.getTitle())).isEqualTo(bookTwo);
}
@SpringBootApplication(scanBasePackageClasses = NonBeanType.class)
@EnableLogging(logLevel = GEMFIRE_LOG_LEVEL)
@EnableCachingDefinedRegions(clientRegionShortcut = ClientRegionShortcut.LOCAL)
static class TestConfiguration { }
}

View File

@@ -0,0 +1,217 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure.cq;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import javax.annotation.Resource;
import org.apache.geode.cache.CacheLoader;
import org.apache.geode.cache.CacheLoaderException;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.LoaderHelper;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.data.gemfire.PartitionedRegionFactoryBean;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.config.annotation.CacheServerApplication;
import org.springframework.data.gemfire.config.annotation.EnableLogging;
import org.springframework.data.gemfire.config.annotation.EnablePdx;
import org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport;
import org.springframework.data.gemfire.tests.integration.config.SubscriptionEnabledClientServerIntegrationTestsConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import example.geode.query.cq.event.TemperatureReading;
import example.geode.query.cq.event.TemperatureReadingsContinuousQueriesHandler;
/**
* Integration tests testing the auto-configuration of Apache Geode/Pivotal GemFire Continuous Query.
*
* @author John Blum
* @see org.junit.Test
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.springframework.boot.autoconfigure.SpringBootApplication
* @see org.springframework.boot.test.context.SpringBootTest
* @see org.springframework.context.annotation.AnnotationConfigApplicationContext
* @see org.springframework.context.annotation.Bean
* @see org.springframework.data.gemfire.config.annotation.CacheServerApplication
* @see org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport
* @see org.springframework.data.gemfire.tests.integration.config.SubscriptionEnabledClientServerIntegrationTestsConfiguration
* @see org.springframework.geode.boot.autoconfigure.ContinuousQueryAutoConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @see example.geode.query.cq.event.TemperatureReading
* @see example.geode.query.cq.event.TemperatureReadingsContinuousQueriesHandler
* @since 1.0.0
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
classes = AutoConfiguredContinuousQueryIntegrationTests.GemFireClientConfiguration.class)
@SuppressWarnings("unused")
public class AutoConfiguredContinuousQueryIntegrationTests extends ForkingClientServerIntegrationTestsSupport {
private static final String GEMFIRE_LOG_LEVEL = "error";
@BeforeClass
public static void startGemFireServer() throws IOException {
startGemFireServer(GemFireServerConfiguration.class);
}
@Autowired
private GemfireTemplate temperatureReadingsTemplate;
@SuppressWarnings("all")
@Resource(name = "TemperatureReadings")
private Region<Long, TemperatureReading> temperatureReadings;
@Autowired
private TemperatureReadingsContinuousQueriesHandler temperatureReadingsHandler;
@Before
public void setup() {
assertThat(this.temperatureReadingsTemplate.<Long, TemperatureReading>get(1L))
.isEqualTo(TemperatureReading.of(99));
assertThat(this.temperatureReadings.sizeOnServer()).isEqualTo(8);
}
@Test
public void assertTemperatureReadingsAreCorrect() {
assertThat(this.temperatureReadingsHandler.getTemperatureReadingCount()).isEqualTo(4);
assertThat(this.temperatureReadingsHandler.getBoilingTemperatures()).contains(300, 242);
assertThat(this.temperatureReadingsHandler.getFreezingTemperatures()).contains(16, -51);
}
@SpringBootApplication
@EnableLogging(logLevel = GEMFIRE_LOG_LEVEL)
public static class GemFireClientConfiguration
extends SubscriptionEnabledClientServerIntegrationTestsConfiguration {
@Bean("TemperatureReadings")
public ClientRegionFactoryBean<Long, TemperatureReading> temperatureReadingsRegion(GemFireCache gemfireCache) {
ClientRegionFactoryBean<Long, TemperatureReading> temperatureReadingsRegion =
new ClientRegionFactoryBean<>();
temperatureReadingsRegion.setCache(gemfireCache);
temperatureReadingsRegion.setClose(false);
temperatureReadingsRegion.setShortcut(ClientRegionShortcut.PROXY);
return temperatureReadingsRegion;
}
@Bean
@DependsOn("TemperatureReadings")
GemfireTemplate temperatureReadingsTemplate(GemFireCache gemfireCache) {
return new GemfireTemplate(gemfireCache.getRegion("/TemperatureReadings"));
}
@Bean
@DependsOn("TemperatureReadings")
TemperatureReadingsContinuousQueriesHandler temperatureReadingsHandler() {
return new TemperatureReadingsContinuousQueriesHandler();
}
}
@EnablePdx
@CacheServerApplication(name = "AutoConfiguredContinuousQueryIntegrationTests", logLevel = GEMFIRE_LOG_LEVEL)
public static class GemFireServerConfiguration {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(GemFireServerConfiguration.class);
applicationContext.registerShutdownHook();
}
@Bean("TemperatureReadings")
public PartitionedRegionFactoryBean<Long, TemperatureReading> temperatureReadingsRegion(GemFireCache gemfireCache) {
PartitionedRegionFactoryBean<Long, TemperatureReading> temperatureReadingsRegion =
new PartitionedRegionFactoryBean<>();
temperatureReadingsRegion.setCache(gemfireCache);
temperatureReadingsRegion.setCacheLoader(temperatureReadingsLoader());
temperatureReadingsRegion.setClose(false);
temperatureReadingsRegion.setPersistent(false);
return temperatureReadingsRegion;
}
private CacheLoader<Long, TemperatureReading> temperatureReadingsLoader() {
return new CacheLoader<Long, TemperatureReading>() {
@Override
public TemperatureReading load(LoaderHelper<Long, TemperatureReading> helper)
throws CacheLoaderException {
long key = helper.getKey();
Region<Long, TemperatureReading> temperatureReadings = helper.getRegion();
recordTemperature(temperatureReadings, ++key, 72);
recordTemperature(temperatureReadings, ++key, 16);
recordTemperature(temperatureReadings, ++key, 101);
recordTemperature(temperatureReadings, ++key, 300);
recordTemperature(temperatureReadings, ++key, -51);
recordTemperature(temperatureReadings, ++key, 242);
recordTemperature(temperatureReadings, ++key, 112);
return TemperatureReading.of(99);
}
private void recordTemperature(Region<Long, TemperatureReading> temperatureReadings,
long key, int temperature) {
sleep(50);
temperatureReadings.put(key, TemperatureReading.of(temperature));
}
@SuppressWarnings("all")
private void sleep(long milliseconds) {
try {
Thread.sleep(milliseconds);
}
catch (InterruptedException ignore) {
}
}
@Override
public void close() { }
};
}
}
}

View File

@@ -0,0 +1,176 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure.function;
import static org.assertj.core.api.Assertions.assertThat;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Optional;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.execute.FunctionService;
import org.apache.shiro.util.Assert;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.data.gemfire.config.annotation.EnableGemFireProperties;
import org.springframework.data.gemfire.config.annotation.EnableLogging;
import org.springframework.data.gemfire.function.annotation.GemfireFunction;
import org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport;
import org.springframework.geode.boot.autoconfigure.function.executions.Calculator;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Integration tests testing the auto-configuration of Spring Data for Apache Geode/Pivotal GemFire
* Function implementations and executions support.
*
* @author John Blum
* @see org.junit.Test
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.execute.FunctionService
* @see org.springframework.boot.autoconfigure.SpringBootApplication
* @see org.springframework.boot.test.context.SpringBootTest
* @see org.springframework.context.annotation.Bean
* @see org.springframework.data.gemfire.function.annotation.GemfireFunction
* @see org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport
* @see org.springframework.geode.boot.autoconfigure.function.executions.Calculator
* @see org.springframework.geode.boot.autoconfigure.FunctionExecutionAutoConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @since 1.0.0
*/
@RunWith(SpringRunner.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@SuppressWarnings("unused")
public class AutoConfiguredFunctionExecutionsIntegrationTests extends IntegrationTestsSupport {
private static final String GEMFIRE_LOG_LEVEL = "error";
@Autowired
private GemFireCache gemfireCache;
@Autowired
private Calculator calculator;
@Test
public void cacheClientIsInGroupTest() {
assertThat(this.gemfireCache).isNotNull();
assertThat(this.gemfireCache.getDistributedSystem().getGroupMembers("test"))
.contains(this.gemfireCache.getDistributedSystem().getDistributedMember());
}
@Test
public void firstFunctionsMustBeRegistered() {
Arrays.stream(CalculatorFunctions.class.getMethods())
.filter(method -> !Object.class.equals(method.getDeclaringClass()))
.map(Method::getName)
.forEach(methodName -> assertThat(FunctionService.isRegistered(methodName))
.describedAs("Function [%s] was not registered", methodName)
.isTrue());
}
@Test
public void thenCalculationsAreCorrect() {
assertThat(this.calculator).isNotNull();
assertThat(extractResult(this.calculator.add(8.0d, 8.0d))).isEqualTo(16.0d);
assertThat(extractResult(this.calculator.divide(16.0d, 4.0d))).isEqualTo(4.0d);
assertThat(extractResult(this.calculator.factorial(5L))).isEqualTo(120L);
assertThat(extractResult(this.calculator.multiply(4.0d, 4.0d))).isEqualTo(16.0d);
assertThat(extractResult(this.calculator.squared(4.0d))).isEqualTo(16.0d);
assertThat(extractResult(this.calculator.squareRoot(16.0d))).isEqualTo(4.0d);
assertThat(extractResult(this.calculator.subtract(16.0d, 8.0d))).isEqualTo(8.0d);
}
private Object extractResult(Object result) {
return Optional.ofNullable(result)
.filter(it -> it instanceof Iterable)
.map(it -> ((Iterable) it).iterator())
.filter(Iterator::hasNext)
.map(Iterator::next)
.map(this::extractResult)
.orElse(result);
}
@SpringBootApplication
@EnableGemFireProperties(groups = "test")
@EnableLogging(logLevel = GEMFIRE_LOG_LEVEL)
static class TestConfiguration {
@Bean
public CalculatorFunctions calculatorFunctions(GemFireCache gemfireCache) {
return new CalculatorFunctions();
}
}
public static class CalculatorFunctions {
@GemfireFunction(id = "add", hasResult = true)
public double add(double operandOne, double operandTwo) {
return operandOne + operandTwo;
}
@GemfireFunction(id = "divide", hasResult = true)
public double divide(double numerator, double divisor) {
return numerator / divisor;
}
@GemfireFunction(id = "factorial", hasResult = true)
public long factorial(long number) {
Assert.isTrue(number > -1, "Number be greater than -1");
long result = number == 2 ? 2 : 1;
while (number > 1) {
result *= number--;
}
return result;
}
@GemfireFunction(id = "multiply", hasResult = true)
public double multiply(double operandOne, double operandTwo) {
return operandOne * operandTwo;
}
@GemfireFunction(id = "squareRoot", hasResult = true)
public double squareRoot(double number) {
return Math.sqrt(number);
}
@GemfireFunction(id = "squared", hasResult = true)
public double squared(double number) {
return number * number;
}
@GemfireFunction(id = "subtract", hasResult = true)
public double subtract(double operandOne, double operandTwo) {
return operandOne - operandTwo;
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure.function.executions;
import org.springframework.data.gemfire.function.annotation.OnMember;
/**
* The {@link Calculator} interface defines Apache Geode/Pivotal GemFire Functions.
*
* @author John Blum
* @see org.springframework.data.gemfire.function.annotation.OnRegion
* @since 1.0.0
*/
@OnMember(groups = "test")
@SuppressWarnings("all")
// TODO change Function returns type when SDG properly handles Function method return types/values
public interface Calculator {
Object add(double operandOne, double operandTwo);
Object divide(double numerator, double divisor);
Object factorial(long number);
Object multiply(double operandOne, double operandTwo);
Object squareRoot(double number);
Object squared(double number);
Object subtract(double operandOne, double operandTwo);
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure.repository;
import static org.assertj.core.api.Assertions.assertThat;
import javax.annotation.Resource;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions;
import org.springframework.data.gemfire.config.annotation.EnableLogging;
import org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport;
import org.springframework.data.gemfire.util.RegionUtils;
import org.springframework.geode.boot.autoconfigure.repository.model.Customer;
import org.springframework.geode.boot.autoconfigure.repository.service.CustomerService;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Integration tests testing the auto-configuration of Spring Data Repositories backed by either Apache Geode
* or Pivotal GemFire.
*
* @author John Blum
* @see org.junit.Test
* @see org.apache.geode.cache.Region
* @see org.springframework.boot.autoconfigure.SpringBootApplication
* @see org.springframework.boot.test.context.SpringBootTest
* @see org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions
* @see org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport
* @see org.springframework.geode.boot.autoconfigure.RepositoriesAutoConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @since 1.0.0
*/
@RunWith(SpringRunner.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@SuppressWarnings("unused")
public class AutoConfiguredRepositoriesIntegrationTests extends IntegrationTestsSupport {
private static final String GEMFIRE_LOG_LEVEL = "error";
@Autowired
private CustomerService customerService;
@Resource(name = "Customers")
private Region<Long, Customer> customers;
@Test
public void customerServiceWasConfiguredCorrectly() {
assertThat(this.customerService).isNotNull();
assertThat(this.customerService.getCustomerRepository()).isNotNull();
}
@Test
public void customersRegionWasConfiguredCorrectly() {
assertThat(this.customers).isNotNull();
assertThat(this.customers.getName()).isEqualTo("Customers");
assertThat(this.customers.getFullPath()).isEqualTo(RegionUtils.toRegionPath("Customers"));
assertThat(this.customers).isEmpty();
}
@Test
public void repositoryWasAutoConfiguredCorrectly() {
Customer jonDoe = Customer.newCustomer("Jon Doe");
assertThat(jonDoe).isNotNull();
assertThat(jonDoe.getName()).isEqualTo("Jon Doe");
assertThat(jonDoe.isNew()).isTrue();
jonDoe = this.customerService.save(jonDoe);
assertThat(jonDoe.isNew()).isFalse();
assertThat(this.customers.get(jonDoe.getId())).isEqualTo(jonDoe);
assertThat(this.customerService.findBy(jonDoe.getName()).orElse(null)).isEqualTo(jonDoe);
}
@SpringBootApplication
@EnableLogging(logLevel = GEMFIRE_LOG_LEVEL)
@EnableEntityDefinedRegions(basePackageClasses = Customer.class,
clientRegionShortcut = ClientRegionShortcut.LOCAL)
static class TestConfiguration { }
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure.repository.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.gemfire.mapping.annotation.Region;
import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
/**
* {@link Customer} class and Abstract Data Type (ADT) modeling a customer.
*
* @author John Blum
* @see org.springframework.data.annotation.Id
* @see org.springframework.data.gemfire.mapping.annotation.Region
* @since 1.0.0
*/
@Data
@Region("Customers")
@RequiredArgsConstructor(staticName = "newCustomer")
public class Customer {
@Id
private Long id;
@NonNull
private String name;
public boolean isNew() {
return getId() == null;
}
public Customer identifiedBy(Long id) {
setId(id);
return this;
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure.repository.repo;
import org.springframework.data.repository.CrudRepository;
import org.springframework.geode.boot.autoconfigure.repository.model.Customer;
/**
* The {@link CustomerRepository} interface defines a Spring Data {@link CrudRepository} for performing basic CRUD
* and simple query data access operations on {@link Customer} objects stored in Apache Geode or Pivotal GemFire.
*
* @author John Blum
* @see org.springframework.data.repository.CrudRepository
* @see org.springframework.geode.boot.autoconfigure.repository.model.Customer
* @since 1.0.0
*/
public interface CustomerRepository extends CrudRepository<Customer, Long> {
Customer findByName(String name);
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure.repository.service;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.geode.boot.autoconfigure.repository.model.Customer;
import org.springframework.geode.boot.autoconfigure.repository.repo.CustomerRepository;
import org.springframework.stereotype.Service;
/**
* The {@link CustomerService} class is an application service for managing {@link Customer Customers}.
*
* @author John Blum
* @see org.springframework.geode.boot.autoconfigure.repository.model.Customer
* @see org.springframework.geode.boot.autoconfigure.repository.repo.CustomerRepository
* @see org.springframework.stereotype.Service
* @since 1.0.0
*/
@Service
public class CustomerService {
private final CustomerRepository customerRepository;
private final AtomicLong identifierSequence = new AtomicLong(0L);
public CustomerService(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
public CustomerRepository getCustomerRepository() {
return Optional.ofNullable(this.customerRepository)
.orElseThrow(() -> newIllegalStateException("CustomerRepository was not properly configured"));
}
public Optional<Customer> findBy(String name) {
return Optional.ofNullable(getCustomerRepository().findByName(name));
}
protected Long nextId() {
return identifierSequence.incrementAndGet();
}
public Customer save(Customer customer) {
return Optional.ofNullable(customer)
.map(it -> {
if (customer.isNew()) {
customer.identifiedBy(nextId());
}
return getCustomerRepository().save(customer);
})
.orElseThrow(() -> newIllegalArgumentException("Customer is required"));
}
}

View File

@@ -0,0 +1,175 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure.security.auth;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.data.gemfire.config.annotation.support.AutoConfiguredAuthenticationInitializer.SECURITY_PASSWORD_PROPERTY;
import static org.springframework.data.gemfire.config.annotation.support.AutoConfiguredAuthenticationInitializer.SECURITY_USERNAME_PROPERTY;
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
import java.io.Serializable;
import java.security.Principal;
import java.util.Arrays;
import java.util.Optional;
import java.util.Properties;
import org.apache.geode.security.AuthenticationFailedException;
import org.apache.geode.security.ResourcePermission;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import example.echo.config.EchoClientConfiguration;
import example.echo.config.EchoServerConfiguration;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
/**
* The {@link AbstractAutoConfiguredSecurityContextIntegrationTests} class is an abstract security context integration test class
* encapsulating configuration and functionality common to both cloud and local security context integration tests.
*
* @author John Blum
* @see java.security.Principal
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.security.ResourcePermission
* @see org.springframework.core.env.Environment
* @see org.springframework.data.gemfire.GemfireTemplate
* @see org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport
* @since 1.0.0
*/
@SuppressWarnings("unused")
public abstract class AbstractAutoConfiguredSecurityContextIntegrationTests
extends ForkingClientServerIntegrationTestsSupport {
private static final String SECURITY_CONTEXT_USERNAME_PROPERTY = "security.context.username.property";
private static final String SECURITY_CONTEXT_PASSWORD_PROPERTY = "security.context.password.property";
@Autowired
private GemfireTemplate echoTemplate;
@Test
public void clientServerAuthWasSuccessful() {
assertThat(this.echoTemplate.<String, String>get("Hello")).isEqualTo("Hello");
assertThat(this.echoTemplate.<String, String>get("Test")).isEqualTo("Test");
assertThat(this.echoTemplate.<String, String>get("Good-Bye")).isEqualTo("Good-Bye");
}
@Configuration
@Import(EchoClientConfiguration.class)
protected static abstract class BaseGemFireClientConfiguration { }
@Configuration
@Import(EchoServerConfiguration.class)
protected static abstract class BaseGemFireServerConfiguration {
@Bean
TestSecurityManager testSecurityManager(Environment environment) {
return new TestSecurityManager(environment);
}
}
public static class TestSecurityManager implements org.apache.geode.security.SecurityManager {
private final String username;
private final String password;
public TestSecurityManager(Environment environment) {
this.username = Optional.ofNullable(environment.getProperty(SECURITY_CONTEXT_USERNAME_PROPERTY))
.filter(StringUtils::hasText)
.orElseThrow(() -> newIllegalArgumentException("Username is required"));
this.password = Optional.ofNullable(environment.getProperty(SECURITY_CONTEXT_PASSWORD_PROPERTY))
.filter(StringUtils::hasText)
.orElseThrow(() -> newIllegalArgumentException("Password is required"));
}
private ClassPathResource resolveApplicationProperties(Environment environment) {
Assert.notNull(environment, "Environment must not be null");
return Arrays.stream(nullSafeArray(environment.getActiveProfiles(), String.class))
.filter(StringUtils::hasText)
.filter(it -> !"default".equalsIgnoreCase(it))
.map(it -> String.format("application-%s.properties", it))
.map(ClassPathResource::new)
.filter(ClassPathResource::exists)
.findFirst()
.orElseThrow(() ->
newIllegalStateException("Unable to resolve application.properties from Environment [%s]",
environment));
}
String getUsername() {
return username;
}
String getPassword() {
return password;
}
@Override
public Object authenticate(Properties credentials) throws AuthenticationFailedException {
String username = credentials.getProperty(SECURITY_USERNAME_PROPERTY);
String password = credentials.getProperty(SECURITY_PASSWORD_PROPERTY);
if (!(getUsername().equals(username) && getPassword().equals(password))) {
throw new AuthenticationFailedException(String.format("Failed to authenticate user [%s]", username));
}
return User.with(username).having(password);
}
@Override
public boolean authorize(Object principal, ResourcePermission permission) {
return true;
}
}
@Data
@ToString(of = "name")
@EqualsAndHashCode(of = "name")
@RequiredArgsConstructor(staticName = "with")
static class User implements Principal, Serializable {
@NonNull
private String name;
private String password;
User having(String password) {
setPassword(password);
return this;
}
}
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure.security.auth.cloud;
import java.io.IOException;
import java.util.Properties;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.gemfire.config.annotation.CacheServerApplication;
import org.springframework.data.gemfire.config.annotation.EnableLocator;
import org.springframework.data.gemfire.config.annotation.EnableLogging;
import org.springframework.data.gemfire.support.GemfireBeanFactoryLocatorProxy;
import org.springframework.geode.boot.autoconfigure.security.auth.AbstractAutoConfiguredSecurityContextIntegrationTests;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Integration test testing the auto-configuration of Apache Geode/Pivotal GemFire Security
* authentication/authorization in a cloud, managed context (e.g. Pivotal CloudFoundry)
*
* @author John Blum
* @see java.security.Principal
* @see java.util.Properties
* @see org.junit.Test
* @see org.apache.geode.cache.GemFireCache
* @see org.springframework.boot.SpringApplication
* @see org.springframework.boot.autoconfigure.SpringBootApplication
* @see org.springframework.boot.test.context.SpringBootTest
* @see org.springframework.data.gemfire.config.annotation.CacheServerApplication
* @see org.springframework.data.gemfire.config.annotation.EnableLocator
* @see org.springframework.data.gemfire.config.annotation.EnableSecurity
* @see org.springframework.geode.boot.autoconfigure.ClientSecurityAutoConfiguration
* @see org.springframework.geode.boot.autoconfigure.PeerSecurityAutoConfiguration
* @see org.springframework.geode.boot.autoconfigure.security.auth.AbstractAutoConfiguredSecurityContextIntegrationTests
* @see org.springframework.test.context.junit4.SpringRunner
* @since 1.0.0
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = AutoConfiguredCloudSecurityContextIntegrationTests.GemFireClientConfiguration.class,
webEnvironment = SpringBootTest.WebEnvironment.NONE)
@SuppressWarnings("unused")
public class AutoConfiguredCloudSecurityContextIntegrationTests
extends AbstractAutoConfiguredSecurityContextIntegrationTests {
private static final String GEMFIRE_LOG_LEVEL = "error";
private static final String VCAP_APPLICATION_PROPERTIES = "application-vcap.properties";
private static Properties vcapApplicationProperties = new Properties();
@BeforeClass
public static void startGemFireServer() throws IOException {
startGemFireServer(GemFireServerConfiguration.class, "-Dspring.profiles.active=security-cloud");
loadVcapApplicationProperties();
GemfireBeanFactoryLocatorProxy.clear();
}
public static void loadVcapApplicationProperties() throws IOException {
vcapApplicationProperties.load(new ClassPathResource(VCAP_APPLICATION_PROPERTIES).getInputStream());
vcapApplicationProperties.stringPropertyNames().forEach(property ->
System.setProperty(property, vcapApplicationProperties.getProperty(property)));
}
@AfterClass
public static void cleanUpUsedResources() {
vcapApplicationProperties.stringPropertyNames().forEach(System::clearProperty);
GemfireBeanFactoryLocatorProxy.clear();
}
@SpringBootApplication
@EnableLogging(logLevel = GEMFIRE_LOG_LEVEL)
static class GemFireClientConfiguration extends BaseGemFireClientConfiguration { }
@SpringBootApplication
@CacheServerApplication(name = "AutoConfiguredCloudSecurityContextIntegrationTests", logLevel = GEMFIRE_LOG_LEVEL)
@EnableLocator(port = 55221)
static class GemFireServerConfiguration extends BaseGemFireServerConfiguration {
public static void main(String[] args) {
SpringApplication.run(GemFireServerConfiguration.class, args);
}
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure.security.auth.local;
import java.io.IOException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.data.gemfire.config.annotation.CacheServerApplication;
import org.springframework.data.gemfire.config.annotation.EnableLogging;
import org.springframework.data.gemfire.support.GemfireBeanFactoryLocatorProxy;
import org.springframework.data.gemfire.tests.integration.config.ClientServerIntegrationTestsConfiguration;
import org.springframework.geode.boot.autoconfigure.security.auth.AbstractAutoConfiguredSecurityContextIntegrationTests;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Integration test testing the auto-configuration of Apache Geode/Pivotal GemFire Security
* authentication/authorization in a local, non-managed context.
*
* @author John Blum
* @see java.security.Principal
* @see org.junit.Test
* @see org.apache.geode.cache.GemFireCache
* @see org.springframework.boot.SpringApplication
* @see org.springframework.boot.autoconfigure.SpringBootApplication
* @see org.springframework.boot.test.context.SpringBootTest
* @see org.springframework.context.annotation.Import
* @see org.springframework.data.gemfire.config.annotation.CacheServerApplication
* @see org.springframework.data.gemfire.config.annotation.EnableSecurity
* @see org.springframework.data.gemfire.tests.integration.config.ClientServerIntegrationTestsConfiguration
* @see org.springframework.geode.boot.autoconfigure.ClientSecurityAutoConfiguration
* @see org.springframework.geode.boot.autoconfigure.PeerSecurityAutoConfiguration
* @see org.springframework.geode.boot.autoconfigure.security.auth.AbstractAutoConfiguredSecurityContextIntegrationTests
* @see org.springframework.test.context.ActiveProfiles
* @see org.springframework.test.context.junit4.SpringRunner
* @since 1.0.0
*/
@ActiveProfiles("security-local-client")
@RunWith(SpringRunner.class)
@SpringBootTest(classes = AutoConfiguredLocalSecurityContextIntegrationTests.GemFireClientConfiguration.class,
webEnvironment = SpringBootTest.WebEnvironment.NONE)
@SuppressWarnings("unused")
public class AutoConfiguredLocalSecurityContextIntegrationTests
extends AbstractAutoConfiguredSecurityContextIntegrationTests {
private static final String GEMFIRE_LOG_LEVEL = "error";
@BeforeClass
public static void startGemFireServer() throws IOException {
GemfireBeanFactoryLocatorProxy.clear();
startGemFireServer(GemFireServerConfiguration.class,
"-Dspring.profiles.active=security-local-server");
}
@AfterClass
public static void cleanUpBeanFactoryLocatorReferences() {
GemfireBeanFactoryLocatorProxy.clear();
}
@SpringBootApplication
@EnableLogging(logLevel = GEMFIRE_LOG_LEVEL)
@Import(ClientServerIntegrationTestsConfiguration.class)
static class GemFireClientConfiguration extends BaseGemFireClientConfiguration { }
@SpringBootApplication
@CacheServerApplication(name = "AutoConfiguredLocalSecurityContextIntegrationTests", logLevel = GEMFIRE_LOG_LEVEL)
static class GemFireServerConfiguration extends BaseGemFireServerConfiguration {
public static void main(String[] args) {
SpringApplication.run(GemFireServerConfiguration.class, args);
}
}
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright 2018 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.geode.boot.autoconfigure.security.ssl;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
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.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.data.gemfire.config.annotation.CacheServerApplication;
import org.springframework.data.gemfire.config.annotation.EnableLogging;
import org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport;
import org.springframework.data.gemfire.tests.integration.config.ClientServerIntegrationTestsConfiguration;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import example.echo.config.EchoClientConfiguration;
import example.echo.config.EchoServerConfiguration;
/**
* Integration tests testing the auto-configuration of Apache Geode/Pivotal GemFire SSL.
*
* @author John Blum
* @see org.junit.Test
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.springframework.boot.autoconfigure.SpringBootApplication
* @see org.springframework.boot.test.context.SpringBootTest
* @see org.springframework.context.annotation.Import
* @see org.springframework.data.gemfire.GemfireTemplate
* @see org.springframework.data.gemfire.config.annotation.CacheServerApplication
* @see org.springframework.data.gemfire.config.annotation.EnableSsl
* @see org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport
* @see org.springframework.data.gemfire.tests.integration.config.ClientServerIntegrationTestsConfiguration
* @see org.springframework.test.context.ActiveProfiles
* @see org.springframework.test.context.junit4.SpringRunner
* @since 1.0.0
*/
@ActiveProfiles("ssl")
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
classes = AutoConfiguredSslIntegrationTests.GemFireClientConfiguration.class)
@SuppressWarnings("unused")
public class AutoConfiguredSslIntegrationTests extends ForkingClientServerIntegrationTestsSupport {
private static final String GEMFIRE_LOG_LEVEL = "error";
private static final String TRUSTED_KEYSTORE_FILENAME = "test-trusted.keystore";
@BeforeClass
public static void startGemFireServer() throws IOException {
startGemFireServer(GemFireServerConfiguration.class, "-Dspring.profiles.active=ssl");
}
@AfterClass
public static void clearSslSystemProperties() {
List<String> sslSystemProperties = System.getProperties().keySet().stream()
.map(String::valueOf)
.map(String::toLowerCase)
.filter(property -> property.contains("ssl"))
.collect(Collectors.toList());
//System.err.printf("SSL System Properties [%s]%n", sslSystemProperties);
sslSystemProperties.forEach(System::clearProperty);
}
@Autowired
private GemfireTemplate echoTemplate;
@Test
public void clientServerCommunicationsSuccessful() {
assertThat(this.echoTemplate).isNotNull();
assertThat(this.echoTemplate.<String, String>get("Hello")).isEqualTo("Hello");
assertThat(this.echoTemplate.<String, String>get("Test")).isEqualTo("Test");
assertThat(this.echoTemplate.<String, String>get("Good-Bye")).isEqualTo("Good-Bye");
}
@SpringBootApplication
@EnableLogging(logLevel = GEMFIRE_LOG_LEVEL)
@Import(EchoClientConfiguration.class)
static class GemFireClientConfiguration extends ClientServerIntegrationTestsConfiguration { }
@SpringBootApplication
@CacheServerApplication(name = "AutoConfiguredSslIntegrationTests", logLevel = GEMFIRE_LOG_LEVEL)
@Import(EchoServerConfiguration.class)
static class GemFireServerConfiguration {
public static void main(String[] args) {
SpringApplication.run(GemFireServerConfiguration.class, args);
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2018 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.geode.config.annotation;
import static org.assertj.core.api.Assertions.assertThat;
import org.apache.geode.cache.GemFireCache;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.gemfire.config.annotation.ClientCacheApplication;
import org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport;
import org.springframework.data.gemfire.tests.mock.annotation.EnableGemFireMockObjects;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Integration tests for {@link UseMemberName} and {@link MemberNameConfiguration}.
*
* @author John Blum
* @see org.springframework.data.gemfire.config.annotation.ClientCacheApplication
* @see org.springframework.data.gemfire.tests.mock.annotation.EnableGemFireMockObjects
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @since 1.0.0
*/
@RunWith(SpringRunner.class)
@ContextConfiguration
@SuppressWarnings("unused")
public class MemberNameConfigurationIntegrationTests extends IntegrationTestsSupport {
@Autowired
private GemFireCache gemfireCache;
@Test
public void memberNameIsCorrect() {
assertThat(this.gemfireCache).isNotNull();
assertThat(this.gemfireCache.getDistributedSystem()).isNotNull();
assertThat(this.gemfireCache.getDistributedSystem().getProperties()).isNotNull();
assertThat(this.gemfireCache.getDistributedSystem().getProperties().getProperty("name"))
.isEqualTo("TestClient");
}
@ClientCacheApplication
@EnableGemFireMockObjects
@UseMemberName("TestClient")
static class TestConfiguration { }
}

View File

@@ -0,0 +1,515 @@
/*
* Copyright 2018 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.geode.core.env;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import java.net.URL;
import java.util.Arrays;
import java.util.Properties;
import java.util.Set;
import org.junit.Test;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.geode.core.env.support.CloudCacheService;
import org.springframework.geode.core.env.support.Service;
import org.springframework.geode.core.env.support.User;
/**
* Unit tests for {@link VcapPropertySource}.
*
* @author John Blum
* @see java.util.Properties
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.core.env.Environment
* @see org.springframework.core.env.PropertiesPropertySource
* @see org.springframework.core.env.PropertySource
* @see org.springframework.geode.core.env.VcapPropertySource
* @since 1.0.0
*/
public class VcapPropertySourceUnitTests {
@Test
public void fromEnvironmentIsSuccessful() {
ConfigurableEnvironment mockEnvironment = mock(ConfigurableEnvironment.class);
MutablePropertySources propertySources = spy(new MutablePropertySources());
PropertySource mockVcapPropertySource = mock(EnumerablePropertySource.class);
when(mockEnvironment.getPropertySources()).thenReturn(propertySources);
doReturn(mockVcapPropertySource).when(propertySources).get(eq("vcap"));
when(mockVcapPropertySource.getName()).thenReturn("vcap");
when(mockVcapPropertySource.containsProperty(anyString())).thenReturn(true);
VcapPropertySource propertySource = VcapPropertySource.from(mockEnvironment);
assertThat(propertySource).isNotNull();
assertThat(propertySource.getSource()).isEqualTo(mockVcapPropertySource);
verify(mockEnvironment, times(1)).getPropertySources();
verify(propertySources, times(1)).get(eq("vcap"));
verify(mockVcapPropertySource, times(1))
.containsProperty(eq("vcap.application.name"));
verify(mockVcapPropertySource, times(1))
.containsProperty(eq("vcap.application.uris"));
}
@Test(expected = IllegalArgumentException.class)
public void fromNonConfigurableEnvironmentThrowsIllegalArgumentException() {
Environment mockEnvironment = mock(Environment.class);
try {
VcapPropertySource.from(mockEnvironment);
}
catch (IllegalArgumentException expected) {
assertThat(expected)
.hasMessage("Environment was not configurable or does not contain an enumerable [vcap] PropertySource");
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verifyZeroInteractions(mockEnvironment);
}
}
@Test(expected = IllegalArgumentException.class)
public void fromConfigurableEnvironmentWithNoVcapPropertySourceThrowsIllegalArgumentException() {
ConfigurableEnvironment mockEnvironment = mock(ConfigurableEnvironment.class);
MutablePropertySources propertySources = spy(new MutablePropertySources());
when(mockEnvironment.getPropertySources()).thenReturn(propertySources);
doReturn(null).when(propertySources).get(anyString());
try {
VcapPropertySource.from(mockEnvironment);
}
catch (IllegalArgumentException expected) {
assertThat(expected)
.hasMessage("Environment was not configurable or does not contain an enumerable [vcap] PropertySource");
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(mockEnvironment, times(1)).getPropertySources();
verify(propertySources, times(1)).get(eq("vcap"));
}
}
@Test(expected = IllegalArgumentException.class)
public void fromConfigurableEnvironmentWithNonEnumerableVcapPropertySourceThrowsIllegalArgumentException() {
ConfigurableEnvironment mockEnvironment = mock(ConfigurableEnvironment.class);
MutablePropertySources propertySources = spy(new MutablePropertySources());
PropertySource mockPropertySource = mock(PropertySource.class);
when(mockEnvironment.getPropertySources()).thenReturn(propertySources);
doReturn(mockPropertySource).when(propertySources).get(eq("vcap"));
when(mockPropertySource.getName()).thenReturn("vcap");
try {
VcapPropertySource.from(mockEnvironment);
}
catch (IllegalArgumentException expected) {
assertThat(expected)
.hasMessage("A valid EnumerablePropertySource named [vcap] with VCAP properties is required",
mockEnvironment);
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(mockEnvironment, times(1)).getPropertySources();
verify(propertySources, times(1)).get(eq("vcap"));
verify(mockPropertySource, times(1)).getName();
verifyNoMoreInteractions(mockPropertySource);
}
}
@Test(expected = IllegalArgumentException.class)
public void fromConfigurableEnvironmentWithEnumerableVcapPropertySourceHavingNoRequiredPropertiesThrowsIllegalArgumentException() {
ConfigurableEnvironment mockEnvironment = mock(ConfigurableEnvironment.class);
MutablePropertySources propertySources = spy(new MutablePropertySources());
PropertySource mockPropertySource = mock(EnumerablePropertySource.class);
when(mockEnvironment.getPropertySources()).thenReturn(propertySources);
doReturn(mockPropertySource).when(propertySources).get(eq("vcap"));
when(mockPropertySource.getName()).thenReturn("vcap");
when(mockPropertySource.containsProperty(eq("vcap.application.name"))).thenReturn(true);
when(mockPropertySource.containsProperty(eq("vcap.application.uris"))).thenReturn(false);
try {
VcapPropertySource.from(mockEnvironment);
}
catch (IllegalArgumentException expected) {
assertThat(expected)
.hasMessage("A valid EnumerablePropertySource named [vcap] with VCAP properties is required",
mockEnvironment);
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(mockEnvironment, times(1)).getPropertySources();
verify(propertySources, times(1)).get(eq("vcap"));
verify(mockPropertySource, times(1)).getName();
verify(mockPropertySource, times(1))
.containsProperty(eq("vcap.application.name"));
verify(mockPropertySource, times(1))
.containsProperty(eq("vcap.application.uris"));
}
}
@Test
public void fromPropertiesIsSuccessful() {
Properties vcap = new Properties();
vcap.setProperty("vcap.application.name", "testApp");
vcap.setProperty("vcap.application.uris", "boot-app.apps.cloud.net");
VcapPropertySource propertySource = VcapPropertySource.from(vcap);
assertThat(propertySource).isNotNull();
assertThat(propertySource.getSource()).isInstanceOf(PropertiesPropertySource.class);
assertThat(propertySource.getProperty("vcap.application.name")).isEqualTo("testApp");
assertThat(propertySource.getProperty("vcap.application.uris")).isEqualTo("boot-app.apps.cloud.net");
}
@Test(expected = IllegalArgumentException.class)
public void fromPropertiesHavingNoRequiredProperties() {
try {
VcapPropertySource.from(new Properties());
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Properties are required");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test(expected = IllegalArgumentException.class)
public void fromNullPropertiesThrowsIllegalArgumentException() {
try {
VcapPropertySource.from((Properties) null);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Properties are required");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void findAllVcapApplicationPropertiesIsSuccessful() {
EnumerablePropertySource mockPropertySource = mock(EnumerablePropertySource.class);
String[] propertyNames = {
"vcap.services.jblum-pcc.credentials.locators",
"vcap.services.jblum-pcc.credentials.users",
"vcap.application.host",
"vcap.application.name",
"vcap.services.jblum-pcc.name",
"vcap.application.port",
"vcap.application.space_name",
"vcap.services.jblum-pcc.plan",
"vcap.application.uris",
"vcap.services.jblum-pcc.tags"
};
when(mockPropertySource.getName()).thenReturn("vcap");
when(mockPropertySource.containsProperty(anyString())).thenAnswer(invocation ->
Arrays.asList(propertyNames).contains(invocation.<String>getArgument(0)));
when(mockPropertySource.getPropertyNames()).thenReturn(propertyNames);
VcapPropertySource propertySource = VcapPropertySource.from(mockPropertySource);
assertThat(propertySource).isNotNull();
assertThat(propertySource.getSource()).isEqualTo(mockPropertySource);
Set<String> vcapApplicationProperties = propertySource.findAllVcapApplicationProperties();
assertThat(vcapApplicationProperties).isNotNull();
assertThat(vcapApplicationProperties).hasSize(5);
assertThat(vcapApplicationProperties)
.containsExactlyInAnyOrder("vcap.application.host", "vcap.application.name", "vcap.application.port",
"vcap.application.space_name", "vcap.application.uris");
verify(mockPropertySource, times(1)).getName();
verify(mockPropertySource, times(1))
.containsProperty(eq("vcap.application.name"));
verify(mockPropertySource, times(1))
.containsProperty(eq("vcap.application.uris"));
verify(mockPropertySource, times(1)).getPropertyNames();
}
@Test
public void findAllVcapServicesPropertiesIsSuccessful() {
EnumerablePropertySource mockPropertySource = mock(EnumerablePropertySource.class);
String[] propertyNames = {
"vcap.services.jblum-pcc.credentials.locators",
"vcap.services.jblum-pcc.credentials.users",
"vcap.application.host",
"vcap.application.name",
"vcap.services.jblum-pcc.name",
"vcap.application.port",
"vcap.application.space_name",
"vcap.services.jblum-pcc.plan",
"vcap.application.uris",
"vcap.services.jblum-pcc.tags"
};
when(mockPropertySource.getName()).thenReturn("vcap");
when(mockPropertySource.containsProperty(anyString())).thenAnswer(invocation ->
Arrays.asList(propertyNames).contains(invocation.<String>getArgument(0)));
when(mockPropertySource.getPropertyNames()).thenReturn(propertyNames);
VcapPropertySource propertySource = VcapPropertySource.from(mockPropertySource);
assertThat(propertySource).isNotNull();
assertThat(propertySource.getSource()).isEqualTo(mockPropertySource);
Set<String> vcapApplicationProperties = propertySource.findAllVcapServicesProperties();
assertThat(vcapApplicationProperties).isNotNull();
assertThat(vcapApplicationProperties).hasSize(5);
assertThat(vcapApplicationProperties)
.containsExactlyInAnyOrder("vcap.services.jblum-pcc.credentials.locators",
"vcap.services.jblum-pcc.credentials.users", "vcap.services.jblum-pcc.name",
"vcap.services.jblum-pcc.plan", "vcap.services.jblum-pcc.tags");
verify(mockPropertySource, times(1)).getName();
verify(mockPropertySource, times(1))
.containsProperty(eq("vcap.application.name"));
verify(mockPropertySource, times(1))
.containsProperty(eq("vcap.application.uris"));
verify(mockPropertySource, times(1)).getPropertyNames();
}
@Test
public void findFirstCloudCacheServiceNameReturnsServiceName() {
Properties vcap = new Properties();
vcap.setProperty("vcap.application.name", "boot-example");
vcap.setProperty("vcap.services.test-pcc.name", "test-pcc");
vcap.setProperty("vcap.services.test-pcc.plan", "small");
vcap.setProperty("vcap.services.test-pcc.tags", "pivotal,database,cloudcache,gemfire");
vcap.setProperty("vcap.application.space_name", "outerspace");
vcap.setProperty("vcap.services.jblum-pcc.name", "jblum-pcc");
vcap.setProperty("vcap.services.jblum-pcc.plan", "small");
vcap.setProperty("vcap.services.jblum-pcc.tags", "cloudcache,database,gemfire,pivotal");
vcap.setProperty("vcap.application.uris", "boot-example.boot-apps.apps.cloud.net");
vcap.setProperty("vcap.services.a-pcc.name", "a-pcc");
vcap.setProperty("vcap.services.a-pcc.plan", "huge");
vcap.setProperty("vcap.services.a-pcc.tags", "pivotal,cloudcache,database");
VcapPropertySource propertySource = VcapPropertySource.from(vcap);
assertThat(propertySource).isNotNull();
assertThat(propertySource.findFirstCloudCacheServiceName()).isEqualTo("jblum-pcc");
}
@Test(expected = IllegalStateException.class)
public void findFirstCloudCacheServiceNameWithInvalidTagsThrowsIllegalStateException() {
Properties vcap = new Properties();
vcap.setProperty("vcap.application.name", "boot-example");
vcap.setProperty("vcap.services.test-pcc.name", "test-pcc");
vcap.setProperty("vcap.services.test-pcc.plan", "small");
vcap.setProperty("vcap.services.test-pcc.tags", "pivotal,gemfire,database");
vcap.setProperty("vcap.application.space_name", "outerspace");
vcap.setProperty("vcap.services.a-pcc.tags", "pivotal,cloudcache,database");
vcap.setProperty("vcap.application.uris", "boot-example.boot-apps.apps.cloud.net");
VcapPropertySource propertySource = VcapPropertySource.from(vcap);
assertThat(propertySource).isNotNull();
try {
propertySource.findFirstCloudCacheServiceName();
}
catch (IllegalStateException expected) {
assertThat(expected).hasMessage("No service with tags [cloudcache, gemfire] was found");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test(expected = IllegalStateException.class)
public void findFirstCloudCacheServiceNameWithNoTagsThrowsIllegalStateException() {
Properties vcap = new Properties();
vcap.setProperty("vcap.application.name", "boot-example");
vcap.setProperty("vcap.services.test-pcc.name", "test-pcc");
vcap.setProperty("vcap.services.test-pcc.plan", "small");
vcap.setProperty("vcap.application.space_name", "outerspace");
vcap.setProperty("vcap.application.uris", "boot-example.boot-apps.apps.cloud.net");
VcapPropertySource propertySource = VcapPropertySource.from(vcap);
assertThat(propertySource).isNotNull();
try {
propertySource.findFirstCloudCacheServiceName();
}
catch (IllegalStateException expected) {
assertThat(expected).hasMessage("No service with tags [cloudcache, gemfire] was found");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void findFirstCloudCacheServiceReturnsCloudCacheService() throws Exception {
URL gfshUrl = new URL("http://skullbox:7070/v1/gemfire");
Properties vcap = new Properties();
vcap.setProperty("vcap.services.test-pcc.name", "test-pcc");
vcap.setProperty("vcap.services.test-pcc.plan", "huge");
vcap.setProperty("vcap.application.name", "boot-example");
vcap.setProperty("vcap.services.test-pcc.credentials.locators", "sandbox[1234],toolbox,xbox[6789]");
vcap.setProperty("vcap.application.space_name", "outerspace");
vcap.setProperty("vcap.services.test-pcc.credentials.urls.gfsh", gfshUrl.toExternalForm());
vcap.setProperty("vcap.application.uris", "boot-example.boot-apps.apps.cloud.net");
vcap.setProperty("vcap.services.test-pcc.tags", "pivotal,cloudcache , database, gemfire ");
VcapPropertySource propertySource = VcapPropertySource.from(vcap);
assertThat(propertySource).isNotNull();
CloudCacheService cloudCacheService = propertySource.findFirstCloudCacheService();
assertThat(cloudCacheService).isNotNull();
assertThat(cloudCacheService.getGfshUrl().orElse(null)).isEqualTo(gfshUrl);
assertThat(cloudCacheService.getLocatorList()).containsExactly(
CloudCacheService.Locator.newLocator("sandbox", 1234),
CloudCacheService.Locator.newLocator("toolbox", 10334),
CloudCacheService.Locator.newLocator("xbox", 6789)
);
}
@Test
public void findFirstUserByRoleClusterOperatorReturnsUser() {
Properties vcap = new Properties();
vcap.setProperty("vcap.application.name", "boot-example");
vcap.setProperty("vcap.services.test-pcc.name", "test-pcc");
vcap.setProperty("vcap.services.test-pcc.credentials.users[0].username", "jdoe");
vcap.setProperty("vcap.services.test-pcc.credentials.users[0].roles", "developer,poweruser,seaswab");
vcap.setProperty("vcap.services.test-pcc.credentials.users[0].password", "test");
vcap.setProperty("vcap.services.test-pcc.tags", "pivotal,cloudcache , database, gemfire ");
vcap.setProperty("vcap.application.space_name", "outerspace");
vcap.setProperty("vcap.services.a-pcc.name", "a-pcc");
vcap.setProperty("vcap.services.a-pcc.credentials.users[0].username", "admin");
vcap.setProperty("vcap.services.a-pcc.credentials.users[0].roles", "cluster_admin");
vcap.setProperty("vcap.services.a-pcc.credentials.users[0].password", "p@55w0rd");
vcap.setProperty("vcap.services.a-pcc.credentials.users[1].username", "root");
vcap.setProperty("vcap.services.a-pcc.credentials.users[1].roles", "cluster_operator");
vcap.setProperty("vcap.services.a-pcc.credentials.users[1].password", "p@55w0rd");
vcap.setProperty("vcap.services.a-pcc.tags", "pivotal,cloudcache,database");
vcap.setProperty("vcap.application.uris", "boot-example.boot-apps.apps.cloud.net");
vcap.setProperty("vcap.services.jblum-pcc.name", "jblum-pcc");
vcap.setProperty("vcap.services.jblum-pcc.credentials.users[0].username", "majorTom");
vcap.setProperty("vcap.services.jblum-pcc.credentials.users[0].roles", "cluster_operator,ground_contoller");
vcap.setProperty("vcap.services.jblum-pcc.credentials.users[0].password", "s3cUr3");
vcap.setProperty("vcap.services.jblum-pcc.credentials.users[1].username", "jimbo");
vcap.setProperty("vcap.services.jblum-pcc.credentials.users[1].roles", "cluster_fuck");
vcap.setProperty("vcap.services.jblum-pcc.credentials.users[1].password", "p@55!t");
vcap.setProperty("vcap.services.jblum-pcc.credentials.users[2].username", "buster");
vcap.setProperty("vcap.services.jblum-pcc.credentials.users[2].roles", "cluster_operator");
vcap.setProperty("vcap.services.jblum-pcc.credentials.users[2].password", "p@55!t");
vcap.setProperty("vcap.services.jblum-pcc.tags", "pivotal,gemfire,database");
VcapPropertySource propertySource = VcapPropertySource.from(vcap);
assertThat(propertySource).isNotNull();
assertThat(propertySource.findFirstUserByRoleClusterOperator(Service.with("test-pcc")).isPresent()).isFalse();
User root = propertySource.findFirstUserByRoleClusterOperator(Service.with("a-pcc")).orElse(null);
assertThat(root).isNotNull();
assertThat(root.getName()).isEqualTo("root");
assertThat(root.getPassword().orElse(null)).isEqualTo("p@55w0rd");
assertThat(root.getRole().orElse(null).isClusterOperator()).isTrue();
User majorTom = propertySource.findFirstUserByRoleClusterOperator(Service.with("jblum-pcc")).orElse(null);
assertThat(majorTom).isNotNull();
assertThat(majorTom.getName()).isEqualTo("majorTom");
assertThat(majorTom.getPassword().orElse(null)).isEqualTo("s3cUr3");
assertThat(majorTom.getRole().orElse(null).isClusterOperator()).isTrue();
}
}

View File

@@ -0,0 +1,242 @@
/*
* Copyright 2018 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.geode.core.env.support;
import static org.assertj.core.api.Assertions.assertThat;
import java.net.URL;
import java.util.List;
import org.junit.Test;
/**
* Unit tests for {@link CloudCacheService}.
*
* @author John Blum
* @see org.junit.Test
* @see org.springframework.geode.core.env.support.CloudCacheService
* @since 1.0.0
*/
public class CloudCacheServiceUnitTests {
@Test
public void withServiceNameLocatorsAndUrlReturnsNewCloudCacheService() throws Exception {
URL gfshUrl = new URL("http://localhost:7070/v1/gemfire");
CloudCacheService service = CloudCacheService.with("gemfire")
.withLocators("boombox[123],cardboardbox[456],mailbox[789],xbox[40404]")
.withGfshUrl(gfshUrl);
assertThat(service).isNotNull();
assertThat(service.getName()).isEqualTo("gemfire");
assertThat(service.getGfshUrl().orElse(null)).isEqualTo(gfshUrl);
assertThat(service.getLocators().orElse(null))
.isEqualTo("boombox[123],cardboardbox[456],mailbox[789],xbox[40404]");
List<CloudCacheService.Locator> locators = service.getLocatorList();
assertThat(locators).isNotNull();
assertThat(locators).hasSize(4);
assertThat(locators.get(0)).isEqualTo(CloudCacheService.Locator.newLocator("boombox", 123));
assertThat(locators.get(1)).isEqualTo(CloudCacheService.Locator.newLocator("cardboardbox", 456));
assertThat(locators.get(2)).isEqualTo(CloudCacheService.Locator.newLocator("mailbox", 789));
assertThat(locators.get(3)).isEqualTo(CloudCacheService.Locator.newLocator("xbox", 40404));
}
@Test
public void parseLocatorWithSingleLetterHostnameAndPort() {
CloudCacheService.Locator locator = CloudCacheService.Locator.parse("x [10336]");
assertThat(locator).isNotNull();
assertThat(locator.getHost()).isEqualTo("x");
assertThat(locator.getPort()).isEqualTo(10336);
}
@Test
public void parseLocatorWithNoHostnameAndPort() {
CloudCacheService.Locator locator = CloudCacheService.Locator.parse(" [1 234] ");
assertThat(locator).isNotNull();
assertThat(locator.getHost()).isEqualTo(CloudCacheService.Locator.DEFAULT_LOCATOR_HOST);
assertThat(locator.getPort()).isEqualTo(1234);
}
@Test
public void parseLocatorWithHostnameAndNoPort() {
CloudCacheService.Locator locator = CloudCacheService.Locator.parse(" chatterbox ");
assertThat(locator).isNotNull();
assertThat(locator.getHost()).isEqualTo("chatterbox");
assertThat(locator.getPort()).isEqualTo(CloudCacheService.Locator.DEFAULT_LOCATOR_PORT);
}
private void testParseLocatorWithInvalidHostPort(String hostPort) {
try {
CloudCacheService.Locator.parse(hostPort);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Locator host/port [%s] is not valid", hostPort);
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test(expected = IllegalArgumentException.class)
public void parseLocatorWithBlankHostPort() {
testParseLocatorWithInvalidHostPort(" ");
}
@Test(expected = IllegalArgumentException.class)
public void parseLocatorWithEmptyHostPort() {
testParseLocatorWithInvalidHostPort("");
}
@Test(expected = IllegalArgumentException.class)
public void parseLocatorWithNullHostPort() {
testParseLocatorWithInvalidHostPort(null);
}
@Test
public void parseLocatorsWithMultipleLocatorHostsPorts() {
List<CloudCacheService.Locator> locators =
CloudCacheService.Locator.parseLocators(" jukebox[12345], matchbox [6789] ");
assertThat(locators).isNotNull();
assertThat(locators).hasSize(2);
assertThat(locators).containsExactly(
CloudCacheService.Locator.newLocator("jukebox", 12345),
CloudCacheService.Locator.newLocator("matchbox", 6789)
);
}
@Test
public void parseLocatorsWithNoLocatorHostPort() {
List<CloudCacheService.Locator> locators = CloudCacheService.Locator.parseLocators(" ");
assertThat(locators).isNotNull();
assertThat(locators).isEmpty();
}
@Test
public void parseLocatorsWithSingleLocatorHostPort() {
List<CloudCacheService.Locator> locators = CloudCacheService.Locator.parseLocators("skullbox[2345]");
assertThat(locators).isNotNull();
assertThat(locators).hasSize(1);
assertThat(locators).containsExactly(CloudCacheService.Locator.newLocator("skullbox", 2345));
}
@Test
public void newLocatorWithHostAndPort() {
CloudCacheService.Locator locator = CloudCacheService.Locator.newLocator("toybox", 8008);
assertThat(locator).isNotNull();
assertThat(locator.getHost()).isEqualTo("toybox");
assertThat(locator.getPort()).isEqualTo(8008);
}
@Test
public void newLocatorWithHostname() {
CloudCacheService.Locator locator = CloudCacheService.Locator.newLocator("unbox");
assertThat(locator).isNotNull();
assertThat(locator.getHost()).isEqualTo("unbox");
assertThat(locator.getPort()).isEqualTo(CloudCacheService.Locator.DEFAULT_LOCATOR_PORT);
}
@Test
public void newLocatorWithPort() {
CloudCacheService.Locator locator = CloudCacheService.Locator.newLocator(6789);
assertThat(locator).isNotNull();
assertThat(locator.getHost()).isEqualTo(CloudCacheService.Locator.DEFAULT_LOCATOR_HOST);
assertThat(locator.getPort()).isEqualTo(6789);
}
@Test
public void newLocatorWithDefaultHostPort() {
CloudCacheService.Locator locator = CloudCacheService.Locator.newLocator();
assertThat(locator).isNotNull();
assertThat(locator.getHost()).isEqualTo(CloudCacheService.Locator.DEFAULT_LOCATOR_HOST);
assertThat(locator.getPort()).isEqualTo(CloudCacheService.Locator.DEFAULT_LOCATOR_PORT);
}
private void testNewLocatorWithInvalidHost(String host) {
try {
CloudCacheService.Locator.newLocator(host);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Host [%s] is required", host);
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test(expected = IllegalArgumentException.class)
public void newLocatorWithBlankHostnameThrowsIllegalArgumentException() {
testNewLocatorWithInvalidHost(" ");
}
@Test(expected = IllegalArgumentException.class)
public void newLocatorWithEmptyHostnameThrowsIllegalArgumentException() {
testNewLocatorWithInvalidHost("");
}
@Test(expected = IllegalArgumentException.class)
public void newLocatorWithNullHostnameThrowsIllegalArgumentException() {
testNewLocatorWithInvalidHost(null);
}
@Test(expected = IllegalArgumentException.class)
public void newLocatorWithInvalidPortNumberThrowsIllegalArgumentException() {
try {
CloudCacheService.Locator.newLocator(-2345);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Port [-2345] must be greater than equal to 0");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void locatorToStringPrintsHostPort() {
assertThat(CloudCacheService.Locator.newLocator("skullbox", 1234).toString())
.isEqualTo("skullbox[1234]");
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2018 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.geode.core.env.support;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
/**
* Unit tests for {@link Service}.
*
* @author John Blum
* @see org.junit.Test
* @see org.springframework.geode.core.env.support.Service
* @since 1.0.0
*/
public class ServiceUnitTests {
@Test
public void withNameReturnsNewService() {
Service service = Service.with("test");
assertThat(service).isNotNull();
assertThat(service.getName()).isEqualTo("test");
}
private void testWithInvalidNameThrowsIllegalArgumentException(String name) {
try {
Service.with(name);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Service name [%s] is required", name);
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test(expected = IllegalArgumentException.class)
public void withBlankServiceNameThrowsIllegalArgumentException() {
testWithInvalidNameThrowsIllegalArgumentException(" ");
}
@Test(expected = IllegalArgumentException.class)
public void withEmptyServiceNameThrowsIllegalArgumentException() {
testWithInvalidNameThrowsIllegalArgumentException("");
}
@Test(expected = IllegalArgumentException.class)
public void withNullServiceNameThrowsIllegalArgumentException() {
testWithInvalidNameThrowsIllegalArgumentException("");
}
@Test
public void toStringReturnsServiceName() {
assertThat(Service.with("test").toString()).isEqualTo("test");
}
}

View File

@@ -0,0 +1,206 @@
/*
* Copyright 2018 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.geode.core.env.support;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import org.junit.Test;
/**
* Unit tests for {@link User}.
*
* @author John Blum
* @see org.junit.Test
* @see org.springframework.geode.core.env.support.User
* @since 1.0.0
*/
public class UserUnitTests {
@Test
public void withNameReturnsNewUser() {
User user = User.with("root");
assertThat(user).isNotNull();
assertThat(user.getName()).isEqualTo("root");
assertThat(user.getPassword().isPresent()).isFalse();
assertThat(user.getRole().isPresent()).isFalse();
}
@Test
public void withNamePasswordAndRoleReturnsNewUser() {
User jdoe = User.with("jdoe")
.withPassword("p@55w0rd!")
.withRole(User.Role.CLUSTER_OPERATOR);
assertThat(jdoe).isNotNull();
assertThat(jdoe.getName()).isEqualTo("jdoe");
assertThat(jdoe.getPassword().orElse(null)).isEqualTo("p@55w0rd!");
assertThat(jdoe.getRole().orElse(null)).isEqualTo(User.Role.CLUSTER_OPERATOR);
}
private void testWithInvalidNameThrowsIllegalArgumentException(String name) {
try {
User.with(name);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("User name [%s] is required", name);
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test(expected = IllegalArgumentException.class)
public void withBlankUserNameThrowsIllegalArgumentException() {
testWithInvalidNameThrowsIllegalArgumentException(" ");
}
@Test(expected = IllegalArgumentException.class)
public void withEmptyUserNameThrowsIllegalArgumentException() {
testWithInvalidNameThrowsIllegalArgumentException("");
}
@Test(expected = IllegalArgumentException.class)
public void withNullUserNameThrowsIllegalArgumentException() {
testWithInvalidNameThrowsIllegalArgumentException(null);
}
@Test
public void compareToReturnsEqualValue() {
assertThat(User.with("root").compareTo(User.with("root"))).isEqualTo(0);
}
@Test
public void compareToReturnsNegativeValue() {
assertThat(User.with("jdoe").compareTo(User.with("root"))).isLessThan(0);
}
@Test
public void compareToReturnsPositiveValue() {
assertThat(User.with("root").compareTo(User.with("jdoe"))).isGreaterThan(0);
}
@Test
@SuppressWarnings("all")
public void equalsObjectsReturnsFalse() {
assertThat(User.with("admin").equals("admin")).isFalse();
}
@Test
public void equalsWithDifferentObjectsReturnsFalse() {
User admin = User.with("admin").withRole(User.Role.CLUSTER_OPERATOR);
User root = User.with("root").withRole(User.Role.CLUSTER_OPERATOR);
assertThat(root.equals(admin)).isFalse();
}
@Test
public void equalsWithEqualObjectsReturnsTrue() {
User root = User.with("root").withRole(User.Role.CLUSTER_OPERATOR);
User rootToo = User.with("root").withPassword("test").withRole(User.Role.DEVELOPER);
assertThat(root.equals(rootToo)).isTrue();
}
@Test
@SuppressWarnings("all")
public void equalsWithIdenticalObjectsReturnsTrue() {
User root = User.with("root");
assertThat(root.equals(root)).isTrue();
}
@Test
public void hashCodeIsCorrect() {
User user = User.with("root");
int hashCode = user.hashCode();
assertThat(hashCode).isNotZero();
assertThat(hashCode).isEqualTo(user.hashCode());
user.withPassword("test").withRole(User.Role.DEVELOPER);
assertThat(user.hashCode()).isEqualTo(hashCode);
assertThat(user.hashCode()).isNotEqualTo(User.with("anotherUser").hashCode());
}
@Test
public void toStringReturnsUserName() {
assertThat(User.with("root").toString()).isEqualTo("root");
}
@Test
public void roleOfEmptyNameReturnsNull() {
assertThat(User.Role.of("")).isNull();
assertThat(User.Role.of(" ")).isNull();
}
@Test
public void roleOfInvalidNameReturnsNull() {
assertThat(User.Role.of("invalid")).isNull();
}
@Test
public void roleOfNulReturnsNull() {
assertThat(User.Role.of(null)).isNull();
}
@Test
public void roleOfRoleNamesEqualsRole() {
Arrays.stream(User.Role.values()).forEach(role -> {
assertThat(User.Role.of(role.name())).isEqualTo(role);
assertThat(User.Role.of(role.toString())).isEqualTo(role);
});
}
@Test
public void isClusterOperator() {
assertThat(User.Role.CLUSTER_OPERATOR.isClusterOperator()).isTrue();
}
@Test
public void isNotClusterOperator() {
assertThat(User.Role.DEVELOPER.isClusterOperator()).isFalse();
}
@Test
public void isDeveloper() {
assertThat(User.Role.DEVELOPER.isDeveloper()).isTrue();
}
@Test
public void isNotDeveloper() {
assertThat(User.Role.CLUSTER_OPERATOR.isDeveloper()).isFalse();
}
@Test
public void toStringReturnsLowercaseName() {
Arrays.stream(User.Role.values())
.forEach(role -> assertThat(role.toString()).isEqualTo(role.name().toLowerCase()));
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2018 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.geode.core.util;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newRuntimeException;
import org.junit.Test;
/**
* Unit tests for {@link ObjectUtils}.
*
* @author John Blum
* @see org.junit.Test
* @see org.springframework.geode.core.util.ObjectUtils
* @since 1.0.0
*/
public class ObjectUtilsUnitTests {
@Test
public void doOperationSafelyReturnsResult() {
assertThat(ObjectUtils.doOperationSafely(() -> "test")).isEqualTo("test");
}
@Test
public void doOperationSafelyReturnsDefaultValue() {
assertThat(ObjectUtils.doOperationSafely(() -> { throw newRuntimeException("test"); },
"default value")).isEqualTo("default value");
}
@Test(expected = IllegalStateException.class)
public void doOperationSafelyThrowsIllegalStateException() {
try {
ObjectUtils.doOperationSafely(() -> { throw newRuntimeException("test"); }, null);
}
catch (IllegalStateException expected) {
assertThat(expected).hasMessage("Failed to execute operation");
assertThat(expected).hasCauseInstanceOf(RuntimeException.class);
assertThat(expected.getCause()).hasMessage("test");
assertThat(expected.getCause()).hasNoCause();
throw expected;
}
}
@Test
public void returnValueThrowOnNullWithNonNullValueReturnsValue() {
assertThat(ObjectUtils.returnValueThrowOnNull("test")).isEqualTo("test");
}
@Test(expected = RuntimeException.class)
public void returnValueThrowOnNullWithNullValueThrowsException() {
try {
ObjectUtils.returnValueThrowOnNull(null, newRuntimeException("test"));
}
catch (RuntimeException expected) {
assertThat(expected).hasMessage("test");
assertThat(expected).hasNoCause();
throw expected;
}
}
}

View File

@@ -0,0 +1,152 @@
/*
* Copyright 2018 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.geode.function.support;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.concurrent.TimeUnit;
import edu.umd.cs.mtc.MultithreadedTestCase;
import edu.umd.cs.mtc.TestFramework;
import org.apache.geode.cache.execute.FunctionException;
import org.apache.geode.distributed.DistributedMember;
import org.junit.Before;
import org.junit.Test;
/**
* Unit tests for {@link AbstractResultCollector}.
*
* @author John Blum
* @see org.junit.Test
* @see org.springframework.geode.function.support.AbstractResultCollector
* @since 1.0.0
*/
public class AbstractResultCollectorUnitTests {
private AbstractResultCollector<Object, Object> resultCollector;
private static <T, S> AbstractResultCollector<T, S> newResultCollector() {
return newResultCollector(() -> {});
}
private static <T, S> AbstractResultCollector<T, S> newResultCollector(Runnable runnable) {
return new AbstractResultCollector<T, S>() {
@Override
public synchronized S getResult() throws FunctionException {
runnable.run();
return super.getResult();
}
@Override
@SuppressWarnings("unchecked")
public void addResult(DistributedMember memberID, T resultOfSingleExecution) {
setResult((S) resultOfSingleExecution);
}
};
}
@Before
public void setup() {
this.resultCollector = newResultCollector();
}
@Test
public void clearResultClearsResult() {
this.resultCollector.setResult("test");
assertThat(this.resultCollector.getResult()).isEqualTo("test");
this.resultCollector.clearResults();
assertThat(this.resultCollector.getResult()).isNull();
}
@Test
public void getResultReturnsResult() {
this.resultCollector.setResult("test");
assertThat(this.resultCollector.getResult()).isEqualTo("test");
}
@Test
public void getResultReturnsResultWithinTimeout() throws Throwable {
TestFramework.runOnce(new ReturnsResultWithinTimeoutMultithreadedTestCase());
}
@Test
public void resultsHaveEnded() {
this.resultCollector.endResults();
assertThat(this.resultCollector.hasResultsEnded()).isTrue();
assertThat(this.resultCollector.hasResultsNotEnded()).isFalse();
}
@Test
public void resultsHaveNotEnded() {
assertThat(this.resultCollector.hasResultsEnded()).isFalse();
assertThat(this.resultCollector.hasResultsNotEnded()).isTrue();
}
@SuppressWarnings("unused")
static class ReturnsResultWithinTimeoutMultithreadedTestCase extends MultithreadedTestCase {
private long startTimestamp;
private AbstractResultCollector<Object, Object> resultCollector;
@Override
public void initialize() {
super.initialize();
this.resultCollector = newResultCollector(() -> waitForTick(1));
this.startTimestamp = System.currentTimeMillis();
}
public void thread1() throws InterruptedException {
Thread.currentThread().setName("ResultCollector.getResult()");
assertThat(this.resultCollector.getResult(500, TimeUnit.MILLISECONDS)).isEqualTo("test");
}
public void thread2() {
Thread.currentThread().setName("ResultCollector.setResult(..)");
waitForTick(1);
this.resultCollector.setResult("test");
}
@Override
public void finish() {
long endTimestamp = System.currentTimeMillis();
assertThat(endTimestamp).isGreaterThan(this.startTimestamp);
assertThat(endTimestamp - this.startTimestamp).isLessThan(TimeUnit.SECONDS.toMillis(2));
}
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2018 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.geode.function.support;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import org.apache.geode.distributed.DistributedMember;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
/**
* Unit tests for {@link SingleResultReturningCollector}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.springframework.geode.function.support.SingleResultReturningCollector
* @since 1.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class SingleResultReturningCollectorUnitTests {
@Mock
private DistributedMember mockDistributedMember;
private SingleResultReturningCollector<Object> resultCollector;
@Before
public void setup() {
this.resultCollector = new SingleResultReturningCollector<>();
}
@Test
public void addResultWithNullIsSafe() {
assertThat(this.resultCollector.getResult()).isNull();
this.resultCollector.addResult(this.mockDistributedMember, null);
assertThat(this.resultCollector.getResult()).isNull();
}
@Test
public void addResultWithSingleObjectReturnsObject() {
assertThat(this.resultCollector.getResult()).isNull();
this.resultCollector.addResult(this.mockDistributedMember, "test");
assertThat(this.resultCollector.getResult()).isEqualTo("test");
}
@Test
public void addResultWithIterableReturnsFirstElement() {
Iterable<String> list = Arrays.asList("one", "two", "three");
assertThat(this.resultCollector.getResult()).isNull();
this.resultCollector.addResult(this.mockDistributedMember, list);
assertThat(this.resultCollector.getResult()).isEqualTo("one");
}
@Test
@SuppressWarnings("unchecked")
public void addResultWithIterableReturningNullIteratorIsSafe() {
Iterable<Object> mockIterable = mock(Iterable.class);
when(mockIterable.iterator()).thenReturn(null);
assertThat(this.resultCollector.getResult()).isNull();
this.resultCollector.addResult(mockDistributedMember, mockIterable);
assertThat(this.resultCollector.getResult()).isNull();
}
@Test
public void addResultWithIterableOfListsReturnsFirstElementInListOne() {
Iterable<String> listOne = Arrays.asList("one", "two", "three");
Iterable<String> listTwo = Arrays.asList("four", "five", "six");
assertThat(this.resultCollector.getResult()).isNull();
this.resultCollector.addResult(this.mockDistributedMember, Arrays.asList(listOne, listTwo));
assertThat(this.resultCollector.getResult()).isEqualTo("one");
}
@Test
public void addResultWithIterableOfListsOfListsReturnsFirstElementInListOne() {
Iterable<String> listOne = Arrays.asList("one", "two", "three");
Iterable<String> listTwo = Arrays.asList("four", "five", "six");
Iterable<String> listThree = Arrays.asList("seven", "eight", "nine");
assertThat(this.resultCollector.getResult()).isNull();
this.resultCollector.addResult(this.mockDistributedMember,
Arrays.asList(Arrays.asList(listOne, listTwo), listThree));
assertThat(this.resultCollector.getResult()).isEqualTo("one");
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2018 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.geode.security.support;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import org.apache.geode.cache.GemFireCache;
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.context.annotation.Bean;
import org.springframework.data.gemfire.config.annotation.EnableSecurity;
import org.springframework.data.gemfire.config.annotation.PeerCacheApplication;
import org.springframework.data.gemfire.support.GemfireBeanFactoryLocatorProxy;
import org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Integration tests for {@link SecurityManagerProxy}.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.security.SecurityManager
* @see org.springframework.context.annotation.Bean
* @see org.springframework.data.gemfire.config.annotation.EnableSecurity
* @see org.springframework.data.gemfire.config.annotation.PeerCacheApplication
* @see org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @since 1.0.0
*/
@RunWith(SpringRunner.class)
@ContextConfiguration
@SuppressWarnings("unused")
public class SecurityManagerProxyIntegrationTests extends IntegrationTestsSupport {
private static final String GEMFIRE_LOG_LEVEL = "error";
@BeforeClass
@AfterClass
public static void cleanUpBeanFactoryLocatorReferences() {
GemfireBeanFactoryLocatorProxy.clear();
}
@Autowired
private org.apache.geode.security.SecurityManager mockSecurityManager;
@Test
public void securityManagerProxyWasConfiguredWithMockSecurityManager() {
assertThat(SecurityManagerProxy.getInstance().getSecurityManager()).isEqualTo(this.mockSecurityManager);
}
@PeerCacheApplication(logLevel = GEMFIRE_LOG_LEVEL, useBeanFactoryLocator = true)
@EnableSecurity(securityManagerClassName =
"org.springframework.geode.security.support.SecurityManagerProxy")
static class TestConfiguration {
@Bean
org.apache.geode.security.SecurityManager mockSecurityManager(GemFireCache gemfireCache) {
return mock(org.apache.geode.security.SecurityManager.class);
}
}
}

View File

@@ -0,0 +1,150 @@
/*
* Copyright 2018 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.geode.security.support;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.security.Principal;
import java.util.Properties;
import org.apache.geode.security.ResourcePermission;
import org.junit.Test;
/**
* Unit tests for {@link SecurityManagerProxy}
*
* @author John Blum
* @see java.security.Principal
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.geode.security.support.SecurityManagerProxy
* @since 1.0.0
*/
public class SecurityManagerProxyUnitTests {
@Test
public void setAndGetSecurityManager() {
org.apache.geode.security.SecurityManager mockSecurityManager =
mock(org.apache.geode.security.SecurityManager.class);
SecurityManagerProxy securityManagerProxy = new SecurityManagerProxy();
securityManagerProxy.setSecurityManager(mockSecurityManager);
assertThat(securityManagerProxy.getSecurityManager()).isEqualTo(mockSecurityManager);
}
@Test(expected = IllegalArgumentException.class)
public void setSecurityManagerToNullThrowsIllegalArgumentException() {
try {
new SecurityManagerProxy().setSecurityManager(null);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("SecurityManager must not be null");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test(expected = IllegalStateException.class)
public void getSecurityManagerWhenUninitializedThrowsIllegalStateException() {
try {
new SecurityManagerProxy().getSecurityManager();
}
catch (IllegalStateException expected) {
assertThat(expected).hasMessage("No SecurityManager configured");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void authenticateDelegatesToConfiguredSecurityManager() {
Properties securityProperties = new Properties();
org.apache.geode.security.SecurityManager mockSecurityManager =
mock(org.apache.geode.security.SecurityManager.class);
when(mockSecurityManager.authenticate(any(Properties.class))).thenReturn("TestUser");
SecurityManagerProxy securityManagerProxy = new SecurityManagerProxy();
securityManagerProxy.setSecurityManager(mockSecurityManager);
assertThat(securityManagerProxy.getSecurityManager()).isEqualTo(mockSecurityManager);
assertThat(securityManagerProxy.authenticate(securityProperties)).isEqualTo("TestUser");
verify(mockSecurityManager, times(1)).authenticate(eq(securityProperties));
}
@Test
public void authorizeDelegatesToConfiguredSecurityManager() {
Principal mockPrincipal = mock(Principal.class);
ResourcePermission resourcePermission =
new ResourcePermission(ResourcePermission.Resource.DATA, ResourcePermission.Operation.READ);
org.apache.geode.security.SecurityManager mockSecurityManager =
mock(org.apache.geode.security.SecurityManager.class);
when(mockSecurityManager.authorize(any(Object.class), any(ResourcePermission.class))).thenReturn(true);
SecurityManagerProxy securityManagerProxy = new SecurityManagerProxy();
securityManagerProxy.setSecurityManager(mockSecurityManager);
assertThat(securityManagerProxy.getSecurityManager()).isEqualTo(mockSecurityManager);
assertThat(securityManagerProxy.authorize(mockPrincipal, resourcePermission)).isTrue();
verify(mockSecurityManager, times(1))
.authorize(eq(mockPrincipal), eq(resourcePermission));
}
@Test
public void closeDelegatesToConfiguredSecurityManager() {
org.apache.geode.security.SecurityManager mockSecurityManager =
mock(org.apache.geode.security.SecurityManager.class);
when(mockSecurityManager.authorize(any(Object.class), any(ResourcePermission.class))).thenReturn(true);
SecurityManagerProxy securityManagerProxy = new SecurityManagerProxy();
securityManagerProxy.setSecurityManager(mockSecurityManager);
assertThat(securityManagerProxy.getSecurityManager()).isEqualTo(mockSecurityManager);
securityManagerProxy.close();
verify(mockSecurityManager, times(1)).close();
}
}

View File

@@ -0,0 +1,54 @@
# Spring Boot application.properties for testing Apache Geode/Pivotal GemFire Security in a cloud context.
vcap.application.application_id=c50bb519-2739-4fa3-8750-02c051e35735
vcap.application.application_name=boot-test
vcap.application.application_uris=boot-test.apps.tunis.cf-app.com
vcap.application.application_uris[0]=boot-test.apps.tunis.cf-app.com
vcap.application.cf_api=https://api.sys.tunis.cf-app.com
vcap.application.host=0.0.0.0
vcap.application.instance_id=babcf301-3b34-4dcf-720e-ccfc
vcap.application.instance_index=0
vcap.application.limits.disk=1024
vcap.application.limits.fds=16384
vcap.application.limits.mem=1024
vcap.application.name=boot-test
vcap.application.port=8080
vcap.application.space_id=271cf083-7855-4b5e-be19-65342d099099
vcap.application.space_name=jblum-space
vcap.application.uris=boot-test.apps.tunis.cf-app.com
vcap.application.uris[0]=boot-test.apps.tunis.cf-app.com
vcap.application.version=d34bbbbd-c35c-4057-baf2-6300cb9aa2aa
vcap.application.application_version=d34bbbbd-c35c-4057-baf2-6300cb9aa2aa
vcap.services.jblum-pcc.credentials.distributed_system_id=0
vcap.services.jblum-pcc.credentials.locators=localhost[55221]
vcap.services.jblum-pcc.credentials.locators[0]=10.0.8.19[55221]
vcap.services.jblum-pcc.credentials.locators[1]=10.0.8.21[55221]
vcap.services.jblum-pcc.credentials.locators[2]=10.0.8.20[55221]
#vcap.services.jblum-pcc.credentials.urls.gfsh=http://cloudcache-9defb33a-6b8b-49f0-bd35-cf6f7b2f222f.sys.tunis.cf-app.com/gemfire/v1
vcap.services.jblum-pcc.credentials.urls.pulse=http://cloudcache-9defb33a-6b8b-49f0-bd35-cf6f7b2f222f.sys.tunis.cf-app.com/pulse
vcap.services.jblum-pcc.credentials.users={password=vaxAi8UuJkBp9csgDvJ5YA, roles=[cluster_operator], username=cluster_operator_CQhqoDaEIT1gobjLryfpBg},{password=egSyyyaM5Q5yUMOVZD6pXA, roles=[developer], username=developer_krCFKddILf8EfWs0laUQ}
vcap.services.jblum-pcc.credentials.users[0].username=cluster_operator_CQhqoDaEIT1gobjLryfpBg
vcap.services.jblum-pcc.credentials.users[0].password=vaxAi8UuJkBp9csgDvJ5YA
vcap.services.jblum-pcc.credentials.users[0].roles=cluster_operator
vcap.services.jblum-pcc.credentials.users[0].roles[0]=cluster_operator
vcap.services.jblum-pcc.credentials.users[1].username=developer_krCFKddILf8EfWs0laUQ
vcap.services.jblum-pcc.credentials.users[1].password=egSyyyaM5Q5yUMOVZD6pXA
vcap.services.jblum-pcc.credentials.users[1].roles=developer
vcap.services.jblum-pcc.credentials.users[1].roles[0]=developer
vcap.services.jblum-pcc.credentials.wan.sender_credentials.active.username=gateway_sender_UJ0YO1pJBEnQP03yt7sVXQ
vcap.services.jblum-pcc.credentials.wan.sender_credentials.active.password=tYHFwByaMN675FuBWDZQiQ
vcap.services.jblum-pcc.label=p-cloudcache
vcap.services.jblum-pcc.name=jblum-pcc
vcap.services.jblum-pcc.plan=small
vcap.services.jblum-pcc.provider=
vcap.services.jblum-pcc.syslog_drain_url=
vcap.services.jblum-pcc.tags=gemfire,cloudcache,database,pivotal
vcap.services.jblum-pcc.tags[0]=gemfire
vcap.services.jblum-pcc.tags[1]=cloudcache
vcap.services.jblum-pcc.tags[2]=database
vcap.services.jblum-pcc.tags[3]=pivotal
vcap.services.jblum-pcc.volume_mounts=
security.context.username.property=${vcap.services.jblum-pcc.credentials.users[0].username}
security.context.password.property=${vcap.services.jblum-pcc.credentials.users[0].password}

View File

@@ -0,0 +1,4 @@
# Spring Boot application.properties for testing Apache Geode/Pivotal GemFire Security in a local context.
spring.data.gemfire.security.username=ghostrider
spring.data.gemfire.security.password=p@55w0rd

View File

@@ -0,0 +1,4 @@
# Spring Boot application.properties for testing Apache Geode/Pivotal GemFire Security in a local context.
security.context.username.property=ghostrider
security.context.password.property=p@55w0rd

View File

@@ -0,0 +1,5 @@
# Spring Boot application.properties for testing Apache Geode/Pivotal GemFire SSL
spring.boot.data.gemfire.security.ssl.keystore.name=test-trusted.keystore
spring.data.gemfire.security.ssl.keystore.password=s3cr3t
spring.data.gemfire.security.ssl.truststore.password=s3cr3t

View File

@@ -0,0 +1,4 @@
# Spring Boot application.properties for testing security in a Pivotal CloudFoundry (PCF) context.
VCAP_APPLICATION={"application_id":"c50bb519-2739-4fa3-8750-02c051e35735","application_name":"boot-test","application_uris":["boot-example.apps.tunis.cf-app.com"],"application_version":"d34bbbbd-c35c-4057-baf2-6300cb9aa2aa","cf_api":"https://api.sys.tunis.cf-app.com","host":"0.0.0.0","instance_id":"babcf301-3b34-4dcf-720e-ccfc","instance_index":0,"limits":{"disk":1024,"fds":16384,"mem":1024},"name":"boot-example","port":8080,"space_id":"271cf083-7855-4b5e-be19-65342d099099","space_name":"jblum-space","uris":["boot-example.apps.tunis.cf-app.com"],"version":"d34bbbbd-c35c-4057-baf2-6300cb9aa2aa"}
VCAP_SERVICES={"p-cloudcache":[{ "credentials": { "distributed_system_id": "0", "locators": [ "localhost[55221]" ], "urls": { "pulse": "http://cloudcache-9defb33a-6b8b-49f0-bd35-cf6f7b2f222f.sys.tunis.cf-app.com/pulse" }, "users": [ { "password": "vaxAi8UuJkBp9csgDvJ5YA", "roles": [ "cluster_operator" ], "username": "cluster_operator_CQhqoDaEIT1gobjLryfpBg" }, { "password": "egSyyyaM5Q5yUMOVZD6pXA", "roles": [ "developer" ], "username": "developer_krCFKddILf8EfWs0laUQ" } ], "wan": { "sender_credentials": { "active": { "password": "tYHFwByaMN675FuBWDZQiQ", "username": "gateway_sender_UJ0YO1pJBEnQP03yt7sVXQ" } } } }, "syslog_drain_url": null, "volume_mounts": [ ], "label": "p-cloudcache", "provider": null, "plan": "small", "name": "jblum-pcc", "tags": [ "gemfire", "cloudcache", "database", "pivotal" ] }]}