Browse Source

DATADOC-48 adding cross-store code to support Mongo->JPA relationships

pull/2/head
Thomas Risberg 15 years ago
parent
commit
c8465d8d03
  1. 2
      spring-data-commons-aspects/.settings/org.eclipse.jdt.core.prefs
  2. 11
      spring-data-commons-aspects/pom.xml
  3. 51
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/ChainingEntityOperationsLocator.java
  4. 52
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/ChainingForeignStoreKeyManagerLocator.java
  5. 88
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/EntityManagerJpaEntityOperations.java
  6. 22
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/EntityOperationsLocator.java
  7. 58
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/ForeignStoreKeyManager.java
  8. 24
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/ForeignStoreKeyManagerLocator.java
  9. 65
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/GeneratedFieldForeignStoreKeyManager.java
  10. 20
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/InvalidFieldAnnotationException.java
  11. 17
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/JpaEntityForeignStoreFieldTransience.aj
  12. 20
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/MappingValidator.java
  13. 45
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/OrderedForeignStoreKeyManager.java
  14. 44
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/PresentKeyForeignStoreKeyManager.java
  15. 99
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/RooConventionEntityOperations.java
  16. 10
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/Roo_GeneratedForeignStoreKeys.java
  17. 172
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/StoreSpanning.aj
  18. 12
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/UnknownEntityClassException.java
  19. 17
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/support/ChangeSetConstructorEntityInstantiator.java
  20. 94
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/support/ChangeSetForeignStoreKeyManager.java
  21. 162
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/support/DefaultManagedSet.java
  22. 27
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/support/ManagedSet.java
  23. 34
      spring-data-commons-aspects/src/main/java/org/springframework/persistence/support/SimpleMappingValidator.java
  24. 7
      spring-data-commons-aspects/template.mf

2
spring-data-commons-aspects/.settings/org.eclipse.jdt.core.prefs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
#Tue Mar 01 12:59:14 EST 2011
#Tue Mar 08 11:29:43 EST 2011
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6

11
spring-data-commons-aspects/pom.xml

@ -80,6 +80,17 @@ @@ -80,6 +80,17 @@
<artifactId>jsr250-api</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.0.2.GA</version>
<scope>test</scope>
</dependency>
<!-- JPA -->
<dependency>

51
spring-data-commons-aspects/src/main/java/org/springframework/persistence/ChainingEntityOperationsLocator.java

@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
package org.springframework.persistence;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.OrderComparator;
import org.springframework.dao.DataAccessException;
/**
* Chaining implementation of entity operations that automatically configures itself
* from a Spring context if available.
*
* @author Rod Johnson
*/
public class ChainingEntityOperationsLocator implements EntityOperationsLocator {
private List<EntityOperations> entityOperationsList = new LinkedList<EntityOperations>();
@Autowired
public void init(ApplicationContext context) {
Map<String, EntityOperations> beansOfType = context.getBeansOfType(EntityOperations.class);
List<EntityOperations> l = new LinkedList<EntityOperations>();
for (EntityOperations eo : beansOfType.values()) {
l.add(eo);
}
Collections.sort(l, new OrderComparator());
for (EntityOperations eo : l) {
add(eo);
}
}
public void add(EntityOperations ef) {
entityOperationsList.add(ef);
}
@Override
public <T> EntityOperations<?,T> entityOperationsFor(Class<T> entityClass, RelatedEntity fs)
throws DataAccessException {
for (EntityOperations eo : entityOperationsList) {
if (eo.supports(entityClass, fs)) {
return eo;
}
}
throw new UnknownEntityClassException(entityClass);
}
}

52
spring-data-commons-aspects/src/main/java/org/springframework/persistence/ChainingForeignStoreKeyManagerLocator.java

@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
package org.springframework.persistence;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.OrderComparator;
import org.springframework.dao.DataAccessException;
/**
* Chaining implementation of ForeignStoreKeyManagerLocator that can be parameterized
* from a Spring ApplicationContext.
*
* @author Rod Johnson
*
*/
public class ChainingForeignStoreKeyManagerLocator implements ForeignStoreKeyManagerLocator {
private List<ForeignStoreKeyManager> delegates = new LinkedList<ForeignStoreKeyManager>();
public void add(ForeignStoreKeyManager fskm) {
delegates.add(fskm);
}
@Autowired
public void init(ApplicationContext context) {
Map<String, ForeignStoreKeyManager> beansOfType = context.getBeansOfType(ForeignStoreKeyManager.class);
List<ForeignStoreKeyManager> l = new LinkedList<ForeignStoreKeyManager>();
for (ForeignStoreKeyManager fskm : beansOfType.values()) {
l.add(fskm);
}
Collections.sort(l, new OrderComparator());
for (ForeignStoreKeyManager fskm : l) {
add(fskm);
}
}
@Override
public <T> ForeignStoreKeyManager<T> foreignStoreKeyManagerFor(Class<T> entityClass, Field f) throws DataAccessException {
for (ForeignStoreKeyManager fskm : delegates) {
if (fskm.isSupportedField(entityClass, f)) {
return fskm;
}
}
throw new IllegalArgumentException("No ForeignStoreKeyManager for " + entityClass + " on " + f);
}
}

