SGF-636 - Add support for GemFire JCA ResourceAdapter.

This commit is contained in:
John Blum
2017-06-30 18:38:41 -07:00
parent 504f1d516b
commit 344ae601dd
14 changed files with 2603 additions and 7 deletions

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2017 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.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
/**
* The {@link EnableGemFireAsLastResource} annotation is used to enable GemFire as a Last Resource in a Spring,
* CMT/JTA Transaction.
*
* @author John Blum
* @see org.springframework.context.annotation.EnableAspectJAutoProxy
* @see org.springframework.context.annotation.Import
* @see org.springframework.data.gemfire.config.annotation.GemFireAsLastResourceConfiguration
* @since 2.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableAspectJAutoProxy
@Import(GemFireAsLastResourceConfiguration.class)
@SuppressWarnings("unused")
public @interface EnableGemFireAsLastResource {
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2017 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.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
/**
* The EnableRegionDataAccessTracing class...
*
* @author John Blum
* @since 1.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableAspectJAutoProxy
@Import(RegionDataAccessTracingConfiguration.class)
@SuppressWarnings("unused")
public @interface EnableRegionDataAccessTracing {
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright 2017 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.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
import java.util.Map;
import java.util.Optional;
import org.apache.geode.cache.GemFireCache;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.config.annotation.support.GemFireAsLastResourceConnectionAcquiringAspect;
import org.springframework.data.gemfire.config.annotation.support.GemFireAsLastResourceConnectionClosingAspect;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* The {@link GemFireAsLastResourceConfiguration} class is a Spring {@link Configuration @Configuration}
* annotated class used to configure the GemFire "Last Resource" Spring Data GemFire {@link Aspect Aspects}.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.aspectj.lang.annotation.Aspect
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.ImportAware
* @see org.springframework.core.annotation.AnnotationAttributes
* @see org.springframework.core.type.AnnotationMetadata
* @see org.springframework.data.gemfire.config.annotation.EnableGemFireAsLastResource
* @see org.springframework.data.gemfire.config.annotation.support.GemFireAsLastResourceConnectionAcquiringAspect
* @see org.springframework.data.gemfire.config.annotation.support.GemFireAsLastResourceConnectionClosingAspect
* @see org.springframework.transaction.annotation.EnableTransactionManagement
* @since 1.0.0
*/
@Configuration
@SuppressWarnings("unused")
public class GemFireAsLastResourceConfiguration implements ImportAware {
private Integer enableTransactionManagementOrder;
/* (non-Javadoc) */
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableTransactionManagementOrder = resolveEnableTransactionManagementOrder(importMetadata);
}
/* (non-Javadoc) */
@SuppressWarnings("all")
protected int resolveEnableTransactionManagementOrder(AnnotationMetadata importMetadata) {
AnnotationAttributes enableTransactionManagementAttributes =
resolveEnableTransactionManagementAttributes(importMetadata);
Integer order = enableTransactionManagementAttributes.getNumber("order");
return Optional.ofNullable(order)
.filter(it -> !(it == Integer.MAX_VALUE || it == Integer.MIN_VALUE))
.orElseThrow(() -> newIllegalArgumentException(
"The @%1$s(order) attribute value [%2$s] must be explicitly set to a value"
+ " other than Integer.MAX_VALUE or Integer.MIN_VALUE",
EnableTransactionManagement.class.getSimpleName(), String.valueOf(order)));
}
/* (non-Javadoc) */
@SuppressWarnings("all")
protected AnnotationAttributes resolveEnableTransactionManagementAttributes(
AnnotationMetadata importMetadata) {
Map<String, Object> enableTransactionManagementAttributes =
importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName());
return Optional.ofNullable(enableTransactionManagementAttributes)
.map(AnnotationAttributes::fromMap)
.orElseThrow(() -> newIllegalStateException(
"The @%1$s annotation may only be used on a Spring application @%2$s class"
+ " that is also annotated with @%3$s having an explicit [order] set",
EnableGemFireAsLastResource.class.getSimpleName(), Configuration.class.getSimpleName(),
EnableTransactionManagement.class.getSimpleName()));
}
/* (non-Javadoc) */
protected Integer getEnableTransactionManagementOrder() {
return Optional.ofNullable(this.enableTransactionManagementOrder)
.orElseThrow(() -> newIllegalStateException(
"The @%1$s(order) attribute was not properly set [%2$s]; Also, please make your"
+ " Spring application @%3$s annotated class is annotated with both @%4$s and @%1$s",
EnableTransactionManagement.class.getSimpleName(), String.valueOf(this.enableTransactionManagementOrder),
Configuration.class.getSimpleName(), EnableGemFireAsLastResource.class.getSimpleName()));
}
/* (non-Javadoc) */
@Bean
public Object gemfireCachePostProcessor(@Autowired(required = false) GemFireCache gemfireCache) {
Optional.ofNullable(gemfireCache).ifPresent(cache -> cache.setCopyOnRead(true));
return null;
}
/* (non-Javadoc) */
@Bean
public GemFireAsLastResourceConnectionAcquiringAspect gemfireJcaConnectionAcquiringAspect() {
GemFireAsLastResourceConnectionAcquiringAspect connectionAcquiringAspect =
new GemFireAsLastResourceConnectionAcquiringAspect();
int order = (getEnableTransactionManagementOrder() + 1);
connectionAcquiringAspect.setOrder(order);
return connectionAcquiringAspect;
}
/* (non-Javadoc) */
@Bean
public GemFireAsLastResourceConnectionClosingAspect gemfireJcaConnectionClosingAspect() {
GemFireAsLastResourceConnectionClosingAspect connectionClosingAspect =
new GemFireAsLastResourceConnectionClosingAspect();
int order = (getEnableTransactionManagementOrder() - 1);
connectionClosingAspect.setOrder(order);
return connectionClosingAspect;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2017 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 org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.gemfire.config.annotation.support.RegionDataAccessTracingAspect;
/**
* The RegionDataAccessTracingConfiguration class...
*
* @author John Blum
* @since 1.0.0
*/
@Configuration
@SuppressWarnings("unused")
public class RegionDataAccessTracingConfiguration {
/* (non-Javadoc) */
@Bean
public RegionDataAccessTracingAspect regionDataAccessTracingAspect() {
return new RegionDataAccessTracingAspect();
}
}

View File

@@ -0,0 +1,577 @@
/*
* Copyright 2017 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.support;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newRuntimeException;
import java.util.Hashtable;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.resource.ResourceException;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.ra.GFConnection;
import org.apache.geode.ra.GFConnectionFactory;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.Ordered;
import org.springframework.data.gemfire.GemfireUtils;
import org.springframework.util.StringUtils;
/**
* {@link AbstractGemFireAsLastResourceAspectSupport} is an abstract base class encapsulating functionality common
* to all AOP Aspect extensions/implementations involving the GemFire JCA ResourceAdapter object registered in
* the JNDI context of a managed environment.
*
* @author John Blum
* @see javax.naming.Context
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.ra.GFConnection
* @see org.slf4j.Logger
* @see org.springframework.core.Ordered
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class AbstractGemFireAsLastResourceAspectSupport implements Ordered {
protected static final boolean DEFAULT_THROW_ON_ERROR = false;
protected static final int DEFAULT_ORDER = Integer.MAX_VALUE;
protected static final Consumer<String> NO_OP_LOGGER = message -> {};
protected static final String DEFAULT_GEMFIRE_JCA_RESOURCE_ADAPTER_JNDI_NAME = "gfe/jca";
private boolean throwOnError = DEFAULT_THROW_ON_ERROR;
private int order = DEFAULT_ORDER;
@Autowired(required = false)
private Context context;
@Autowired(required = false)
private GemFireCache gemfireCache;
private final Logger logger = newLogger();
@Value("${spring.data.gemfire.jca.resource-adapter.jndi.name:"
+ DEFAULT_GEMFIRE_JCA_RESOURCE_ADAPTER_JNDI_NAME + "}")
private String gemfireJcaResourceAdapterJndiName;
@Value("${spring.data.gemfire.naming.context.factory:}")
private String initialContextFactory;
@Value("${spring.data.gemfire.naming.context.provider-url:}")
private String providerUrl;
/**
* Returns a reference to the naming {@link Context}.
*
* @return a reference to the naming {@link Context}.
* @see javax.naming.Context
*/
protected synchronized Context getContext() {
return this.context;
}
/**
* Returns a reference to the {@link GemFireCache} used to interact with GemFire.
*
* @param <T> {@link Class} sub-type of the {@link GemFireCache} in use.
* @return a reference to the {@link GemFireCache}.
* @see org.apache.geode.cache.GemFireCache
*/
@SuppressWarnings("unchecked")
protected synchronized <T extends GemFireCache> T getGemFireCache() {
return (T) this.gemfireCache;
}
/**
* Returns the configured reference to GemFire's JCA ResourceAdapter registered in the managed environment's
* JNDI context.
*
* @return a {@link String} containing the configured reference to GemFire's JCA ResourceAdapter
* registered in the managed environment's JNDI context.
*/
public String getGemFireJcaResourceAdapterJndiName() {
return this.gemfireJcaResourceAdapterJndiName;
}
/**
* Returns the configured, fully-qualified classname of the {@link javax.naming.spi.InitialContextFactory}
* used to construct the {@link InitialContext} that is then used to lookup managed objects registered
* in the JNDI context of the managed environment.
*
* @return the configured, fully-qualified classname of the {@link javax.naming.spi.InitialContextFactory}
* used to construct the {@link InitialContext}.
*/
public String getInitialContextFactory() {
return this.initialContextFactory;
}
/**
* Returns a reference to the {@link Logger} used to record debug, info, warning and error messages
* logged by the application.
*
* @return a reference to the configured {@link Logger} used by the application for logging purposes.
* @see org.slf4j.Logger
*/
protected Logger getLogger() {
return this.logger;
}
/**
* Sets the order of this AOP Aspect relative to other Aspects in the chain of Aspects configured
* in Spring's Transaction Management.
*
* @param order int value specifying the relative order of this Aspect.
* @see org.springframework.core.Ordered
*/
public void setOrder(int order) {
this.order = order;
}
/**
* Returns the order of this AOP Aspect relative to other Aspects in the chain of Aspects configured
* in Spring's Transaction Management.
*
* @return an int value specifying the relative order of this Aspect.
* @see org.springframework.core.Ordered
*/
@Override
public int getOrder() {
return Optional.of(this.order)
.filter(order -> !(order == Integer.MAX_VALUE || order == Integer.MIN_VALUE))
.orElseGet(this::getDefaultOrder);
}
/**
* Returns the default order used by this AOP Aspect in the chain of Aspects configured
* in Spring's Transaction Management.
*
* @return an int value specifying the default order used by this AOP Aspect in the chain of Aspects
* configured in Spring's Transaction Management.
*/
protected Integer getDefaultOrder() {
return DEFAULT_ORDER;
}
/**
* Returns the URL of the Naming Context Provider as a {@link String}.
*
* @return the URL of the Naming Context Provider.
*/
public String getProviderUrl() {
return this.providerUrl;
}
/**
* Determines whether an Exception should be thrown when this Aspect is unable to perform its function.
*
* Alternatively (and by default) the error condition is simply logged.
*
* Defaults to {@literal false}.
*
* @return a boolean value indicating whether this Aspect should throw an Exception when an error occurs
* preventing this Aspect from performing its normal function.
*/
public boolean isThrowOnError() {
return this.throwOnError;
}
/**
* Defines an AOP Pointcut identifying Join Points during the execution of the application's components
* on which this Aspect should be applied.
*
* This particular Pointcut identifies {@link org.springframework.transaction.annotation.Transactional} annotated
* application service {@link Class types}. That is, the {@link org.springframework.transaction.annotation.Transactional}
* annotation is used at the class level.
*
* @see org.aspectj.lang.annotation.Pointcut
*/
@Pointcut("@within(org.springframework.transaction.annotation.Transactional)")
protected void atTransactionalType() {}
/**
* Defines an AOP Pointcut identifying Join Points during the execution of the application's components
* on which this Aspect should be applied.
*
* This particular Pointcut identifies {@link org.springframework.transaction.annotation.Transactional} annotated
* application service {@link java.lang.reflect.Method methods}. That is, the
* {@link org.springframework.transaction.annotation.Transactional} annotation is used at the service class,
* service {@link java.lang.reflect.Method method} level.
*
* @see org.aspectj.lang.annotation.Pointcut
*/
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
protected void atTransactionalMethod() {}
/**
* Formats the given {@link String message} with the provided array of {@link Object arguments}.
*
* @param message {@link String} containing the message to format.
* @param args array of {@link Object arguments} used to format the message.
* @return the {@link String message} formatted with the provided array of {@link Object arguments}.
* @see java.lang.String#format(String, Object...)
*/
protected String format(String message, Object... args) {
return String.format(message, args);
}
/**
* Logs the given {@link String message} formatted with the given array of {@link Object arguments}
* at {@link Level#DEBUG} level when debugging is enabled.
*
* @param <T> {@link Class} type extension of {@link AbstractGemFireAsLastResourceAspectSupport}.
* @param message {@link String} containing the message to log.
* @param args array of {@link Object arguments} used to format the message.
* @return this aspect.
* @see org.slf4j.Logger#isDebugEnabled()
* @see org.slf4j.Logger#debug(String, Object...)
* @see #format(String, Object...)
*/
@SuppressWarnings("unchecked")
protected <T extends AbstractGemFireAsLastResourceAspectSupport> T logDebugInfo(String message, Object... args) {
Logger logger = getLogger();
if (logger.isDebugEnabled()) {
logger.debug(format(message, args), args);
}
return (T) this;
}
/**
* Logs the given {@link String message} formatted with the given array of {@link Object arguments}
* at {@link Level#INFO} level when info logging is enabled.
*
* @param <T> {@link Class} type extension of {@link AbstractGemFireAsLastResourceAspectSupport}.
* @param message {@link String} containing the message to log.
* @param args array of {@link Object arguments} used to format the message.
* @return this aspect.
* @see org.slf4j.Logger#isInfoEnabled()
* @see org.slf4j.Logger#info(String, Object...)
* @see #format(String, Object...)
*/
@SuppressWarnings("unchecked")
protected <T extends AbstractGemFireAsLastResourceAspectSupport> T logInfo(String message, Object... args) {
Logger logger = getLogger();
if (logger.isInfoEnabled()) {
logger.info(format(message, args), args);
}
return (T) this;
}
/**
* Logs the given {@link String message} formatted with the given array of {@link Object arguments}
* at {@link Level#TRACE} level when tracing is enabled.
*
* @param <T> {@link Class} type extension of {@link AbstractGemFireAsLastResourceAspectSupport}.
* @param message {@link String} containing the message to log.
* @param args array of {@link Object arguments} used to format the message.
* @return this aspect.
* @see java.util.function.Supplier
* @see #logTraceInfo(Supplier)
*/
protected <T extends AbstractGemFireAsLastResourceAspectSupport> T logTraceInfo(String message, Object... args) {
return logTraceInfo(() -> format(message, args));
}
/**
* Logs the given {@link Supplier log message} at {@link Level#TRACE} level when tracing is enabled.
*
* @param <T> {@link Class} type extension of {@link AbstractGemFireAsLastResourceAspectSupport}.
* @param logMessage {@link Supplier} of the message to log.
* @return this aspect.
* @see java.util.function.Supplier
* @see org.slf4j.Logger#isTraceEnabled()
* @see org.slf4j.Logger#trace(String)
*/
@SuppressWarnings("unchecked")
protected <T extends AbstractGemFireAsLastResourceAspectSupport> T logTraceInfo(Supplier<String> logMessage) {
Logger logger = getLogger();
if (logger.isTraceEnabled()) {
logger.trace(logMessage.get());
}
return (T) this;
}
/**
* Logs the given {@link String message} formatted with the given array of {@link Object arguments}
* at {@link Level#WARN} level when warnings are enabled.
*
* @param <T> {@link Class} type extension of {@link AbstractGemFireAsLastResourceAspectSupport}.
* @param message {@link String} containing the message to log.
* @param args array of {@link Object arguments} used to format the message.
* @return this aspect.
* @see org.slf4j.Logger#isWarnEnabled()
* @see org.slf4j.Logger#warn(String, Object...)
* @see #format(String, Object...)
*/
@SuppressWarnings("unchecked")
protected <T extends AbstractGemFireAsLastResourceAspectSupport> T logWarning(String message, Object... args) {
Logger logger = getLogger();
if (logger.isWarnEnabled()) {
logger.warn(format(message, args), args);
}
return (T) this;
}
/**
* Logs the given {@link String message} formatted with the given array of {@link Object arguments}
* at {@link Level#ERROR} level when error logging is enabled.
*
* @param <T> {@link Class} type extension of {@link AbstractGemFireAsLastResourceAspectSupport}.
* @param message {@link String} containing the message to log.
* @param args array of {@link Object arguments} used to format the message.
* @return this aspect.
* @see org.slf4j.Logger#isErrorEnabled()
* @see org.slf4j.Logger#error(String, Object...)
* @see #format(String, Object...)
*/
@SuppressWarnings("unchecked")
protected <T extends AbstractGemFireAsLastResourceAspectSupport> T logError(String message, Object... args) {
Logger logger = getLogger();
if (logger.isErrorEnabled()) {
logger.error(format(message, args), args);
}
return (T) this;
}
/**
* Constructs a new instance of the {@link InitialContext} configured with the given {@link Hashtable environment}.
*
* @param environment {@link Hashtable} containing environment configuration meta-data used to access
* the JNDI context in a managed environment.
* @return a new instance of the {@link InitialContext} configured with the given {@link Hashtable environment}.
* @throws NamingException if the {@link InitialContext} could not be initialized with
* the provided {@link Hashtable environment}.
* @see javax.naming.InitialContext
* @see java.util.Hashtable
*/
protected InitialContext newInitialContext(Hashtable<?, ?> environment) throws NamingException {
return new InitialContext(environment);
}
/**
* Constructs a new instance of {@link Logger} used by this application to log messages.
*
* @return a new, configured instance of {@link Logger}.
*/
protected Logger newLogger() {
return LoggerFactory.getLogger(getClass());
}
/**
* Resolves the {@link Context} used to perform lookups of registered, managed objects in a management environment.
*
* @return the {@link Context} used to perform lookups of registered, managed objects in a managed environment.
* @throws IllegalStateException if the {@link Context} could not be resolved.
* @see org.apache.geode.cache.GemFireCache#getJNDIContext()
* @see #newInitialContext(Hashtable)
* @see #resolveEnvironment()
* @see #resolveGemFireCache()
* @see #getContext()
* @see javax.naming.Context
*/
protected synchronized Context resolveContext() {
Context context = getContext();
if (context == null) {
Hashtable<?, ?> resolvedEnvironment = resolveEnvironment();
try {
context = this.context = newInitialContext(resolvedEnvironment);
}
catch (NamingException cause) {
context = this.context = Optional.ofNullable(resolveGemFireCache()).map(GemFireCache::getJNDIContext)
.orElseThrow(() -> newIllegalStateException(cause,
"Failed to initialize an %1$s with the provided Environment configuration ['%2$s']",
InitialContext.class.getName(), resolvedEnvironment));
}
}
return context;
}
/**
* Resolves the {@link Hashtable environment} used by the application to configure the {@link InitialContext}.
*
* @return the resolved {@link Hashtable environment} used to configure the {@link InitialContext}.
* @see #getInitialContextFactory()
* @see #getProviderUrl()
* @see java.util.Hashtable
*/
protected Hashtable<?, ?> resolveEnvironment() {
Hashtable<Object, Object> environment = new Hashtable<>();
environment.put(Context.INITIAL_CONTEXT_FACTORY, getInitialContextFactory());
environment.put(Context.PROVIDER_URL, getProviderUrl());
return environment;
}
/**
* Resolves a reference to the {@link GemFireCache} required by this Aspect to perform its function.
*
* This method either returns the configured {@link GemFireCache} instance or looks up
* the {@link GemFireCache} instance using GemFire's API.
*
* @return a reference to the resolved {@link GemFireCache} instance.
* @see org.springframework.data.gemfire.GemfireUtils#resolveGemFireCache()
* @see org.apache.geode.cache.GemFireCache
* @see #getGemFireCache()
*/
protected synchronized GemFireCache resolveGemFireCache() {
GemFireCache gemfireCache = getGemFireCache();
if (gemfireCache == null) {
gemfireCache = this.gemfireCache = GemfireUtils.resolveGemFireCache();
}
return gemfireCache;
}
/**
* Resolves the configured JNDI name used to lookup and resolve the GemFire JCA ResourceAdapter object
* from the JNDI context in the managed environment.
*
* @return a {@link String} containing the JNDI name used to lookup and resolve the GemFire JCA ResourceAdapter
* object from the JNDI context in the managed environment.
* @see #getGemFireJcaResourceAdapterJndiName()
*/
protected String resolveGemFireJcaResourceAdapterJndiName() {
String gemfireJcaResourceAdapterJndiName = getGemFireJcaResourceAdapterJndiName();
return (StringUtils.hasText(gemfireJcaResourceAdapterJndiName) ? gemfireJcaResourceAdapterJndiName
: DEFAULT_GEMFIRE_JCA_RESOURCE_ADAPTER_JNDI_NAME);
}
/**
* Builder method used to set the {@link #isThrowOnError() throwOnError} property.
*
* The {@link #isThrowOnError() throwOnError} property is used to indicate whether an Exception should be thrown
* when an error occurs during the normal operation and function of this Aspect. By default, the error condition
* is simply logged.
*
* @param <T> {@link Class sub-type} of this Aspect.
* @param throwOnError boolean value used to set the {@link #isThrowOnError() throwOnError} property.
* @return this Aspect.
* @see #isThrowOnError()
*/
@SuppressWarnings("unchecked")
public <T extends AbstractGemFireAsLastResourceAspectSupport> T withThrowOnError(boolean throwOnError) {
this.throwOnError = throwOnError;
return (T) this;
}
protected static class GemFireConnectionHolder {
private static final ThreadLocal<GFConnection> gemfireConnection = new ThreadLocal<>();
public static GFConnection acquire(GFConnectionFactory connectionFactory) {
return acquire(connectionFactory, DEFAULT_THROW_ON_ERROR, NO_OP_LOGGER);
}
public static GFConnection acquire(GFConnectionFactory connectionFactory, boolean throwOnError,
Consumer<String> logger) {
try {
return of(connectionFactory.getConnection());
}
catch (ResourceException cause) {
String message =
String.format("Failed to acquire GemFire Connection from GemFire's JCA ResourceAdapter: %s",
cause.getMessage());
if (throwOnError) {
throw newRuntimeException(cause, message);
}
else {
logger.accept(message);
return null;
}
}
}
/* (non-Javadoc) */
public static GFConnection of(GFConnection connection) {
gemfireConnection.set(connection);
return connection;
}
/* (non-Javadoc) */
public static Optional<GFConnection> get() {
return Optional.ofNullable(gemfireConnection.get());
}
public static void close() {
close(DEFAULT_THROW_ON_ERROR, NO_OP_LOGGER);
}
/* (non-Javadoc) */
public static void close(boolean throwOnError, Consumer<String> logger) {
get().ifPresent(connection -> {
try {
connection.close();
}
catch (ResourceException cause) {
String message = String.format("Failed to close GemFire Connection: %s", cause.getMessage());
if (throwOnError) {
throw newRuntimeException(cause, message);
}
else {
logger.accept(message);
}
}
});
}
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2017 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.support;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newRuntimeException;
import javax.naming.NamingException;
import org.apache.geode.ra.GFConnectionFactory;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
/**
* The {@link GemFireAsLastResourceConnectionAcquiringAspect} class is a {@link AbstractGemFireAsLastResourceAspectSupport}
* implementation responsible for acquiring a GemFire Connection from GemFire's JCA ResourceAdapter,
* {@link GFConnectionFactory} after a CMT/JTA Transaction is began, which is initiated by
* Spring's Transaction infrastructure.
*
* @author John Blum
* @see org.apache.geode.ra.GFConnectionFactory
* @see org.aspectj.lang.annotation.Aspect
* @see org.aspectj.lang.annotation.Before
* @see org.springframework.data.gemfire.config.annotation.support.AbstractGemFireAsLastResourceAspectSupport
* @since 2.0.0
*/
@SuppressWarnings("unused")
@Aspect
public class GemFireAsLastResourceConnectionAcquiringAspect extends AbstractGemFireAsLastResourceAspectSupport {
private static final int DEFAULT_ORDER = 2048000;
@Autowired(required = false)
private GFConnectionFactory gemfireConnectionFactory;
/**
* Acquires (opens) a GemFire JCA ResourceAdapter Connection after the Spring CMT/JTA Transaction begins.
*/
@Before("atTransactionalType() || atTransactionalMethod()")
public void doGemFireConnectionFactoryGetConnection() {
logTraceInfo("Acquiring GemFire Connection from GemFire JCA ResourceAdapter registered at [%s]...",
resolveGemFireJcaResourceAdapterJndiName());
GemFireConnectionHolder.acquire(resolveGemFireConnectionFactory(), isThrowOnError(), this::logError);
}
/* (non-Javadoc) */
synchronized GFConnectionFactory resolveGemFireConnectionFactory() {
GFConnectionFactory connectionFactory = getGemFireConnectionFactory();
if (connectionFactory == null) {
String resolvedGemFireJcaResourceAdapterJndiName = resolveGemFireJcaResourceAdapterJndiName();
try {
connectionFactory = this.gemfireConnectionFactory =
(GFConnectionFactory) resolveContext().lookup(resolvedGemFireJcaResourceAdapterJndiName);
}
catch (NamingException cause) {
throw newRuntimeException(cause,
"Failed to resolve a GFConnectionFactory from the configured JNDI context name [%s]",
resolvedGemFireJcaResourceAdapterJndiName);
}
}
return connectionFactory;
}
/**
* Returns the default order used by this AOP Aspect in the chain of Aspects configured
* in Spring's Transaction Management.
*
* @return an int value specifying the default order used by this AOP Aspect in the chain of Aspects
* configured in Spring's Transaction Management.
*/
@Override
protected Integer getDefaultOrder() {
return DEFAULT_ORDER;
}
/**
* Returns a reference to the configured {@link GFConnectionFactory} instance.
*
* @return a reference to the configured {@link GFConnectionFactory} instance; may be {@literal null}.
* @see org.apache.geode.ra.GFConnectionFactory
*/
public synchronized GFConnectionFactory getGemFireConnectionFactory() {
return this.gemfireConnectionFactory;
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2017 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.support;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
/**
* The {@link GemFireAsLastResourceConnectionClosingAspect} class is a {@link AbstractGemFireAsLastResourceAspectSupport}
* implementation responsible for closing the GemFire Connection obtained from the GemFire JCA ResourceAdapter
* deployed in a managed environment when using GemFire as the Last Resource in a CMT/JTA Transaction
* initiated from Spring's Transaction infrastructure.
*
* @author John Blum
* @see org.aspectj.lang.annotation.Aspect
* @see org.aspectj.lang.annotation.After
* @see org.springframework.data.gemfire.config.annotation.support.AbstractGemFireAsLastResourceAspectSupport
* @since 2.0.0
*/
@SuppressWarnings("unused")
@Aspect
public class GemFireAsLastResourceConnectionClosingAspect extends AbstractGemFireAsLastResourceAspectSupport {
private static final int DEFAULT_ORDER = 1024000;
/**
* Closes the GemFire JCA ResourceAdapter Connection after the Spring CMT/JTA Transaction completes.
*/
@After("atTransactionalType() || atTransactionalMethod()")
public void doGemFireConnectionClose() {
logTraceInfo("Closing GemFire Connection...");
GemFireConnectionHolder.close(isThrowOnError(), this::logWarning);
}
/**
* Returns the default order used by this AOP Aspect in the chain of Aspects configured
* in Spring's Transaction Management.
*
* @return an int value specifying the default order used by this AOP Aspect in the chain of Aspects
* configured in Spring's Transaction Management.
*/
@Override
protected Integer getDefaultOrder() {
return DEFAULT_ORDER;
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2017 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.support;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The RegionDataAccessTracingAspect class...
*
* @author John Blum
* @since 1.0.0
*/
@Aspect
@SuppressWarnings("unused")
public class RegionDataAccessTracingAspect {
private final Logger logger = newLogger();
/* (non-Javadoc) */
protected Logger newLogger() {
return LoggerFactory.getLogger(getClass());
}
/* (non-Javadoc) */
protected String getCurrentThreadStackTrace() {
StringWriter writer = new StringWriter();
new Throwable().printStackTrace(new PrintWriter(writer));
return writer.toString();
}
/* (non-Javadoc) */
protected Logger getLogger() {
return this.logger;
}
/* (non-Javadoc) */
@Pointcut("target(com.gemstone.gemfire.cache.Region)")
private void regionPointcut() {}
/* (non-Javadoc) */
@Pointcut("execution(* com.gemstone.gemfire.cache.Region.create(..))"
+ " || execution(* com.gemstone.gemfire.cache.Region.get(..))"
+ " || execution(* com.gemstone.gemfire.cache.Region.getAll(..))"
+ " || execution(* com.gemstone.gemfire.cache.Region.put(..))"
+ " || execution(* com.gemstone.gemfire.cache.Region.putAll(..))"
+ " || execution(* com.gemstone.gemfire.cache.Region.putIfAbsent(..))"
+ " || execution(* com.gemstone.gemfire.cache.Region.remove(..))"
+ " || execution(* com.gemstone.gemfire.cache.Region.replace(..))"
+ " || execution(* com.gemstone.gemfire.cache.Region.selectValue(..))"
+ " || execution(* com.gemstone.gemfire.cache.Region.values(..))"
+ "")
private void regionDataAccessPointcut() {}
/* (non-Javadoc) */
@Before("regionPointcut() && regionDataAccessPointcut()")
public void regionDataAccessTracingAdvice() {
getLogger().trace("Region data access call [{}]", getCurrentThreadStackTrace());
}
}