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); }