package org.springframework.data.simpledb.core; import java.io.Serializable; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.simpledb.core.domain.DomainManager; import org.springframework.data.simpledb.core.entity.EntityWrapper; import org.springframework.data.simpledb.query.QueryUtils; import org.springframework.data.simpledb.query.SdbItemQuery; import org.springframework.data.simpledb.reflection.ReflectionUtils; import org.springframework.data.simpledb.repository.support.entityinformation.SimpleDbEntityInformation; import org.springframework.data.simpledb.repository.support.entityinformation.SimpleDbEntityInformationSupport; import org.springframework.util.Assert; import com.amazonaws.services.simpledb.AmazonSimpleDB; import com.amazonaws.services.simpledb.model.SelectResult; /** * Performs Domain Management based on Domain Management Policy on each operation */ public abstract class AbstractSimpleDbTemplate implements SimpleDbOperations { private final int serviceUnavailableMaxRetries; private final SimpleDb simpleDb; private final AmazonSimpleDB simpleDbClient; private final DomainManager domainManager; public AbstractSimpleDbTemplate(SimpleDb simpleDb) { Assert.notNull(simpleDb); this.simpleDb = simpleDb; this.simpleDbClient = simpleDb.getSimpleDbClient(); this.serviceUnavailableMaxRetries = simpleDb.getUnavailableServiceRetries(); this.domainManager = new DomainManager(); } public abstract <T> Page<T> executePagedQueryImpl(Class<T> entityClass, String query, Pageable pageable, boolean consistentRead, SimpleDbEntityInformation<T, ?> entityInformation); public abstract <T> T createOrUpdateImpl(T domainItem, EntityWrapper<T, ?> entity); public abstract void deleteAttributesImpl(String domainName, String itemName); public abstract <T> void deleteImpl(T domainItem, SimpleDbEntityInformation<T, ?> entityInformation, EntityWrapper<T, ?> entity); public abstract <T> List<T> findAllQueryImpl(Class<T> entityClass, SimpleDbEntityInformation<T, ?> entityInformation); public abstract <T> List<T> recursiveFindImpl(Class<T> entityClass, String query, boolean consistentRead, SimpleDbEntityInformation<T, ?> entityInformation); public abstract <T> long countImpl(boolean consistentRead, SimpleDbEntityInformation<T, ?> entityInformation); public abstract SelectResult invokeFindImpl(boolean consistentRead, String escapedQuery); public abstract <T> long countImpl(String query, boolean consistentRead, SimpleDbEntityInformation<T, ?> entityInformation); public abstract <T, ID extends Serializable> T readImpl(ID id, Class<T> entityClass, boolean consistentRead, SimpleDbEntityInformation<T, ?> entityInformation); public abstract <T> List<T> findImpl(SimpleDbEntityInformation<T, ?> entityInformation, String query, String nextToken, boolean consistentRead); protected abstract <T, ID> void updateImpl(ID id, Class<T> entityClass, Map<String, ? extends Object> propertyMap); @Override public final AmazonSimpleDB getDB() { return simpleDb.getSimpleDbClient(); } @Override public final SimpleDb getSimpleDb() { return simpleDb; } @Override public final String getDomainName(Class<?> entityClass) { return simpleDb.getDomain(entityClass); } @Override public final <T> T createOrUpdate(final T domainItem) { final SimpleDbEntityInformation<T, ?> entityInformation = getEntityInformation(domainItem.getClass()); final EntityWrapper<T, ?> entity = getEntityWrapper(domainItem, entityInformation); manageSimpleDbDomains(entityInformation); final List<T> items = new ArrayList<T>(); new AbstractServiceUnavailableOperationRetrier(serviceUnavailableMaxRetries) { @Override public void execute() { items.add(createOrUpdateImpl(domainItem, entity)); } }.executeWithRetries(); return items.size() > 0 ? items.get(0) : null; } @Override public final void delete(final String domainName, final String itemName) { manageSimpleDbDomain(domainName); new AbstractServiceUnavailableOperationRetrier(serviceUnavailableMaxRetries) { @Override public void execute() { deleteAttributesImpl(domainName, itemName); } }.executeWithRetries(); } @Override public final <T> void delete(final T domainItem) { final SimpleDbEntityInformation<T, ?> entityInformation = getEntityInformation(domainItem.getClass()); final EntityWrapper<T, ?> entity = getEntityWrapper(domainItem, entityInformation); manageSimpleDbDomain(entityInformation.getDomain()); new AbstractServiceUnavailableOperationRetrier(serviceUnavailableMaxRetries) { @Override public void execute() { deleteImpl(domainItem, entityInformation, entity); } }.executeWithRetries(); } @Override public <T, ID> void delete(Class<T> entityClass, ID id) { delete(getDomainName(entityClass), (String) id); } @Override public <T> void delete(Iterable<? extends T> entities) { for (T entity : entities) { delete(entity); } } @Override public final <T> void deleteAll(Class<T> entityClass) { for (T element : findAll(entityClass, true)) { delete(element); } } @Override public final <T, ID extends Serializable> T read(ID id, Class<T> entityClass) { return read(id, entityClass, simpleDb.isConsistentRead()); } @Override public final <T, ID extends Serializable> T read(final ID id, final Class<T> entityClass, final boolean consistentRead) { final SimpleDbEntityInformation<T, ?> entityInformation = getEntityInformation(entityClass); manageSimpleDbDomain(entityInformation.getDomain()); final List<T> items = new ArrayList<T>(); new AbstractServiceUnavailableOperationRetrier(serviceUnavailableMaxRetries) { @Override public void execute() { items.add(readImpl(id, entityClass, consistentRead, entityInformation)); } }.executeWithRetries(); return items.size() > 0 ? items.get(0) : null; } @Override public final <T> long count(Class<T> entityClass) { return count(entityClass, simpleDb.isConsistentRead()); } @Override public final <T> long count(String query, Class<T> entityClass) { return count(query, entityClass, simpleDb.isConsistentRead()); } @Override public final <T> long count(final String query, final Class<T> entityClass, final boolean consistentRead) { final SimpleDbEntityInformation<T, ?> entityInformation = getEntityInformation(entityClass); manageSimpleDbDomain(entityInformation.getDomain()); final List<Long> items = new ArrayList<Long>(); new AbstractServiceUnavailableOperationRetrier(serviceUnavailableMaxRetries) { @Override public void execute() { items.add(countImpl(query, consistentRead, entityInformation)); } }.executeWithRetries(); return items.size() > 0 ? items.get(0) : 0; } @Override public final <T> long count(final Class<T> entityClass, final boolean consistentRead) { final SimpleDbEntityInformation<T, ?> entityInformation = getEntityInformation(entityClass); manageSimpleDbDomain(entityInformation.getDomain()); final List<Long> items = new ArrayList<Long>(); new AbstractServiceUnavailableOperationRetrier(serviceUnavailableMaxRetries) { @Override public void execute() { items.add(countImpl(consistentRead, entityInformation)); } }.executeWithRetries(); return items.size() > 0 ? items.get(0) : 0; } @Override public final <T> List<T> find(Class<T> entityClass, String query) { return find(entityClass, query, simpleDb.isConsistentRead()); } @Override public <T> List<T> find(final Class<T> entityClass, final String query, final boolean consistentRead) { final SimpleDbEntityInformation<T, ?> entityInformation = getEntityInformation(entityClass); manageSimpleDbDomain(entityInformation.getDomain()); final List<T> items = new ArrayList<T>(); new AbstractServiceUnavailableOperationRetrier(serviceUnavailableMaxRetries) { @Override public void execute() { items.addAll(recursiveFindImpl(entityClass, query, consistentRead, entityInformation)); } }.executeWithRetries(); return items; } protected <T> List<T> find(final SimpleDbEntityInformation<T, ?> entityInformation, final String query, final String nextToken, final boolean consistentRead) { manageSimpleDbDomain(entityInformation.getDomain()); final List<T> items = new ArrayList<T>(); new AbstractServiceUnavailableOperationRetrier(serviceUnavailableMaxRetries) { @Override public void execute() { items.addAll(findImpl(entityInformation, query, nextToken, consistentRead)); } }.executeWithRetries(); return items; } @Override public final <T> List<T> findAll(Class<T> entityClass) { return findAll(entityClass, simpleDb.isConsistentRead()); } @Override public final <T> List<T> findAll(final Class<T> entityClass, final boolean consistentRead) { final SimpleDbEntityInformation<T, ?> entityInformation = getEntityInformation(entityClass); manageSimpleDbDomain(entityInformation.getDomain()); final List<T> items = new ArrayList<T>(); new AbstractServiceUnavailableOperationRetrier(serviceUnavailableMaxRetries) { @Override public void execute() { items.addAll(findAllQueryImpl(entityClass, entityInformation)); } }.executeWithRetries(); return items; } @Override public final <T> Page<T> executePagedQuery(Class<T> entityClass, String query, Pageable pageable) { return executePagedQuery(entityClass, query, pageable, simpleDb.isConsistentRead()); } @Override public final <T> Page<T> executePagedQuery(final Class<T> entityClass, final String query, final Pageable pageable, final boolean consistentRead) { final SimpleDbEntityInformation<T, ?> entityInformation = getEntityInformation(entityClass); manageSimpleDbDomain(entityInformation.getDomain()); final List<Page<T>> pages = new ArrayList<Page<T>>(); new AbstractServiceUnavailableOperationRetrier(serviceUnavailableMaxRetries) { @Override public void execute() { pages.add(executePagedQueryImpl(entityClass, query, pageable, consistentRead, entityInformation)); } }.executeWithRetries(); return pages.isEmpty() ? null : pages.get(0); } @Override public <T, ID> void update(final ID id, final Class<T> entityClass, final Map<String, ? extends Object> propertyMap) { manageSimpleDbDomain(getDomainName(entityClass)); new AbstractServiceUnavailableOperationRetrier(serviceUnavailableMaxRetries) { @Override public void execute() { updateImpl(id, entityClass, propertyMap); } }.executeWithRetries(); } @Override public <T> SdbItemQuery<T> createQuery(Class<T> entityClass, String rawWhereClause, Object...queryParams) { String whereClause = rawWhereClause; // replace positional parameters final String paramPlaceholder = "\\?"; for (int i = 0; QueryUtils.hasBindParameter(whereClause); i++) { whereClause = QueryUtils.replaceOneParameterInQuery( whereClause, paramPlaceholder, queryParams[i]); } // add select * from `domainName` String query = String.format("select * from `%s` where %s", getDomainName(entityClass), whereClause); return new SdbItemQuery<T>(entityClass, query, this); } protected final <T> EntityWrapper<T, ?> getEntityWrapper(T domainItem, SimpleDbEntityInformation<T, ?> entityInformation) { final EntityWrapper<T, ?> entityWrapper = new EntityWrapper<T, Serializable>(entityInformation, domainItem); return entityWrapper; } @SuppressWarnings("unchecked") private <T> SimpleDbEntityInformation<T, ?> getEntityInformation(Class<?> domainClass) { String simpleDbDomain = simpleDb.getSimpleDbDomain().getDomain(domainClass); return (SimpleDbEntityInformation<T, ?>) SimpleDbEntityInformationSupport.getMetadata(domainClass, simpleDbDomain); } private <T> void manageSimpleDbDomain(final String domainName) { domainManager.manageDomain(domainName, simpleDb.getDomainManagementPolicy(), simpleDbClient); } private <T> void manageSimpleDbDomains(final SimpleDbEntityInformation<T, ?> entityInformation) { List<Field> nestedReferences = ReflectionUtils.getReferenceAttributesList(entityInformation.getJavaType()); entityInformation.validateReferenceFields(nestedReferences); for (Field eachNestedReference : nestedReferences) { manageSimpleDbDomain(getDomainName(eachNestedReference.getType())); } manageSimpleDbDomain(entityInformation.getDomain()); } }