88
spring-data-commons-aspects/src/main/java/org/springframework/persistence/EntityManagerJpaEntityOperations.java

@ -0,0 +1,88 @@ @@ -0,0 +1,88 @@
package org.springframework.persistence;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.dao.DataAccessException;
/**
* Implementation of entity operations that works on any entity
* that adheres to Roo persist() and static finder conventions.
* Does not depend on Roo, merely on Roo conventions, which can
* also be implemented by hand.
*
* @author Rod Johnson
*/
public class EntityManagerJpaEntityOperations extends OrderedEntityOperations {
@PersistenceContext
private EntityManager entityManager;
public static Object invoke(Class<?> clazz, String methodName,
Object target, Class<?>[] argTypes, Object... args) {
try {
Method m = clazz.getMethod(methodName, argTypes);
return m.invoke(target, (Object[]) args);
} catch (Exception ex) {
// TODO FIX ME
// System.out.println(ex + ": checked exceptions are stupid");
throw new IllegalArgumentException(ex);
}
}
public static Object invokeNoArgMethod(Class<?> clazz, String methodName, Object target) {
return invoke(clazz, methodName, target, (Class<?>[]) null,
(Object[]) null);
}
@Override
public Object findEntity(Class entityClass, Object pk)
throws DataAccessException {
String findMethod = "find" + entityClass.getSimpleName();
Object found = entityManager.find(entityClass, pk);
log.info("Lookup [" + entityClass.getName() + "] by pk=[" + pk + "] using EntityManager.find() - found [" + found + "]");
return found;
}
@Override
public Object findUniqueKey(Object entity) throws DataAccessException {
String idMethodName = "getId";
return invokeNoArgMethod(entity.getClass(), idMethodName, entity);
}
@Override
public boolean isTransient(Object entity) throws DataAccessException {
return findUniqueKey(entity) == null;
}
@Override
public Object makePersistent(Object owner, Object entity, Field f, RelatedEntity fs) throws DataAccessException {
if (log.isDebugEnabled()) {
log.debug("Making entity persistent: BEFORE [" + entity + "]");
}
entityManager.persist(entity);
Object key = findUniqueKey(entity);
log.info("Making entity persistent: AFTER [" + entity + "]");
return key;
}
@Override
public boolean supports(Class entityClass, RelatedEntity fs) {
return entityClass.isAnnotationPresent(Entity.class);
}
@Override
public boolean cacheInEntity() {
return false;
}
@Override
public boolean isTransactional() {
// TODO Need to have a better test
return true;
}}

22
spring-data-commons-aspects/src/main/java/org/springframework/persistence/EntityOperationsLocator.java

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
package org.springframework.persistence;
import org.springframework.dao.DataAccessException;
/**
* Interface to be implemented by classes that can find EntityOperations
* implementations to handle particular classes.
*
* @author Rod Johnson
*/
public interface EntityOperationsLocator {
/**
* Find the EntityOperations for this class.
* @param fs ForeignStore annotation (may be null)
* @param entityClass
* @return
* @throws DataAccessException if no EntityOperations can be found.
*/
<T> EntityOperations<?,T> entityOperationsFor(Class<T> entityClass, RelatedEntity fs) throws DataAccessException;
}

58
spring-data-commons-aspects/src/main/java/org/springframework/persistence/ForeignStoreKeyManager.java

@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
package org.springframework.persistence;
import java.lang.reflect.Field;
import java.util.Set;
import org.springframework.dao.DataAccessException;
/**
* Interface to be implemented to infer or compute foreign store
* key values and possibly store them.
*
* @author Rod Johnson
*
*/
public interface ForeignStoreKeyManager<T> {
/**
* Is this entity class one we can store additional
* state in related to fields annotated with ForeignStore.
* @param entityClass
* @param foreignStore
* @return
*/
boolean isSupportedField(Class<T> entityClass, Field foreignStore);
/**
*
* @param entity
* @param foreignStore
* @return null if not yet persistent
* @throws DataAccessException if the key cannot be computed or stored
*/
<K> K findForeignStoreKey(T entity, Field foreignStore, Class<K> keyClass) throws DataAccessException;
/**
* Can be a NOP if the key is inferred
* @param entity
* @param foreignStore
* @param pk
* @throws DataAccessException
*/
void storeForeignStoreKey(T entity, Field foreignStore, Object pk) throws DataAccessException;
/**
* Clear out the foreign key value
* Can be a NOP if the key is inferred
* @param entity
* @param foreignStore
* @param keyClass class of the key
* @throws DataAccessException
*/
void clearForeignStoreKey(T entity, Field foreignStore, Class<?> keyClass) throws DataAccessException;
<K> Set<K> findForeignStoreKeySet(T entity, Field foreignStore, Class<K> keyClass) throws DataAccessException;
void storeForeignStoreKeySet(T entity, Field foreignStore, Set<Object> keys) throws DataAccessException;
}

