Repository: tinkerpop
Updated Branches:
  refs/heads/TINKERPOP-1829 [created] a5f48d910


TINKERPOP-1829 Made detachment configurable in EventStrategy

The default behavior is representative of how EventStrategy currently works so 
there is no breaking change here. In the interest of avoiding breaking changes, 
I didn't want to shift the API to make the change cleaner and thus created 
TINKERPOP-1831 for future consideration.


Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/a5f48d91
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/a5f48d91
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/a5f48d91

Branch: refs/heads/TINKERPOP-1829
Commit: a5f48d91069d10fe8abdcbb0559315bae10ead5f
Parents: ac99e3c
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Fri Nov 10 06:46:10 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Fri Nov 10 06:46:10 2017 -0500

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |    1 +
 docs/src/reference/the-traversal.asciidoc       |   12 +
 .../upgrade/release-3.2.x-incubating.asciidoc   |   10 +
 .../process/traversal/step/filter/DropStep.java |   13 +-
 .../process/traversal/step/map/AddEdgeStep.java |    4 +-
 .../traversal/step/map/AddVertexStartStep.java  |    4 +-
 .../traversal/step/map/AddVertexStep.java       |    4 +-
 .../step/sideEffect/AddPropertyStep.java        |   21 +-
 .../strategy/decoration/EventStrategy.java      |   68 ++
 .../util/reference/ReferenceProperty.java       |    2 +-
 .../util/reference/ReferenceVertexProperty.java |    2 +-
 .../decoration/EventStrategyProcessTest.java    | 1046 +++++++++++++++++-
 12 files changed, 1165 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a5f48d91/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index ffd9de5..3a2b5b5 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -23,6 +23,7 @@ 
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 [[release-3-2-7]]
 === TinkerPop 3.2.7 (Release Date: NOT OFFICIALLY RELEASED YET)
 
+* Provided a method to configure detachment options with `EventStrategy`.
 * Fixed an `ArrayOutOfBoundsException` in `hasId()` for the rare situation 
when the provided collection is empty.
 * Bump to Netty 4.0.52
 * `TraversalVertexProgram` `profile()` now accounts for worker iteration in 
`GraphComputer` OLAP.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a5f48d91/docs/src/reference/the-traversal.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/reference/the-traversal.asciidoc 
b/docs/src/reference/the-traversal.asciidoc
index 68773f5..a37a8bc 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -2853,6 +2853,18 @@ WARNING: `EventStrategy` is not meant for usage in 
tracking global mutations acr
 words, a mutation in one JVM process is not raised as an event in a different 
JVM process.  In addition, events are
 not raised when mutations occur outside of the `Traversal` context.
 
+Another default configuration for `EventStrategy` revolves around the concept 
of "detachment". Graph elements are
+detached from the graph as copies when passed to referring mutation events. 
Therefore, when adding a new `Vertex` in
+TinkerGraph, the event will not contain a `TinkerVertex` but will instead 
include a `DetachedVertex`. This behavior
+can be modified with the `detach()` method on the `EventStrategy.Builder` 
which accepts the following inputs: `null`
+meaning no detachment and the return of the original element, 
`DetachedFactory` which is the same as the default
+behavior, and `ReferenceFactory` which will return "reference" elements only 
with no properties.
+
+IMPORTANT: If setting the `detach()` configuration to `null`, be aware that 
transactional graphs will likely create a
+new transaction immediately following the `commit()` that raises the events. 
The graph elements raised in the events
+may also not behave as "snapshots" at the time of their creation as they are 
"live" references to actual database
+elements.
+
 === PartitionStrategy
 
 image::partition-graph.png[width=325]

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a5f48d91/docs/src/upgrade/release-3.2.x-incubating.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/upgrade/release-3.2.x-incubating.asciidoc 
b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
index c09f9c6..1589069 100644
--- a/docs/src/upgrade/release-3.2.x-incubating.asciidoc
+++ b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
@@ -29,6 +29,16 @@ 
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 
 Please see the 
link:https://github.com/apache/tinkerpop/blob/3.2.7/CHANGELOG.asciidoc#release-3-2-7[changelog]
 for a complete list of all the modifications that are part of this release.
 
+==== EventStrategy Detachment
+
+`EventStrategy` forced detachment of mutated elements prior to raising them in 
events. While this was a desired
+outcome, it may not have always fit every use case. For example, a user may 
have wanted a reference element or the
+actual element itself. As a result, `EventStrategy` has changed to allow it to 
be constructed with a `detach()`
+option, where it is possible to specify any of the following: `null` for no 
detachment, `DetachedFactory` for the
+original behavior, and `ReferenceFactory` for detachment that returns 
reference elements.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1829[TINKERPOP-1829]
+
 ==== Embedded Remote Connection
 
 As Gremlin Language Variants (GLVs) expand their usage and use of 
`withRemote()` becomes more common, the need to mock

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a5f48d91/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
index 87790eb..cd95e48 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
@@ -24,6 +24,7 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
 import 
org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
 import 
org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ListCallbackRegistry;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Element;
 import org.apache.tinkerpop.gremlin.structure.Property;
