moving to datastore-graph project due to AspectJ compiler issue
This commit is contained in:
42
.settings/org.eclipse.ajdt.core.prefs
Normal file
42
.settings/org.eclipse.ajdt.core.prefs
Normal file
@@ -0,0 +1,42 @@
|
||||
#Mon Mar 29 14:00:24 PDT 2010
|
||||
eclipse.preferences.version=1
|
||||
org.aspectj.ajdt.core.compiler.BuildOptions.showweavemessages=false
|
||||
org.aspectj.ajdt.core.compiler.lint.BrokeSerialVersionCompatibility=ignore
|
||||
org.aspectj.ajdt.core.compiler.lint.CannotImplementLazyTJP=ignore
|
||||
org.aspectj.ajdt.core.compiler.lint.InvalidAbsoluteTypeName=warning
|
||||
org.aspectj.ajdt.core.compiler.lint.NeedSerialVersionUIDField=ignore
|
||||
org.aspectj.ajdt.core.compiler.lint.NoInterfaceCtorJoinpoint=warning
|
||||
org.aspectj.ajdt.core.compiler.lint.ShadowNotInStructure=ignore
|
||||
org.aspectj.ajdt.core.compiler.lint.TypeNotExposedToWeaver=warning
|
||||
org.aspectj.ajdt.core.compiler.lint.UnresolvableMember=warning
|
||||
org.aspectj.ajdt.core.compiler.lint.WildcardTypeName=ignore
|
||||
org.aspectj.ajdt.core.compiler.lint.adviceDidNotMatch=warning
|
||||
org.aspectj.ajdt.core.compiler.lint.annotationAsTargetForDecpIgnored=warning
|
||||
org.aspectj.ajdt.core.compiler.lint.calculatingSerialVersionUID=ignore
|
||||
org.aspectj.ajdt.core.compiler.lint.cantFindType=error
|
||||
org.aspectj.ajdt.core.compiler.lint.cantFindTypeAffectingJPMatch=warning
|
||||
org.aspectj.ajdt.core.compiler.lint.cantMatchArrayTypeOnVarargs=ignore
|
||||
org.aspectj.ajdt.core.compiler.lint.elementAlreadyAnnotated=warning
|
||||
org.aspectj.ajdt.core.compiler.lint.enumAsTargetForDecpIgnored=warning
|
||||
org.aspectj.ajdt.core.compiler.lint.invalidTargetForAnnotation=warning
|
||||
org.aspectj.ajdt.core.compiler.lint.multipleAdviceStoppingLazyTjp=ignore
|
||||
org.aspectj.ajdt.core.compiler.lint.noExplicitConstructorCall=warning
|
||||
org.aspectj.ajdt.core.compiler.lint.noGuardForLazyTjp=ignore
|
||||
org.aspectj.ajdt.core.compiler.lint.noJoinpointsForBridgeMethods=warning
|
||||
org.aspectj.ajdt.core.compiler.lint.runtimeExceptionNotSoftened=warning
|
||||
org.aspectj.ajdt.core.compiler.lint.swallowedExceptionInCatchBlock=ignore
|
||||
org.aspectj.ajdt.core.compiler.lint.uncheckedAdviceConversion=warning
|
||||
org.aspectj.ajdt.core.compiler.lint.uncheckedArgument=warning
|
||||
org.aspectj.ajdt.core.compiler.lint.unmatchedTargetKind=warning
|
||||
org.aspectj.ajdt.core.compiler.lint.unorderedAdviceAtShadow=ignore
|
||||
org.aspectj.ajdt.core.compiler.list.UnmatchedSuperTypeInCall=warning
|
||||
org.aspectj.ajdt.core.compiler.weaver.XHasMember=false
|
||||
org.aspectj.ajdt.core.compiler.weaver.XNoInline=false
|
||||
org.aspectj.ajdt.core.compiler.weaver.XNotReweavable=false
|
||||
org.aspectj.ajdt.core.compiler.weaver.XSerializableAspects=false
|
||||
org.aspectj.ajdt.core.compiler.weaver.timers=false
|
||||
org.aspectj.ajdt.core.compiler.weaver.verbose=false
|
||||
org.aspectj.ajdt.core.complier.lint.aspectExcludedByConfiguration=ignore
|
||||
org.eclipse.ajdt.core.builder.incrementalCompilationOptimizations=true
|
||||
org.eclipse.ajdt.core.compiler.nonStandardOptions=-Xset\:pipelineCompilation\=false -XhasMember
|
||||
org.eclipse.ajdt.core.compiler.useProjectSettings=true
|
||||
@@ -0,0 +1,189 @@
|
||||
package org.springframework.persistence.graph.neo4j;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.aspectj.lang.Signature;
|
||||
import org.aspectj.lang.reflect.FieldSignature;
|
||||
import org.neo4j.graphdb.DynamicRelationshipType;
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.RelationshipType;
|
||||
import org.neo4j.util.GraphDatabaseUtil;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.persistence.graph.Direction;
|
||||
import org.springframework.persistence.graph.GraphEntity;
|
||||
import org.springframework.persistence.graph.Relationship;
|
||||
import org.springframework.persistence.support.AbstractTypeAnnotatingMixinFields;
|
||||
import org.springframework.persistence.support.EntityInstantiator;
|
||||
|
||||
/**
|
||||
* Aspect to turn an object annotated with GraphEntity into a graph entity using Neo4J.
|
||||
* Delegates all field access (except for fields assumed to be transient)
|
||||
* to an underlying Neo4 graph node.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEntity,NodeBacked> {
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// 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 Neo4J Graph Database Service
|
||||
private GraphDatabaseService graphDatabaseService;
|
||||
|
||||
private EntityInstantiator<NodeBacked,Node> graphEntityInstantiator;
|
||||
|
||||
private GraphDatabaseUtil graphDatabaseUtil;
|
||||
|
||||
@Autowired
|
||||
public void init(GraphDatabaseService gds, EntityInstantiator<NodeBacked, Node> gei) {
|
||||
this.graphDatabaseService = gds;
|
||||
this.graphEntityInstantiator = gei;
|
||||
this.graphDatabaseUtil = new GraphDatabaseUtil(gds);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Advise user-defined constructors of NodeBacked objects to create a new
|
||||
// Neo4J backing node
|
||||
//-------------------------------------------------------------------------
|
||||
pointcut arbitraryUserConstructorOfNodeBackedObject(NodeBacked entity) :
|
||||
execution((@GraphEntity *).new(..)) &&
|
||||
!execution((@GraphEntity *).new(Node)) &&
|
||||
this(entity);
|
||||
|
||||
|
||||
// Create a new node in the Graph if no Node was passed in a constructor
|
||||
before(NodeBacked entity) : arbitraryUserConstructorOfNodeBackedObject(entity) {
|
||||
entity.setUnderlyingNode(graphDatabaseService.createNode());
|
||||
log.info("User-defined constructor called on class " + entity.getClass() + "; created Node [" + entity.getUnderlyingNode() +"]; " +
|
||||
"Updating metamodel");
|
||||
// TODO pull naming out into a strategy interface
|
||||
Node subReference = Neo4jHelper.findSubreferenceNode(entity.getClass(), graphDatabaseService);
|
||||
entity.getUnderlyingNode().createRelationshipTo(subReference, Neo4jHelper.INSTANCE_OF_RELATIONSHIP_TYPE);
|
||||
graphDatabaseUtil.incrementAndGetCounter(subReference, Neo4jHelper.SUBREFERENCE_NODE_COUNTER_KEY);
|
||||
}
|
||||
|
||||
|
||||
// Introduced field
|
||||
private Node NodeBacked.underlyingNode;
|
||||
|
||||
public void NodeBacked.setUnderlyingNode(Node n) {
|
||||
this.underlyingNode = n;
|
||||
}
|
||||
|
||||
public Node NodeBacked.getUnderlyingNode() {
|
||||
return underlyingNode;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Equals and hashCode for Neo4j entities.
|
||||
// Final to prevent overriding.
|
||||
//-------------------------------------------------------------------------
|
||||
// TODO could use template method for further checks if needed
|
||||
public final boolean NodeBacked.equals(Object obj) {
|
||||
if (obj instanceof NodeBacked) {
|
||||
return this.getUnderlyingNode().equals(((NodeBacked) obj).getUnderlyingNode());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public final int NodeBacked.hashCode() {
|
||||
return getUnderlyingNode().hashCode();
|
||||
}
|
||||
|
||||
|
||||
Object around(NodeBacked entity) : entityFieldGet(entity) {
|
||||
Field f = ((FieldSignature) thisJoinPoint.getSignature()).getField();
|
||||
|
||||
// TODO fix arrays
|
||||
if (f.getType().isPrimitive() || f.getType().equals(String.class)) {
|
||||
String propName = getNeo4jPropertyName(thisJoinPoint.getSignature());
|
||||
log.info("GET " + f + " <- Neo4J simple node property [" + propName + "]");
|
||||
return entity.getUnderlyingNode().getProperty(propName, null);
|
||||
}
|
||||
|
||||
// Look for a relationship
|
||||
if (isNeo4jRelationshipField(f)) {
|
||||
Relationship r = f.getAnnotation(Relationship.class);
|
||||
if (r == null) {
|
||||
throw new IllegalStateException("Must have @Relationship on " + f);
|
||||
}
|
||||
Node me = entity.getUnderlyingNode();
|
||||
if (me == null) {
|
||||
throw new IllegalStateException("Entity must have a backing Node");
|
||||
}
|
||||
RelationshipType type = DynamicRelationshipType.withName(r.type());
|
||||
org.neo4j.graphdb.Relationship singleRelationship = me.getSingleRelationship(type, r.direction().toNeo4jDir());
|
||||
|
||||
// TODO is this correct
|
||||
if (singleRelationship == null) {
|
||||
log.info("GET " + f + ": " + f.getType().getName() + ": not set yet so returning field value");
|
||||
return proceed(entity);
|
||||
}
|
||||
Node targetNode = singleRelationship.getOtherNode(me);
|
||||
log.info("GET " + f + ": " + f.getType().getName() + ": setting from relationship " + singleRelationship);
|
||||
return graphEntityInstantiator.createEntityFromState(targetNode, (Class<NodeBacked>) f.getType());
|
||||
}
|
||||
|
||||
// Must be transient
|
||||
log.info("Ignored GET " + f + ": " + f.getType().getName() + " not primitive or GraphEntity");
|
||||
return proceed(entity);
|
||||
}
|
||||
|
||||
|
||||
Object around(NodeBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
|
||||
Field f = ((FieldSignature) thisJoinPoint.getSignature()).getField();
|
||||
|
||||
// TODO fix arrays
|
||||
if (f.getType().isPrimitive() || f.getType().equals(String.class)) {
|
||||
String propName = getNeo4jPropertyName(thisJoinPoint.getSignature());
|
||||
entity.getUnderlyingNode().setProperty(propName, newVal);
|
||||
log.info("SET " + f + " -> Neo4J simple node property [" + propName + "] with value=[" + newVal + "]");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Look for a relationship
|
||||
if (isNeo4jRelationshipField(f)) {
|
||||
Relationship r = f.getAnnotation(Relationship.class);
|
||||
if (r == null) {
|
||||
throw new IllegalStateException("Must have @Relationship on " + f);
|
||||
}
|
||||
graphEntityFieldSet(entity, r, (NodeBacked) newVal);
|
||||
log.info("SET " + f + " -> Neo4J relationship with value=[" + newVal + "]");
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
log.info("Ignored SET " + f + ": " + f.getType().getName() + " not primitive or GraphEntity");
|
||||
return proceed(entity, newVal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void graphEntityFieldSet(NodeBacked entity, Relationship r, NodeBacked newVal) {
|
||||
Node me = entity.getUnderlyingNode();
|
||||
RelationshipType type = DynamicRelationshipType.withName(r.type());
|
||||
Node targetNode = newVal.getUnderlyingNode();
|
||||
if (r.direction() == Direction.OUTGOING) {
|
||||
me.createRelationshipTo(targetNode, type);
|
||||
}
|
||||
else {
|
||||
targetNode.createRelationshipTo(me, type);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNeo4jRelationshipField(Field f) {
|
||||
//return f.getType().isAnnotationPresent(GraphEntity.class);
|
||||
return NodeBacked.class.isAssignableFrom(f.getType());
|
||||
}
|
||||
|
||||
|
||||
private String getNeo4jPropertyName(Signature sig) {
|
||||
return sig.toShortString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package org.springframework.persistence.support;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Try for a constructor taking state: failing that, try a no-arg
|
||||
* constructor and then setUnderlyingNode().
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public abstract class AbstractConstructorEntityInstantiator<BACKING_INTERFACE, STATE> implements EntityInstantiator<BACKING_INTERFACE, STATE> {
|
||||
|
||||
private final Log log = LogFactory.getLog(getClass());
|
||||
|
||||
final public <T extends BACKING_INTERFACE> T createEntityFromState(STATE n, Class<T> c) {
|
||||
try {
|
||||
return fromStateInternal(n, c);
|
||||
} catch (InstantiationException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
final private <T extends BACKING_INTERFACE> T fromStateInternal(STATE n, Class<T> c) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
|
||||
// TODO this is fragile
|
||||
Class<? extends STATE> stateInterface = (Class<? extends STATE>) n.getClass().getInterfaces()[0];
|
||||
Constructor<T> nodeConstructor = ClassUtils.getConstructorIfAvailable(c, stateInterface);
|
||||
if (nodeConstructor != null) {
|
||||
// TODO is this the correct way to instantiate or does Spring have a preferred way?
|
||||
log.info("Using " + c + " constructor taking " + stateInterface);
|
||||
return nodeConstructor.newInstance(n);
|
||||
}
|
||||
|
||||
Constructor<T> noArgConstructor = ClassUtils.getConstructorIfAvailable(c);
|
||||
if (noArgConstructor != null) {
|
||||
log.info("Using " + c + " no-arg constructor");
|
||||
T t = noArgConstructor.newInstance();
|
||||
setState(t, n);
|
||||
return t;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(getClass().getSimpleName() + ": entity " + c + " must have either a constructor taking [" + stateInterface +
|
||||
"] or a no-arg constructor and state set method");
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement to set state
|
||||
* @param entity
|
||||
* @param s
|
||||
*/
|
||||
protected abstract void setState(BACKING_INTERFACE entity, STATE s);
|
||||
|
||||
}
|
||||
@@ -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 <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();
|
||||
|
||||
}
|
||||
@@ -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 <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;
|
||||
|
||||
}
|
||||
@@ -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<BACKING_INTERFACE,STATE> {
|
||||
|
||||
/*
|
||||
* 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 extends BACKING_INTERFACE> T createEntityFromState(STATE s, Class<T> c);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user