24
spring-data-commons-aspects/src/main/java/org/springframework/persistence/ForeignStoreKeyManagerLocator.java

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
package org.springframework.persistence;
import java.lang.reflect.Field;
import org.springframework.dao.DataAccessException;
/**
* Interface to be implemented by classes that can find ForeignStoreKeyManager
* implementations to handle particular entities.
*
* @author Rod Johnson
*/
public interface ForeignStoreKeyManagerLocator {
/**
* Find the ForeignStoreKeyManager for this class.
* @param f field the RelatedEntity annotation is on
* @param entityClass
* @return
* @throws DataAccessException if no ForeignStoreKeyManager can be found.
*/
<T> ForeignStoreKeyManager<T> foreignStoreKeyManagerFor(Class<T> entityClass, Field f) throws DataAccessException;
}

65
spring-data-commons-aspects/src/main/java/org/springframework/persistence/GeneratedFieldForeignStoreKeyManager.java

@ -0,0 +1,65 @@ @@ -0,0 +1,65 @@
package org.springframework.persistence;
import java.lang.reflect.Field;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
/**
* Stores keys in generated additional persistent fields
* that Roo will add. e.g.
*
* <pre>
* atForeignStore
* Person Person;
*
* long person_id;
*
* </pre>
* @author
*
*/
public class GeneratedFieldForeignStoreKeyManager extends
OrderedForeignStoreKeyManager<Roo_GeneratedForeignStoreKeys> {
private final Log log = LogFactory.getLog(getClass());
@Override
public Object findForeignStoreKey(Roo_GeneratedForeignStoreKeys entity, Field foreignStore, Class requiredClass)
throws DataAccessException {
String methodName = "get" + propertyName(foreignStore);
Object key = RooConventionEntityOperations.invokeNoArgMethod(entity.getClass(), methodName, entity);
log.info("FIND foreign store property " + foreignStore + " <- Entity generated String property [" + methodName + "] returned [" + key + "]");
return key;
}
@Override
public void storeForeignStoreKey(Roo_GeneratedForeignStoreKeys entity, Field foreignStore,
Object key) throws DataAccessException {
String methodName = "set" + propertyName(foreignStore);
RooConventionEntityOperations.invoke(entity.getClass(), methodName, entity, new Class<?>[] { key.getClass()}, key);
log.info("STORE foreign store property " + foreignStore + " -> Entity generated String property [" + methodName + "] with key value [" + key + "]");
}
@Override
public void clearForeignStoreKey(Roo_GeneratedForeignStoreKeys entity, Field foreignStore, Class keyClass) throws DataAccessException {
String methodName = "set" + propertyName(foreignStore);
RooConventionEntityOperations.invoke(entity.getClass(), methodName, entity, new Class<?>[] { keyClass }, null);
log.info("CKEAR foreign store property " + foreignStore + " -> Entity generated String property [" + methodName + "]");
}
@Override
public boolean isSupportedField(Class clazz, Field f) {
// Check for marker interface
return Roo_GeneratedForeignStoreKeys.class.isAssignableFrom(clazz);
}
private String propertyName(Field f) {
return "_" + f.getName() + "_Id";
}
}

20
spring-data-commons-aspects/src/main/java/org/springframework/persistence/InvalidFieldAnnotationException.java

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
package org.springframework.persistence;
import java.lang.reflect.Field;
import org.springframework.dao.InvalidDataAccessApiUsageException;
/**
* Exception thrown on an attempt to use a field with an invalid
* RelatedEntity annotation.
*
* @author Rod Johnson
*/
public class InvalidFieldAnnotationException extends
InvalidDataAccessApiUsageException {
public InvalidFieldAnnotationException(Class<?> entityClass, Field f, String reason) {
super("Field [" + f.getName() + "] has invalid RelatedEntity annotation: reason='" + reason + "'", null);
}
}

17
spring-data-commons-aspects/src/main/java/org/springframework/persistence/JpaEntityForeignStoreFieldTransience.aj

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
package org.springframework.persistence;
import javax.persistence.Transient;
import javax.persistence.Entity;
/**
* Aspect to annotate @ForeignStore fields as JPA @Transient to stop
* JPA trying to manage them itself
* @author Rod Johnson
*
*/
public privileged aspect JpaEntityForeignStoreFieldTransience {
declare @field : @RelatedEntity * (@Entity *).* : @Transient;
}

