SGF-559 - Configure Geode Integrated Security with annotations.

This commit is contained in:
John Blum
2016-10-28 14:00:06 -07:00
parent 0f0468e68d
commit 5a33995b42
18 changed files with 542 additions and 59 deletions

View File

@@ -88,8 +88,7 @@ public abstract class AbstractCacheConfiguration implements BeanClassLoaderAware
private static final AtomicBoolean CUSTOM_EDITORS_REGISTERED = new AtomicBoolean(false);
private static final AtomicBoolean DEFINED_INDEXES_APPLICATION_LISTENER_REGISTERED = new AtomicBoolean(false);
private static final AtomicBoolean DISK_STORE_DIRECTORY_BEAN_POST_PROCESSOR_REGISTERED = new AtomicBoolean(false);
private static final AtomicBoolean PDX_DISK_STORE_AWARE_BEAN_FACTORY_POST_PROCESSOR_REGISTERED =
new AtomicBoolean(false);
private static final AtomicBoolean PDX_DISK_STORE_AWARE_BEAN_FACTORY_POST_PROCESSOR_REGISTERED = new AtomicBoolean(false);
protected static final boolean DEFAULT_CLOSE = true;
protected static final boolean DEFAULT_COPY_ON_READ = false;
@@ -270,7 +269,6 @@ public abstract class AbstractCacheConfiguration implements BeanClassLoaderAware
registerCustomEditorBeanFactoryPostProcessor(importMetadata);
registerDefinedIndexesApplicationListener(importMetadata);
registerDiskStoreDirectoryBeanPostProcessor(importMetadata);
registerPdxDiskStoreAwareBeanFactoryPostProcessor(importMetadata);
}
/**
@@ -323,6 +321,8 @@ public abstract class AbstractCacheConfiguration implements BeanClassLoaderAware
setPdxPersistent(Boolean.TRUE.equals(enablePdxAttributes.get("persistent")));
setPdxReadSerialized(Boolean.TRUE.equals(enablePdxAttributes.get("readSerialized")));
setPdxSerializer(resolvePdxSerializer((String) enablePdxAttributes.get("serializerBeanName")));
registerPdxDiskStoreAwareBeanFactoryPostProcessor(importMetadata);
}
}
@@ -383,9 +383,13 @@ public abstract class AbstractCacheConfiguration implements BeanClassLoaderAware
/* (non-Javadoc) */
protected void registerPdxDiskStoreAwareBeanFactoryPostProcessor(AnnotationMetadata importMetadata) {
if (PDX_DISK_STORE_AWARE_BEAN_FACTORY_POST_PROCESSOR_REGISTERED.compareAndSet(false, true)) {
register(BeanDefinitionBuilder.rootBeanDefinition(PdxDiskStoreAwareBeanFactoryPostProcessor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
if (StringUtils.hasText(pdxDiskStoreName())) {
if (PDX_DISK_STORE_AWARE_BEAN_FACTORY_POST_PROCESSOR_REGISTERED.compareAndSet(false, true)) {
register(BeanDefinitionBuilder.rootBeanDefinition(PdxDiskStoreAwareBeanFactoryPostProcessor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
.addConstructorArgValue(pdxDiskStoreName())
.getBeanDefinition());
}
}
}

View File

@@ -0,0 +1,255 @@
/*
* 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.Collections;
import java.util.List;
import javax.annotation.PostConstruct;
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.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
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.context.annotation.ConfigurationCondition;
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.SecurityManager
* @see org.apache.shiro.spring.LifecycleBeanPostProcessor
* @see org.springframework.context.annotation.Condition
* @see org.springframework.context.annotation.Conditional
* @since 1.0.0
*/
@Configuration
@Conditional(ApacheShiroSecurityConfiguration.ApacheShiroPresentCondition.class)
@SuppressWarnings("unused")
public class ApacheShiroSecurityConfiguration {
@Autowired(required = false)
private List<Realm> realms = Collections.emptyList();
@Autowired(required = false)
private org.apache.shiro.mgt.SecurityManager shiroSecurityManager;
/**
* {@link Bean} definition to configure and register an Apache Shiro, Spring {@link LifecycleBeanPostProcessor}
* used to automatically call lifecycle callback methods on Shiro security components during Spring container
* initialization and destruction phases.
*
* 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}.
*
* @return an Apache Shiro, Spring {@link LifecycleBeanPostProcessor} bean.
* @see org.apache.shiro.spring.LifecycleBeanPostProcessor
*/
@Bean
//@Conditional(ShiroRealmsConfigured.class)
public BeanPostProcessor shiroLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* {@link Bean} definition used to 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, thus evaluating any security configuration
* logic internally in Apache Geode that may potentially overwrite the Spring configuration.
*
* @return an Apache Shiro {@link org.apache.shiro.mgt.SecurityManager} implementation used to secure Apache Geode.
* @see org.apache.shiro.mgt.SecurityManager
* @see #isRealmsPresent()
* @see #getRealms()
*/
@Bean
//@Conditional(ShiroRealmsConfigured.class)
public org.apache.shiro.mgt.SecurityManager shiroSecurityManager(GemFireCache gemfireCache) {
return (isRealmsPresent() ? new DefaultSecurityManager(getRealms()) : null);
}
/**
* Post processes the Apache Shiro security components by registering the Apach Shiro
* {@link org.apache.shiro.mgt.SecurityManager} if present with the Apache Shiro security framework
* and proceeds to enable Apache Geode security.
*
* @throws IllegalStateException if an Apache Shiro {@link org.apache.shiro.mgt.SecurityManager} is present
* and Apache Geode security could not be enabled.
* @see #registerSecurityManager(org.apache.shiro.mgt.SecurityManager)
* @see #enableApacheGeodeSecurity()
*/
@PostConstruct
public void postProcess() {
if (this.shiroSecurityManager != null) {
registerSecurityManager(this.shiroSecurityManager);
if (!enableApacheGeodeSecurity()) {
throw new IllegalStateException("Failed to enable security services in Apache Geode");
}
}
}
/**
* 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) {
Field isIntegratedSecurity = ReflectionUtils.findField(securityService.getClass(),
"isIntegratedSecurity", Boolean.TYPE);
if (isIntegratedSecurity != null) {
ReflectionUtils.makeAccessible(isIntegratedSecurity);
ReflectionUtils.setField(isIntegratedSecurity, securityService, true);
return true;
}
}
return false;
}
/**
* Returns the {@link List} of Apache Shiro {@link Realm Realms} configured in
* this Spring {@link org.springframework.context.ApplicationContext}.
*
* @return a {@link List} of configured/declared Apache Shiro {@link Realm Realms}.
* @see org.apache.shiro.realm.Realm
*/
protected List<Realm> getRealms() {
return this.realms;
}
/**
* Determines whether any Apache Shiro {@link Realm Realms} were configured in
* this Spring {@link org.springframework.context.ApplicationContext}.
*
* @return a boolean value indicating whether any Apache Shiro {@link Realm Realms} were declared and configured
* in this Spring {@link org.springframework.context.ApplicationContext}.
* @see #getRealms()
*/
protected boolean isRealmsPresent() {
return !CollectionUtils.isEmpty(getRealms());
}
/**
* 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_PROCESOR_CLASS_NAME =
"org.apache.shiro.spring.LifecycleBeanPostProcessor";
/**
* @inheritDoc
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return ClassUtils.isPresent(APACHE_SHIRO_LIFECYCLE_BEAN_POST_PROCESOR_CLASS_NAME,
context.getClassLoader());
}
}
/**
* A Spring {@link Condition} implementation that determines whether the user declared and configured
* any Apache Shiro {@link Realm Realms}, which are necessary to configure the Apache Shiro security framework
* with security meta-data used to secure Apache Geode.
*
* @see org.springframework.context.annotation.ConfigurationCondition
*/
public static class ShiroRealmsConfigured implements ConfigurationCondition {
/**
* @inheritDoc
*/
@Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN;
}
/**
* @inheritDoc
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
ListableBeanFactory beanFactory = context.getBeanFactory();
ApacheShiroSecurityConfiguration securityConfiguration =
beanFactory.getBean(ApacheShiroSecurityConfiguration.class);
return (securityConfiguration.isRealmsPresent() || !beanFactory.getBeansOfType(Realm.class).isEmpty());
}
}
}

View File

@@ -40,6 +40,18 @@ public class AuthConfiguration extends EmbeddedServiceConfigurationSupport {
public static final String DEFAULT_SECURITY_LOG_LEVEL = "config";
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";
/* (non-Javadoc) */
@Override
protected Class getAnnotationType() {
@@ -51,32 +63,32 @@ public class AuthConfiguration extends EmbeddedServiceConfigurationSupport {
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();

View File

@@ -0,0 +1,111 @@
/*
* 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
* @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 "";
}

View File

@@ -0,0 +1,91 @@
/*
* 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_PEER_AUTH_INIT = "security-peer-auth-init";
protected static final String SECURITY_MANAGER = "security-manager";
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() {
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"));
gemfireProperties.setProperty(SECURITY_PEER_AUTH_INIT,
annotationAttributes.get("peerAuthenticationInitializer"));
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.setPropertyIfNotDefault(SECURITY_POST_PROCESSOR,
annotationAttributes.get("securityPostProcessorClass"), Void.class);
gemfireProperties.setProperty(SECURITY_POST_PROCESSOR,
annotationAttributes.get("securityPostProcessorClassName"));
return gemfireProperties.build();
}
}

View File

@@ -89,7 +89,8 @@ public class PdxDiskStoreAwareBeanFactoryPostProcessor implements BeanFactoryPos
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#getBeanNamesForType(Class)
*/
private void postProcessPdxDiskStoreDependencies(ConfigurableListableBeanFactory beanFactory,
final Class<?>... beanTypes) {
Class<?>... beanTypes) {
for (Class<?> beanType : beanTypes) {
for (String beanName : beanFactory.getBeanNamesForType(beanType)) {
if (!beanName.equalsIgnoreCase(getPdxDiskStoreName())) {

View File

@@ -32,6 +32,7 @@ public abstract class ArrayUtils {
* @param elements variable list of arguments to return as an array.
* @return an arry for the given varargs {@code elements}.
*/
@SafeVarargs
public static <T> T[] asArray(T... elements) {
return elements;
}
@@ -45,7 +46,7 @@ public abstract class ArrayUtils {
* @return the first element in the array or {@literal null} if the array is null or empty.
* @see #getFirst(Object[], Object)
*/
public static <T> T getFirst(T... array) {
public static <T> T getFirst(T[] array) {
return getFirst(array, null);
}