@@ -49,13 +50,14 @@ public final class DropStep<S> extends FilterStep<S> 
implements Mutating<Event>
         if (s instanceof Element) {
             final Element toRemove = (Element) s;
             if (callbackRegistry != null) {
+                final EventStrategy eventStrategy = 
getTraversal().getStrategies().getStrategy(EventStrategy.class).get();
                 final Event removeEvent;
                 if (s instanceof Vertex)
-                    removeEvent = new 
Event.VertexRemovedEvent(DetachedFactory.detach((Vertex) s, true));
+                    removeEvent = new 
Event.VertexRemovedEvent(eventStrategy.detach((Vertex) s));
                 else if (s instanceof Edge)
-                    removeEvent = new 
Event.EdgeRemovedEvent(DetachedFactory.detach((Edge) s, true));
+                    removeEvent = new 
Event.EdgeRemovedEvent(eventStrategy.detach((Edge) s));
                 else if (s instanceof VertexProperty)
-                    removeEvent = new 
Event.VertexPropertyRemovedEvent(DetachedFactory.detach((VertexProperty) s, 
true));
+                    removeEvent = new 
Event.VertexPropertyRemovedEvent(eventStrategy.detach((VertexProperty) s));
                 else
                     throw new IllegalStateException("The incoming object is 
not removable: " + s);
 
@@ -66,11 +68,12 @@ public final class DropStep<S> extends FilterStep<S> 
implements Mutating<Event>
         } else if (s instanceof Property) {
             final Property toRemove = (Property) s;
             if (callbackRegistry != null) {
+                final EventStrategy eventStrategy = 
getTraversal().getStrategies().getStrategy(EventStrategy.class).get();
                 final Event.ElementPropertyEvent removeEvent;
                 if (toRemove.element() instanceof Edge)
-                    removeEvent = new Event.EdgePropertyRemovedEvent((Edge) 
toRemove.element(), DetachedFactory.detach(toRemove));
+                    removeEvent = new 
Event.EdgePropertyRemovedEvent(eventStrategy.detach((Edge) toRemove.element()), 
eventStrategy.detach(toRemove));
                 else if (toRemove.element() instanceof VertexProperty)
-                    removeEvent = new 
Event.VertexPropertyPropertyRemovedEvent((VertexProperty) toRemove.element(), 
DetachedFactory.detach(toRemove));
+                    removeEvent = new 
Event.VertexPropertyPropertyRemovedEvent(eventStrategy.detach((VertexProperty) 
toRemove.element()), eventStrategy.detach(toRemove));
                 else
                     throw new IllegalStateException("The incoming object is 
not removable: " + s);
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a5f48d91/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
index efe5906..b5e52c5 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
@@ -29,6 +29,7 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
 import 
org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
 import 
org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ListCallbackRegistry;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Graph;
@@ -104,7 +105,8 @@ public final class AddEdgeStep<S> extends MapStep<S, Edge>
 
         final Edge edge = fromVertex.addEdge(edgeLabel, toVertex, 
this.parameters.getKeyValues(traverser, TO, FROM, T.label));
         if (callbackRegistry != null) {
-            final Event.EdgeAddedEvent vae = new 
Event.EdgeAddedEvent(DetachedFactory.detach(edge, true));
+            final EventStrategy eventStrategy = 
getTraversal().getStrategies().getStrategy(EventStrategy.class).get();
+            final Event.EdgeAddedEvent vae = new 
Event.EdgeAddedEvent(eventStrategy.detach(edge));
             callbackRegistry.getCallbacks().forEach(c -> c.accept(vae));
         }
         return edge;

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a5f48d91/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
index 7de8839..7eb018b 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
@@ -31,6 +31,7 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
 import 
org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
 import 
org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ListCallbackRegistry;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import 
org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
 import org.apache.tinkerpop.gremlin.structure.T;
@@ -84,7 +85,8 @@ public final class AddVertexStartStep extends 
AbstractStep<Vertex, Vertex>
             final TraverserGenerator generator = 
this.getTraversal().getTraverserGenerator();
             final Vertex vertex = 
this.getTraversal().getGraph().get().addVertex(this.parameters.getKeyValues(generator.generate(false,
 (Step) this, 1L)));
             if (this.callbackRegistry != null) {
-                final Event.VertexAddedEvent vae = new 
Event.VertexAddedEvent(DetachedFactory.detach(vertex, true));
+                final EventStrategy eventStrategy = 
getTraversal().getStrategies().getStrategy(EventStrategy.class).get();
+                final Event.VertexAddedEvent vae = new 
Event.VertexAddedEvent(eventStrategy.detach(vertex));
                 this.callbackRegistry.getCallbacks().forEach(c -> 
c.accept(vae));
             }
             return generator.generate(vertex, this, 1L);

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a5f48d91/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
index c35fa80..521c5ac 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
@@ -28,6 +28,7 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
 import 
org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
 import 
org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ListCallbackRegistry;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.structure.T;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
@@ -76,7 +77,8 @@ public final class AddVertexStep<S> extends MapStep<S, Vertex>
     protected Vertex map(final Traverser.Admin<S> traverser) {
         final Vertex vertex = 
this.getTraversal().getGraph().get().addVertex(this.parameters.getKeyValues(traverser));
         if (this.callbackRegistry != null) {
-            final Event.VertexAddedEvent vae = new 
Event.VertexAddedEvent(DetachedFactory.detach(vertex, true));
+            final EventStrategy eventStrategy = 
getTraversal().getStrategies().getStrategy(EventStrategy.class).get();
+            final Event.VertexAddedEvent vae = new 
Event.VertexAddedEvent(eventStrategy.detach(vertex));
             this.callbackRegistry.getCallbacks().forEach(c -> c.accept(vae));
         }
         return vertex;

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a5f48d91/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
index 3de8932..7509f86 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
@@ -28,6 +28,7 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
 import 
org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
 import 
org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ListCallbackRegistry;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Element;
@@ -91,26 +92,26 @@ public final class AddPropertyStep<S extends Element> 
extends SideEffectStep<S>
 
         final Element element = traverser.get();
 
-
         if (this.callbackRegistry != null) {
+            final EventStrategy eventStrategy = 
getTraversal().getStrategies().getStrategy(EventStrategy.class).get();
             final Property currentProperty = traverser.get().property(key);
             final boolean newProperty = element instanceof Vertex ? 
currentProperty == VertexProperty.empty() : currentProperty == Property.empty();
             final Event.ElementPropertyChangedEvent evt;
             if (element instanceof Vertex)
-                evt = new 
Event.VertexPropertyChangedEvent(DetachedFactory.detach((Vertex) element, true),
+                evt = new 
Event.VertexPropertyChangedEvent(eventStrategy.detach((Vertex) element),
                         newProperty ?
-                                new DetachedVertexProperty(null, key, null, 
null) :
-                                DetachedFactory.detach((VertexProperty) 
currentProperty, true), value, vertexPropertyKeyValues);
+                                eventStrategy.empty(element, key) :
+                                eventStrategy.detach((VertexProperty) 
currentProperty), value, vertexPropertyKeyValues);
             else if (element instanceof Edge)
-                evt = new 
Event.EdgePropertyChangedEvent(DetachedFactory.detach((Edge) element, true),
+                evt = new 
Event.EdgePropertyChangedEvent(eventStrategy.detach((Edge) element),
                         newProperty ?
-                                new DetachedProperty(key, null) :
-                                DetachedFactory.detach(currentProperty), 
value);
+                                eventStrategy.empty(element, key) :
+                                eventStrategy.detach(currentProperty), value);
             else if (element instanceof VertexProperty)
-                evt = new 
Event.VertexPropertyPropertyChangedEvent(DetachedFactory.detach((VertexProperty)
 element, true),
+                evt = new 
Event.VertexPropertyPropertyChangedEvent(eventStrategy.detach((VertexProperty) 
element),
                         newProperty ?
-                                new DetachedProperty(key, null) :
-                                DetachedFactory.detach(currentProperty), 
value);
+                                eventStrategy.empty(element, key) :
+                                eventStrategy.detach(currentProperty), value);
             else
                 throw new IllegalStateException(String.format("The incoming 
object cannot be processed by change eventing in %s:  %s", 
AddPropertyStep.class.getName(), element));
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a5f48d91/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategy.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategy.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategy.java
index 30b48fa..b4824c8 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategy.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategy.java
@@ -26,8 +26,19 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventCallb
 import 
org.apache.tinkerpop.gremlin.process.traversal.step.util.event.MutationListener;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Element;
 import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Property;
 import org.apache.tinkerpop.gremlin.structure.Transaction;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedProperty;
+import 
org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceFactory;
+import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceProperty;
+import 
org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexProperty;
 
 import java.io.Serializable;
 import java.util.ArrayDeque;
@@ -46,10 +57,53 @@ import java.util.List;
  */
 public final class EventStrategy extends 
AbstractTraversalStrategy<TraversalStrategy.DecorationStrategy> implements 
TraversalStrategy.DecorationStrategy {
     private final EventQueue eventQueue;
+    private final Class<?> detachmentFactory;
 
     private EventStrategy(final Builder builder) {
         this.eventQueue = builder.eventQueue;
         this.eventQueue.setListeners(builder.listeners);
+        this.detachmentFactory = builder.detachmentFactory;
+    }
+
+    public Class<?> getDetachmentFactory() {
+        return this.detachmentFactory;
+    }
+
+    /**
+     * Applies the appropriate detach operation to elements that will be 
raised in mutation events.
+     */
+    public <R> R detach(final R attached) {
+        if (null == detachmentFactory)
+            return attached;
+        else if (detachmentFactory.equals(DetachedFactory.class))
+            return DetachedFactory.detach(attached, true);
+        else if (detachmentFactory.equals(ReferenceFactory.class))
+            return ReferenceFactory.detach(attached);
+        else
+            throw new IllegalStateException("Unknown detachment option using " 
+ detachmentFactory.getSimpleName());
+    }
+
+    /**
+     * For newly created properties that do not yet exist, an empty {@link 
Property} is required that just contains
+     * a key as a reference.
+     */
+    public <R extends Property> R empty(final Element element, final String 
key) {
+        // currently the "no detachment" model simply returns a Detached value 
to maintain consistency with the
+        // original API that already existed (where returning "Detached" was 
the only option). This could probably
+        // change in the future to use an "empty" property or perhaps the 
"change" event API could change all together
+        // and have a different return.
+        if (null == detachmentFactory || 
detachmentFactory.equals(DetachedFactory.class)) {
+            if (element instanceof Vertex)
+                return (R) new DetachedVertexProperty(null, key, null, null);
+            else
+                return (R) new DetachedProperty(key, null);
+        } else if (detachmentFactory.equals(ReferenceFactory.class)) {
+            if (element instanceof Vertex)
+                return (R) new ReferenceVertexProperty(new 
DetachedVertexProperty(null, key, null, null));
+            else
+                return (R) new ReferenceProperty(new DetachedProperty(key, 
null));
+        } else
+            throw new IllegalStateException("Unknown empty detachment option 
using " + detachmentFactory.getSimpleName());
     }
 
     @Override
@@ -78,6 +132,7 @@ public final class EventStrategy extends 
AbstractTraversalStrategy<TraversalStra
     public final static class Builder {
         private final List<MutationListener> listeners = new ArrayList<>();
         private EventQueue eventQueue = new DefaultEventQueue();
+        private Class<?> detachmentFactory = DetachedFactory.class;
 
         Builder() {}
 
@@ -91,6 +146,19 @@ public final class EventStrategy extends 
AbstractTraversalStrategy<TraversalStra
             return this;
         }
 
+        /**
+         * Configures the method of detachment for element provided in 
mutation callback events. If configured with
+         * {@code null} for no detachment with a transactional graph, be aware 
that accessing the evented elements
+         * after {@code commit()} will likely open new transactions.
+         *
+         * @param factoryClass must be either {@code null} (for no 
detachment), {@link ReferenceFactory} for elements
+         *                     with no properties or {@link DetachedFactory} 
for elements with properties.
+         */
+        public Builder detach(final Class<?> factoryClass) {
+            detachmentFactory = factoryClass;
+            return this;
+        }
+
         public EventStrategy create() {
             return new EventStrategy(this);
         }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a5f48d91/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/reference/ReferenceProperty.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/reference/ReferenceProperty.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/reference/ReferenceProperty.java
index dc86518..074f863 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/reference/ReferenceProperty.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/reference/ReferenceProperty.java
@@ -47,7 +47,7 @@ public class ReferenceProperty<V> implements 
Attachable<Property<V>>, Serializab
     }
 
     public ReferenceProperty(final Property<V> property) {
-        this.element = ReferenceFactory.detach(property.element());
+        this.element = null == property.element() ? null : 
ReferenceFactory.detach(property.element());
         this.key = property.key();
         this.value = property.value();
     }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a5f48d91/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/reference/ReferenceVertexProperty.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/reference/ReferenceVertexProperty.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/reference/ReferenceVertexProperty.java
index e607ebb..c8fdd5c 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/reference/ReferenceVertexProperty.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/reference/ReferenceVertexProperty.java
@@ -42,7 +42,7 @@ public class ReferenceVertexProperty<V> extends 
ReferenceElement<VertexProperty<
 
     public ReferenceVertexProperty(final VertexProperty<V> vertexProperty) {
         super(vertexProperty);
-        this.vertex = ReferenceFactory.detach(vertexProperty.element());
+        this.vertex = null == vertexProperty.element() ? null : 
ReferenceFactory.detach(vertexProperty.element());
         this.value = vertexProperty.value();
     }
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a5f48d91/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyProcessTest.java
----------------------------------------------------------------------
diff --git 
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyProcessTest.java
 
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyProcessTest.java
index 12a3e0a..e7a392c 100644
--- 
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyProcessTest.java
+++ 
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyProcessTest.java
@@ -28,6 +28,13 @@ import org.apache.tinkerpop.gremlin.structure.Graph;
 import org.apache.tinkerpop.gremlin.structure.Property;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
+import 
org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceEdge;
+import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceFactory;
+import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertex;
+import 
org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexProperty;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 import org.junit.Test;
 
@@ -36,9 +43,11 @@ import java.util.List;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
 import static org.junit.Assert.assertEquals;
 
 /**
@@ -544,6 +553,7 @@ public class EventStrategyProcessTest extends 
AbstractGremlinProcessTest {
         final MutationListener listener = new AbstractMutationListener() {
             @Override
             public void vertexPropertyPropertyRemoved(final VertexProperty 
element, final Property property) {
+                assertThat(element, instanceOf(DetachedVertexProperty.class));
                 assertEquals(label, element.label());
                 assertEquals(value, element.value());
                 assertEquals("dah", property.value());
@@ -582,6 +592,7 @@ public class EventStrategyProcessTest extends 
AbstractGremlinProcessTest {
         final MutationListener listener = new AbstractMutationListener() {
             @Override
             public void vertexPropertyPropertyChanged(final VertexProperty 
element, final Property oldValue, final Object setValue) {
+                assertThat(element, instanceOf(DetachedVertexProperty.class));
                 assertEquals(label, element.label());
                 assertEquals(value, element.value());
                 assertEquals("dah", oldValue.value());
@@ -620,6 +631,7 @@ public class EventStrategyProcessTest extends 
AbstractGremlinProcessTest {
         final MutationListener listener = new AbstractMutationListener() {
             @Override
             public void vertexPropertyPropertyChanged(final VertexProperty 
element, final Property oldValue, final Object setValue) {
+                assertThat(element, instanceOf(DetachedVertexProperty.class));
                 assertEquals(label, element.label());
                 assertEquals(value, element.value());
                 assertEquals(null, oldValue.value());
@@ -658,6 +670,7 @@ public class EventStrategyProcessTest extends 
AbstractGremlinProcessTest {
         final MutationListener listener = new AbstractMutationListener() {
             @Override
             public void edgePropertyRemoved(final Edge element, final Property 
property) {
+                assertThat(element, instanceOf(DetachedEdge.class));
                 assertEquals(label, element.label());
                 assertEquals(inId, element.inVertex().id());
                 assertEquals(outId, element.outVertex().id());
@@ -696,6 +709,7 @@ public class EventStrategyProcessTest extends 
AbstractGremlinProcessTest {
         final MutationListener listener = new AbstractMutationListener() {
             @Override
             public void edgePropertyChanged(final Edge element, final Property 
oldValue, final Object setValue) {
+                assertThat(element, instanceOf(DetachedEdge.class));
                 assertEquals(label, element.label());
                 assertEquals(inId, element.inVertex().id());
                 assertEquals(outId, element.outVertex().id());
@@ -734,6 +748,7 @@ public class EventStrategyProcessTest extends 
AbstractGremlinProcessTest {
         final MutationListener listener = new AbstractMutationListener() {
             @Override
             public void edgePropertyChanged(final Edge element, final Property 
oldValue, final Object setValue) {
+                assertThat(element, instanceOf(DetachedEdge.class));
                 assertEquals(label, element.label());
                 assertEquals(inId, element.inVertex().id());
                 assertEquals(outId, element.outVertex().id());
@@ -772,6 +787,7 @@ public class EventStrategyProcessTest extends 
AbstractGremlinProcessTest {
         final MutationListener listener = new AbstractMutationListener() {
             @Override
             public void edgeRemoved(final Edge element) {
+                assertThat(element, instanceOf(DetachedEdge.class));
                 assertEquals(label, element.label());
                 assertEquals(inId, element.inVertex().id());
                 assertEquals(outId, element.outVertex().id());
@@ -803,6 +819,7 @@ public class EventStrategyProcessTest extends 
AbstractGremlinProcessTest {
         final MutationListener listener = new AbstractMutationListener() {
             @Override
             public void edgeAdded(final Edge element) {
+                assertThat(element, instanceOf(DetachedEdge.class));
                 assertEquals("self", element.label());
                 assertEquals(id, element.inVertex().id());
                 assertEquals(id, element.outVertex().id());
@@ -838,6 +855,7 @@ public class EventStrategyProcessTest extends 
AbstractGremlinProcessTest {
         final MutationListener listener = new AbstractMutationListener() {
             @Override
             public void vertexPropertyRemoved(final VertexProperty element) {
+                assertThat(element, instanceOf(DetachedVertexProperty.class));
                 assertEquals(label, element.label());
                 assertEquals(value, element.value());
                 triggered.set(true);
@@ -869,10 +887,9 @@ public class EventStrategyProcessTest extends 
AbstractGremlinProcessTest {
         v.property("to-change", "blah");
 
         final MutationListener listener = new AbstractMutationListener() {
-
-
             @Override
             public void vertexPropertyChanged(final Vertex element, final 
VertexProperty oldValue, final Object setValue, final Object... 
vertexPropertyKeyValues) {
+                assertThat(element, instanceOf(DetachedVertex.class));
                 assertEquals(label, element.label());
                 assertEquals(id, element.id());
                 assertEquals("to-change", oldValue.key());
@@ -908,6 +925,7 @@ public class EventStrategyProcessTest extends 
AbstractGremlinProcessTest {
         final MutationListener listener = new AbstractMutationListener() {
             @Override
             public void vertexPropertyChanged(final Vertex element, final 
VertexProperty oldValue, final Object setValue, final Object... 
vertexPropertyKeyValues) {
+                assertThat(element, instanceOf(DetachedVertex.class));
                 assertEquals(label, element.label());
                 assertEquals(id, element.id());
                 assertEquals("new", oldValue.key());
@@ -943,6 +961,7 @@ public class EventStrategyProcessTest extends 
AbstractGremlinProcessTest {
         final MutationListener listener = new AbstractMutationListener() {
             @Override
             public void vertexPropertyChanged(final Vertex element, final 
Property oldValue, final Object setValue, final Object... 
vertexPropertyKeyValues) {
+                assertThat(element, instanceOf(DetachedVertex.class));
                 assertEquals(label, element.label());
                 assertEquals(id, element.id());
                 assertEquals("new", oldValue.key());
@@ -977,6 +996,7 @@ public class EventStrategyProcessTest extends 
AbstractGremlinProcessTest {
         final MutationListener listener = new AbstractMutationListener() {
             @Override
             public void vertexRemoved(final Vertex element) {
+                assertThat(element, instanceOf(DetachedVertex.class));
                 assertEquals(id, element.id());
                 assertEquals(label, element.label());
                 triggered.set(true);
@@ -1005,6 +1025,7 @@ public class EventStrategyProcessTest extends 
AbstractGremlinProcessTest {
         final MutationListener listener = new AbstractMutationListener() {
             @Override
             public void vertexAdded(final Vertex element) {
+                assertThat(element, instanceOf(DetachedVertex.class));
                 assertEquals("thing", element.label());
                 assertEquals("there", element.value("here"));
                 triggered.set(true);
@@ -1025,6 +1046,1027 @@ public class EventStrategyProcessTest extends 
AbstractGremlinProcessTest {
         assertThat(triggered.get(), is(true));
     }
 
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, 
feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES)
+    public void shouldReferencePropertyOfVertexPropertyWhenRemoved() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final VertexProperty vp = v.property("xxx","blah");
+        final String label = vp.label();
+        final Object value = vp.value();
+        final Property p = vp.property("to-drop", "dah");
+        vp.property("not-dropped", "yay!");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void vertexPropertyPropertyRemoved(final VertexProperty 
element, final Property property) {
+                assertThat(element, instanceOf(ReferenceVertexProperty.class));
+                assertEquals(label, element.label());
+                assertEquals(value, element.value());
+                assertEquals("dah", property.value());
+                assertEquals("to-drop", property.key());
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.V(v).properties("xxx").properties("to-drop").drop().iterate();
+        tryCommit(graph);
+
+        assertEquals(1, IteratorUtils.count(g.V(v).properties()));
+        assertEquals(1, IteratorUtils.count(g.V(v).properties().properties()));
+        assertEquals("yay!", 
g.V(vp.element()).properties("xxx").values("not-dropped").next());
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, 
feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES)
+    public void shouldReferencePropertyOfVertexPropertyWhenChanged() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final VertexProperty vp = v.property("xxx","blah");
+        final String label = vp.label();
+        final Object value = vp.value();
+        vp.property("to-change", "dah");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void vertexPropertyPropertyChanged(final VertexProperty 
element, final Property oldValue, final Object setValue) {
+                assertThat(element, instanceOf(ReferenceVertexProperty.class));
+                assertEquals(label, element.label());
+                assertEquals(value, element.value());
+                assertEquals("dah", oldValue.value());
+                assertEquals("to-change", oldValue.key());
+                assertEquals("bah", setValue);
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.V(v).properties("xxx").property("to-change","bah").iterate();
+        tryCommit(graph);
+
+        assertEquals(1, IteratorUtils.count(g.V(v).properties()));
+        assertEquals(1, IteratorUtils.count(g.V(v).properties().properties()));
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, 
feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES)
+    public void shouldReferencePropertyOfVertexPropertyWhenNew() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final VertexProperty vp = v.property("xxx","blah");
+        final String label = vp.label();
+        final Object value = vp.value();
+        vp.property("to-change", "dah");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void vertexPropertyPropertyChanged(final VertexProperty 
element, final Property oldValue, final Object setValue) {
+                assertThat(element, instanceOf(ReferenceVertexProperty.class));
+                assertEquals(label, element.label());
+                assertEquals(value, element.value());
+                assertEquals(null, oldValue.value());
+                assertEquals("new", oldValue.key());
+                assertEquals("yay!", setValue);
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.V(v).properties("xxx").property("new","yay!").iterate();
+        tryCommit(graph);
+
+        assertEquals(1, IteratorUtils.count(g.V(v).properties()));
+        assertEquals(2, IteratorUtils.count(g.V(v).properties().properties()));
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE)
+    public void shouldReferencePropertyOfEdgeWhenRemoved() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final Edge e = v.addEdge("self", v, "not-dropped", "yay!");
+        final String label = e.label();
+        final Object inId = v.id();
+        final Object outId = v.id();
+        e.property("to-drop", "dah");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void edgePropertyRemoved(final Edge element, final Property 
property) {
+                assertThat(element, instanceOf(ReferenceEdge.class));
+                assertEquals(label, element.label());
+                assertEquals(inId, element.inVertex().id());
+                assertEquals(outId, element.outVertex().id());
+                assertEquals("dah", property.value());
+                assertEquals("to-drop", property.key());
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.E(e).properties("to-drop").drop().iterate();
+        tryCommit(graph);
+
+        assertEquals(1, IteratorUtils.count(g.E(e).properties()));
+        assertEquals("yay!", e.value("not-dropped"));
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE)
+    public void shouldReferencePropertyOfEdgeWhenChanged() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final Edge e = v.addEdge("self", v);
+        final String label = e.label();
+        final Object inId = v.id();
+        final Object outId = v.id();
+        e.property("to-change", "no!");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void edgePropertyChanged(final Edge element, final Property 
oldValue, final Object setValue) {
+                assertThat(element, instanceOf(ReferenceEdge.class));
+                assertEquals(label, element.label());
+                assertEquals(inId, element.inVertex().id());
+                assertEquals(outId, element.outVertex().id());
+                assertEquals("no!", oldValue.value());
+                assertEquals("to-change", oldValue.key());
+                assertEquals("yay!", setValue);
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.E(e).property("to-change","yay!").iterate();
+        tryCommit(graph);
+
+        assertEquals(1, IteratorUtils.count(g.E(e).properties()));
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE)
+    public void shouldReferencePropertyOfEdgeWhenNew() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final Edge e = v.addEdge("self", v);
+        final String label = e.label();
+        final Object inId = v.id();
+        final Object outId = v.id();
+        e.property("to-change", "no!");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void edgePropertyChanged(final Edge element, final Property 
oldValue, final Object setValue) {
+                assertThat(element, instanceOf(ReferenceEdge.class));
+                assertEquals(label, element.label());
+                assertEquals(inId, element.inVertex().id());
+                assertEquals(outId, element.outVertex().id());
+                assertEquals(null, oldValue.value());
+                assertEquals("new", oldValue.key());
+                assertEquals("yay!", setValue);
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.E(e).property("new","yay!").iterate();
+        tryCommit(graph);
+
+        assertEquals(2, IteratorUtils.count(g.E(e).properties()));
+        assertThat(triggered.get(), is(true));
+    }
+
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE)
+    public void shouldReferenceEdgeWhenRemoved() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final Edge e = v.addEdge("self", v, "dropped", "yay!");
+        final String label = e.label();
+        final Object inId = v.id();
+        final Object outId = v.id();
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void edgeRemoved(final Edge element) {
+                assertThat(element, instanceOf(ReferenceEdge.class));
+                assertEquals(label, element.label());
+                assertEquals(inId, element.inVertex().id());
+                assertEquals(outId, element.outVertex().id());
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.E(e).drop().iterate();
+        tryCommit(graph);
+
+        assertVertexEdgeCounts(graph, 1, 0);
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE)
+    public void shouldReferenceEdgeWhenAdded() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final Object id = v.id();
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void edgeAdded(final Edge element) {
+                assertThat(element, instanceOf(ReferenceEdge.class));
+                assertEquals("self", element.label());
+                assertEquals(id, element.inVertex().id());
+                assertEquals(id, element.outVertex().id());
+                assertThat(element.properties().hasNext(), is(false));
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.V(v).as("a").addE("self").property("here", 
"there").from("a").to("a").iterate();
+        tryCommit(graph);
+
+        assertVertexEdgeCounts(graph, 1, 1);
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    public void shouldReferenceVertexPropertyWhenRemoved() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final VertexProperty vp = v.property("to-remove","blah");
+        final String label = vp.label();
+        final Object value = vp.value();
+        final VertexProperty vpToKeep = v.property("to-keep","dah");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void vertexPropertyRemoved(final VertexProperty element) {
+                assertThat(element, instanceOf(ReferenceVertexProperty.class));
+                assertEquals(label, element.label());
+                assertEquals(value, element.value());
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.V(v).properties("to-remove").drop().iterate();
+        tryCommit(graph);
+
+        assertEquals(1, IteratorUtils.count(g.V(v).properties()));
+        assertEquals(vpToKeep.value(), g.V(v).values("to-keep").next());
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    public void shouldReferenceVertexPropertyWhenChanged() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final String label = v.label();
+        final Object id = v.id();
+        v.property("to-change", "blah");
+
+        final MutationListener listener = new AbstractMutationListener() {
+
+
+            @Override
+            public void vertexPropertyChanged(final Vertex element, final 
VertexProperty oldValue, final Object setValue, final Object... 
vertexPropertyKeyValues) {
+                assertThat(element, instanceOf(ReferenceVertex.class));
+                assertEquals(label, element.label());
+                assertEquals(id, element.id());
+                assertEquals("to-change", oldValue.key());
+                assertEquals("blah", oldValue.value());
+                assertEquals("dah", setValue);
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.V(v).property(VertexProperty.Cardinality.single, "to-change", 
"dah").iterate();
+        tryCommit(graph);
+
+        assertEquals(1, IteratorUtils.count(g.V(v).properties()));
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    public void shouldRefereceVertexPropertyWhenNew() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final String label = v.label();
+        final Object id = v.id();
+        v.property("old","blah");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void vertexPropertyChanged(final Vertex element, final 
VertexProperty oldValue, final Object setValue, final Object... 
vertexPropertyKeyValues) {
+                assertThat(element, instanceOf(ReferenceVertex.class));
+                assertEquals(label, element.label());
+                assertEquals(id, element.id());
+                assertEquals("new", oldValue.key());
+                assertEquals(null, oldValue.value());
+                assertEquals("dah", setValue);
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.V(v).property(VertexProperty.Cardinality.single, "new", 
"dah").iterate();
+        tryCommit(graph);
+
+        assertEquals(2, IteratorUtils.count(g.V(v).properties()));
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    public void shouldReferenceVertexPropertyWhenNewDeprecated() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final String label = v.label();
+        final Object id = v.id();
+        v.property("old","blah");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void vertexPropertyChanged(final Vertex element, final 
Property oldValue, final Object setValue, final Object... 
vertexPropertyKeyValues) {
+                assertThat(element, instanceOf(ReferenceVertex.class));
+                assertEquals(label, element.label());
+                assertEquals(id, element.id());
+                assertEquals("new", oldValue.key());
+                assertEquals(null, oldValue.value());
+                assertEquals("dah", setValue);
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.V(v).property(VertexProperty.Cardinality.single, "new", 
"dah").iterate();
+        tryCommit(graph);
+
+        assertEquals(2, IteratorUtils.count(g.V(v).properties()));
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    public void shouldReferenceVertexWhenRemoved() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final String label = v.label();
+        final Object id = v.id();
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void vertexRemoved(final Vertex element) {
+                assertThat(element, instanceOf(ReferenceVertex.class));
+                assertEquals(id, element.id());
+                assertEquals(label, element.label());
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.V(v).drop().iterate();
+        tryCommit(graph);
+
+        assertVertexEdgeCounts(graph, 0, 0);
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    public void shouldReferenceVertexWhenAdded() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void vertexAdded(final Vertex element) {
+                assertThat(element, instanceOf(ReferenceVertex.class));
+                assertEquals("thing", element.label());
+                assertThat(element.properties("here").hasNext(), is(false));
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.addV("thing").property("here", "there").iterate();
+        tryCommit(graph);
+
+        assertVertexEdgeCounts(graph, 1, 0);
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, 
feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES)
+    public void shouldUseActualPropertyOfVertexPropertyWhenRemoved() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final VertexProperty vp = v.property("xxx","blah");
+        final String label = vp.label();
+        final Object value = vp.value();
+        final Property p = vp.property("to-drop", "dah");
+        vp.property("not-dropped", "yay!");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void vertexPropertyPropertyRemoved(final VertexProperty 
element, final Property property) {
+                assertEquals(vp, element);
+                assertEquals(p, property);
+                assertEquals("dah", property.value());
+                assertEquals("to-drop", property.key());
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.V(v).properties("xxx").properties("to-drop").drop().iterate();
+        tryCommit(graph);
+
+        assertEquals(1, IteratorUtils.count(g.V(v).properties()));
+        assertEquals(1, IteratorUtils.count(g.V(v).properties().properties()));
+        assertEquals("yay!", 
g.V(vp.element()).properties("xxx").values("not-dropped").next());
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, 
feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES)
+    public void shouldUseActualPropertyOfVertexPropertyWhenChanged() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final VertexProperty vp = v.property("xxx","blah");
+        final String label = vp.label();
+        final Object value = vp.value();
+        vp.property("to-change", "dah");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void vertexPropertyPropertyChanged(final VertexProperty 
element, final Property oldValue, final Object setValue) {
+                assertEquals(vp, element);
+                assertEquals(label, element.label());
+                assertEquals(value, element.value());
+                assertEquals("dah", oldValue.value());
+                assertEquals("to-change", oldValue.key());
+                assertEquals("bah", setValue);
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.V(v).properties("xxx").property("to-change","bah").iterate();
+        tryCommit(graph);
+
+        assertEquals(1, IteratorUtils.count(g.V(v).properties()));
+        assertEquals(1, IteratorUtils.count(g.V(v).properties().properties()));
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, 
feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES)
+    public void shouldUseActualPropertyOfVertexPropertyWhenNew() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final VertexProperty vp = v.property("xxx","blah");
+        final String label = vp.label();
+        final Object value = vp.value();
+        vp.property("to-change", "dah");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void vertexPropertyPropertyChanged(final VertexProperty 
element, final Property oldValue, final Object setValue) {
+                assertEquals(vp, element);
+                assertEquals(label, element.label());
+                assertEquals(value, element.value());
+                assertEquals(null, oldValue.value());
+                assertEquals("new", oldValue.key());
+                assertEquals("yay!", setValue);
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.V(v).properties("xxx").property("new","yay!").iterate();
+        tryCommit(graph);
+
+        assertEquals(1, IteratorUtils.count(g.V(v).properties()));
+        assertEquals(2, IteratorUtils.count(g.V(v).properties().properties()));
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE)
+    public void shouldUseActualPropertyOfEdgeWhenRemoved() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final Edge e = v.addEdge("self", v, "not-dropped", "yay!");
+        final String label = e.label();
+        final Object inId = v.id();
+        final Object outId = v.id();
+        e.property("to-drop", "dah");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void edgePropertyRemoved(final Edge element, final Property 
property) {
+                assertEquals(e, element);
+                assertEquals(label, element.label());
+                assertEquals(inId, element.inVertex().id());
+                assertEquals(outId, element.outVertex().id());
+                assertEquals("dah", property.value());
+                assertEquals("to-drop", property.key());
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.E(e).properties("to-drop").drop().iterate();
+        tryCommit(graph);
+
+        assertEquals(1, IteratorUtils.count(g.E(e).properties()));
+        assertEquals("yay!", e.value("not-dropped"));
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE)
+    public void shouldUseActualPropertyOfEdgeWhenChanged() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final Edge e = v.addEdge("self", v);
+        final String label = e.label();
+        final Object inId = v.id();
+        final Object outId = v.id();
+        e.property("to-change", "no!");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void edgePropertyChanged(final Edge element, final Property 
oldValue, final Object setValue) {
+                assertEquals(e, element);
+                assertEquals(label, element.label());
+                assertEquals(inId, element.inVertex().id());
+                assertEquals(outId, element.outVertex().id());
+                assertEquals("no!", oldValue.value());
+                assertEquals("to-change", oldValue.key());
+                assertEquals("yay!", setValue);
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.E(e).property("to-change","yay!").iterate();
+        tryCommit(graph);
+
+        assertEquals(1, IteratorUtils.count(g.E(e).properties()));
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE)
+    public void shouldUseActualPropertyOfEdgeWhenNew() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final Edge e = v.addEdge("self", v);
+        final String label = e.label();
+        final Object inId = v.id();
+        final Object outId = v.id();
+        e.property("to-change", "no!");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void edgePropertyChanged(final Edge element, final Property 
oldValue, final Object setValue) {
+                assertEquals(e, element);
+                assertEquals(label, element.label());
+                assertEquals(inId, element.inVertex().id());
+                assertEquals(outId, element.outVertex().id());
+                assertEquals(null, oldValue.value());
+                assertEquals("new", oldValue.key());
+                assertEquals("yay!", setValue);
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.E(e).property("new","yay!").iterate();
+        tryCommit(graph);
+
+        assertEquals(2, IteratorUtils.count(g.E(e).properties()));
+        assertThat(triggered.get(), is(true));
+    }
+
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE)
+    public void shouldUseActualEdgeWhenRemoved() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final Edge e = v.addEdge("self", v, "dropped", "yay!");
+        final String label = e.label();
+        final Object inId = v.id();
+        final Object outId = v.id();
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void edgeRemoved(final Edge element) {
+                assertEquals(e, element);
+                assertEquals(label, element.label());
+                assertEquals(inId, element.inVertex().id());
+                assertEquals(outId, element.outVertex().id());
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.E(e).drop().iterate();
+        tryCommit(graph);
+
+        assertVertexEdgeCounts(graph, 1, 0);
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE)
+    public void shouldUseActualEdgeWhenAdded() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final Object id = v.id();
+
+        final AtomicReference<Edge> eventedEdge = new AtomicReference<>();
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void edgeAdded(final Edge element) {
+                eventedEdge.set(element);
+                assertEquals("self", element.label());
+                assertEquals(id, element.inVertex().id());
+                assertEquals(id, element.outVertex().id());
+                assertThat(element.properties().hasNext(), is(false));
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        final Edge e = gts.V(v).as("a").addE("self").property("here", 
"there").from("a").to("a").next();
+        tryCommit(graph);
+
+        assertVertexEdgeCounts(graph, 1, 1);
+        assertThat(triggered.get(), is(true));
+        assertEquals(e, eventedEdge.get());
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    public void shouldUseActualVertexPropertyWhenRemoved() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final VertexProperty vp = v.property("to-remove","blah");
+        final String label = vp.label();
+        final Object value = vp.value();
+        final VertexProperty vpToKeep = v.property("to-keep","dah");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void vertexPropertyRemoved(final VertexProperty element) {
+                assertEquals(vp, element);
+                assertEquals(label, element.label());
+                assertEquals(value, element.value());
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.V(v).properties("to-remove").drop().iterate();
+        tryCommit(graph);
+
+        assertEquals(1, IteratorUtils.count(g.V(v).properties()));
+        assertEquals(vpToKeep.value(), g.V(v).values("to-keep").next());
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    public void shouldUseActualVertexPropertyWhenChanged() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final String label = v.label();
+        final Object id = v.id();
+        v.property("to-change", "blah");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void vertexPropertyChanged(final Vertex element, final 
VertexProperty oldValue, final Object setValue, final Object... 
vertexPropertyKeyValues) {
+                assertEquals(v, element);
+                assertEquals(label, element.label());
+                assertEquals(id, element.id());
+                assertEquals("to-change", oldValue.key());
+                assertEquals("blah", oldValue.value());
+                assertEquals("dah", setValue);
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.V(v).property(VertexProperty.Cardinality.single, "to-change", 
"dah").iterate();
+        tryCommit(graph);
+
+        assertEquals(1, IteratorUtils.count(g.V(v).properties()));
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    public void shouldUseActualVertexPropertyWhenNew() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final String label = v.label();
+        final Object id = v.id();
+        v.property("old","blah");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void vertexPropertyChanged(final Vertex element, final 
VertexProperty oldValue, final Object setValue, final Object... 
vertexPropertyKeyValues) {
+                assertEquals(v, element);
+                assertEquals(label, element.label());
+                assertEquals(id, element.id());
+                assertEquals("new", oldValue.key());
+                assertEquals(null, oldValue.value());
+                assertEquals("dah", setValue);
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.V(v).property(VertexProperty.Cardinality.single, "new", 
"dah").iterate();
+        tryCommit(graph);
+
+        assertEquals(2, IteratorUtils.count(g.V(v).properties()));
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    public void shouldUseActualVertexPropertyWhenNewDeprecated() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final String label = v.label();
+        final Object id = v.id();
+        v.property("old","blah");
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void vertexPropertyChanged(final Vertex element, final 
Property oldValue, final Object setValue, final Object... 
vertexPropertyKeyValues) {
+                assertEquals(v, element);
+                assertEquals(label, element.label());
+                assertEquals(id, element.id());
+                assertEquals("new", oldValue.key());
+                assertEquals(null, oldValue.value());
+                assertEquals("dah", setValue);
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.V(v).property(VertexProperty.Cardinality.single, "new", 
"dah").iterate();
+        tryCommit(graph);
+
+        assertEquals(2, IteratorUtils.count(g.V(v).properties()));
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    public void shouldUseActualVertexWhenRemoved() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+        final Vertex v = graph.addVertex();
+        final String label = v.label();
+        final Object id = v.id();
+
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void vertexRemoved(final Vertex element) {
+                assertEquals(v, element);
+                assertEquals(id, element.id());
+                assertEquals(label, element.label());
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        gts.V(v).drop().iterate();
+        tryCommit(graph);
+
+        assertVertexEdgeCounts(graph, 0, 0);
+        assertThat(triggered.get(), is(true));
+    }
+
+    @Test
+    @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+    public void shouldUseActualVertexWhenAdded() {
+        final AtomicBoolean triggered = new AtomicBoolean(false);
+
+        final AtomicReference<Vertex> eventedVertex = new AtomicReference<>();
+        final MutationListener listener = new AbstractMutationListener() {
+            @Override
+            public void vertexAdded(final Vertex element) {
+                eventedVertex.set(element);
+                assertEquals("thing", element.label());
+                assertThat(element.properties("here").hasNext(), is(false));
+                triggered.set(true);
+            }
+        };
+        final EventStrategy.Builder builder = 
EventStrategy.build().addListener(listener).detach(ReferenceFactory.class);
+
+        if (graph.features().graph().supportsTransactions())
+            builder.eventQueue(new 
EventStrategy.TransactionalEventQueue(graph));
+
+        final EventStrategy eventStrategy = builder.create();
+        final GraphTraversalSource gts = create(eventStrategy);
+
+        final Vertex v = gts.addV("thing").property("here", "there").next();
+        tryCommit(graph);
+
+        assertVertexEdgeCounts(graph, 1, 0);
+        assertThat(triggered.get(), is(true));
+        assertEquals(v, eventedVertex.get());
+    }
+
     private GraphTraversalSource create(final EventStrategy strategy) {
         return graphProvider.traversal(graph, strategy);
     }

Reply via email to