pruned unused cross-store prototype classes; changed package names
This commit is contained in:
1
pom.xml
1
pom.xml
@@ -11,7 +11,6 @@
|
||||
<modules>
|
||||
<module>spring-data-commons-parent</module>
|
||||
<module>spring-data-commons-core</module>
|
||||
<module>spring-data-commons-aspects</module>
|
||||
</modules>
|
||||
|
||||
<developers>
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry including="**/*.aj|**/*.java" kind="src" output="target/classes" path="src/main/java"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
|
||||
<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>spring-data-commons-aspects</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.ajdt.core.ajbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.maven.ide.eclipse.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.ajdt.ui.ajnature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.maven.ide.eclipse.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -1,13 +0,0 @@
|
||||
#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
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||
org.eclipse.jdt.core.compiler.source=1.6
|
||||
@@ -1,9 +0,0 @@
|
||||
#Wed Nov 17 12:20:40 EST 2010
|
||||
activeProfiles=
|
||||
eclipse.preferences.version=1
|
||||
fullBuildGoals=process-test-resources
|
||||
includeModules=false
|
||||
resolveWorkspaceProjects=true
|
||||
resourceFilterGoals=process-resources resources\:testResources
|
||||
skipCompilerPlugin=true
|
||||
version=1
|
||||
@@ -1,186 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-commons-parent</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../spring-data-commons-parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-data-commons-aspects</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Data Commons Aspects</name>
|
||||
<properties>
|
||||
<aspectj.version>1.6.11.M2</aspectj.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
|
||||
<!-- Spring -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aspects</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Data -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-commons-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Logging -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>javax.mail</groupId>
|
||||
<artifactId>mail</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.jms</groupId>
|
||||
<artifactId>jms</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jdmk</groupId>
|
||||
<artifactId>jmxtools</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jmx</groupId>
|
||||
<artifactId>jmxri</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<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>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
<artifactId>hibernate-jpa-2.0-api</artifactId>
|
||||
<version>1.0.0.Final</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjrt</artifactId>
|
||||
<version>${aspectj.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-maven-snapshot</id>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<name>Springframework Maven SNAPSHOT Repository</name>
|
||||
<url>http://maven.springframework.org/snapshot</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jboss-repository</id>
|
||||
<name>JBoss Public Repository</name>
|
||||
<url>http://repository.jboss.org/nexus/content/groups/public-jboss</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-maven-milestone</id>
|
||||
<name>Springframework Maven Milestone Repository</name>
|
||||
<url>http://maven.springframework.org/milestone</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>aspectj-maven-plugin</artifactId>
|
||||
<version>1.2</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjrt</artifactId>
|
||||
<version>${aspectj.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjtools</artifactId>
|
||||
<version>${aspectj.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<outxml>true</outxml>
|
||||
<aspectLibraries>
|
||||
<aspectLibrary>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aspects</artifactId>
|
||||
</aspectLibrary>
|
||||
</aspectLibraries>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.springsource.bundlor</groupId>
|
||||
<artifactId>com.springsource.bundlor.maven</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,32 +0,0 @@
|
||||
package org.springframework.persistence;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* Listener interface for asynchronous storage operations.
|
||||
* Can be annotated with OnlyOnFailure as an optimization
|
||||
* if the listener is only interested in compensating transactions
|
||||
* in the event of write failure.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*
|
||||
* @param <V> new value type
|
||||
*/
|
||||
public interface AsynchStoreCompletionListener<V> {
|
||||
|
||||
/**
|
||||
* Constant indicating no store completion action
|
||||
*/
|
||||
class NONE implements AsynchStoreCompletionListener<Object> {
|
||||
public void onCompletion(AsynchStoreCompletionListener.StoreResult result, Object newValue, Field foreignStore) {}
|
||||
}
|
||||
|
||||
enum StoreResult {
|
||||
SUCCESS,
|
||||
FAILURE,
|
||||
INDETERMINATE
|
||||
};
|
||||
|
||||
void onCompletion(StoreResult result, V newValue, Field foreignStore);
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
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;
|
||||
}}
|
||||
@@ -1,78 +0,0 @@
|
||||
package org.springframework.persistence;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
/**
|
||||
* Interface to be implemented for each persistence technology,
|
||||
* handling operations for the relevant entity type.
|
||||
* Parameters: Key=K, Entity class=E
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public interface EntityOperations<K,E> {
|
||||
|
||||
/**
|
||||
* Is this clazz supported by the current EntityOperations?
|
||||
* @param entityClass
|
||||
* @param fs ForeignStore annotation, may be null
|
||||
* @return
|
||||
*/
|
||||
boolean supports(Class<?> entityClass, RelatedEntity fs);
|
||||
|
||||
/**
|
||||
* Return null if not found
|
||||
* @param <T>
|
||||
* @param entityClass
|
||||
* @param pk
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
E findEntity(Class<E> entityClass, K pk) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Find the unique key for the given entity whose class this EntityOperations
|
||||
* understands. For example, it might be the id property value.
|
||||
* @param entity
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
K findUniqueKey(E entity) throws DataAccessException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param entityClass
|
||||
* @return the type of the unique key for this supported entity
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
Class<?> uniqueKeyType(Class<K> entityClass) throws DataAccessException;
|
||||
|
||||
boolean isTransient(E entity) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Persist. Will cause key to be non-null.
|
||||
* @param owner Persistent root entity, which has the RelatedEntity field
|
||||
* @param entity
|
||||
* @param f Foreign store field for entity being persisted
|
||||
* @param fs ForeignStore annotation
|
||||
* @throws DataAccessException
|
||||
* @return the new unique key
|
||||
*/
|
||||
K makePersistent(Object owner, E entity, Field f, RelatedEntity fs) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Is this type of entity transactional?
|
||||
* @return
|
||||
*/
|
||||
boolean isTransactional();
|
||||
|
||||
/**
|
||||
* Should the field be cached in the entity? For some entity types
|
||||
* such as streams, there should be no caching, and the value
|
||||
* should be retrieved from the persistent store every time.
|
||||
* @return
|
||||
*/
|
||||
boolean cacheInEntity();
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
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;
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
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;
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
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;
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
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;
|
||||
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
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;
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package org.springframework.persistence;
|
||||
|
||||
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 EntityOperations implementations
|
||||
* that adds ordering support.
|
||||
* @author Rod Johnson
|
||||
*
|
||||
* @param <K>
|
||||
* @param <E>
|
||||
*/
|
||||
public abstract class OrderedEntityOperations<K, E> implements EntityOperations<K, E>, 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient default. Subclasses with non-Long key types can override this if they wish.
|
||||
*/
|
||||
@Override
|
||||
public Class<?> uniqueKeyType(Class<K> entityClass) throws DataAccessException {
|
||||
return Long.class;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package org.springframework.persistence;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation indicating that a field may be stored in a foreign store
|
||||
* and specifying the necessary guarantees. Conceptual rather than
|
||||
* implementation-specific.
|
||||
* @see ForeignStoreKeyManager
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface RelatedEntity {
|
||||
|
||||
/**
|
||||
*
|
||||
* Optional information as to how to compute or locate the key value.
|
||||
* Some strategies may take this into account.
|
||||
*/
|
||||
String keyExpression() default "";
|
||||
|
||||
/**
|
||||
* Should we use the key of the present entity
|
||||
* @return
|
||||
*/
|
||||
boolean sameKey() default false;
|
||||
|
||||
/**
|
||||
* Policies for persistence
|
||||
* @return
|
||||
*/
|
||||
PersistencePolicy policy() default @PersistencePolicy();
|
||||
|
||||
/**
|
||||
* Name for the preferred data store. Merely a hint. May not be followed.
|
||||
* @return
|
||||
*/
|
||||
String preferredStore() default "";
|
||||
|
||||
/**
|
||||
* Is asynchronous store OK?
|
||||
* @return
|
||||
*/
|
||||
boolean asynchStore() default false;
|
||||
|
||||
// TODO - indicates if an asynchronous write should begin
|
||||
// only after commit of a transaction
|
||||
boolean afterCommit() default false;
|
||||
|
||||
/**
|
||||
* Completion listener class. Only used if asynchStore is true.
|
||||
* Must have a no-arg constructor.
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends AsynchStoreCompletionListener> storeCompletionListenerClass() default AsynchStoreCompletionListener.NONE.class;
|
||||
|
||||
String storeCompletionListenerBeanName() default "";
|
||||
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
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;
|
||||
}}
|
||||
@@ -1,10 +0,0 @@
|
||||
package org.springframework.persistence;
|
||||
|
||||
/**
|
||||
* Tag interface introduced to objects that have introduced foreign store keys
|
||||
* @author Rod Johnson
|
||||
*
|
||||
*/
|
||||
public interface Roo_GeneratedForeignStoreKeys {
|
||||
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
package org.springframework.persistence.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.aspectj.lang.reflect.FieldSignature;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
* Aspect that saves field access in a ChangeSet
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Thomas Risberg
|
||||
*/
|
||||
public abstract aspect AbstractDeferredUpdateMixinFields<ET extends Annotation> extends AbstractTypeAnnotatingMixinFields<ET, ChangeSetBacked> {
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Configure aspect for whole system.
|
||||
// init() method can be invoked automatically if the aspect is a Spring
|
||||
// bean, or called in user code.
|
||||
//-------------------------------------------------------------------------
|
||||
// Aspect shared config
|
||||
|
||||
private ChangeSetPersister<Object> changeSetPersister;
|
||||
|
||||
private ChangeSetSynchronizer<ChangeSetBacked> changeSetManager;
|
||||
|
||||
public void setChangeSetConfiguration(ChangeSetConfiguration<Object> changeSetConfiguration) {
|
||||
this.changeSetPersister = changeSetConfiguration.getChangeSetPersister();
|
||||
this.changeSetManager = changeSetConfiguration.getChangeSetManager();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Advise user-defined constructors of ChangeSetBacked objects to create a new
|
||||
// backing ChangeSet
|
||||
//-------------------------------------------------------------------------
|
||||
pointcut arbitraryUserConstructorOfChangeSetBackedObject(ChangeSetBacked entity) :
|
||||
execution((@ET ChangeSetBacked+).new(..)) &&
|
||||
!execution((@ET ChangeSetBacked+).new(ChangeSet)) &&
|
||||
this(entity);
|
||||
|
||||
// Or could use cflow
|
||||
pointcut finderConstructorOfChangeSetBackedObject(ChangeSetBacked entity, ChangeSet cs) :
|
||||
execution((@ET ChangeSetBacked+).new(ChangeSet)) &&
|
||||
this(entity) &&
|
||||
args(cs);
|
||||
|
||||
|
||||
before(ChangeSetBacked entity) : arbitraryUserConstructorOfChangeSetBackedObject(entity) {
|
||||
entity.itdChangeSetPersister = changeSetPersister;
|
||||
log.info("User-defined constructor called on ChangeSetBacked object of class " + entity.getClass());
|
||||
// Populate all properties
|
||||
ChangeSet changeSet = new HashMapChangeSet();
|
||||
changeSetManager.populateChangeSet(changeSet, entity);
|
||||
entity.setChangeSet(changeSet);
|
||||
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
throw new InvalidDataAccessResourceUsageException("No transaction synchronization is active");
|
||||
}
|
||||
TransactionSynchronizationManager.registerSynchronization(new ChangedSetBackedTransactionSynchronization(changeSetPersister, entity));
|
||||
}
|
||||
|
||||
before(ChangeSetBacked entity, ChangeSet changeSet) : finderConstructorOfChangeSetBackedObject(entity, changeSet) {
|
||||
entity.itdChangeSetPersister = changeSetPersister;
|
||||
changeSetManager.populateEntity(changeSet, entity);
|
||||
|
||||
// Now leave an empty ChangeSet to listen only to future changes
|
||||
entity.setChangeSet(new HashMapChangeSet());
|
||||
|
||||
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
throw new InvalidDataAccessResourceUsageException("No transaction synchronization is active");
|
||||
}
|
||||
TransactionSynchronizationManager.registerSynchronization(new ChangedSetBackedTransactionSynchronization(changeSetPersister, entity));
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// ChangeSet-related mixins
|
||||
//-------------------------------------------------------------------------
|
||||
// Introduced field
|
||||
private ChangeSet ChangeSetBacked.changeSet;
|
||||
|
||||
private ChangeSetPersister<?> ChangeSetBacked.itdChangeSetPersister;
|
||||
|
||||
public void ChangeSetBacked.setChangeSet(ChangeSet cs) {
|
||||
this.changeSet = cs;
|
||||
}
|
||||
|
||||
public ChangeSet ChangeSetBacked.getChangeSet() {
|
||||
return changeSet;
|
||||
}
|
||||
|
||||
// Flush the entity state to the persistent store
|
||||
public void ChangeSetBacked.flush() {
|
||||
itdChangeSetPersister.persistState(this.getClass(), this.changeSet);
|
||||
}
|
||||
|
||||
public Object ChangeSetBacked.getId() {
|
||||
return itdChangeSetPersister.getPersistentId(this.getClass(), this.changeSet);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Around advice for field get/set
|
||||
//-------------------------------------------------------------------------
|
||||
// Nothing to do on field get unless laziness desired
|
||||
|
||||
Object around(ChangeSetBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
|
||||
Field f = ((FieldSignature) thisJoinPoint.getSignature()).getField();
|
||||
|
||||
String propName = f.getName();//getRedisPropertyName(thisJoinPoint.getSignature());
|
||||
if (newVal instanceof Number) {
|
||||
log.info("SET " + f + " -> ChangeSet number value property [" + propName + "] with value=[" + newVal + "]");
|
||||
entity.getChangeSet().set(propName, (Number) newVal);
|
||||
}
|
||||
else if (newVal instanceof String) {
|
||||
log.info("SET " + f + " -> ChangeSet string value property [" + propName + "] with value=[" + newVal + "]");
|
||||
entity.getChangeSet().set(propName, (String) newVal);
|
||||
}
|
||||
else {
|
||||
log.info("Don't know how to SET " + f + " with value=[" + newVal + "]");
|
||||
}
|
||||
return proceed(entity, newVal);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package org.springframework.persistence.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.annotation.Configurable;
|
||||
import org.springframework.persistence.RelatedEntity;
|
||||
|
||||
/**
|
||||
* Abstract superaspect to advise field read and write
|
||||
* and introduce a mixin interface.
|
||||
*
|
||||
* @param <N> type of introduced interface
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
privileged abstract public aspect AbstractMixinFields<N> {
|
||||
|
||||
protected final Log log = LogFactory.getLog(getClass());
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// ITDs to add behavior and state to classes
|
||||
//-------------------------------------------------------------------------
|
||||
// Enable Spring DI for all mixed-in objects
|
||||
declare @type: N+: @Configurable;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Advice for field get/set to delegate to backing Node.
|
||||
//-------------------------------------------------------------------------
|
||||
protected pointcut entityFieldGet(N entity) :
|
||||
get(* N+.*) &&
|
||||
this(entity) &&
|
||||
!(get(@RelatedEntity * *)
|
||||
|| get(* N.*)
|
||||
|| getsNotToAdvise());
|
||||
|
||||
/**
|
||||
* Never matches. Subclasses can override to exempt certain field reads from advice
|
||||
*/
|
||||
protected pointcut getsNotToAdvise();
|
||||
|
||||
protected pointcut entityFieldSet(N entity, Object newVal) :
|
||||
set(* N+.*) &&
|
||||
this(entity) &&
|
||||
args(newVal) &&
|
||||
!(set(@RelatedEntity * *) ||
|
||||
set(* N.*) ||
|
||||
setsNotToAdvise());
|
||||
|
||||
/**
|
||||
* Never matches. Subclasses can override to exempt certain field writes from advice
|
||||
*/
|
||||
protected pointcut setsNotToAdvise();
|
||||
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package org.springframework.persistence.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
/**
|
||||
* Abstract superaspect for aspects that advice
|
||||
* field access with a mixin for all types annotated with
|
||||
* a given annotation.
|
||||
*
|
||||
* @param <ET> annotation on entity
|
||||
* @param <N> type of introduced interface
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
privileged abstract public aspect AbstractTypeAnnotatingMixinFields<ET extends Annotation, N>
|
||||
extends AbstractMixinFields<N> {
|
||||
|
||||
// ITD to introduce N state to Annotated objects
|
||||
declare parents : (@ET *) implements N;
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package org.springframework.persistence.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
|
||||
/**
|
||||
* Interface representing the set of changes in an entity.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Thomas Risberg
|
||||
*
|
||||
*/
|
||||
public interface ChangeSet {
|
||||
|
||||
<T> T get(String key, Class<T> requiredClass, ConversionService cs);
|
||||
|
||||
void set(String key, Object o);
|
||||
|
||||
Map<String, Object> getValues();
|
||||
|
||||
Object removeProperty(String k);
|
||||
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package org.springframework.persistence.support;
|
||||
|
||||
|
||||
/**
|
||||
* Interface introduced to objects exposing ChangeSet information
|
||||
* @author Rod Johnson
|
||||
* @author Thomas Risberg
|
||||
*/
|
||||
public interface ChangeSetBacked {
|
||||
|
||||
ChangeSet getChangeSet();
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
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>{
|
||||
|
||||
protected void setState(ChangeSetBacked entity, ChangeSet cs) {
|
||||
entity.setChangeSet(cs);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package org.springframework.persistence.support;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by classes that can synchronize
|
||||
* between data stores and ChangeSets.
|
||||
* @author Rod Johnson
|
||||
*
|
||||
* @param <K> entity key
|
||||
*/
|
||||
public interface ChangeSetPersister<K> {
|
||||
|
||||
String ID_KEY = "_id";
|
||||
|
||||
String CLASS_KEY = "_class";
|
||||
|
||||
/**
|
||||
* TODO how to tell when not found? throw exception?
|
||||
*/
|
||||
void getPersistentState(Class<? extends ChangeSetBacked> entityClass, K key, ChangeSet changeSet) throws DataAccessException, NotFoundException;
|
||||
|
||||
/**
|
||||
* Return id
|
||||
* @param cs
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
K getPersistentId(Class<? extends ChangeSetBacked> entityClass, ChangeSet cs) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Return key
|
||||
* @param cs Key may be null if not persistent
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
K persistState(Class<? extends ChangeSetBacked> entityClass, ChangeSet cs) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Exception thrown in alternate control flow if getPersistentState
|
||||
* finds no entity data.
|
||||
*/
|
||||
class NotFoundException extends Exception {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package org.springframework.persistence.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by classes that can synchronize
|
||||
* between entities and ChangeSets.
|
||||
* @author Rod Johnson
|
||||
*
|
||||
* @param <E>
|
||||
*/
|
||||
public interface ChangeSetSynchronizer<E extends ChangeSetBacked> {
|
||||
|
||||
Map<String, Class<?>> persistentFields(Class<? extends E> entityClassClass);
|
||||
|
||||
/**
|
||||
* Take all entity fields into a changeSet.
|
||||
* @param entity
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
void populateChangeSet(ChangeSet changeSet, E entity) throws DataAccessException;
|
||||
|
||||
void populateEntity(ChangeSet changeSet, E entity) throws DataAccessException;
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package org.springframework.persistence.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
|
||||
public class ChangedSetBackedTransactionSynchronization implements TransactionSynchronization {
|
||||
|
||||
protected final Log log = LogFactory.getLog(getClass());
|
||||
|
||||
private ChangeSetPersister<Object> changeSetPersister;
|
||||
|
||||
private ChangeSetBacked entity;
|
||||
|
||||
private int changeSetTxStatus = -1;
|
||||
|
||||
public ChangedSetBackedTransactionSynchronization(ChangeSetPersister<Object> changeSetPersister, ChangeSetBacked entity) {
|
||||
this.changeSetPersister = changeSetPersister;
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
log.debug("After Commit called for " + entity);
|
||||
changeSetPersister.persistState(entity.getClass(), entity.getChangeSet());
|
||||
changeSetTxStatus = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(int status) {
|
||||
log.debug("After Completion called with status = " + status);
|
||||
if (changeSetTxStatus == 0) {
|
||||
if (status == STATUS_COMMITTED) {
|
||||
// this is good
|
||||
log.debug("ChangedSetBackedTransactionSynchronization completed successfully for " + this.entity);
|
||||
}
|
||||
else {
|
||||
// this could be bad - TODO: compensate
|
||||
log.error("ChangedSetBackedTransactionSynchronization failed for " + this.entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeCommit(boolean readOnly) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeCompletion() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume() {
|
||||
throw new IllegalStateException("ChangedSetBackedTransactionSynchronization does not support transaction suspension currently.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suspend() {
|
||||
throw new IllegalStateException("ChangedSetBackedTransactionSynchronization does not support transaction suspension currently.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package org.springframework.persistence.support;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
|
||||
/**
|
||||
* Simple ChangeSet implementation backed by a HashMap.
|
||||
* @author Thomas Risberg
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public class HashMapChangeSet implements ChangeSet {
|
||||
|
||||
private Map<String, Object> values;
|
||||
|
||||
public HashMapChangeSet(Map<String,Object> values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public HashMapChangeSet() {
|
||||
this(new HashMap<String, Object>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String key, Object o) {
|
||||
values.put(key, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HashMapChangeSet: values=[" + values + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getValues() {
|
||||
return Collections.unmodifiableMap(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object removeProperty(String k) {
|
||||
return this.values.remove(k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T get(String key, Class<T> requiredClass, ConversionService conversionService) {
|
||||
return conversionService.convert(values.get(key), requiredClass);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
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;
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package org.springframework.persistence.support;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
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.RelatedEntity;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.FieldCallback;
|
||||
import org.springframework.util.ReflectionUtils.FieldFilter;
|
||||
|
||||
/**
|
||||
* Synchronizes fields to ChangeSets, regardless of visibility.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public class SimpleReflectiveChangeSetSynchronizer implements ChangeSetSynchronizer<ChangeSetBacked> {
|
||||
|
||||
/**
|
||||
* Filter matching infrastructure fields, so they can be excluded
|
||||
*/
|
||||
private static FieldFilter PERSISTABLE_FIELDS = new FieldFilter() {
|
||||
@Override
|
||||
public boolean matches(Field f) {
|
||||
return !(
|
||||
f.isSynthetic() ||
|
||||
Modifier.isStatic(f.getModifiers()) ||
|
||||
Modifier.isTransient(f.getModifiers()) ||
|
||||
f.getName().startsWith("ajc$") ||
|
||||
f.isAnnotationPresent(RelatedEntity.class)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
private final Log log = LogFactory.getLog(getClass());
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
@Autowired
|
||||
public SimpleReflectiveChangeSetSynchronizer(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Class<?>> persistentFields(Class<? extends ChangeSetBacked> entityClass) {
|
||||
final Map<String, Class<?>> fields = new HashMap<String, Class<?>>();
|
||||
ReflectionUtils.doWithFields(entityClass, new FieldCallback() {
|
||||
@Override
|
||||
public void doWith(Field f) throws IllegalArgumentException, IllegalAccessException {
|
||||
fields.put(f.getName(), f.getType());
|
||||
}
|
||||
}, PERSISTABLE_FIELDS);
|
||||
return fields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateChangeSet(final ChangeSet changeSet, final ChangeSetBacked entity) throws DataAccessException {
|
||||
ReflectionUtils.doWithFields(entity.getClass(), new FieldCallback() {
|
||||
@Override
|
||||
public void doWith(Field f) throws IllegalArgumentException, IllegalAccessException {
|
||||
f.setAccessible(true);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("POPULATE ChangeSet value from entity field: " + f);
|
||||
}
|
||||
changeSet.set(f.getName(), f.get(entity));
|
||||
}
|
||||
}, PERSISTABLE_FIELDS);
|
||||
String classShortName = ClassUtils.getShortName(entity.getClass());
|
||||
changeSet.set("_class", classShortName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateEntity(final ChangeSet changeSet, final ChangeSetBacked entity) throws DataAccessException {
|
||||
ReflectionUtils.doWithFields(entity.getClass(), new FieldCallback() {
|
||||
@Override
|
||||
public void doWith(Field f) throws IllegalArgumentException, IllegalAccessException {
|
||||
if (changeSet.getValues().containsKey(f.getName())) {
|
||||
f.setAccessible(true);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("POPULATE entity from ChangeSet for field: " + f);
|
||||
}
|
||||
Object val = changeSet.get(f.getName(), f.getType(), conversionService);
|
||||
f.set(entity, val);
|
||||
}
|
||||
}
|
||||
}, PERSISTABLE_FIELDS);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
Bundle-SymbolicName: org.springframework.data.commons.aspects
|
||||
Bundle-Name: Spring Data Commons Aspects
|
||||
Bundle-Vendor: SpringSource
|
||||
Bundle-ManifestVersion: 2
|
||||
Import-Package:
|
||||
sun.reflect;version="0";resolution:=optional
|
||||
Excluded-Imports:
|
||||
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)",
|
||||
org.w3c.dom.*;version="0"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.persistence.support;
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.data.support;
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.data.support;
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
|
||||
/**
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.persistence.support;
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
public class ChangeSetConfiguration<T> {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.data.support;
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.data.support;
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.persistence.support;
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
|
||||
/**
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.data.support;
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.persistence.support;
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
/**
|
||||
* @author Michael Hunger
|
||||
@@ -1,13 +0,0 @@
|
||||
package org.springframework.data.serialization;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface SerializationStore {
|
||||
|
||||
}
|
||||
@@ -2,8 +2,8 @@ package org.springframework.data.transaction;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.support.ChangeSetBacked;
|
||||
import org.springframework.data.support.ChangeSetPersister;
|
||||
import org.springframework.data.persistence.ChangeSetBacked;
|
||||
import org.springframework.data.persistence.ChangeSetPersister;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
|
||||
public class ChangeSetBackedTransactionSynchronization implements TransactionSynchronization {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.persistence.transaction;
|
||||
package org.springframework.data.transaction;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
@@ -1,32 +0,0 @@
|
||||
package org.springframework.persistence;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* Listener interface for asynchronous storage operations.
|
||||
* Can be annotated with OnlyOnFailure as an optimization
|
||||
* if the listener is only interested in compensating transactions
|
||||
* in the event of write failure.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*
|
||||
* @param <V> new value type
|
||||
*/
|
||||
public interface AsynchStoreCompletionListener<V> {
|
||||
|
||||
/**
|
||||
* Constant indicating no store completion action
|
||||
*/
|
||||
class NONE implements AsynchStoreCompletionListener<Object> {
|
||||
public void onCompletion(AsynchStoreCompletionListener.StoreResult result, Object newValue, Field foreignStore) {}
|
||||
}
|
||||
|
||||
enum StoreResult {
|
||||
SUCCESS,
|
||||
FAILURE,
|
||||
INDETERMINATE
|
||||
};
|
||||
|
||||
void onCompletion(StoreResult result, V newValue, Field foreignStore);
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package org.springframework.persistence;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Persistence policies that can be attached to entities or relationship
|
||||
* fields.
|
||||
* @author rodjohnson
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.FIELD})
|
||||
public @interface PersistencePolicy {
|
||||
|
||||
enum LATENCY_SENSITIVITY { NONE, MEDIUM, HIGH };
|
||||
|
||||
boolean largeObject() default false;
|
||||
|
||||
boolean queryable() default true;
|
||||
|
||||
boolean immutable() default false;
|
||||
|
||||
boolean transactional() default true;
|
||||
|
||||
boolean lossAcceptable() default false;
|
||||
|
||||
// TODO freshness, or should this be handled separately in a caching annotation
|
||||
|
||||
LATENCY_SENSITIVITY latencySensitivity() default LATENCY_SENSITIVITY.HIGH;
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package org.springframework.persistence;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation indicating that a field may be stored in a foreign store
|
||||
* and specifying the necessary guarantees. Conceptual rather than
|
||||
* implementation-specific.
|
||||
* @see ForeignStoreKeyManager
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface RelatedEntity {
|
||||
|
||||
/**
|
||||
*
|
||||
* Optional information as to how to compute or locate the key value.
|
||||
* Some strategies may take this into account.
|
||||
*/
|
||||
String keyExpression() default "";
|
||||
|
||||
/**
|
||||
* Should we use the key of the present entity
|
||||
* @return
|
||||
*/
|
||||
boolean sameKey() default false;
|
||||
|
||||
/**
|
||||
* Policies for persistence
|
||||
* @return
|
||||
*/
|
||||
PersistencePolicy policy() default @PersistencePolicy();
|
||||
|
||||
/**
|
||||
* Name for the preferred data store. Merely a hint. May not be followed.
|
||||
* @return
|
||||
*/
|
||||
String preferredStore() default "";
|
||||
|
||||
/**
|
||||
* Is asynchronous store OK?
|
||||
* @return
|
||||
*/
|
||||
boolean asynchStore() default false;
|
||||
|
||||
// TODO - indicates if an asynchronous write should begin
|
||||
// only after commit of a transaction
|
||||
boolean afterCommit() default false;
|
||||
|
||||
/**
|
||||
* Completion listener class. Only used if asynchStore is true.
|
||||
* Must have a no-arg constructor.
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends AsynchStoreCompletionListener> storeCompletionListenerClass() default AsynchStoreCompletionListener.NONE.class;
|
||||
|
||||
String storeCompletionListenerBeanName() default "";
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user