From fb2c81efdde4a5cafc0e601ebc87a20bc26d5e30 Mon Sep 17 00:00:00 2001 From: trisberg Date: Tue, 27 Jul 2010 18:00:20 -0400 Subject: [PATCH] first part of moving over cross-store classes/aspect; added aspectj compile support --- .classpath | 10 ++- .project | 73 +++++++++---------- pom.xml | 50 +++++++++++++ .../AsynchStoreCompletionListener.java | 32 ++++++++ .../persistence/PersistencePolicy.java | 34 +++++++++ .../persistence/RelatedEntity.java | 65 +++++++++++++++++ .../support/AbstractMixinFields.aj | 54 ++++++++++++++ .../AbstractTypeAnnotatingMixinFields.aj | 21 ++++++ .../support/EntityInstantiator.java | 32 ++++++++ 9 files changed, 332 insertions(+), 39 deletions(-) create mode 100644 src/main/java/org/springframework/persistence/AsynchStoreCompletionListener.java create mode 100644 src/main/java/org/springframework/persistence/PersistencePolicy.java create mode 100644 src/main/java/org/springframework/persistence/RelatedEntity.java create mode 100644 src/main/java/org/springframework/persistence/support/AbstractMixinFields.aj create mode 100644 src/main/java/org/springframework/persistence/support/AbstractTypeAnnotatingMixinFields.aj create mode 100644 src/main/java/org/springframework/persistence/support/EntityInstantiator.java diff --git a/.classpath b/.classpath index bf4230d7a..322cd924e 100644 --- a/.classpath +++ b/.classpath @@ -1,6 +1,7 @@ - + + @@ -13,7 +14,11 @@ - + + + + + @@ -22,4 +27,5 @@ + \ No newline at end of file diff --git a/.project b/.project index b7475b87c..4ec64ec39 100644 --- a/.project +++ b/.project @@ -1,38 +1,37 @@ - - data-commons - - - - - - org.eclipse.wst.common.project.facet.core.builder - - - - - org.eclipse.wst.validation.validationbuilder - - - - - org.eclipse.ajdt.core.ajbuilder - - - - - org.springframework.ide.eclipse.core.springbuilder - - - - - - org.eclipse.wst.common.project.facet.core.nature - org.eclipse.jdt.core.javanature - org.eclipse.wst.common.modulecore.ModuleCoreNature - org.eclipse.jem.workbench.JavaEMFNature - org.eclipse.ajdt.ui.ajnature - com.springsource.sts.roo.core.nature - org.springframework.ide.eclipse.core.springnature - - + data-commons + + + + + org.eclipse.wst.common.project.facet.core.builder + + + org.eclipse.wst.validation.validationbuilder + + + org.eclipse.ajdt.core.ajbuilder + + + org.springframework.ide.eclipse.core.springbuilder + + + org.eclipse.ajdt.core.ajbuilder + + + aspectPath + org.springframework.aspects + + + + + + org.eclipse.wst.common.project.facet.core.nature + org.eclipse.jdt.core.javanature + org.eclipse.wst.common.modulecore.ModuleCoreNature + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.ajdt.ui.ajnature + com.springsource.sts.roo.core.nature + org.springframework.ide.eclipse.core.springnature + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index a93b778ee..b4ba2f43a 100644 --- a/pom.xml +++ b/pom.xml @@ -9,6 +9,7 @@ UTF-8 3.0.2.RELEASE + 1.6.9 1.6.0 @@ -120,6 +121,11 @@ spring-tx ${spring.version} + + org.aspectj + aspectjrt + ${aspectj.version} + org.mockito @@ -133,6 +139,13 @@ 1.8.0 test + + + + javax.persistence + persistence-api + 1.0.0 + @@ -146,6 +159,43 @@ 1.6 + + org.codehaus.mojo + aspectj-maven-plugin + 1.0 + + + + org.aspectj + aspectjrt + ${aspectj.version} + + + org.aspectj + aspectjtools + ${aspectj.version} + + + + + + compile + test-compile + + + + + true + + + org.springframework + spring-aspects + + + 1.6 + 1.6 + + org.apache.maven.plugins maven-surefire-plugin diff --git a/src/main/java/org/springframework/persistence/AsynchStoreCompletionListener.java b/src/main/java/org/springframework/persistence/AsynchStoreCompletionListener.java new file mode 100644 index 000000000..4f417bb57 --- /dev/null +++ b/src/main/java/org/springframework/persistence/AsynchStoreCompletionListener.java @@ -0,0 +1,32 @@ +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 new value type + */ +public interface AsynchStoreCompletionListener { + + /** + * Constant indicating no store completion action + */ + class NONE implements AsynchStoreCompletionListener { + public void onCompletion(AsynchStoreCompletionListener.StoreResult result, Object newValue, Field foreignStore) {} + } + + enum StoreResult { + SUCCESS, + FAILURE, + INDETERMINATE + }; + + void onCompletion(StoreResult result, V newValue, Field foreignStore); + +} diff --git a/src/main/java/org/springframework/persistence/PersistencePolicy.java b/src/main/java/org/springframework/persistence/PersistencePolicy.java new file mode 100644 index 000000000..63d91895a --- /dev/null +++ b/src/main/java/org/springframework/persistence/PersistencePolicy.java @@ -0,0 +1,34 @@ +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; + +} diff --git a/src/main/java/org/springframework/persistence/RelatedEntity.java b/src/main/java/org/springframework/persistence/RelatedEntity.java new file mode 100644 index 000000000..f9e538c82 --- /dev/null +++ b/src/main/java/org/springframework/persistence/RelatedEntity.java @@ -0,0 +1,65 @@ +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 storeCompletionListenerClass() default AsynchStoreCompletionListener.NONE.class; + + String storeCompletionListenerBeanName() default ""; + +} diff --git a/src/main/java/org/springframework/persistence/support/AbstractMixinFields.aj b/src/main/java/org/springframework/persistence/support/AbstractMixinFields.aj new file mode 100644 index 000000000..9e6473ea3 --- /dev/null +++ b/src/main/java/org/springframework/persistence/support/AbstractMixinFields.aj @@ -0,0 +1,54 @@ +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 type of introduced interface + * + * @author Rod Johnson + */ +privileged abstract public aspect AbstractMixinFields { + + 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(); + +} diff --git a/src/main/java/org/springframework/persistence/support/AbstractTypeAnnotatingMixinFields.aj b/src/main/java/org/springframework/persistence/support/AbstractTypeAnnotatingMixinFields.aj new file mode 100644 index 000000000..ba63e81e4 --- /dev/null +++ b/src/main/java/org/springframework/persistence/support/AbstractTypeAnnotatingMixinFields.aj @@ -0,0 +1,21 @@ +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 annotation on entity + * @param type of introduced interface + * + * @author Rod Johnson + */ +privileged abstract public aspect AbstractTypeAnnotatingMixinFields + extends AbstractMixinFields { + + // ITD to introduce N state to Annotated objects + declare parents : (@ET *) implements N; + +} diff --git a/src/main/java/org/springframework/persistence/support/EntityInstantiator.java b/src/main/java/org/springframework/persistence/support/EntityInstantiator.java new file mode 100644 index 000000000..f17db133e --- /dev/null +++ b/src/main/java/org/springframework/persistence/support/EntityInstantiator.java @@ -0,0 +1,32 @@ +package org.springframework.persistence.support; + + +/** + * Interface to be implemented by classes that can instantiate and + * configure entities. + * The framework must do this when creating objects resulting from finders, + * even when there may be no no-arg constructor supplied by the user. + * + * @author Rod Johnson + */ +public interface EntityInstantiator { + + /* + * The best solution if available is to add a constructor that takes Node + * to each GraphEntity. This means generating an aspect beside every + * class as Roo presently does. + * + * An alternative that does not require Roo + * is a user-authored constructor taking Node and calling setUnderlyingNode() + * but this is less elegant and pollutes the domain object. + * + * If the user supplies a no-arg constructor, instantiation can occur by invoking it + * prior to calling setUnderlyingNode(). + * + * If the user does NOT supply a no-arg constructor, we must rely on Sun-specific + * code to instantiate entities without invoking a constructor. + */ + + T createEntityFromState(STATE s, Class c); + +}