AbstractDynamoDBConfiguration.java
/*
* Copyright © 2018 spring-data-dynamodb (https://github.com/prasanna0586/spring-data-dynamodb)
*
* 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.socialsignin.spring.data.dynamodb.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.socialsignin.spring.data.dynamodb.mapping.DynamoDBMappingContext;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.lang.NonNull;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import java.util.HashSet;
import java.util.Set;
/**
* Abstract configuration class for setting up Spring Data DynamoDB using JavaConfig.
*
* This class provides a base implementation for DynamoDB configuration, handling
* the scanning and initialization of DynamoDB entities annotated with {@link DynamoDbBean}.
* Subclasses must provide a concrete implementation of the {@link #amazonDynamoDB()} method
* to supply the DynamoDB client bean.
*
* The configuration automatically scans the base packages for DynamoDB mapped entities
* and creates a {@link DynamoDBMappingContext} to manage the mapping of these entities.
* By default, the base package to scan is determined from the concrete configuration class
* that extends this class.
* @author Prasanna Kumar Ramachandran
*/
@Configuration
public abstract class AbstractDynamoDBConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDynamoDBConfiguration.class);
/**
* Default constructor for the abstract DynamoDB configuration class.
* <p>
* This constructor is used by Spring to instantiate the configuration class
* when it is being processed as a Spring bean.
*/
public AbstractDynamoDBConfiguration() {
}
/**
* Returns the DynamoDB client bean for use throughout the application.
* <p>
* Subclasses must implement this method to provide a concrete {@link DynamoDbClient} instance.
* This client is used for all DynamoDB operations in the Spring Data DynamoDB framework.
* @return the DynamoDB client bean configured for this application
*/
public abstract DynamoDbClient amazonDynamoDB();
/**
* Return the base packages to scan for mapped {@link DynamoDbBean}s. Will return the package name of the
* configuration class' (the concrete class, not this one here) by default. So if you have a
* {@code com.acme.AppConfig} extending {@link AbstractDynamoDBConfiguration} the base package will be considered
* {@code com.acme} unless the method is overriden to implement alternate behaviour.
* @return the base package to scan for mapped {@link DynamoDbBean} classes or {@literal null} to not enable
* scanning for entities.
*/
@NonNull
protected String[] getMappingBasePackages() {
Package mappingBasePackage = getClass().getPackage();
String basePackage = mappingBasePackage == null ? null : mappingBasePackage.getName();
return new String[] { basePackage };
}
/**
* Creates a {@link DynamoDBMappingContext} equipped with entity classes scanned from the mapping base package.
* @see #getMappingBasePackages()
* @return A newly created {@link DynamoDBMappingContext}
* @throws ClassNotFoundException
* if the class with {@link DynamoDbBean} annotation can't be loaded
*/
@NonNull
@Bean
public DynamoDBMappingContext dynamoDBMappingContext() throws ClassNotFoundException {
DynamoDBMappingContext mappingContext = new DynamoDBMappingContext();
mappingContext.setInitialEntitySet(getInitialEntitySet());
return mappingContext;
}
/**
* Scans the mapping base package for classes annotated with {@link DynamoDbBean}.
* @see #getMappingBasePackages()
* @return All classes with {@link DynamoDbBean} annotation
* @throws ClassNotFoundException
* if the class with {@link DynamoDbBean} annotation can't be loaded
*/
@NonNull
protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
Set<Class<?>> initialEntitySet = new HashSet<>();
String[] basePackages = getMappingBasePackages();
for (String basePackage : basePackages) {
LOGGER.trace("getInitialEntitySet. basePackage: {}", basePackage);
if (StringUtils.hasText(basePackage)) {
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
false);
componentProvider.addIncludeFilter(new AnnotationTypeFilter(DynamoDbBean.class));
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
String candidateClass = candidate.getBeanClassName();
if (candidateClass != null) {
LOGGER.trace("getInitialEntitySet. candidate: {}", candidateClass);
initialEntitySet.add(ClassUtils.forName(candidateClass,
AbstractDynamoDBConfiguration.class.getClassLoader()));
} else {
LOGGER.warn("getInitialEntitySet. candidate: {} did not provide a class", candidate);
}
}
}
}
return initialEntitySet;
}
}