20
spring-data-commons-aspects/src/main/java/org/springframework/persistence/MappingValidator.java

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
package org.springframework.persistence;
import java.lang.reflect.Field;
import org.springframework.dao.InvalidDataAccessApiUsageException;
/**
* Interface to validate RelatedAnnotation annotation usage
* and other mapping constructs.
*
* @author Rod Johnson
*
*/
public interface MappingValidator {
void validateGet(Class<?> entityClass, Field f, RelatedEntity re) throws InvalidDataAccessApiUsageException;
void validateSetTo(Class<?> entityClass, Field f, RelatedEntity re, Object newVal) throws InvalidDataAccessApiUsageException, IllegalArgumentException;
}

45
spring-data-commons-aspects/src/main/java/org/springframework/persistence/OrderedForeignStoreKeyManager.java

@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
package org.springframework.persistence;
import java.lang.reflect.Field;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.Ordered;
import org.springframework.dao.DataAccessException;
/**
* Convenient base class for ForeignStoreKeyManager implementations that adds
* ordering support.
*
* @author Rod Johnson
*/
public abstract class OrderedForeignStoreKeyManager<T> implements ForeignStoreKeyManager<T>, Ordered {
protected final Log log = LogFactory.getLog(getClass());
private int order = Integer.MAX_VALUE;
@Override
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
/**
* Subclasses can override if they support collection management.
*/
@Override
public <K> Set<K> findForeignStoreKeySet(T entity, Field foreignStore, Class<K> keyClass) throws DataAccessException {
throw new UnsupportedOperationException();
}
@Override
public void storeForeignStoreKeySet(T entity, Field foreignStore, Set<Object> keys) throws DataAccessException {
throw new UnsupportedOperationException();
}
}

44
spring-data-commons-aspects/src/main/java/org/springframework/persistence/PresentKeyForeignStoreKeyManager.java

@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
package org.springframework.persistence;
import java.lang.reflect.Field;
import org.springframework.dao.DataAccessException;
/**
* ForeignStoreKeyManager implementation that uses the key of the present
* entity.
*
* @author Rod Johnson
*
*/
public class PresentKeyForeignStoreKeyManager extends OrderedForeignStoreKeyManager {
private final EntityOperationsLocator eoLocator;
public PresentKeyForeignStoreKeyManager(EntityOperationsLocator eoLocator) {
this.eoLocator = eoLocator;
}
@Override
public Object findForeignStoreKey(Object entity, Field foreignStore, Class requiredClass) throws DataAccessException {
EntityOperations eo = eoLocator.entityOperationsFor(entity.getClass(), foreignStore.getAnnotation(RelatedEntity.class));
return eo.findUniqueKey(entity);
}
@Override
public boolean isSupportedField(Class clazz, Field foreignStore) {
RelatedEntity fs = foreignStore.getAnnotation(RelatedEntity.class);
return fs.sameKey();
}
@Override
public void storeForeignStoreKey(Object entity, Field foreignStore, Object pk) throws DataAccessException {
// Nothing to do
}
@Override
public void clearForeignStoreKey(Object entity, Field foreignStore, Class keyClass) throws DataAccessException {
// Nothing to do
}
}

99
spring-data-commons-aspects/src/main/java/org/springframework/persistence/RooConventionEntityOperations.java

@ -0,0 +1,99 @@ @@ -0,0 +1,99 @@
package org.springframework.persistence;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.springframework.dao.DataAccessException;
/**
* Implementation of entity operations that works on any entity
* that adheres to Roo persist() and static finder conventions.
* Does not depend on Roo, merely on Roo conventions, which can
* also be implemented by hand.
*
* @author Rod Johnson
*/
public class RooConventionEntityOperations extends OrderedEntityOperations {
/**
* Utility method
*
* @param clazz
* @param methodName
* @param target
* @param args
* @return
*/
public static Object invoke(Class<?> clazz, String methodName,
Object target, Class<?>[] argTypes, Object... args) {
try {
Method m = clazz.getMethod(methodName, argTypes);
return m.invoke(target, (Object[]) args);
} catch (Exception ex) {
// TODO FIX ME
// System.out.println(ex + ": checked exceptions are stupid");
throw new IllegalArgumentException(ex);
}
}
public static Object invokeNoArgMethod(Class<?> clazz, String methodName, Object target) {
return invoke(clazz, methodName, target, (Class<?>[]) null,
(Object[]) null);
}
@Override
public Object findEntity(Class entityClass, Object pk)
throws DataAccessException {
String findMethod = "find" + entityClass.getSimpleName();
Object found = invoke(entityClass, findMethod, entityClass,
new Class<?>[] { pk.getClass() }, pk);
log.info("Lookup [" + entityClass.getName() + "] by pk=[" + pk + "] using static finder method '" +
findMethod + "' found [" + found + "]");
return found;
}
@Override
public Object findUniqueKey(Object entity) throws DataAccessException {
String idMethodName = "getId";
return invokeNoArgMethod(entity.getClass(), idMethodName, entity);
}
@Override
public boolean isTransient(Object entity) throws DataAccessException {
return findUniqueKey(entity) == null;
}
@Override
public Object makePersistent(Object owner, Object entity, Field f, RelatedEntity fs) throws DataAccessException {
if (log.isDebugEnabled()) {
log.debug("Making entity persistent: BEFORE [" + entity + "]");
}
String persistMethodName = "persist";
invokeNoArgMethod(entity.getClass(), persistMethodName, entity);
Object key = findUniqueKey(entity);
log.info("Making entity persistent: AFTER [" + entity + "]");
return key;
}
@Override
public boolean supports(Class clazz, RelatedEntity fs) {
try {
// TODO fix this
clazz.getMethod("getId");
return true;
} catch (Exception ex) {
return false;
}
}
@Override
public boolean cacheInEntity() {
return false;
}
@Override
public boolean isTransactional() {
// TODO Need to have a better test
return true;
}}

