StalactiteRepositoryConfigExtension.java
package org.codefilarete.stalactite.spring.repository.config;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
import org.codefilarete.stalactite.spring.repository.StalactiteRepository;
import org.codefilarete.stalactite.spring.repository.StalactiteRepositoryFactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.io.ResourceLoader;
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
import org.springframework.data.repository.config.DefaultRepositoryConfiguration;
import org.springframework.data.repository.config.RepositoryConfiguration;
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
import org.springframework.data.repository.config.RepositoryConfigurationSource;
import org.springframework.data.repository.config.XmlRepositoryConfigurationSource;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Stalactite specific configuration extension parsing custom attributes from the XML namespace and
* {@link EnableStalactiteRepositories} annotation.
* Implementation is inspired by {@link org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension}
* which was taken as an example.
*
* @author Guillaume Mary
*/
public class StalactiteRepositoryConfigExtension extends RepositoryConfigurationExtensionSupport {
private static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
private static final String ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE = "enableDefaultTransactions";
@Override
public String getModuleName() {
return "Stalactite";
}
@Override
public String getRepositoryFactoryBeanClassName() {
return StalactiteRepositoryFactoryBean.class.getName();
}
@Override
protected String getModulePrefix() {
return getModuleName().toLowerCase(Locale.US);
}
/* No annotations with Stalactite, so no reason to scan for entities
@Override
protected Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
return Arrays.asList(Entity.class, MappedSuperclass.class);
}
*/
@Override
protected Collection<Class<?>> getIdentifyingTypes() {
return Collections.singleton(StalactiteRepository.class);
}
@Override
public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource source) {
Optional<String> transactionManagerRef = source.getAttribute("transactionManagerRef");
builder.addPropertyValue("transactionManager", transactionManagerRef.orElse(DEFAULT_TRANSACTION_MANAGER_BEAN_NAME));
}
@Override
public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource config) {
AnnotationAttributes attributes = config.getAttributes();
builder.addPropertyValue(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE,
attributes.getBoolean(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE));
}
@Override
public void postProcess(BeanDefinitionBuilder builder, XmlRepositoryConfigurationSource config) {
Optional<String> enableDefaultTransactions = config.getAttribute(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE);
if (enableDefaultTransactions.isPresent() && StringUtils.hasText(enableDefaultTransactions.get())) {
builder.addPropertyValue(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE, enableDefaultTransactions.get());
}
}
@Override
protected ClassLoader getConfigurationInspectionClassLoader(ResourceLoader loader) {
ClassLoader classLoader = loader.getClassLoader();
return classLoader != null && LazyJvmAgent.isActive(loader.getClassLoader())
? new InspectionClassLoader(loader.getClassLoader())
: loader.getClassLoader();
}
/**
* Overridden to adapt the configSource argument to Stalactite case and ovoid exception throwing : some expected
* options are not available in {@link EnableStalactiteRepositories} but {@link AnnotationRepositoryConfigurationSource}
* doesn't handle their missing correctly and throws an exception.
*
* @param definition the definition of the bean-repository being created
* @param configSource a {@link AnnotationRepositoryConfigurationSource} that gives access to the real {@link EnableStalactiteRepositories} info
* @return a {@link DefaultRepositoryConfiguration}
* @param <T> type of configuration source, for us a {@link AnnotationRepositoryConfigurationSource}
*/
@Override
protected <T extends RepositoryConfigurationSource> RepositoryConfiguration<T> getRepositoryConfiguration(
BeanDefinition definition, T configSource) {
// Note that we can cast given argument to AnnotationRepositoryConfigurationSource because that's what is
// instantiated in RepositoryBeanDefinitionRegistrarSupport.registerBeanDefinitions(..) which is the code
// that invokes us with different intermediaries.
T repositoryConfigurationSource = (T) new EnableStalactiteRepositoriesRepositoryConfigurationSource((AnnotationRepositoryConfigurationSource) configSource);
return new DefaultRepositoryConfiguration<>(repositoryConfigurationSource, definition, this);
}
/**
* Utility to determine if a lazy Java agent is being used that might transform classes at a later time.
*/
static class LazyJvmAgent {
private static final Set<String> AGENT_CLASSES;
static {
Set<String> agentClasses = new LinkedHashSet<>();
agentClasses.add("org.springframework.instrument.InstrumentationSavingAgent");
agentClasses.add("org.eclipse.persistence.internal.jpa.deployment.JavaSECMPInitializerAgent");
AGENT_CLASSES = Collections.unmodifiableSet(agentClasses);
}
private LazyJvmAgent() {}
/**
* Determine if any agent is active.
*
* @return {@literal true} if an agent is active.
*/
static boolean isActive(@Nullable ClassLoader classLoader) {
return AGENT_CLASSES.stream() //
.anyMatch(agentClass -> ClassUtils.isPresent(agentClass, classLoader));
}
}
}