DynamoDBRepositoryFactory.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.repository.support;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.socialsignin.spring.data.dynamodb.core.DynamoDBOperations;
import org.socialsignin.spring.data.dynamodb.repository.DynamoDBCrudRepository;
import org.socialsignin.spring.data.dynamodb.repository.query.DynamoDBQueryLookupStrategy;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.ValueExpressionDelegate;
import org.springframework.data.util.Version;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import software.amazon.awssdk.core.util.VersionInfo;
import java.util.Optional;
import java.util.StringTokenizer;
import static org.springframework.data.querydsl.QuerydslUtils.QUERY_DSL_PRESENT;
/**
* Repository factory for creating DynamoDB repository instances with support for entity information,
* query lookup strategies, and repository base classes.
* @author Prasanna Kumar Ramachandran
*/
public class DynamoDBRepositoryFactory extends RepositoryFactorySupport {
private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDBRepositoryFactory.class);
static {
final String DEVELOPMENT = "DEVELOPMENT";
// SDK v2: Get version from VersionInfo utility class
String awsSdkVersion = VersionInfo.SDK_VERSION;
String springDataVersion = Version.class.getPackage().getImplementationVersion();
String thisSpecVersion = DynamoDBRepositoryFactory.class.getPackage().getSpecificationVersion();
String thisImplVersion = DynamoDBRepositoryFactory.class.getPackage().getImplementationVersion();
if (thisImplVersion == null || thisSpecVersion == null) {
thisSpecVersion = DEVELOPMENT;
thisImplVersion = DEVELOPMENT;
}
LOGGER.info("Spring Data DynamoDB Version: {} ({})", thisImplVersion, thisSpecVersion);
LOGGER.info("Spring Data Version: {}", springDataVersion);
LOGGER.info("AWS SDK Version: {}", awsSdkVersion);
LOGGER.info("Java Version: {} - {} {}", System.getProperty("java.version"),
System.getProperty("java.vm.name"), System.getProperty("java.vm.version"));
LOGGER.info("Platform Details: {} {}", System.getProperty("os.name"),
System.getProperty("os.version"));
if (!DEVELOPMENT.equals(thisImplVersion) && !isCompatible(springDataVersion, thisSpecVersion)) {
LOGGER.warn("This Spring Data DynamoDB implementation might not be compatible with the available Spring Data classes on the classpath!{}NoDefClassFoundExceptions or similar might occur!", System.lineSeparator());
}
}
/**
* Checks if the specification version is compatible with the implementation version.
* @param spec the specification version
* @param impl the implementation version
* @return true if compatible, false otherwise
*/
protected static boolean isCompatible(@Nullable String spec, @Nullable String impl) {
if (spec == null && impl == null) {
return false;
} else if (spec == null) {
spec = "";
} else if (impl == null) {
impl = "";
}
StringTokenizer specTokenizer = new StringTokenizer(spec, ".");
StringTokenizer implTokenizer = new StringTokenizer(impl, ".");
String specMajor = specTokenizer.hasMoreTokens() ? specTokenizer.nextToken() : "0";
String specMinor = specTokenizer.hasMoreTokens() ? specTokenizer.nextToken() : "0";
String implMajor = implTokenizer.hasMoreTokens() ? implTokenizer.nextToken() : "0";
String implMinor = implTokenizer.hasMoreTokens() ? implTokenizer.nextToken() : "0";
return specMajor.equals(implMajor) && specMinor.equals(implMinor);
}
private final DynamoDBOperations dynamoDBOperations;
/**
* Creates a new DynamoDB repository factory.
* @param dynamoDBOperations the DynamoDB operations instance
*/
public DynamoDBRepositoryFactory(DynamoDBOperations dynamoDBOperations) {
this.dynamoDBOperations = dynamoDBOperations;
}
@NonNull
@Override
public <T, ID> DynamoDBEntityInformation<T, ID> getEntityInformation(@NonNull final Class<T> domainClass) {
final DynamoDBEntityMetadataSupport<T, ID> metadata = new DynamoDBEntityMetadataSupport<>(domainClass,
this.dynamoDBOperations);
return metadata.getEntityInformation();
}
@NonNull
@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key,
@NonNull ValueExpressionDelegate valueExpressionDelegate) {
return Optional.of(DynamoDBQueryLookupStrategy.create(dynamoDBOperations, key));
}
/**
* Callback to create a {@link DynamoDBCrudRepository} instance with the given {@link RepositoryMetadata}.
* @param <T> Type of the Entity
* @param <ID> Type of the Hash (Primary) Key
* @param metadata Metadata of the entity
* @see #getTargetRepository(RepositoryInformation)
* @return the created {@link DynamoDBCrudRepository} instance
*/
@NonNull
@SuppressWarnings({ "unchecked", "rawtypes" })
protected <T, ID> DynamoDBCrudRepository<?, ?> getDynamoDBRepository(@NonNull RepositoryMetadata metadata) {
return new SimpleDynamoDBPagingAndSortingRepository(getEntityInformation(metadata.getDomainType()),
dynamoDBOperations, getEnableScanPermissions(metadata));
}
/**
* Gets the scan permissions for the given repository metadata.
* @param metadata the repository metadata
* @return the enable scan permissions
*/
@NonNull
protected EnableScanPermissions getEnableScanPermissions(@NonNull RepositoryMetadata metadata) {
return new EnableScanAnnotationPermissions(metadata.getRepositoryInterface());
}
@NonNull
@Override
protected Class<?> getRepositoryBaseClass(@NonNull RepositoryMetadata metadata) {
if (isQueryDslRepository(metadata.getRepositoryInterface())) {
throw new IllegalArgumentException("QueryDsl Support has not been implemented yet.");
}
return SimpleDynamoDBPagingAndSortingRepository.class;
}
private static boolean isQueryDslRepository(@NonNull Class<?> repositoryInterface) {
return QUERY_DSL_PRESENT && QuerydslPredicateExecutor.class.isAssignableFrom(repositoryInterface);
}
@NonNull
@Override
protected Object getTargetRepository(@NonNull RepositoryInformation metadata) {
return getDynamoDBRepository(metadata);
}
}