10
spring-data-commons-aspects/src/main/java/org/springframework/persistence/Roo_GeneratedForeignStoreKeys.java

@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
package org.springframework.persistence;
/**
* Tag interface introduced to objects that have introduced foreign store keys
* @author Rod Johnson
*
*/
public interface Roo_GeneratedForeignStoreKeys {
}

172
spring-data-commons-aspects/src/main/java/org/springframework/persistence/StoreSpanning.aj

@ -0,0 +1,172 @@ @@ -0,0 +1,172 @@
package org.springframework.persistence;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.reflect.FieldSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.persistence.support.DefaultManagedSet;
import org.springframework.persistence.support.ManagedSet;
import org.springframework.persistence.support.ManagedSet.ChangeListener;
/**
* Aspect to handle ForeignStore annotation indicating navigation to a
* potentially different persistence store.
*
* Can be configured via invoking init() method or through Spring
* autowiring if beans named "entityOperationsLocator" and
* "foreignStoreKeyManager" are provided.
*
* @author Rod Johnson
*/
public privileged aspect StoreSpanning {
private final Log log = LogFactory.getLog(getClass());
private EntityOperationsLocator entityOperationsLocator;
private ForeignStoreKeyManagerLocator foreignStoreKeyManagerLocator;
private MappingValidator mappingValidator;
@Autowired
public void init(EntityOperationsLocator eol, ForeignStoreKeyManagerLocator fskml) {
this.entityOperationsLocator = eol;
this.foreignStoreKeyManagerLocator = fskml;
}
@Autowired(required=false)
public void setMappingValidator(MappingValidator mv) {
this.mappingValidator = mv;
}
public pointcut foreignEntityFieldGet(Object entity, RelatedEntity fs) :
get(@RelatedEntity * *) &&
this(entity) &&
@annotation(fs);
public pointcut foreignEntityFieldSet(Object entity, RelatedEntity fs, Object newVal) :
set(@RelatedEntity * *) &&
this(entity) &&
@annotation(fs) &&
args(newVal);
@SuppressWarnings("unchecked")
Object around(Object entity, RelatedEntity fs) : foreignEntityFieldGet(entity, fs) {
Field f = ((FieldSignature) thisJoinPoint.getSignature()).getField();
log.info("GET: Handling foreign store " + f);
if (this.mappingValidator != null) {
this.mappingValidator.validateGet(entity.getClass(), f, fs);
}
Object fieldValue = proceed(entity, fs);
// What if it was set to null?
if (fieldValue != null) {
log.info("GET " + f + ": returning actual field value");
return fieldValue;
}
// Must retrieve
if (Set.class.isAssignableFrom(f.getType())) {
// TODO empty set, store class
log.info("GET " + f + ": Retrieving ManagedSet");
ForeignStoreKeyManager foreignStoreKeyManager = foreignStoreKeyManagerLocator.foreignStoreKeyManagerFor(entity.getClass(), f);
// TODO fix me, this is fragile
ParameterizedType genericType = (ParameterizedType) f.getGenericType();
Class entityClass = (Class) genericType.getActualTypeArguments()[0];
Class keyClass = entityOperationsLocator.entityOperationsFor(entityClass, fs).uniqueKeyType(entityClass);
Set keySet = foreignStoreKeyManager.findForeignStoreKeySet(entity, f, keyClass);
ManagedSet managedSet = DefaultManagedSet.fromKeySet(keySet, entityClass, entityOperationsLocator);
return managedSet;
}
else if (Collection.class.isAssignableFrom(f.getType())) {
throw new UnsupportedOperationException("Unsupported collection type " + f.getType() + " in entity class " + entity.getClass());
}
else {
return findScalarEntity(entity, f, fs);
}
}
private Object findScalarEntity(Object entity, Field f, RelatedEntity fs) {
EntityOperations eo = entityOperationsLocator.entityOperationsFor(f.getType(), fs);
Class keyType = eo.uniqueKeyType(f.getType());
ForeignStoreKeyManager foreignStoreKeyManager = foreignStoreKeyManagerLocator.foreignStoreKeyManagerFor(entity.getClass(), f);
Object pk = foreignStoreKeyManager.findForeignStoreKey(entity, f, keyType);
if (pk != null) {
log.debug("GET " + f + ": entity find for key=[" + pk + "] of class [" + pk.getClass() + "]");
Object found = eo.findEntity(f.getType(), pk);
log.info("GET " + f + ": entity find for key=[" + pk + "] found [" + found + "]");
return found;
}
else {
log.info("GET " + f + ": no key found, returning null");
return null;
}
}
// TODO handle explicit set to null
@SuppressWarnings("unchecked")
Object around(final Object entity, RelatedEntity fs, Object newVal) : foreignEntityFieldSet(entity, fs, newVal) {
final Field f = ((FieldSignature) thisJoinPoint.getSignature()).getField();
if (this.mappingValidator != null) {
this.mappingValidator.validateSetTo(entity.getClass(), f, fs, newVal);
}
log.info("SET: Handling foreign store " + f);
if (newVal != null) {
if (Set.class.isAssignableFrom(f.getType())) {
log.info("Setting set: Creating ManagedSet");
final ManagedSet managedSet = DefaultManagedSet.fromEntitySet((Set) newVal, entityOperationsLocator);
final ForeignStoreKeyManager foreignStoreKeyManager = foreignStoreKeyManagerLocator.foreignStoreKeyManagerFor(entity.getClass(), f);
foreignStoreKeyManager.storeForeignStoreKeySet(entity, f, managedSet.getKeySet());
managedSet.addListener(new ChangeListener() {
@Override
public void onDirty() {
foreignStoreKeyManager.storeForeignStoreKeySet(entity, f, managedSet.getKeySet());
}
});
return proceed(entity, fs, managedSet);
}
else if (Collection.class.isAssignableFrom(f.getType())) {
throw new UnsupportedOperationException("Unsupported collection type " + f.getType() + " in entity class " + entity.getClass());
}
else {
EntityOperations eo = handleScalarFieldSet(entity, f, fs, newVal);
// Don't store it in the entity if the entity type doesn't support
// it, for example
// because it shouldn't be read repeatedly (as with a stream)
if (!eo.cacheInEntity()) {
return null;
}
}
}
return proceed(entity, fs, newVal);
}
private EntityOperations handleScalarFieldSet(Object entity, Field f, RelatedEntity fs, Object newVal) {
EntityOperations eo = entityOperationsLocator.entityOperationsFor(f.getType(), fs);
Object pk = eo.findUniqueKey(newVal);
System.err.println("TODO: test whether current entity is persistent");
if (pk == null) {
// Entity is transient for now
log.info("SET " + f + ": no foreign store key to store; entity has no persistent identity, MAKING PERSISTENT");
pk = eo.makePersistent(entity,newVal, f, fs);
}
if (pk != null) {
ForeignStoreKeyManager foreignStoreKeyManager = foreignStoreKeyManagerLocator.foreignStoreKeyManagerFor(entity.getClass(), f);
foreignStoreKeyManager.storeForeignStoreKey(entity, f, pk);
log.info("SET " + f + ": stored foreign store key=[" + pk + "]");
}
return eo;
}
}

