SGF-569 - Move Off-Heap, Redis Adapter and Security Annotation config from apache-geode to SDG 2.0.x.
This commit is contained in:
@@ -17,7 +17,7 @@ apply plugin: "java"
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'idea'
|
||||
apply from: "$rootDir/maven.gradle"
|
||||
apply plugin: 'bundlor'
|
||||
//apply plugin: 'bundlor'
|
||||
apply plugin: 'propdeps'
|
||||
apply plugin: 'docbook-reference'
|
||||
apply plugin: 'org.asciidoctor.gradle.asciidoctor'
|
||||
@@ -101,6 +101,7 @@ dependencies {
|
||||
compile("io.pivotal.gemfire:geode-cq:$gemfireVersion")
|
||||
compile("io.pivotal.gemfire:geode-wan:$gemfireVersion")
|
||||
optional("com.google.code.findbugs:annotations:$googleCodeFindbugsVersion")
|
||||
optional("org.apache.shiro:shiro-spring:$shiroVersion")
|
||||
runtime("antlr:antlr:$antlrVersion")
|
||||
|
||||
optional "javax.enterprise:cdi-api:$cdiVersion"
|
||||
@@ -112,8 +113,8 @@ dependencies {
|
||||
testCompile("org.springframework:spring-test:$springVersion") {
|
||||
exclude group: "commons-logging", module: "commons-logging"
|
||||
}
|
||||
testCompile "junit:junit:$junitVersion"
|
||||
testCompile "javax.annotation:jsr250-api:1.0", optional
|
||||
testCompile "junit:junit:$junitVersion"
|
||||
testCompile "org.apache.openwebbeans.test:cditest-owb:$openwebbeansVersion"
|
||||
testCompile "org.assertj:assertj-core:$assertjVersion"
|
||||
testCompile "org.hamcrest:hamcrest-core:$hamcrestVersion"
|
||||
@@ -132,9 +133,11 @@ dependencies {
|
||||
sharedResources "org.springframework.data.build:spring-data-build-resources:$springDataBuildVersion@zip"
|
||||
}
|
||||
|
||||
/*
|
||||
bundlor {
|
||||
manifestTemplate = file("template.mf").text
|
||||
}
|
||||
*/
|
||||
|
||||
jar {
|
||||
manifest.attributes['Implementation-Title'] = project.name
|
||||
|
||||
@@ -14,6 +14,7 @@ multiThreadedtcVersion=1.01
|
||||
openwebbeansVersion=1.2.8
|
||||
servletApiVersion=2.5
|
||||
slf4jVersion=1.7.21
|
||||
shiroVersion=1.3.1
|
||||
spring.range="[5.0.0, 6.0.0)"
|
||||
springVersion=5.0.0.M3
|
||||
springDataBuildVersion=2.0.0.BUILD-SNAPSHOT
|
||||
|
||||
8
pom.xml
8
pom.xml
@@ -17,6 +17,7 @@
|
||||
|
||||
<properties>
|
||||
<antlr.version>2.7.7</antlr.version>
|
||||
<apache-shiro.version>1.3.1</apache-shiro.version>
|
||||
<assertj.version>2.5.0</assertj.version>
|
||||
<gemfire.version>9.0.0-beta.1</gemfire.version>
|
||||
<google-code-findbugs.version>2.0.2</google-code-findbugs.version>
|
||||
@@ -98,6 +99,13 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.shiro</groupId>
|
||||
<artifactId>shiro-spring</artifactId>
|
||||
<version>${apache-shiro.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.shell</groupId>
|
||||
<artifactId>spring-shell</artifactId>
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
include::{resourcesDir}/org/springframework/data/gemfire/config/spring-gemfire-1.8.xsd[]
|
||||
include::{resourcesDir}/org/springframework/data/gemfire/config/spring-gemfire-2.0.xsd[]
|
||||
----
|
||||
|
||||
== Spring Data GemFire Data Access Schema (gfe-data)
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
include::{resourcesDir}/org/springframework/data/gemfire/config/spring-data-gemfire-1.8.xsd[]
|
||||
include::{resourcesDir}/org/springframework/data/gemfire/config/spring-data-gemfire-2.0.xsd[]
|
||||
----
|
||||
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.config.annotation;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.internal.security.SecurityService;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.mgt.DefaultSecurityManager;
|
||||
import org.apache.shiro.realm.Realm;
|
||||
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
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.OrderComparator;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.data.gemfire.util.CollectionUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* The {@link ApacheShiroSecurityConfiguration} class is a Spring {@link Configuration @Configuration} component
|
||||
* responsible for configuring and initializing the Apache Shiro security framework in order to secure Apache Geode
|
||||
* administrative and data access operations.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.apache.geode.internal.security.SecurityService
|
||||
* @see org.apache.shiro.mgt.DefaultSecurityManager
|
||||
* @see org.apache.shiro.realm.Realm
|
||||
* @see org.apache.shiro.spring.LifecycleBeanPostProcessor
|
||||
* @see org.springframework.beans.factory.BeanFactoryAware
|
||||
* @see org.springframework.beans.factory.ListableBeanFactory
|
||||
* @see org.springframework.context.annotation.Bean
|
||||
* @see org.springframework.context.annotation.Condition
|
||||
* @see org.springframework.context.annotation.Conditional
|
||||
* @see org.springframework.context.annotation.Configuration
|
||||
* @see org.springframework.data.gemfire.config.annotation.ApacheShiroSecurityConfiguration.ApacheShiroPresentCondition
|
||||
* @since 1.9.0
|
||||
*/
|
||||
@Configuration
|
||||
@Conditional(ApacheShiroSecurityConfiguration.ApacheShiroPresentCondition.class)
|
||||
@SuppressWarnings("unused")
|
||||
public class ApacheShiroSecurityConfiguration implements BeanFactoryAware {
|
||||
|
||||
private ListableBeanFactory beanFactory;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
Assert.isInstanceOf(ListableBeanFactory.class, beanFactory);
|
||||
this.beanFactory = (ListableBeanFactory) beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the Spring {@link BeanFactory}.
|
||||
*
|
||||
* @return a reference to the Spring {@link BeanFactory}.
|
||||
* @throws IllegalStateException if the Spring {@link BeanFactory} was not properly initialized.
|
||||
* @see org.springframework.beans.factory.BeanFactory
|
||||
*/
|
||||
protected ListableBeanFactory getBeanFactory() {
|
||||
Assert.state(this.beanFactory != null, "BeanFactory was not properly initialized");
|
||||
return this.beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Bean} definition to define, configure and register an Apache Shiro Spring
|
||||
* {@link LifecycleBeanPostProcessor} to automatically call lifecycle callback methods
|
||||
* on Shiro security components during Spring container initialization and destruction phases.
|
||||
*
|
||||
* @return an instance of the Apache Shiro Spring {@link LifecycleBeanPostProcessor} to handle the lifecycle
|
||||
* of Apache Shiro security framework components.
|
||||
* @see org.apache.shiro.spring.LifecycleBeanPostProcessor
|
||||
*/
|
||||
@Bean
|
||||
public BeanPostProcessor shiroLifecycleBeanPostProcessor() {
|
||||
return new LifecycleBeanPostProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Bean} definition to define, configure and register an Apache Shiro
|
||||
* {@link org.apache.shiro.mgt.SecurityManager} implementation to secure Apache Geode.
|
||||
*
|
||||
* The registration of this {@link Bean} definition is dependent upon whether the user is using Apache Shiro
|
||||
* to secure Apache Geode, which is determined by the presence of Apache Shiro {@link Realm Realms}
|
||||
* declared in the Spring {@link org.springframework.context.ApplicationContext}.
|
||||
*
|
||||
* This {@link Bean} definition declares a dependency on the Apache Geode {@link GemFireCache} instance
|
||||
* in order to ensure the Geode cache is created and initialized first. This ensures that any internal Geode
|
||||
* security configuration logic is evaluated and processed before SDG attempts to configure Apache Shiro
|
||||
* as Apache Geode's security provider.
|
||||
*
|
||||
* Additionally, this {@link Bean} definition will register the Apache Shiro
|
||||
* {@link org.apache.geode.security.SecurityManager} with the Apache Shiro security framework
|
||||
*
|
||||
* Finally, this method proceeds to enable Apache Geode security.
|
||||
|
||||
* @return an Apache Shiro {@link org.apache.shiro.mgt.SecurityManager} implementation used to secure Apache Geode.
|
||||
* @throws IllegalStateException if an Apache Shiro {@link org.apache.shiro.mgt.SecurityManager} was registered
|
||||
* with the Apache Shiro security framework but Apache Geode security could not be enabled.
|
||||
* @see org.apache.shiro.mgt.SecurityManager
|
||||
* @see #registerSecurityManager(org.apache.shiro.mgt.SecurityManager)
|
||||
* @see #enableApacheGeodeSecurity()
|
||||
* @see #resolveRealms()
|
||||
* @see #registerSecurityManager(org.apache.shiro.mgt.SecurityManager)
|
||||
* @see #enableApacheGeodeSecurity()
|
||||
*/
|
||||
@Bean
|
||||
public org.apache.shiro.mgt.SecurityManager shiroSecurityManager(GemFireCache gemfireCache) {
|
||||
org.apache.shiro.mgt.SecurityManager shiroSecurityManager = null;
|
||||
|
||||
List<Realm> realms = resolveRealms();
|
||||
|
||||
if (!realms.isEmpty()) {
|
||||
shiroSecurityManager = registerSecurityManager(new DefaultSecurityManager(realms));
|
||||
|
||||
if (!enableApacheGeodeSecurity()) {
|
||||
throw new IllegalStateException("Failed to enable security services in Apache Geode");
|
||||
}
|
||||
}
|
||||
|
||||
return shiroSecurityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves all the Apache Shiro {@link Realm Realms} declared and configured as Spring managed beans
|
||||
* in the Spring {@link org.springframework.context.ApplicationContext}.
|
||||
*
|
||||
* This method will order the Realms according to priority order to ensure that the Apache Shiro Realms
|
||||
* are applied in the correct sequence, as declared/configured.
|
||||
*
|
||||
* @return a {@link List} of all Apache Shiro {@link Realm Realms} declared and configured as Spring managed beans
|
||||
* in the Spring {@link org.springframework.context.ApplicationContext}.
|
||||
* @see org.springframework.beans.factory.ListableBeanFactory#getBeansOfType(Class, boolean, boolean)
|
||||
* @see org.springframework.core.OrderComparator
|
||||
* @see org.apache.shiro.realm.Realm
|
||||
*/
|
||||
protected List<Realm> resolveRealms() {
|
||||
try {
|
||||
Map<String, Realm> realmBeans = getBeanFactory().getBeansOfType(Realm.class, false, true);
|
||||
List<Realm> realms = new ArrayList<>(CollectionUtils.nullSafeMap(realmBeans).values());
|
||||
Collections.sort(realms, OrderComparator.INSTANCE);
|
||||
return realms;
|
||||
}
|
||||
catch (Exception ignore) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given Apache Shiro {@link org.apache.shiro.mgt.SecurityManager} with the Apache Shiro
|
||||
* security framework.
|
||||
*
|
||||
* @param securityManager {@link org.apache.shiro.mgt.SecurityManager} to register.
|
||||
* @return the given {@link org.apache.shiro.mgt.SecurityManager} reference.
|
||||
* @throws IllegalArgumentException if {@link org.apache.shiro.mgt.SecurityManager} is {@literal null}.
|
||||
* @see org.apache.shiro.SecurityUtils#setSecurityManager(org.apache.shiro.mgt.SecurityManager)
|
||||
* @see org.apache.shiro.mgt.SecurityManager
|
||||
*/
|
||||
protected org.apache.shiro.mgt.SecurityManager registerSecurityManager(
|
||||
org.apache.shiro.mgt.SecurityManager securityManager) {
|
||||
|
||||
Assert.notNull(securityManager, "The Apache Shiro SecurityManager to register must not be null");
|
||||
|
||||
SecurityUtils.setSecurityManager(securityManager);
|
||||
|
||||
return securityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Apache Geode, Integrated Security {@link SecurityService} property {@literal isIntegratedSecurity}
|
||||
* to {@literal true} to indicate that Apache Geode security is enabled.
|
||||
*
|
||||
* @return a boolean value indicating whether Apache Geode's Integrated Security framework services
|
||||
* were successfully enabled.
|
||||
* @see org.apache.geode.internal.security.SecurityService#getSecurityService()
|
||||
*/
|
||||
protected boolean enableApacheGeodeSecurity() {
|
||||
SecurityService securityService = SecurityService.getSecurityService();
|
||||
|
||||
if (securityService != null) {
|
||||
String isIntegratedSecurityFieldName = "isIntegratedSecurity";
|
||||
|
||||
Field isIntegratedSecurity = ReflectionUtils.findField(securityService.getClass(),
|
||||
isIntegratedSecurityFieldName, Boolean.class);
|
||||
|
||||
isIntegratedSecurity = (isIntegratedSecurity != null ? isIntegratedSecurity
|
||||
: ReflectionUtils.findField(securityService.getClass(), isIntegratedSecurityFieldName, Boolean.TYPE));
|
||||
|
||||
if (isIntegratedSecurity != null) {
|
||||
ReflectionUtils.makeAccessible(isIntegratedSecurity);
|
||||
ReflectionUtils.setField(isIntegratedSecurity, securityService, Boolean.TRUE);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A Spring {@link Condition} to determine whether the user has included (declared) the 'shiro-spring' dependency
|
||||
* on their application's classpath, which is necessary for configuring Apache Shiro to secure Apache Geode
|
||||
* in a Spring context.
|
||||
*
|
||||
* @see org.springframework.context.annotation.Condition
|
||||
*/
|
||||
public static class ApacheShiroPresentCondition implements Condition {
|
||||
|
||||
protected static final String APACHE_SHIRO_LIFECYCLE_BEAN_POST_PROCESSOR_CLASS_NAME =
|
||||
"org.apache.shiro.spring.LifecycleBeanPostProcessor";
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
return ClassUtils.isPresent(APACHE_SHIRO_LIFECYCLE_BEAN_POST_PROCESSOR_CLASS_NAME,
|
||||
context.getClassLoader());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,43 +40,59 @@ public class AuthConfiguration extends EmbeddedServiceConfigurationSupport {
|
||||
|
||||
public static final String DEFAULT_SECURITY_LOG_LEVEL = "config";
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static final String GEMFIRE_SECURITY_PROPERTY_FILE = "gemfireSecurityPropertyFile";
|
||||
protected static final String SECURITY_CLIENT_ACCESSOR = "security-client-accessor";
|
||||
protected static final String SECURITY_CLIENT_ACCESSOR_POST_PROCESSOR = "security-client-accessor-pp";
|
||||
protected static final String SECURITY_CLIENT_AUTH_INIT = "security-client-auth-init";
|
||||
protected static final String SECURITY_CLIENT_AUTHENTICATOR = "security-client-authenticator";
|
||||
protected static final String SECURITY_CLIENT_DIFFIE_HELLMAN_ALGORITHM = "security-client-dhalgo";
|
||||
protected static final String SECURITY_LOG_FILE = "security-log-file";
|
||||
protected static final String SECURITY_LOG_LEVEL = "security-log-level";
|
||||
protected static final String SECURITY_PEER_AUTH_INIT = "security-peer-auth-init";
|
||||
protected static final String SECURITY_PEER_AUTHENTICATOR = "security-peer-authenticator";
|
||||
protected static final String SECURITY_PEER_VERIFY_MEMBER_TIMEOUT = "security-peer-verifymember-timeout";
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
protected Class getAnnotationType() {
|
||||
return EnableAuth.class;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
protected Properties toGemFireProperties(Map<String, Object> annotationAttributes) {
|
||||
PropertiesBuilder gemfireProperties = PropertiesBuilder.create();
|
||||
|
||||
gemfireProperties.setProperty("gemfireSecurityPropertyFile", annotationAttributes.get("securityPropertiesFile"));
|
||||
gemfireProperties.setProperty(GEMFIRE_SECURITY_PROPERTY_FILE, annotationAttributes.get("securityPropertiesFile"));
|
||||
|
||||
gemfireProperties.setProperty("security-client-accessor", annotationAttributes.get("clientAccessor"));
|
||||
gemfireProperties.setProperty(SECURITY_CLIENT_ACCESSOR, annotationAttributes.get("clientAccessor"));
|
||||
|
||||
gemfireProperties.setProperty("security-client-accessor-pp",
|
||||
gemfireProperties.setProperty(SECURITY_CLIENT_ACCESSOR_POST_PROCESSOR,
|
||||
annotationAttributes.get("clientAccessPostOperation"));
|
||||
|
||||
gemfireProperties.setProperty("security-client-auth-init",
|
||||
gemfireProperties.setProperty(SECURITY_CLIENT_AUTH_INIT,
|
||||
annotationAttributes.get("clientAuthenticationInitializer"));
|
||||
|
||||
gemfireProperties.setProperty("security-client-authenticator", annotationAttributes.get("clientAuthenticator"));
|
||||
gemfireProperties.setProperty(SECURITY_CLIENT_AUTHENTICATOR, annotationAttributes.get("clientAuthenticator"));
|
||||
|
||||
gemfireProperties.setProperty("security-client-dhalgo",
|
||||
gemfireProperties.setProperty(SECURITY_CLIENT_DIFFIE_HELLMAN_ALGORITHM,
|
||||
annotationAttributes.get("clientDiffieHellmanAlgorithm"));
|
||||
|
||||
gemfireProperties.setProperty("security-peer-auth-init",
|
||||
gemfireProperties.setProperty(SECURITY_PEER_AUTH_INIT,
|
||||
annotationAttributes.get("peerAuthenticationInitializer"));
|
||||
|
||||
gemfireProperties.setProperty("security-peer-authenticator", annotationAttributes.get("peerAuthenticator"));
|
||||
gemfireProperties.setProperty(SECURITY_PEER_AUTHENTICATOR, annotationAttributes.get("peerAuthenticator"));
|
||||
|
||||
gemfireProperties.setPropertyIfNotDefault("security-peer-verifymember-timeout",
|
||||
gemfireProperties.setPropertyIfNotDefault(SECURITY_PEER_VERIFY_MEMBER_TIMEOUT,
|
||||
annotationAttributes.get("peerVerifyMemberTimeout"), DEFAULT_PEER_VERIFY_MEMBER_TIMEOUT);
|
||||
|
||||
gemfireProperties.setProperty("security-log-file", annotationAttributes.get("securityLogFile"));
|
||||
gemfireProperties.setProperty(SECURITY_LOG_FILE, annotationAttributes.get("securityLogFile"));
|
||||
|
||||
gemfireProperties.setPropertyIfNotDefault("security-log-level",
|
||||
gemfireProperties.setPropertyIfNotDefault(SECURITY_LOG_LEVEL,
|
||||
annotationAttributes.get("securityLogLevel"), DEFAULT_SECURITY_LOG_LEVEL);
|
||||
|
||||
return gemfireProperties.build();
|
||||
|
||||
@@ -34,10 +34,11 @@ import org.springframework.context.annotation.Import;
|
||||
* annotated class to configure and enable GemFire/Geode's Authentication and Authorization framework services.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.config.annotation.AuthConfiguration
|
||||
* @see org.apache.geode.security.AccessControl
|
||||
* @see org.apache.geode.security.AuthInitialize
|
||||
* @see org.apache.geode.security.Authenticator
|
||||
* @see org.springframework.context.annotation.Import
|
||||
* @see org.springframework.data.gemfire.config.annotation.AuthConfiguration
|
||||
* @see <a href="http://gemfire.docs.pivotal.io/docs-gemfire/latest/managing/security/authentication_overview.html">Authentication</a>
|
||||
* @see <a href="http://gemfire.docs.pivotal.io/docs-gemfire/latest/managing/security/authorization_overview.html">Authorization</a>
|
||||
* @since 1.9.0
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.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.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* The EnableOffHeap annotation marks a Spring {@link org.springframework.context.annotation.Configuration @Configuration}
|
||||
* annotated class to configure and enable Apache Geode Off-Heap Memory support and data storage
|
||||
* in Geode's cache {@link org.apache.geode.cache.Region Regions}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.config.annotation.OffHeapConfiguration
|
||||
* @since 1.9.0
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
@Import(OffHeapConfiguration.class)
|
||||
@SuppressWarnings("unused")
|
||||
public @interface EnableOffHeap {
|
||||
|
||||
/**
|
||||
* Specifies the size of off-heap memory in megabytes (m) or gigabytes (g). For example:
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* off-heap-memory-size=4096m
|
||||
* off-heap-memory-size=120g
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* Defaults to unset.
|
||||
*/
|
||||
String memorySize();
|
||||
|
||||
/**
|
||||
* Idenfitied the Regions by name in which the off-heap memory setting will be applied.
|
||||
*
|
||||
* Defaults to all Regions.
|
||||
*/
|
||||
String[] regionNames() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.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.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* The EnableRedisServer annotation marks a Spring {@link org.springframework.context.annotation.Configuration}
|
||||
* class to embed the Redis service in the GemFire server-side data member node.
|
||||
*
|
||||
* The Redis service implements the Redis server protocol enabling Redis clients to connect and speak
|
||||
* to Pivotal GemFire or Apache Geode.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.config.annotation.MemcachedServerConfiguration
|
||||
* @since 1.9.0
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Import(RedisServerConfiguration.class)
|
||||
@SuppressWarnings("unused")
|
||||
public @interface EnableRedisServer {
|
||||
|
||||
/**
|
||||
* Configures the Network bind-address on which the Redis server will accept connections.
|
||||
*
|
||||
* Defaults to {@literal localhost}.
|
||||
*/
|
||||
String bindAddress() default "";
|
||||
|
||||
/**
|
||||
* Configures the Network port on which the Redis server will listen for Redis client connections.
|
||||
*
|
||||
* Defaults to {@literal 6379}.
|
||||
*/
|
||||
int port() default RedisServerConfiguration.DEFAULT_REDIS_PORT;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.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.security.AuthInitialize;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* The {@link EnableSecurity} annotation marks a Spring {@link org.springframework.context.annotation.Configuration}
|
||||
* annotated class to configure and enable Apache Geode's Security features for authentication, authorization
|
||||
* and post processing.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see GeodeIntegratedSecurityConfiguration
|
||||
* @see org.apache.geode.security.AuthInitialize
|
||||
* @see org.apache.geode.security.SecurityManager
|
||||
* @see org.apache.geode.security.PostProcessor
|
||||
* @see org.springframework.context.annotation.Import
|
||||
* @see org.springframework.data.gemfire.config.annotation.ApacheShiroSecurityConfiguration
|
||||
* @see org.springframework.data.gemfire.config.annotation.GeodeIntegratedSecurityConfiguration
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
@Import({ ApacheShiroSecurityConfiguration.class, GeodeIntegratedSecurityConfiguration.class })
|
||||
@SuppressWarnings({ "unused" })
|
||||
public @interface EnableSecurity {
|
||||
|
||||
/**
|
||||
* Used for authentication. Static creation method returning an {@link AuthInitialize} object,
|
||||
* which obtains credentials for clients.
|
||||
*
|
||||
* Defaults to unset.
|
||||
*/
|
||||
String clientAuthenticationInitializer() default "";
|
||||
|
||||
/**
|
||||
* Used with authentication. Static creation method returning an {@link AuthInitialize} object, which obtains
|
||||
* credentials for peers in a distributed system.
|
||||
*
|
||||
* Defaults to unset.
|
||||
*/
|
||||
String peerAuthenticationInitializer() default "";
|
||||
|
||||
/**
|
||||
* Specifies the application {@link Class} type implementing the Apache Geode
|
||||
* {@link org.apache.geode.security.SecurityManager} interface to enable security in Apache Geode.
|
||||
*
|
||||
* Defaults to {@link Void}.
|
||||
*/
|
||||
Class<?> securityManagerClass() default Void.class;
|
||||
|
||||
/**
|
||||
* Specifies the fully-qualified class name of the application {@link Class} implementing the Apache Geode
|
||||
* {@link org.apache.geode.security.SecurityManager} interface to enable security in Apache Geode.
|
||||
*
|
||||
* Use this Annotation attribute if you are uncertain whether the application class is on the classpath or not.
|
||||
*
|
||||
* Default is unset.
|
||||
*/
|
||||
String securityManagerClassName() default "";
|
||||
|
||||
/**
|
||||
* Specifies the application {@link Class} type implementing the Apache Geode
|
||||
* {@link org.apache.geode.security.PostProcessor} interface, which used to transform sensitive data
|
||||
* returned from secure data access operations.
|
||||
*
|
||||
* Defaults to {@link Void}.
|
||||
*/
|
||||
Class<?> securityPostProcessorClass() default Void.class;
|
||||
|
||||
/**
|
||||
* Specifies the fully-qualified class name of the application {@link Class} implementing the Apache Geode
|
||||
* {@link org.apache.geode.security.PostProcessor} interface, which used to transform sensitive data
|
||||
* returned from secure data access operations.
|
||||
*
|
||||
* Use this Annotation attribute if you are uncertain whether the application class is on the classpath or not.
|
||||
*
|
||||
* Default is unset.
|
||||
*/
|
||||
String securityPostProcessorClassName() default "";
|
||||
|
||||
/**
|
||||
* Sets the Geode System Property referring to the location of an Apache Shiro INI file used to configure
|
||||
* the Apache Shiro Security Framework to secure Apache Geode.
|
||||
*
|
||||
* Default is unset.
|
||||
*/
|
||||
String shiroIniResourcePath() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.config.annotation;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.data.gemfire.config.annotation.support.EmbeddedServiceConfigurationSupport;
|
||||
import org.springframework.data.gemfire.util.PropertiesBuilder;
|
||||
|
||||
/**
|
||||
* The {@link GeodeIntegratedSecurityConfiguration} class is a {@link EmbeddedServiceConfigurationSupport} implementation
|
||||
* that enables Apache Geode's Integrated Security framework and services.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.config.annotation.support.EmbeddedServiceConfigurationSupport
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class GeodeIntegratedSecurityConfiguration extends EmbeddedServiceConfigurationSupport {
|
||||
|
||||
protected static final String SECURITY_CLIENT_AUTH_INIT = "security-client-auth-init";
|
||||
protected static final String SECURITY_MANAGER = "security-manager";
|
||||
protected static final String SECURITY_PEER_AUTH_INIT = "security-peer-auth-init";
|
||||
protected static final String SECURITY_POST_PROCESSOR = "security-post-processor";
|
||||
protected static final String SECURITY_SHIRO_INIT = "security-shiro-init";
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
protected Class getAnnotationType() {
|
||||
return EnableSecurity.class;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected boolean isShiroSecurityConfigured() {
|
||||
try {
|
||||
// NOTE experimental...
|
||||
//return resolveBean(ApacheShiroSecurityConfiguration.class).isRealmsPresent();
|
||||
return false;
|
||||
}
|
||||
catch (Exception ignore) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected boolean isShiroSecurityNotConfigured() {
|
||||
return !isShiroSecurityConfigured();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
protected Properties toGemFireProperties(Map<String, Object> annotationAttributes) {
|
||||
PropertiesBuilder gemfireProperties = new PropertiesBuilder();
|
||||
|
||||
gemfireProperties.setProperty(SECURITY_CLIENT_AUTH_INIT,
|
||||
annotationAttributes.get("clientAuthenticationInitializer"));
|
||||
|
||||
if (isShiroSecurityNotConfigured()) {
|
||||
gemfireProperties.setPropertyIfNotDefault(SECURITY_MANAGER,
|
||||
annotationAttributes.get("securityManagerClass"), Void.class);
|
||||
|
||||
gemfireProperties.setProperty(SECURITY_MANAGER, annotationAttributes.get("securityManagerClassName"));
|
||||
|
||||
gemfireProperties.setProperty(SECURITY_SHIRO_INIT, annotationAttributes.get("shiroIniResourcePath"));
|
||||
}
|
||||
|
||||
gemfireProperties.setProperty(SECURITY_PEER_AUTH_INIT,
|
||||
annotationAttributes.get("peerAuthenticationInitializer"));
|
||||
|
||||
gemfireProperties.setPropertyIfNotDefault(SECURITY_POST_PROCESSOR,
|
||||
annotationAttributes.get("securityPostProcessorClass"), Void.class);
|
||||
|
||||
gemfireProperties.setProperty(SECURITY_POST_PROCESSOR,
|
||||
annotationAttributes.get("securityPostProcessorClassName"));
|
||||
|
||||
return gemfireProperties.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.config.annotation;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.gemfire.GenericRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.LocalRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.PartitionedRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.ReplicatedRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.config.annotation.support.EmbeddedServiceConfigurationSupport;
|
||||
import org.springframework.data.gemfire.util.ArrayUtils;
|
||||
import org.springframework.data.gemfire.util.CollectionUtils;
|
||||
import org.springframework.data.gemfire.util.PropertiesBuilder;
|
||||
|
||||
/**
|
||||
* The OffHeapConfiguration class is a Spring {@link org.springframework.context.annotation.ImportBeanDefinitionRegistrar}
|
||||
* capable of enabling GemFire cache {@link Region Regions} to use Off-Heap memory for data storage.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.config.annotation.EnableOffHeap
|
||||
* @see org.springframework.data.gemfire.config.annotation.support.EmbeddedServiceConfigurationSupport
|
||||
* @since 1.9.0
|
||||
*/
|
||||
public class OffHeapConfiguration extends EmbeddedServiceConfigurationSupport {
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@Override
|
||||
protected Class getAnnotationType() {
|
||||
return EnableOffHeap.class;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@Override
|
||||
protected void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
|
||||
Map<String, Object> annotationAttributes, BeanDefinitionRegistry registry) {
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
OffHeapBeanFactoryPostProcessor.class);
|
||||
|
||||
builder.addConstructorArgValue(annotationAttributes.get("regionNames"));
|
||||
|
||||
registry.registerBeanDefinition(generateBeanName(OffHeapBeanFactoryPostProcessor.class),
|
||||
builder.getBeanDefinition());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@Override
|
||||
protected Properties toGemFireProperties(Map<String, Object> annotationAttributes) {
|
||||
PropertiesBuilder gemfireProperties = PropertiesBuilder.create();
|
||||
|
||||
gemfireProperties.setProperty("off-heap-memory-size", annotationAttributes.get("memorySize"));
|
||||
|
||||
return gemfireProperties.build();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
protected static class OffHeapBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
|
||||
|
||||
protected static final Set<String> REGION_FACTORY_BEAN_TYPES = new HashSet<String>(5);
|
||||
|
||||
static {
|
||||
REGION_FACTORY_BEAN_TYPES.add(ClientRegionFactoryBean.class.getName());
|
||||
REGION_FACTORY_BEAN_TYPES.add(GenericRegionFactoryBean.class.getName());
|
||||
REGION_FACTORY_BEAN_TYPES.add(LocalRegionFactoryBean.class.getName());
|
||||
REGION_FACTORY_BEAN_TYPES.add(PartitionedRegionFactoryBean.class.getName());
|
||||
REGION_FACTORY_BEAN_TYPES.add(ReplicatedRegionFactoryBean.class.getName());
|
||||
}
|
||||
|
||||
private final Set<String> regionNames;
|
||||
|
||||
protected OffHeapBeanFactoryPostProcessor(String[] regionNames) {
|
||||
this(CollectionUtils.asSet(ArrayUtils.nullSafeArray(regionNames, String.class)));
|
||||
}
|
||||
|
||||
protected OffHeapBeanFactoryPostProcessor(Set<String> regionNames) {
|
||||
this.regionNames = CollectionUtils.nullSafeSet(regionNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||
BeanDefinition bean = beanFactory.getBeanDefinition(beanName);
|
||||
|
||||
if (isTargetedRegionBean(beanName, bean, beanFactory)) {
|
||||
bean.getPropertyValues().addPropertyValue("offHeap", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean isTargetedRegionBean(String beanName, BeanDefinition bean,
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
return (isRegionBean(bean) && isNamedRegion(beanName, bean, beanFactory));
|
||||
}
|
||||
|
||||
boolean isRegionBean(BeanDefinition bean) {
|
||||
return (bean != null && REGION_FACTORY_BEAN_TYPES.contains(bean.getBeanClassName()));
|
||||
}
|
||||
|
||||
boolean isNamedRegion(String beanName, BeanDefinition bean, ConfigurableListableBeanFactory beanFactory) {
|
||||
return (CollectionUtils.isEmpty(regionNames) || CollectionUtils.containsAny(regionNames,
|
||||
getBeanNames(beanName, bean, beanFactory)));
|
||||
}
|
||||
|
||||
Collection<String> getBeanNames(String beanName, BeanDefinition bean, BeanFactory beanFactory) {
|
||||
Collection<String> beanNames = new HashSet<String>();
|
||||
|
||||
beanNames.add(beanName);
|
||||
Collections.addAll(beanNames, beanFactory.getAliases(beanName));
|
||||
|
||||
PropertyValue regionName = bean.getPropertyValues().getPropertyValue("regionName");
|
||||
|
||||
if (regionName != null) {
|
||||
Object regionNameValue = regionName.getValue();
|
||||
|
||||
if (regionNameValue != null) {
|
||||
beanNames.add(regionNameValue.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return beanNames;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.config.annotation;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.data.gemfire.config.annotation.support.EmbeddedServiceConfigurationSupport;
|
||||
import org.springframework.data.gemfire.util.PropertiesBuilder;
|
||||
|
||||
/**
|
||||
* The RedisServerConfiguration class is a Spring {@link org.springframework.context.annotation.ImportBeanDefinitionRegistrar}
|
||||
* that applies additional GemFire configuration by way of GemFire System properties to configure
|
||||
* an embedded Redis server.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.config.annotation.EnableRedisServer
|
||||
* @see org.springframework.data.gemfire.config.annotation.support.EmbeddedServiceConfigurationSupport
|
||||
* @since 1.9.0
|
||||
*/
|
||||
public class RedisServerConfiguration extends EmbeddedServiceConfigurationSupport {
|
||||
|
||||
protected static final int DEFAULT_REDIS_PORT = 6379;
|
||||
|
||||
@Override
|
||||
protected Class getAnnotationType() {
|
||||
return EnableRedisServer.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Properties toGemFireProperties(Map<String, Object> annotationAttributes) {
|
||||
return new PropertiesBuilder()
|
||||
.setProperty("redis-bind-address", annotationAttributes.get("bindAddress"))
|
||||
.setProperty("redis-port", resolvePort((Integer)annotationAttributes.get("port"), DEFAULT_REDIS_PORT))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,8 @@ import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -53,8 +53,9 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio
|
||||
* @param elements array of objects to add to the {@link Set}.
|
||||
* @return an unmodifiable {@link Set} containing the elements from the given object array.
|
||||
*/
|
||||
@SafeVarargs
|
||||
public static <T> Set<T> asSet(T... elements) {
|
||||
Set<T> set = new HashSet<T>(elements.length);
|
||||
Set<T> set = new HashSet<>(elements.length);
|
||||
Collections.addAll(set, elements);
|
||||
return Collections.unmodifiableSet(set);
|
||||
}
|
||||
@@ -68,12 +69,8 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio
|
||||
* @see java.lang.Iterable
|
||||
* @see java.util.Enumeration
|
||||
*/
|
||||
public static <T> Iterable<T> iterable(final Enumeration<T> enumeration) {
|
||||
return new Iterable<T>() {
|
||||
@Override public Iterator<T> iterator() {
|
||||
return org.springframework.util.CollectionUtils.toIterator(enumeration);
|
||||
}
|
||||
};
|
||||
public static <T> Iterable<T> iterable(Enumeration<T> enumeration) {
|
||||
return () -> org.springframework.util.CollectionUtils.toIterator(enumeration);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,12 +82,8 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio
|
||||
* @see java.lang.Iterable
|
||||
* @see java.util.Iterator
|
||||
*/
|
||||
public static <T> Iterable<T> iterable(final Iterator<T> iterator) {
|
||||
return new Iterable<T>() {
|
||||
@Override public Iterator<T> iterator() {
|
||||
return iterator;
|
||||
}
|
||||
};
|
||||
public static <T> Iterable<T> iterable(Iterator<T> iterator) {
|
||||
return () -> iterator;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,7 +98,7 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio
|
||||
* @see java.util.Collection
|
||||
*/
|
||||
public static <T> Collection<T> nullSafeCollection(Collection<T> collection) {
|
||||
return (collection != null ? collection : Collections.<T>emptyList());
|
||||
return (collection != null ? collection : Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,23 +112,7 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio
|
||||
* @see java.util.Iterator
|
||||
*/
|
||||
public static <T> Iterable<T> nullSafeIterable(Iterable<T> iterable) {
|
||||
return (iterable != null ? iterable : new Iterable<T>() {
|
||||
@Override public Iterator<T> iterator() {
|
||||
return new Iterator<T>() {
|
||||
@Override public boolean hasNext() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public T next() {
|
||||
throw new NoSuchElementException("No more elements");
|
||||
}
|
||||
|
||||
@Override public void remove() {
|
||||
throw new UnsupportedOperationException("Operation not supported");
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
return (iterable != null ? iterable : Collections::emptyIterator);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,7 +126,7 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio
|
||||
* @see java.util.List
|
||||
*/
|
||||
public static <T> List<T> nullSafeList(List<T> list) {
|
||||
return (list != null ? list : Collections.<T>emptyList());
|
||||
return (list != null ? list : Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,7 +141,7 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio
|
||||
* @see java.util.Map
|
||||
*/
|
||||
public static <K, V> Map<K, V> nullSafeMap(Map<K, V> map) {
|
||||
return (map != null ? map : Collections.<K, V>emptyMap());
|
||||
return (map != null ? map : Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,7 +155,7 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio
|
||||
* @see java.util.Set
|
||||
*/
|
||||
public static <T> Set<T> nullSafeSet(Set<T> set) {
|
||||
return (set != null ? set : Collections.<T>emptySet());
|
||||
return (set != null ? set : Collections.emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,7 +188,7 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio
|
||||
public static <T> List<T> subList(List<T> source, int... indices) {
|
||||
Assert.notNull(source, "List must not be null");
|
||||
|
||||
List<T> result = new ArrayList<T>(indices.length);
|
||||
List<T> result = new ArrayList<>(indices.length);
|
||||
|
||||
for (int index : indices) {
|
||||
result.add(source.get(index));
|
||||
@@ -219,4 +196,23 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static String toString(Map<?, ?> map) {
|
||||
StringBuilder builder = new StringBuilder("{\n");
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (Map.Entry<?, ?> entry : new TreeMap<>(map).entrySet()) {
|
||||
builder.append(++count > 1 ? ",\n" : "");
|
||||
builder.append("\t");
|
||||
builder.append(entry.getKey());
|
||||
builder.append(" = ");
|
||||
builder.append(entry.getValue());
|
||||
}
|
||||
|
||||
builder.append("\n}");
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ public abstract class DistributedSystemUtils extends SpringUtils {
|
||||
|
||||
public static final String DURABLE_CLIENT_ID_PROPERTY_NAME = DistributionConfig.DURABLE_CLIENT_ID_NAME;
|
||||
public static final String DURABLE_CLIENT_TIMEOUT_PROPERTY_NAME = DistributionConfig.DURABLE_CLIENT_TIMEOUT_NAME;
|
||||
public static final String GEMFIRE_PREFIX = DistributionConfig.GEMFIRE_PREFIX;
|
||||
public static final String NAME_PROPERTY_NAME = DistributionConfig.NAME_NAME;
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static Properties configureDurableClient(Properties gemfireProperties,
|
||||
@@ -85,5 +87,4 @@ public abstract class DistributedSystemUtils extends SpringUtils {
|
||||
public static <T extends DistributedSystem> T getDistributedSystem(GemFireCache gemfireCache) {
|
||||
return (gemfireCache != null ? (T) gemfireCache.getDistributedSystem() : null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -102,9 +102,9 @@ public class GemfireTemplateQueriesOnGroupedPooledClientCacheRegionsIntegrationT
|
||||
@BeforeClass
|
||||
public static void setupGemFireCluster() throws Exception {
|
||||
serverOne = ProcessExecutor.launch(createDirectory("serverOne"), GemFireCacheServerOneConfiguration.class);
|
||||
waitForCacheServerToStart("localhost", 41414);
|
||||
waitForServerToStart("localhost", 41414);
|
||||
serverTwo = ProcessExecutor.launch(createDirectory("serverTwo"), GemFireCacheServerTwoConfiguration.class);
|
||||
waitForCacheServerToStart("localhost", 42424);
|
||||
waitForServerToStart("localhost", 42424);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
||||
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.config.annotation;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.security.Principal;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.apache.geode.LogWriter;
|
||||
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.apache.geode.cache.client.ServerOperationException;
|
||||
import org.apache.geode.distributed.DistributedMember;
|
||||
import org.apache.geode.security.AuthInitialize;
|
||||
import org.apache.geode.security.AuthenticationFailedException;
|
||||
import org.apache.geode.security.NotAuthorizedException;
|
||||
import org.apache.geode.security.ResourcePermission;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.FixMethodOrder;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runners.MethodSorters;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.data.gemfire.LocalRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.process.ProcessWrapper;
|
||||
import org.springframework.data.gemfire.test.support.ClientServerIntegrationTestsSupport;
|
||||
import org.springframework.data.gemfire.util.CollectionUtils;
|
||||
import org.springframework.data.gemfire.util.PropertiesBuilder;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* Abstract base test class for implementing Apache Geode Integrated Security Integration Tests.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.FixMethodOrder
|
||||
* @see org.junit.Test
|
||||
* @see lombok
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.springframework.data.gemfire.test.support.ClientServerIntegrationTestsSupport
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class AbstractGeodeSecurityIntegrationTests extends ClientServerIntegrationTestsSupport {
|
||||
|
||||
protected static final int CACHE_SERVER_PORT = 13531;
|
||||
|
||||
protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractGeodeSecurityIntegrationTests.class);
|
||||
|
||||
protected static final String CACHE_SERVER_HOST = "localhost";
|
||||
protected static final String GEODE_SECURITY_PROFILE_PROPERTY = "geode.security.profile";
|
||||
protected static final String SECURITY_PASSWORD_PROPERTY = "security-password";
|
||||
protected static final String SECURITY_USERNAME_PROPERTY = "security-username";
|
||||
|
||||
private static final AtomicInteger RUN_COUNT = new AtomicInteger(0);
|
||||
|
||||
private static ProcessWrapper geodeServerProcess;
|
||||
|
||||
@BeforeClass
|
||||
public static void runGeodeServer() throws IOException {
|
||||
String geodeSecurityProfile = System.getProperty(GEODE_SECURITY_PROFILE_PROPERTY);
|
||||
|
||||
if (StringUtils.hasText(geodeSecurityProfile)) {
|
||||
runGeodeServer(geodeSecurityProfile);
|
||||
}
|
||||
}
|
||||
|
||||
protected static ProcessWrapper runGeodeServer(String geodeSecurityProfile) throws IOException {
|
||||
Assert.hasText(geodeSecurityProfile, String.format("The '%s' System property must be set",
|
||||
GEODE_SECURITY_PROFILE_PROPERTY));
|
||||
|
||||
geodeServerProcess = run(GeodeServerConfiguration.class,
|
||||
String.format("-Dgemfire.log-file=%s", logFile()),
|
||||
String.format("-Dgemfire.log-level=%s", logLevel(TEST_GEMFIRE_LOG_LEVEL)),
|
||||
String.format("-Dspring.profiles.active=apache-geode-server,%s", geodeSecurityProfile));
|
||||
|
||||
waitForServerToStart(CACHE_SERVER_HOST, CACHE_SERVER_PORT);
|
||||
|
||||
return geodeServerProcess;
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopGeodeServer() {
|
||||
System.clearProperty(GEODE_SECURITY_PROFILE_PROPERTY);
|
||||
stop(geodeServerProcess);
|
||||
}
|
||||
|
||||
@Rule
|
||||
public ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Resource(name = "Echo")
|
||||
private Region<String, String> echo;
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void authorizedUser() {
|
||||
assertThat(echo.get("one")).isEqualTo("one");
|
||||
assertThat(echo.put("two", "four")).isNull();
|
||||
assertThat(echo.get("two")).isEqualTo("four");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unauthorizedUser() {
|
||||
assertThat(echo.get("one")).isEqualTo("one");
|
||||
|
||||
exception.expect(ServerOperationException.class);
|
||||
exception.expectCause(is(instanceOf(NotAuthorizedException.class)));
|
||||
exception.expectMessage(containsString("analyst not authorized for DATA:WRITE:Echo:two"));
|
||||
|
||||
echo.put("two", "four");
|
||||
}
|
||||
|
||||
protected static abstract class AuthInitializeSupport implements AuthInitialize {
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void init(LogWriter systemLogger, LogWriter securityLogger) throws AuthenticationFailedException {
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public Properties getCredentials(Properties securityProperties, DistributedMember server, boolean isPeer)
|
||||
throws AuthenticationFailedException {
|
||||
|
||||
return getCredentials(securityProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
}
|
||||
|
||||
public static class GeodeClientAuthInitialize extends AuthInitializeSupport {
|
||||
|
||||
protected static final User ANALYST = User.newUser("analyst").with("p@55w0rd");
|
||||
protected static final User SCIENTIST = User.newUser("scientist").with("w0rk!ng4u");
|
||||
|
||||
private final User user;
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static GeodeClientAuthInitialize create() {
|
||||
return new GeodeClientAuthInitialize(RUN_COUNT.incrementAndGet() < 2 ? SCIENTIST : ANALYST);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public GeodeClientAuthInitialize(User user) {
|
||||
Assert.notNull(user, "User cannot be null");
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public Properties getCredentials(Properties securityProperties) {
|
||||
User user = getUser();
|
||||
|
||||
return new PropertiesBuilder()
|
||||
.setProperty(SECURITY_USERNAME_PROPERTY, user.getName())
|
||||
.setProperty(SECURITY_PASSWORD_PROPERTY, user.getCredentials())
|
||||
.build();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected User getUser() {
|
||||
return this.user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
User user = getUser();
|
||||
return String.format("%1$s:%2$s", user.getName(), user.getCredentials());
|
||||
}
|
||||
}
|
||||
|
||||
@ClientCacheApplication(name = "GeodeSecurityIntegrationTestsClient", logLevel = TEST_GEMFIRE_LOG_LEVEL,
|
||||
servers = { @ClientCacheApplication.Server(port = CACHE_SERVER_PORT) })
|
||||
@EnableAuth(clientAuthenticationInitializer = "org.springframework.data.gemfire.config.annotation.AbstractGeodeSecurityIntegrationTests$GeodeClientAuthInitialize.create")
|
||||
@Profile("apache-geode-client")
|
||||
static class GeodeClientConfiguration {
|
||||
|
||||
@Bean("Echo")
|
||||
ClientRegionFactoryBean<String, String> echoRegion(GemFireCache gemfireCache) {
|
||||
ClientRegionFactoryBean<String, String> echoRegion = new ClientRegionFactoryBean<>();
|
||||
|
||||
echoRegion.setCache(gemfireCache);
|
||||
echoRegion.setClose(false);
|
||||
echoRegion.setShortcut(ClientRegionShortcut.PROXY);
|
||||
|
||||
return echoRegion;
|
||||
}
|
||||
}
|
||||
|
||||
@CacheServerApplication(name = "GeodeSecurityIntegrationTestsServer", logLevel = TEST_GEMFIRE_LOG_LEVEL,
|
||||
port = CACHE_SERVER_PORT)
|
||||
@Import({
|
||||
ApacheShiroIniGeodeSecurityIntegrationTests.ApacheShiroIniConfiguration.class,
|
||||
ApacheShiroRealmGeodeSecurityIntegrationTests.ApacheShiroRealmConfiguration.class,
|
||||
ApacheGeodeSecurityManagerGeodeSecurityIntegrationTests.ApacheGeodeSecurityManagerConfiguration.class
|
||||
})
|
||||
@Profile("apache-geode-server")
|
||||
public static class GeodeServerConfiguration {
|
||||
|
||||
public static void main(String[] args) {
|
||||
runSpringApplication(GeodeServerConfiguration.class, args);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private GemFireCache gemfireCache;
|
||||
|
||||
@Bean("Echo")
|
||||
LocalRegionFactoryBean<String, String> echoRegion(GemFireCache gemfireCache) {
|
||||
LocalRegionFactoryBean<String, String> echoRegion = new LocalRegionFactoryBean<>();
|
||||
|
||||
echoRegion.setCache(gemfireCache);
|
||||
echoRegion.setCacheLoader(echoCacheLoader());
|
||||
echoRegion.setClose(false);
|
||||
echoRegion.setPersistent(false);
|
||||
|
||||
return echoRegion;
|
||||
}
|
||||
|
||||
CacheLoader<String, String> echoCacheLoader() {
|
||||
return new CacheLoader<String, String>() {
|
||||
@Override
|
||||
public String load(LoaderHelper<String, String> helper) throws CacheLoaderException {
|
||||
return helper.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void postProcess() {
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info("Geode Distributed System Properties [{}]",
|
||||
CollectionUtils.toString(gemfireCache.getDistributedSystem().getProperties()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
@EqualsAndHashCode(of = { "name", "credentials" })
|
||||
@RequiredArgsConstructor(staticName = "newUser")
|
||||
@SuppressWarnings("all")
|
||||
public static class User implements Iterable<Role>, Principal, Serializable {
|
||||
|
||||
private Set<Role> roles = new HashSet<>();
|
||||
|
||||
@NonNull
|
||||
private String name;
|
||||
private String credentials;
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public boolean hasPermission(ResourcePermission permission) {
|
||||
for (Role role : this) {
|
||||
if (role.hasPermission(permission)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public boolean hasRole(Role role) {
|
||||
return this.roles.contains(role);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public Iterator<Role> iterator() {
|
||||
return Collections.unmodifiableSet(this.roles).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public User with(String credentials) {
|
||||
this.credentials = credentials;
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public User with(Role... roles) {
|
||||
Collections.addAll(this.roles, roles);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
@EqualsAndHashCode(of = "name")
|
||||
@RequiredArgsConstructor(staticName = "newRole")
|
||||
@SuppressWarnings("unsed")
|
||||
public static class Role implements Iterable<ResourcePermission>, Serializable {
|
||||
|
||||
@NonNull
|
||||
private String name;
|
||||
|
||||
private Set<ResourcePermission> permissions = new HashSet<>();
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public boolean hasPermission(ResourcePermission permission) {
|
||||
for (ResourcePermission thisPermission : this) {
|
||||
if (thisPermission.implies(permission)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public Iterator<ResourcePermission> iterator() {
|
||||
return Collections.unmodifiableSet(this.permissions).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public Role with(ResourcePermission... permissions) {
|
||||
Collections.addAll(this.permissions, permissions);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.config.annotation;
|
||||
|
||||
import static org.apache.geode.security.ResourcePermission.Operation;
|
||||
import static org.apache.geode.security.ResourcePermission.Resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.geode.security.AuthenticationFailedException;
|
||||
import org.apache.geode.security.ResourcePermission;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* Integration tests for Apache Geode Integrated Security using an application-specific, Apache Geode
|
||||
* {@link org.apache.geode.security.SecurityManager}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.config.annotation.AbstractGeodeSecurityIntegrationTests
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = AbstractGeodeSecurityIntegrationTests.GeodeClientConfiguration.class)
|
||||
@ActiveProfiles("apache-geode-client")
|
||||
public class ApacheGeodeSecurityManagerGeodeSecurityIntegrationTests extends AbstractGeodeSecurityIntegrationTests {
|
||||
|
||||
protected static final String GEODE_SECURITY_MANAGER_PROPERTY_CONFIGURATION_PROFILE =
|
||||
"geode-security-manager-property-configuration";
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() throws IOException {
|
||||
runGeodeServer(GEODE_SECURITY_MANAGER_PROPERTY_CONFIGURATION_PROFILE);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableSecurity(securityManagerClassName = "org.springframework.data.gemfire.config.annotation.ApacheGeodeSecurityManagerGeodeSecurityIntegrationTests$TestGeodeSecurityManager")
|
||||
@Profile(GEODE_SECURITY_MANAGER_PROPERTY_CONFIGURATION_PROFILE)
|
||||
public static class ApacheGeodeSecurityManagerConfiguration {
|
||||
}
|
||||
|
||||
protected interface GeodeSecurityRepository {
|
||||
|
||||
ResourcePermission CLUSTER_MANAGE = new ResourcePermission(Resource.CLUSTER, Operation.MANAGE);
|
||||
ResourcePermission CLUSTER_READ = new ResourcePermission(Resource.CLUSTER, Operation.READ);
|
||||
ResourcePermission CLUSTER_WRITE = new ResourcePermission(Resource.CLUSTER, Operation.WRITE);
|
||||
|
||||
ResourcePermission DATA_MANAGE = new ResourcePermission(Resource.DATA, Operation.MANAGE);
|
||||
ResourcePermission DATA_READ = new ResourcePermission(Resource.DATA, Operation.READ);
|
||||
ResourcePermission DATA_WRITE = new ResourcePermission(Resource.DATA, Operation.WRITE);
|
||||
|
||||
Role ADMIN = Role.newRole("ADMIN").with(CLUSTER_MANAGE, CLUSTER_READ, CLUSTER_WRITE);
|
||||
Role DBA = Role.newRole("DBA").with(DATA_MANAGE, DATA_READ, DATA_WRITE);
|
||||
Role DATA_SCIENTIST = Role.newRole("DATA_SCIENTIST").with(DATA_READ, DATA_WRITE);
|
||||
Role DATA_ANALYST = Role.newRole("DATA_ANALYST").with(DATA_READ);
|
||||
Role GUEST = Role.newRole("GUEST");
|
||||
|
||||
User root = User.newUser("root").with("s3cr3t!").with(ADMIN, DBA);
|
||||
User scientist = User.newUser("scientist").with("w0rk!ng4u").with(DATA_SCIENTIST);
|
||||
User analyst = User.newUser("analyst").with("p@55w0rd").with(DATA_ANALYST);
|
||||
User guest = User.newUser("guest").with("guest").with(GUEST);
|
||||
|
||||
Set<User> users = new HashSet<>(Arrays.asList(root, scientist, analyst, guest));
|
||||
|
||||
default User findBy(String username) {
|
||||
return users.stream().filter((user) -> user.getName().equals(username)).findFirst().get();
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class GeodeSecurityManagerSupport implements org.apache.geode.security.SecurityManager {
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void init(Properties securityProps) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestGeodeSecurityManager extends GeodeSecurityManagerSupport {
|
||||
|
||||
private final GeodeSecurityRepository securityRepository;
|
||||
|
||||
public TestGeodeSecurityManager() {
|
||||
this.securityRepository = new GeodeSecurityRepository() { };
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public Object authenticate(Properties credentials) throws AuthenticationFailedException {
|
||||
String username = credentials.getProperty(SECURITY_USERNAME_PROPERTY);
|
||||
String password = credentials.getProperty(SECURITY_PASSWORD_PROPERTY);
|
||||
|
||||
User user = securityRepository.findBy(username);
|
||||
|
||||
if (user == null || !user.getCredentials().equals(password)) {
|
||||
throw new AuthenticationFailedException(String.format("Failed to authenticate user [%s]", username));
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean authorize(Object principal, ResourcePermission permission) {
|
||||
return (principal instanceof User && ((User) principal).hasPermission(permission));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.config.annotation;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* Integration tests for Apache Geode Integrated Security using an Apache Shiro INI security configuration resource.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.config.annotation.AbstractGeodeSecurityIntegrationTests
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = AbstractGeodeSecurityIntegrationTests.GeodeClientConfiguration.class)
|
||||
@ActiveProfiles("apache-geode-client")
|
||||
public class ApacheShiroIniGeodeSecurityIntegrationTests extends AbstractGeodeSecurityIntegrationTests {
|
||||
|
||||
protected static final String SHIRO_INI_CONFIGURATION_PROFILE = "shiro-ini-configuration";
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() throws IOException {
|
||||
runGeodeServer(SHIRO_INI_CONFIGURATION_PROFILE);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableSecurity(shiroIniResourcePath = "shiro.ini")
|
||||
@Profile(SHIRO_INI_CONFIGURATION_PROFILE)
|
||||
public static class ApacheShiroIniConfiguration {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.config.annotation;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.geode.internal.security.shiro.GeodePermissionResolver;
|
||||
import org.apache.shiro.realm.text.PropertiesRealm;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* Integration tests for Apache Geode Integrated Security using an Apache Shiro Realm security configuration
|
||||
* as a Spring managed bean in a Spring {@link org.springframework.context.ApplicationContext}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.config.annotation.AbstractGeodeSecurityIntegrationTests
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = AbstractGeodeSecurityIntegrationTests.GeodeClientConfiguration.class)
|
||||
@ActiveProfiles("apache-geode-client")
|
||||
public class ApacheShiroRealmGeodeSecurityIntegrationTests extends AbstractGeodeSecurityIntegrationTests {
|
||||
|
||||
protected static final String SHIRO_REALM_CONFIGURATION_PROFILE = "shiro-realm-configuration";
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() throws IOException {
|
||||
runGeodeServer(SHIRO_REALM_CONFIGURATION_PROFILE);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableSecurity
|
||||
@Profile(SHIRO_REALM_CONFIGURATION_PROFILE)
|
||||
@SuppressWarnings("unused")
|
||||
public static class ApacheShiroRealmConfiguration {
|
||||
|
||||
@Bean
|
||||
public PropertiesRealm shiroRealm() {
|
||||
PropertiesRealm propertiesRealm = new PropertiesRealm();
|
||||
propertiesRealm.setResourcePath("classpath:shiro.properties");
|
||||
propertiesRealm.setPermissionResolver(new GeodePermissionResolver());
|
||||
return propertiesRealm;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,17 +17,7 @@
|
||||
|
||||
package org.springframework.data.gemfire.config.annotation;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@@ -43,18 +33,13 @@ 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.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.gemfire.PartitionedRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.process.ProcessExecutor;
|
||||
import org.springframework.data.gemfire.process.ProcessWrapper;
|
||||
import org.springframework.data.gemfire.test.support.FileSystemUtils;
|
||||
import org.springframework.data.gemfire.test.support.IOUtils;
|
||||
import org.springframework.data.gemfire.test.support.ThreadUtils;
|
||||
import org.springframework.data.gemfire.test.support.ClientServerIntegrationTestsSupport;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* Test suite of test cases testing the contract and functionality of the {@link CacheServerApplication}
|
||||
@@ -66,15 +51,15 @@ import org.springframework.util.Assert;
|
||||
* @see org.springframework.data.gemfire.config.annotation.CacheServerApplication
|
||||
* @see org.springframework.data.gemfire.config.annotation.ClientCacheApplication
|
||||
* @see org.springframework.test.context.ContextConfiguration
|
||||
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
|
||||
* @see org.springframework.test.context.junit4.SpringRunner
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
* @see org.apache.geode.cache.server.CacheServer
|
||||
* @since 1.9.0
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = ClientServerCacheApplicationIntegrationTests.ClientCacheApplicationConfiguration.class)
|
||||
@SuppressWarnings("all")
|
||||
public class ClientServerCacheApplicationIntegrationTests {
|
||||
public class ClientServerCacheApplicationIntegrationTests extends ClientServerIntegrationTestsSupport {
|
||||
|
||||
private static final int PORT = 12480;
|
||||
|
||||
@@ -82,56 +67,15 @@ public class ClientServerCacheApplicationIntegrationTests {
|
||||
|
||||
@BeforeClass
|
||||
public static void setupGemFireServer() throws Exception {
|
||||
String serverName = ClientServerCacheApplicationIntegrationTests.class.getSimpleName();
|
||||
gemfireServerProcess = run(CacheServerApplicationConfiguration.class, String.format("-Dgemfire.name=%1$s",
|
||||
asApplicationName(ClientServerCacheApplicationIntegrationTests.class)));
|
||||
|
||||
File serverWorkingDirectory = new File(FileSystemUtils.WORKING_DIRECTORY, serverName.toLowerCase());
|
||||
|
||||
Assert.isTrue(serverWorkingDirectory.isDirectory() || serverWorkingDirectory.mkdirs());
|
||||
|
||||
List<String> arguments = new ArrayList<String>();
|
||||
|
||||
arguments.add(String.format("-Dgemfire.name=%1$s", serverName));
|
||||
|
||||
gemfireServerProcess = ProcessExecutor.launch(serverWorkingDirectory,
|
||||
CacheServerApplicationConfiguration.class,
|
||||
arguments.toArray(new String[arguments.size()]));
|
||||
|
||||
waitForServerStart(TimeUnit.SECONDS.toMillis(20));
|
||||
|
||||
System.out.println("GemFire Cache Server Process for Client/Server cache application should be running...");
|
||||
}
|
||||
|
||||
private static void waitForServerStart(final long milliseconds) {
|
||||
ThreadUtils.timedWait(milliseconds, TimeUnit.SECONDS.toMillis(1), new ThreadUtils.WaitCondition() {
|
||||
AtomicBoolean connected = new AtomicBoolean(false);
|
||||
|
||||
public boolean waiting() {
|
||||
Socket socket = null;
|
||||
|
||||
try {
|
||||
if (!connected.get()) {
|
||||
socket = new Socket("localhost", PORT);
|
||||
connected.set(true);
|
||||
}
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
}
|
||||
finally {
|
||||
IOUtils.close(socket);
|
||||
}
|
||||
|
||||
return !connected.get();
|
||||
}
|
||||
});
|
||||
waitForServerToStart("localhost", PORT);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownGemFireServer() {
|
||||
gemfireServerProcess.shutdown();
|
||||
|
||||
if (Boolean.valueOf(System.getProperty("spring.data.gemfire.fork.clean", Boolean.TRUE.toString()))) {
|
||||
org.springframework.util.FileSystemUtils.deleteRecursively(gemfireServerProcess.getWorkingDirectory());
|
||||
}
|
||||
stop(gemfireServerProcess);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@@ -141,22 +85,19 @@ public class ClientServerCacheApplicationIntegrationTests {
|
||||
private Region<String, String> echo;
|
||||
|
||||
@Test
|
||||
public void clientEchoProxyRegionEchoesKeysForValues() {
|
||||
assertThat(echo.get("Hello"), is(equalTo("Hello")));
|
||||
assertThat(echo.get("Test"), is(equalTo("Test")));
|
||||
public void echoClientProxyRegionEchoesKeysForValues() {
|
||||
assertThat(echo.get("Hello")).isEqualTo("Hello");
|
||||
assertThat(echo.get("Test")).isEqualTo("Test");
|
||||
}
|
||||
|
||||
@CacheServerApplication(name = "ClientServerCacheApplicationIntegrationTests", logLevel = "warn", port = 12480)
|
||||
@CacheServerApplication(name = "ClientServerCacheApplicationIntegrationTests", logLevel = "warn", port = PORT)
|
||||
public static class CacheServerApplicationConfiguration {
|
||||
|
||||
public static void main(String[] args) {
|
||||
AnnotationConfigApplicationContext applicationContext =
|
||||
new AnnotationConfigApplicationContext(CacheServerApplicationConfiguration.class);
|
||||
|
||||
applicationContext.registerShutdownHook();
|
||||
runSpringApplication(CacheServerApplicationConfiguration.class, args);
|
||||
}
|
||||
|
||||
@Bean(name = "Echo")
|
||||
@Bean("Echo")
|
||||
public PartitionedRegionFactoryBean<String, String> echoRegion(Cache gemfireCache) {
|
||||
PartitionedRegionFactoryBean<String, String> echoRegion =
|
||||
new PartitionedRegionFactoryBean<String, String>();
|
||||
@@ -183,7 +124,7 @@ public class ClientServerCacheApplicationIntegrationTests {
|
||||
}
|
||||
}
|
||||
|
||||
@ClientCacheApplication(logLevel = "warn", servers = { @ClientCacheApplication.Server(port = 12480)})
|
||||
@ClientCacheApplication(logLevel = "warn", servers = { @ClientCacheApplication.Server(port = PORT)})
|
||||
static class ClientCacheApplicationConfiguration {
|
||||
|
||||
@Bean(name = "Echo")
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
|
||||
package org.springframework.data.gemfire.config.annotation;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@@ -33,21 +31,20 @@ import org.junit.runner.RunWith;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.gemfire.PartitionedRegionFactoryBean;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* Test suite of test case testing the contract and functionality of the {@link PeerCacheApplication} SDG annotation
|
||||
* for configuring and bootstrapping a GemFire peer cache instance.
|
||||
* Integration tests for {@link PeerCacheApplication} SDG annotation.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.springframework.data.gemfire.config.annotation.PeerCacheApplication
|
||||
* @see org.springframework.test.context.ContextConfiguration
|
||||
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
|
||||
* @see org.springframework.test.context.junit4.SpringRunner
|
||||
* @see org.apache.geode.cache.Cache
|
||||
* @since 1.9.0
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = PeerCacheApplicationIntegrationTests.PeerCacheApplicationConfiguration.class)
|
||||
@SuppressWarnings("all")
|
||||
public class PeerCacheApplicationIntegrationTests {
|
||||
@@ -57,8 +54,8 @@ public class PeerCacheApplicationIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void echoPartitionRegionEchoesKeysAsValues() {
|
||||
assertThat(echo.get("Hello"), is(equalTo("Hello")));
|
||||
assertThat(echo.get("Test"), is(equalTo("Test")));
|
||||
assertThat(echo.get("Hello")).isEqualTo("Hello");
|
||||
assertThat(echo.get("Test")).isEqualTo("Test");
|
||||
}
|
||||
|
||||
//@EnableLocator
|
||||
@@ -67,7 +64,7 @@ public class PeerCacheApplicationIntegrationTests {
|
||||
@PeerCacheApplication(name = "PeerCacheApplicationIntegrationTests", logLevel="warn")
|
||||
static class PeerCacheApplicationConfiguration {
|
||||
|
||||
@Bean(name = "Echo")
|
||||
@Bean("Echo")
|
||||
PartitionedRegionFactoryBean<String, String> echoRegion(Cache gemfireCache) {
|
||||
PartitionedRegionFactoryBean<String, String> echoRegion =
|
||||
new PartitionedRegionFactoryBean<String, String>();
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.data.gemfire.test.support.FileSystemUtils;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -31,6 +32,7 @@ import org.springframework.util.StringUtils;
|
||||
* The {@link ProcessExecutor} class is a utility class for launching and running Java processes.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.io.File
|
||||
* @see java.lang.Process
|
||||
* @see java.lang.ProcessBuilder
|
||||
* @see java.lang.System
|
||||
@@ -67,11 +69,7 @@ public abstract class ProcessExecutor {
|
||||
|
||||
ProcessWrapper processWrapper = new ProcessWrapper(process, ProcessConfiguration.create(processBuilder));
|
||||
|
||||
processWrapper.register(new ProcessInputStreamListener() {
|
||||
@Override public void onInput(final String input) {
|
||||
System.err.printf("[FORK-OUT] - %s%n", input);
|
||||
}
|
||||
});
|
||||
processWrapper.register((input) -> System.err.printf("[FORK] - %s%n", input));
|
||||
|
||||
return processWrapper;
|
||||
}
|
||||
@@ -79,7 +77,7 @@ public abstract class ProcessExecutor {
|
||||
protected static String[] buildCommand(String classpath, Class<?> type, String... args) {
|
||||
Assert.notNull(type, "The main Java class to launch must not be null");
|
||||
|
||||
List<String> command = new ArrayList<String>();
|
||||
List<String> command = new ArrayList<>();
|
||||
List<String> programArgs = Collections.emptyList();
|
||||
|
||||
command.add(JAVA_EXE.getAbsolutePath());
|
||||
@@ -90,7 +88,7 @@ public abstract class ProcessExecutor {
|
||||
command.addAll(getSpringGemFireSystemProperties());
|
||||
|
||||
if (args != null) {
|
||||
programArgs = new ArrayList<String>(args.length);
|
||||
programArgs = new ArrayList<>(args.length);
|
||||
|
||||
for (String arg : args) {
|
||||
if (isJvmOption(arg)) {
|
||||
@@ -109,15 +107,10 @@ public abstract class ProcessExecutor {
|
||||
}
|
||||
|
||||
protected static Collection<? extends String> getSpringGemFireSystemProperties() {
|
||||
List<String> springGemfireSystemProperties = new ArrayList<String>();
|
||||
|
||||
for (String property : System.getProperties().stringPropertyNames()) {
|
||||
if (property.startsWith(SPRING_GEMFIRE_SYSTEM_PROPERTY_PREFIX)) {
|
||||
springGemfireSystemProperties.add(String.format("-D%1$s=%2$s", property, System.getProperty(property)));
|
||||
}
|
||||
}
|
||||
|
||||
return springGemfireSystemProperties;
|
||||
return System.getProperties().stringPropertyNames().stream()
|
||||
.filter(property -> property.startsWith(SPRING_GEMFIRE_SYSTEM_PROPERTY_PREFIX))
|
||||
.map(property -> String.format("-D%1$s=%2$s", property, System.getProperty(property)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected static boolean isJvmOption(String option) {
|
||||
|
||||
@@ -18,14 +18,12 @@ package org.springframework.data.gemfire.process;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -61,18 +59,20 @@ public class ProcessWrapper {
|
||||
|
||||
protected static final long DEFAULT_WAIT_TIME_MILLISECONDS = TimeUnit.SECONDS.toMillis(15);
|
||||
|
||||
private final List<ProcessInputStreamListener> listeners = new CopyOnWriteArrayList<ProcessInputStreamListener>();
|
||||
private final List<ProcessInputStreamListener> listeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
protected final Logger log = Logger.getLogger(getClass().getName());
|
||||
|
||||
private final Process process;
|
||||
private final ProcessConfiguration processConfiguration;
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public ProcessWrapper(Process process, ProcessConfiguration processConfiguration) {
|
||||
Assert.notNull(process, "Process must not be null");
|
||||
|
||||
Assert.notNull(processConfiguration, "The context and configuration meta-data providing details about"
|
||||
+ " the environment in which the process is running and how the process was configured must not be null");
|
||||
Assert.notNull(processConfiguration, "The context and configuration meta-data providing details"
|
||||
+ " about the environment in which the process is running and how the process was configured and executed"
|
||||
+ " must not be null");
|
||||
|
||||
this.process = process;
|
||||
this.processConfiguration = processConfiguration;
|
||||
@@ -80,39 +80,42 @@ public class ProcessWrapper {
|
||||
init();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private void init() {
|
||||
newThread("Process OUT Stream Reader", newProcessInputStreamReader(process.getInputStream())).start();
|
||||
newThread("Process OUT Stream Reader Thread",
|
||||
newProcessInputStreamReaderRunnable(process.getInputStream())).start();
|
||||
|
||||
if (!isRedirectingErrorStream()) {
|
||||
newThread("Process ERR Stream Reader", newProcessInputStreamReader(process.getErrorStream())).start();
|
||||
newThread("Process ERR Stream Reader Thread",
|
||||
newProcessInputStreamReaderRunnable(process.getErrorStream())).start();
|
||||
}
|
||||
}
|
||||
|
||||
protected Runnable newProcessInputStreamReader(final InputStream in) {
|
||||
return new Runnable() {
|
||||
@Override public void run() {
|
||||
if (isRunning()) {
|
||||
BufferedReader inputReader = new BufferedReader(new InputStreamReader(in));
|
||||
/* (non-Javadoc) */
|
||||
protected Runnable newProcessInputStreamReaderRunnable(InputStream in) {
|
||||
return () -> {
|
||||
if (isRunning()) {
|
||||
BufferedReader inputReader = new BufferedReader(new InputStreamReader(in));
|
||||
|
||||
try {
|
||||
for (String input = inputReader.readLine(); input != null; input = inputReader.readLine()) {
|
||||
for (ProcessInputStreamListener listener : listeners) {
|
||||
listener.onInput(input);
|
||||
}
|
||||
try {
|
||||
for (String input = inputReader.readLine(); input != null; input = inputReader.readLine()) {
|
||||
for (ProcessInputStreamListener listener : listeners) {
|
||||
listener.onInput(input);
|
||||
}
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
// Ignore IO error and just stop reading from the process input stream
|
||||
// An IO error occurred most likely because the process was terminated
|
||||
}
|
||||
finally {
|
||||
IOUtils.close(inputReader);
|
||||
}
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
// Ignore IO error and just stop reading from the process input stream
|
||||
// An IO error occurred most likely because the process was terminated
|
||||
}
|
||||
finally {
|
||||
IOUtils.close(inputReader);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected Thread newThread(String name, Runnable task) {
|
||||
Assert.hasText(name, "Thread name must be specified");
|
||||
Assert.notNull(task, "Thread task must not be null");
|
||||
@@ -125,22 +128,37 @@ public class ProcessWrapper {
|
||||
return thread;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public boolean isAlive() {
|
||||
return ProcessUtils.isAlive(process);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public boolean isNotAlive() {
|
||||
return !isAlive();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public List<String> getCommand() {
|
||||
return processConfiguration.getCommand();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public String getCommandString() {
|
||||
return processConfiguration.getCommandString();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public Map<String, String> getEnvironment() {
|
||||
return processConfiguration.getEnvironment();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public int getPid() {
|
||||
return ProcessUtils.findAndReadPid(getWorkingDirectory());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public int safeGetPid() {
|
||||
try {
|
||||
return getPid();
|
||||
@@ -150,22 +168,32 @@ public class ProcessWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public boolean isRedirectingErrorStream() {
|
||||
return processConfiguration.isRedirectingErrorStream();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public boolean isNotRunning() {
|
||||
return !isRunning();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public boolean isRunning() {
|
||||
return ProcessUtils.isRunning(process);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public File getWorkingDirectory() {
|
||||
return processConfiguration.getWorkingDirectory();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public int exitValue() {
|
||||
return process.exitValue();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public int safeExitValue() {
|
||||
try {
|
||||
return exitValue();
|
||||
@@ -175,38 +203,37 @@ public class ProcessWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public String readLogFile() throws IOException {
|
||||
File[] logFiles = FileSystemUtils.listFiles(getWorkingDirectory(), new FileFilter() {
|
||||
@Override public boolean accept(File pathname) {
|
||||
return (pathname != null && (pathname.isDirectory() || pathname.getAbsolutePath().endsWith(".log")));
|
||||
}
|
||||
});
|
||||
File[] logFiles = FileSystemUtils.listFiles(getWorkingDirectory(),
|
||||
(path) -> (path != null && (path.isDirectory() || path.getAbsolutePath().endsWith(".log"))));
|
||||
|
||||
if (logFiles.length > 0) {
|
||||
return readLogFile(logFiles[0]);
|
||||
}
|
||||
else {
|
||||
throw new FileNotFoundException(String.format(
|
||||
"No log files found in process's working directory [%s]", getWorkingDirectory()));
|
||||
"No log files found in process's [%d] working directory [%s]",
|
||||
safeGetPid(), getWorkingDirectory()));
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public String readLogFile(File log) throws IOException {
|
||||
return FileUtils.read(log);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public boolean register(ProcessInputStreamListener listener) {
|
||||
return (listener != null && listeners.add(listener));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public void registerShutdownHook() {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
|
||||
@Override public void run() {
|
||||
shutdown();
|
||||
}
|
||||
}));
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public void signalStop() {
|
||||
try {
|
||||
ProcessUtils.signalStop(process);
|
||||
@@ -220,28 +247,28 @@ public class ProcessWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public int stop() {
|
||||
return stop(DEFAULT_WAIT_TIME_MILLISECONDS);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public int stop(long milliseconds) {
|
||||
if (isRunning()) {
|
||||
boolean interrupted = false;
|
||||
int exitValue = -1;
|
||||
final int pid = safeGetPid();
|
||||
final long timeout = (System.currentTimeMillis() + milliseconds);
|
||||
final AtomicBoolean exited = new AtomicBoolean(false);
|
||||
int pid = safeGetPid();
|
||||
long timeout = (System.currentTimeMillis() + milliseconds);
|
||||
AtomicBoolean exited = new AtomicBoolean(false);
|
||||
|
||||
ExecutorService executorService = Executors.newSingleThreadExecutor();
|
||||
|
||||
try {
|
||||
Future<Integer> futureExitValue = executorService.submit(new Callable<Integer>() {
|
||||
@Override public Integer call() throws Exception {
|
||||
process.destroy();
|
||||
int exitValue = process.waitFor();
|
||||
exited.set(true);
|
||||
return exitValue;
|
||||
}
|
||||
Future<Integer> futureExitValue = executorService.submit(() -> {
|
||||
process.destroy();
|
||||
int localExitValue = process.waitFor();
|
||||
exited.set(true);
|
||||
return localExitValue;
|
||||
});
|
||||
|
||||
while (!exited.get() && System.currentTimeMillis() < timeout) {
|
||||
@@ -277,6 +304,7 @@ public class ProcessWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public int shutdown() {
|
||||
if (isRunning()) {
|
||||
log.info(String.format("Stopping process [%d]...%n", safeGetPid()));
|
||||
@@ -287,19 +315,18 @@ public class ProcessWrapper {
|
||||
return stop();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public boolean unregister(ProcessInputStreamListener listener) {
|
||||
return listeners.remove(listener);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public void waitFor() {
|
||||
waitFor(DEFAULT_WAIT_TIME_MILLISECONDS);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public void waitFor(long milliseconds) {
|
||||
ThreadUtils.timedWait(milliseconds, 500, new ThreadUtils.WaitCondition() {
|
||||
@Override public boolean waiting() {
|
||||
return isRunning();
|
||||
}
|
||||
});
|
||||
ThreadUtils.timedWait(milliseconds, 500, this::isRunning);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ public abstract class ProcessUtils {
|
||||
|
||||
protected static final String TERM_TOKEN = "<TERM/>";
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static int currentPid() {
|
||||
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
|
||||
String runtimeMXBeanName = runtimeMXBean.getName();
|
||||
@@ -77,6 +78,12 @@ public abstract class ProcessUtils {
|
||||
cause);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean isAlive(Process process) {
|
||||
return (process != null && process.isAlive());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean isRunning(int processId) {
|
||||
/*
|
||||
for (VirtualMachineDescriptor vmDescriptor : VirtualMachine.list()) {
|
||||
@@ -87,9 +94,11 @@ public abstract class ProcessUtils {
|
||||
|
||||
return false;
|
||||
*/
|
||||
|
||||
throw new UnsupportedOperationException("operation not supported");
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean isRunning(Process process) {
|
||||
try {
|
||||
process.exitValue();
|
||||
@@ -100,6 +109,7 @@ public abstract class ProcessUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static void signalStop(Process process) throws IOException {
|
||||
if (isRunning(process)) {
|
||||
OutputStream processOutputStream = process.getOutputStream();
|
||||
@@ -108,12 +118,14 @@ public abstract class ProcessUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("all")
|
||||
public static void waitForStopSignal() {
|
||||
Scanner in = new Scanner(System.in);
|
||||
while (!TERM_TOKEN.equals(in.next()));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static int findAndReadPid(File workingDirectory) {
|
||||
File pidFile = findPidFile(workingDirectory);
|
||||
|
||||
@@ -126,6 +138,7 @@ public abstract class ProcessUtils {
|
||||
return readPid(pidFile);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("all")
|
||||
protected static File findPidFile(File workingDirectory) {
|
||||
Assert.isTrue(FileSystemUtils.isDirectory(workingDirectory), String.format(
|
||||
@@ -144,6 +157,7 @@ public abstract class ProcessUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("all")
|
||||
public static int readPid(File pidFile) {
|
||||
Assert.isTrue(pidFile != null && pidFile.isFile(), String.format(
|
||||
@@ -159,10 +173,10 @@ public abstract class ProcessUtils {
|
||||
return Integer.parseInt(pidValue);
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
throw new PidUnavailableException(String.format("PID file [%1$s] not found", pidFile), e);
|
||||
throw new PidUnavailableException(String.format("PID file [%s] not found", pidFile), e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new PidUnavailableException(String.format("failed to read PID from file [%1$s]", pidFile), e);
|
||||
throw new PidUnavailableException(String.format("failed to read PID from file [%s]", pidFile), e);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
throw new PidUnavailableException(String.format(
|
||||
@@ -173,6 +187,7 @@ public abstract class ProcessUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("all")
|
||||
public static void writePid(File pidFile, int pid) throws IOException {
|
||||
Assert.isTrue(pidFile != null && (pidFile.isFile() || pidFile.createNewFile()), String.format(
|
||||
@@ -191,20 +206,28 @@ public abstract class ProcessUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static class DirectoryPidFileFilter extends PidFileFilter {
|
||||
|
||||
protected static final DirectoryPidFileFilter INSTANCE = new DirectoryPidFileFilter();
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean accept(File path) {
|
||||
return (path != null && (path.isDirectory() || super.accept(path)));
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static class PidFileFilter implements FileFilter {
|
||||
|
||||
protected static final PidFileFilter INSTANCE = new PidFileFilter();
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean accept(File path) {
|
||||
return (path != null && path.isFile() && path.getName().toLowerCase().endsWith(".pid"));
|
||||
|
||||
@@ -18,21 +18,32 @@
|
||||
package org.springframework.data.gemfire.test.support;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.asArray;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.apache.geode.cache.server.CacheServer;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.data.gemfire.process.ProcessExecutor;
|
||||
import org.springframework.data.gemfire.process.ProcessWrapper;
|
||||
import org.springframework.data.gemfire.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* The {@link ClientServerIntegrationTestsSupport} class is a abstract base class encapsulating common functionality
|
||||
* to support the implementation of GemFire client/server tests.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.io.File
|
||||
* @see org.apache.geode.cache.server.CacheServer
|
||||
* @see org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
* @see org.springframework.data.gemfire.process.ProcessExecutor
|
||||
* @see org.springframework.data.gemfire.process.ProcessWrapper
|
||||
* @see org.springframework.data.gemfire.test.support.SocketUtils
|
||||
* @see org.springframework.data.gemfire.test.support.ThreadUtils
|
||||
* @since 1.9.0
|
||||
@@ -41,37 +52,164 @@ import org.apache.geode.cache.server.CacheServer;
|
||||
public class ClientServerIntegrationTestsSupport {
|
||||
|
||||
protected static final long DEFAULT_WAIT_DURATION = TimeUnit.SECONDS.toMillis(20);
|
||||
protected static final long DEFAULT_WAIT_INTERVAL = 500L;
|
||||
protected static final long DEFAULT_WAIT_INTERVAL = 500L; // milliseconds
|
||||
|
||||
protected static final String DEFAULT_GEMFIRE_LOG_FILE = "gemfire-server.log";
|
||||
protected static final String DEFAULT_GEMFIRE_LOG_LEVEL = "config";
|
||||
protected static final String DIRECTORY_DELETE_ON_EXIT_PROPERTY = "sdg.directory.delete-on-exit";
|
||||
protected static final String GEMFIRE_LOG_FILE_PROPERTY = "gemfire.log.file";
|
||||
protected static final String GEMFIRE_LOG_LEVEL_PROPERTY = "gemfire.log.level";
|
||||
protected static final String PROCESS_RUN_MANUAL_PROPERTY = "sdg.process.run.manual";
|
||||
protected static final String SYSTEM_PROPERTIES_LOG_FILE = "system-properties.log";
|
||||
protected static final String TEST_GEMFIRE_LOG_LEVEL = "warning";
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String asApplicationName(Class<?> type) {
|
||||
return type.getSimpleName();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String asDirectoryName(Class<?> type) {
|
||||
return String.format("%1$s-%2$s", asApplicationName(type),
|
||||
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-hh-mm-ss")));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static File createDirectory(String pathname) {
|
||||
File directory = new File(pathname);
|
||||
return createDirectory(new File(pathname));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static File createDirectory(File directory) {
|
||||
assertThat(directory.isDirectory() || directory.mkdirs())
|
||||
.as(String.format("Failed to create directory [%s]", directory)).isTrue();
|
||||
|
||||
directory.deleteOnExit();
|
||||
if (isDeleteDirectoryOnExit()) {
|
||||
directory.deleteOnExit();
|
||||
}
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean isDeleteDirectoryOnExit() {
|
||||
return Boolean.valueOf(System.getProperty(DIRECTORY_DELETE_ON_EXIT_PROPERTY, Boolean.TRUE.toString()));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static int intValue(Number number) {
|
||||
return (number != null ? number.intValue() : 0);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String logFile() {
|
||||
return logFile(DEFAULT_GEMFIRE_LOG_FILE);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String logFile(String defaultLogFilePathname) {
|
||||
return System.getProperty(GEMFIRE_LOG_FILE_PROPERTY, defaultLogFilePathname);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String logLevel() {
|
||||
return logLevel(DEFAULT_GEMFIRE_LOG_LEVEL);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String logLevel(String defaultLogLevel) {
|
||||
return System.getProperty(GEMFIRE_LOG_LEVEL_PROPERTY, defaultLogLevel);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static void logSystemProperties() throws IOException {
|
||||
FileUtils.write(new File(SYSTEM_PROPERTIES_LOG_FILE),
|
||||
String.format("%s", CollectionUtils.toString(System.getProperties())));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static ProcessWrapper run(Class<?> type, String... arguments) throws IOException {
|
||||
return run(createDirectory(asDirectoryName(type)), type, arguments);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static ProcessWrapper run(File workingDirectory, Class<?> type, String... arguments) throws IOException {
|
||||
return (isProcessRunManual() ? null
|
||||
: ProcessExecutor.launch(createDirectory(workingDirectory), type, arguments));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static ProcessWrapper run(String classpath, Class<?> type, String... arguments) throws IOException {
|
||||
return run(createDirectory(asDirectoryName(type)), classpath, type, arguments);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static ProcessWrapper run(File workingDirectory, String classpath, Class<?> type, String... arguments)
|
||||
throws IOException {
|
||||
|
||||
return (isProcessRunManual() ? null
|
||||
: ProcessExecutor.launch(createDirectory(workingDirectory), classpath, type, arguments));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean isProcessRunManual() {
|
||||
return Boolean.getBoolean(PROCESS_RUN_MANUAL_PROPERTY);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static AnnotationConfigApplicationContext runSpringApplication(Class<?> annotatedClass, String... args) {
|
||||
return runSpringApplication(asArray(annotatedClass), args);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static AnnotationConfigApplicationContext runSpringApplication(Class<?>[] annotatedClasses,
|
||||
String... args) {
|
||||
|
||||
AnnotationConfigApplicationContext applicationContext =
|
||||
new AnnotationConfigApplicationContext(annotatedClasses);
|
||||
|
||||
applicationContext.registerShutdownHook();
|
||||
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean stop(ProcessWrapper process) {
|
||||
return stop(process, DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean stop(ProcessWrapper process, long duration) {
|
||||
if (process != null) {
|
||||
process.stop(duration);
|
||||
|
||||
if (process.isNotRunning() && isDeleteDirectoryOnExit()) {
|
||||
FileSystemUtils.deleteRecursive(process.getWorkingDirectory());
|
||||
}
|
||||
|
||||
return process.isRunning();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitForCacheServerToStart(CacheServer cacheServer) {
|
||||
return waitForCacheServerToStart(cacheServer.getBindAddress(), cacheServer.getPort(), DEFAULT_WAIT_DURATION);
|
||||
return waitForServerToStart(cacheServer.getBindAddress(), cacheServer.getPort(), DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitForCacheServerToStart(CacheServer cacheServer, long duration) {
|
||||
return waitForCacheServerToStart(cacheServer.getBindAddress(), cacheServer.getPort(), duration);
|
||||
return waitForServerToStart(cacheServer.getBindAddress(), cacheServer.getPort(), duration);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitForCacheServerToStart(String host, int port) {
|
||||
return waitForCacheServerToStart(host, port, DEFAULT_WAIT_DURATION);
|
||||
protected static boolean waitForServerToStart(String host, int port) {
|
||||
return waitForServerToStart(host, port, DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitForCacheServerToStart(final String host, final int port, long duration) {
|
||||
protected static boolean waitForServerToStart(final String host, final int port, long duration) {
|
||||
return ThreadUtils.timedWait(duration, DEFAULT_WAIT_INTERVAL, new ThreadUtils.WaitCondition() {
|
||||
AtomicBoolean connected = new AtomicBoolean(false);
|
||||
|
||||
|
||||
@@ -32,11 +32,6 @@ import org.junit.Test;
|
||||
*/
|
||||
public class ArrayUtilsUnitTests {
|
||||
|
||||
@SafeVarargs
|
||||
protected static <T> T[] asArray(T... array) {
|
||||
return array;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asArrayRetursEmptyArray() {
|
||||
Object[] array = ArrayUtils.asArray();
|
||||
@@ -65,12 +60,13 @@ public class ArrayUtilsUnitTests {
|
||||
|
||||
@Test
|
||||
public void getFirstWithNonNullArray() {
|
||||
assertThat(ArrayUtils.getFirst(asArray(1, 2, 3))).isEqualTo(1);
|
||||
assertThat(ArrayUtils.getFirst(ArrayUtils.asArray(1, 2, 3))).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirstWithNullOrEmptyArrayAndNoDefaultReturnsNull() {
|
||||
assertThat((Object) ArrayUtils.getFirst(null)).isNull();
|
||||
assertThat((Object) ArrayUtils.getFirst(new Object[0])).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -181,7 +181,7 @@ public class CollectionUtilsUnitTests {
|
||||
assertThat(iterable.iterator().hasNext()).isFalse();
|
||||
}
|
||||
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void nullSafeIterableIterator() {
|
||||
Iterable<Object> iterable = CollectionUtils.nullSafeIterable(null);
|
||||
|
||||
@@ -196,14 +196,12 @@ public class CollectionUtilsUnitTests {
|
||||
iterator.next();
|
||||
}
|
||||
catch (NoSuchElementException ignore) {
|
||||
assertThat(ignore.getMessage()).isEqualTo("No more elements");
|
||||
assertThat(ignore.getCause()).isNull();
|
||||
|
||||
try {
|
||||
iterator.remove();
|
||||
}
|
||||
catch (UnsupportedOperationException expected) {
|
||||
assertThat(expected.getMessage()).isEqualTo("Operation not supported");
|
||||
catch (IllegalStateException expected) {
|
||||
assertThat(expected.getCause()).isNull();
|
||||
|
||||
throw expected;
|
||||
|
||||
42
src/test/resources/shiro.ini
Normal file
42
src/test/resources/shiro.ini
Normal file
@@ -0,0 +1,42 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You 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.
|
||||
|
||||
# Shiro INI configuration
|
||||
|
||||
# Objects and their properties are defined here, such as the securityManager, Realms and anything else needed
|
||||
# to build the SecurityManager
|
||||
[main]
|
||||
|
||||
# The 'users' section is for simple deployments when you only need a small number of statically-defined
|
||||
# set of User accounts.
|
||||
# username = password, roleName1, roleName2, …, roleNameN
|
||||
[users]
|
||||
root = s3cr3t!, ADMIN, DBA
|
||||
scientist = w0rk!ng4u, DATA_SCIENTIST
|
||||
analyst = p@55w0rd, DATA_ANALYST
|
||||
guest = guest, GUEST
|
||||
|
||||
# The 'roles' section is for simple deployments when you only need a small number of statically-defined roles.
|
||||
# rolename = permissionDefinition1, permissionDefinition2, …, permissionDefinitionN
|
||||
[roles]
|
||||
ADMIN = CLUSTER:MANAGE, CLUSTER:READ, CLUSTER:WRITE
|
||||
DBA = DATA:MANAGE, DATA:READ, DATA:WRITE
|
||||
DATA_SCIENTIST = DATA:READ, DATA:WRITE
|
||||
DATA_ANALYST = DATA:READ
|
||||
GUEST = NULL
|
||||
|
||||
# The 'urls' section is used for url-based security in web applications. We'll discuss this section
|
||||
# in the Web documentation
|
||||
[urls]
|
||||
32
src/test/resources/shiro.properties
Normal file
32
src/test/resources/shiro.properties
Normal file
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright 2016 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
#
|
||||
|
||||
# Apache Shiro PropertiesRealm security properties configuration file
|
||||
# used to configure Apache Geode Security
|
||||
|
||||
# Apache Geode System Users and assigned Roles
|
||||
user.root = s3c3rt!, ADMIN, DBA
|
||||
user.scientist = w0rk!ng4u, DATA_SCIENTIST
|
||||
user.analyst = p@55w0rd, DATA_ANALYST
|
||||
user.guest = guest, GUEST
|
||||
|
||||
# Apache Geode Roles and granted (Resource) Permissions
|
||||
role.ADMIN = CLUSTER:MANAGE, CLUSTER:READ, CLUSTER:WRITE
|
||||
role.DBA = DATA:MANAGE, DATA:READ, DATA:WRITE
|
||||
role.DATA_SCIENTIST = DATA:READ, DATA:WRITE
|
||||
role.DATA_ANALYST = DATA:READ
|
||||
role.GUEST = NULL
|
||||
15
template.mf
15
template.mf
@@ -3,13 +3,14 @@ Bundle-Name: Spring Data GemFire
|
||||
Bundle-SymbolicName: org.springframework.data.gemfire
|
||||
Bundle-Vendor: Pivotal Software, Inc.
|
||||
Import-Package: sun.reflect;version="0";resolution:=optional
|
||||
Import-Template: javax.enterprise.*;version="[1.0,2.0)";resolution:=optional,
|
||||
Import-Template: javax.enterprise.*;version="[1.0, 2.0)";resolution:=optional,
|
||||
org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional,
|
||||
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
|
||||
org.apache.geode.*;version="[8.0.0,9.0.0)",
|
||||
org.aspectj.*;version="[1.8.2, 2.0.0)";resolution:=optional,
|
||||
com.fasterxml.jackson.*;version="[2.4.1,3.0.0)";resolution:=optional,
|
||||
org.slf4j.*;version="[1.7.0,2.0)",
|
||||
org.springframework.*;version="[4.0.6, 5.0.0)",
|
||||
org.springframework.data.*;version="[1.9.0,2.0.0)",
|
||||
org.apache.geode.*;version="[0.0.0, 10.0.0)",
|
||||
org.apache.shiro.*;version="[1.3.1, 2.0.0)";resolution:=optional,
|
||||
org.aspectj.*;version="[1.8.9, 2.0.0)";resolution:=optional,
|
||||
com.fasterxml.jackson.*;version="[2.8.4, 3.0.0)";resolution:=optional,
|
||||
org.slf4j.*;version="[1.7.21, 2.0)",
|
||||
org.springframework.*;version="[5.0.0, 6.0.0)",
|
||||
org.springframework.data.*;version="[2.0.0, 3.0.0)",
|
||||
org.w3c.dom.*;version="0"
|
||||
|
||||
Reference in New Issue
Block a user