12
spring-data-commons-aspects/src/main/java/org/springframework/persistence/UnknownEntityClassException.java

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
package org.springframework.persistence;
import org.springframework.dao.UncategorizedDataAccessException;
public class UnknownEntityClassException extends
UncategorizedDataAccessException {
public UnknownEntityClassException(Class<?> entityClass) {
super("Unknown entity class [" + entityClass.getName() + "]", null);
}
}

17
spring-data-commons-aspects/src/main/java/org/springframework/persistence/support/ChangeSetConstructorEntityInstantiator.java

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
package org.springframework.persistence.support;
/**
* Try for a constructor taking a ChangeSet: failing that, try a no-arg
* constructor and then setChangeSet().
*
* @author Rod Johnson
*/
public class ChangeSetConstructorEntityInstantiator extends AbstractConstructorEntityInstantiator<ChangeSetBacked, ChangeSet>{
@Override
protected void setState(ChangeSetBacked entity, ChangeSet cs) {
entity.setChangeSet(cs);
}
}

94
spring-data-commons-aspects/src/main/java/org/springframework/persistence/support/ChangeSetForeignStoreKeyManager.java

@ -0,0 +1,94 @@ @@ -0,0 +1,94 @@
package org.springframework.persistence.support;
import java.lang.reflect.Field;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.dao.DataAccessException;
import org.springframework.persistence.OrderedForeignStoreKeyManager;
/**
* ForeignStoreKeyManager implementation that backs the foreign key to a
* ChangeSet.
*
* @author Thomas Risberg
* @author Rod Johnson
*/
public class ChangeSetForeignStoreKeyManager extends OrderedForeignStoreKeyManager<ChangeSetBacked> {
public static final String FOREIGN_STORE_SET_PREFIX = "S";
protected final Log log = LogFactory.getLog(getClass());
private String fieldDelimiter = ".";
private final ConversionService conversionService;
public String getFieldDelimiter() {
return fieldDelimiter;
}
public void setFieldDelimiter(String fieldDelimiter) {
this.fieldDelimiter = fieldDelimiter;
}
@Autowired
public ChangeSetForeignStoreKeyManager(ConversionService conversionService) {
this.conversionService = conversionService;
}
@Override
public void clearForeignStoreKey(ChangeSetBacked entity, Field foreignStore, Class<?> keyClass) throws DataAccessException {
String propName = propertyName(entity, foreignStore);
entity.getChangeSet().removeProperty(propName);
log.info("CLEAR foreign store property " + foreignStore + " <- ChangeSetBacked foreign key property [" + propName + "]");
}
@Override
public <K> K findForeignStoreKey(ChangeSetBacked entity, Field foreignStore, Class<K> keyClass) throws DataAccessException {
String propName = propertyName(entity, foreignStore);
System.err.println("+++ " + entity.getChangeSet().getValues());
K key = entity.getChangeSet().get(propName, keyClass, this.conversionService);
log.info("FIND foreign store property " + foreignStore + " <- ChangeSetBacked foreign key property [" + propName + "] returned ["
+ key + "]");
return key;
}
@Override
public boolean isSupportedField(Class<ChangeSetBacked> entityClass, Field foreignStore) {
return ChangeSetBacked.class.isAssignableFrom(entityClass);
}
@Override
public void storeForeignStoreKey(ChangeSetBacked entity, Field foreignStore, Object pk) throws DataAccessException {
String propName = propertyName(entity, foreignStore);
entity.getChangeSet().set(propName, pk);
log.info("STORE foreign store property " + foreignStore + " -> ChangeSetBacked foreign key property [" + propName
+ "] with key value [" + pk + "]");
}
@Override
public <K> Set<K> findForeignStoreKeySet(ChangeSetBacked entity, Field foreignStore, Class<K> keyClass) throws DataAccessException {
Set keySet = entity.getChangeSet().get(foreignStoreKeyName(foreignStore), Set.class, this.conversionService);
// if (keySet != null && !keySet.isEmpty())
// System.out.println("KeySET=**************" + keySet + ", 0th type=" + keySet.iterator().next().getClass());
return keySet;
}
private String foreignStoreKeyName(Field foreignStore) {
return FOREIGN_STORE_SET_PREFIX + getFieldDelimiter() + foreignStore.getName();
}
@Override
public void storeForeignStoreKeySet(ChangeSetBacked entity, Field foreignStore, Set<Object> keys) throws DataAccessException {
entity.getChangeSet().set(foreignStoreKeyName(foreignStore), keys);
}
private String propertyName(ChangeSetBacked rb, Field f) {
return rb.getClass().getSimpleName() + getFieldDelimiter() + f.getName();
}
}

162
spring-data-commons-aspects/src/main/java/org/springframework/persistence/support/DefaultManagedSet.java

@ -0,0 +1,162 @@ @@ -0,0 +1,162 @@
package org.springframework.persistence.support;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.springframework.dao.DataAccessException;
import org.springframework.persistence.EntityOperations;
import org.springframework.persistence.EntityOperationsLocator;
public class DefaultManagedSet implements ManagedSet {
public static DefaultManagedSet fromEntitySet(Set entitySet, EntityOperationsLocator eol) {
DefaultManagedSet dms = new DefaultManagedSet(eol);
if (entitySet != null) {
for (Object entity : entitySet) {
dms.add(entity);
}
}
return dms;
}
public static DefaultManagedSet fromKeySet(Set keySet, Class<?> entityClass, EntityOperationsLocator eol) throws DataAccessException {
DefaultManagedSet dms = new DefaultManagedSet(eol);
if (keySet != null) {
for (Object key : keySet) {
dms.keySet.add(key);
EntityOperations eo = eol.entityOperationsFor(entityClass, null);
dms.entitySet.add(eo.findEntity(entityClass, key));
}
}
return dms;
}
private final Set keySet = new HashSet();
private final Set entitySet = new HashSet();
private boolean dirty;
private List<ChangeListener> listeners = new LinkedList<ChangeListener>();
private final EntityOperationsLocator entityOperationsLocator;
private DefaultManagedSet(EntityOperationsLocator eol) {
this.entityOperationsLocator = eol;
}
@Override
public void addListener(ChangeListener l) {
this.listeners.add(l);
}
protected void publishEvent() {
this.dirty = true;
for (ChangeListener l : listeners) {
l.onDirty();
}
}
@Override
public Set getKeySet() {
return this.keySet;
}
@Override
public boolean isDirty() {
return this.dirty;
}
@Override
public boolean add(Object e) {
if (entitySet.contains(e)) {
return false;
}
EntityOperations eo = entityOperationsLocator.entityOperationsFor(e.getClass(), null);
Object key = eo.findUniqueKey(e);
if (key == null) {
eo.makePersistent(null, e, null, null);
}
key = eo.findUniqueKey(e);
keySet.add(key);
entitySet.add(e);
publishEvent();
return true;
}
@Override
public boolean addAll(Collection c) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
this.entitySet.clear();
this.keySet.clear();
publishEvent();
}
@Override
public boolean contains(Object o) {
return entitySet.contains(o);
}
@Override
public boolean containsAll(Collection c) {
return entitySet.containsAll(c);
}
@Override
public boolean isEmpty() {
return keySet.isEmpty();
}
@Override
public Iterator iterator() {
return entitySet.iterator();
}
@Override
public boolean remove(Object e) {
if (!entitySet.contains(e)) {
return false;
}
EntityOperations eo = entityOperationsLocator.entityOperationsFor(e.getClass(), null);
keySet.remove(eo.findUniqueKey(e));
entitySet.remove(e);
publishEvent();
return true;
}
@Override
public boolean removeAll(Collection c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection c) {
throw new UnsupportedOperationException();
}
@Override
public int size() {
return keySet.size();
}
@Override
public Object[] toArray() {
throw new UnsupportedOperationException();
}
@Override
public Object[] toArray(Object[] a) {
throw new UnsupportedOperationException();
}
}

27
spring-data-commons-aspects/src/main/java/org/springframework/persistence/support/ManagedSet.java

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
package org.springframework.persistence.support;
import java.util.Set;
public interface ManagedSet extends Set {
Set getKeySet();
void addListener(ChangeListener l);
boolean isDirty();
interface ChangeListener {
void onDirty();
}
// TODO move into managed collection
// TODO insertions, deletions
// after markSynchronized()
// This may be wrong, shouldn't it give something back for a ChangeSet?
// void retrieve(EntityOperationsLocator eol) throws DataAccessException;
//
// void persist(EntityOperationsLocator eol) throws DataAccessException;
}

34
spring-data-commons-aspects/src/main/java/org/springframework/persistence/support/SimpleMappingValidator.java

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
package org.springframework.persistence.support;
import java.lang.reflect.Field;
import javax.validation.constraints.NotNull;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.persistence.AsynchStoreCompletionListener;
import org.springframework.persistence.InvalidFieldAnnotationException;
import org.springframework.persistence.MappingValidator;
import org.springframework.persistence.RelatedEntity;
// TODO fancier version could discover many rules, with annotations etc.
// Also invoke the relevant EntityOperations
public class SimpleMappingValidator implements MappingValidator {
@Override
public void validateGet(Class<?> entityClass, Field f, RelatedEntity re) throws InvalidDataAccessApiUsageException {
// Validate the annotation
if (!AsynchStoreCompletionListener.NONE.class.equals(re.storeCompletionListenerClass())
&& !"".equals(re.storeCompletionListenerBeanName())) {
throw new InvalidFieldAnnotationException(entityClass, f,
"Can't have storeCompletionListener class and bean name on same annotation");
}
}
public void validateSetTo(Class<?> entityClass, Field f, RelatedEntity re, Object newVal) throws InvalidDataAccessApiUsageException,
IllegalArgumentException {
if (newVal == null && f.isAnnotationPresent(NotNull.class)) {
throw new IllegalArgumentException("Can't set non-null field [" + f.getName() + " to null");
}
}
}

7
spring-data-commons-aspects/template.mf

@ -5,14 +5,19 @@ Bundle-ManifestVersion: 2 @@ -5,14 +5,19 @@ Bundle-ManifestVersion: 2
Import-Package:
sun.reflect;version="0";resolution:=optional
Excluded-Imports:
org.springframework.persistence
org.springframework.persistence,
org.springframework.persistence.support
Import-Template:
org.springframework.beans.*;version="[3.0.0, 4.0.0)",
org.springframework.context.*;version="[3.0.0, 4.0.0)",
org.springframework.core.*;version="[3.0.0, 4.0.0)",
org.springframework.dao.*;version="[3.0.0, 4.0.0)",
org.springframework.orm.*;version="[3.0.0, 4.0.0)",
org.springframework.transaction..*;version="[3.0.0, 4.0.0)",
org.springframework.util.*;version="[3.0.0, 4.0.0)",
org.springframework.data.core.*;version="[1.0.0, 2.0.0)",
javax.validation.*;version="[1.0.0, 2.0.0)";resolution:=optional,
javax.persistence.*;version="[1.0.0, 3.0.0)";resolution:=optional,
org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional,
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
org.aspectj.*;version="[1.6.5, 2.0.0)",

Loading…
Cancel
Save