Author: jukka
Date: Mon Jan 27 15:24:51 2014
New Revision: 1561710
URL: http://svn.apache.org/r1561710
Log:
OAK-1332: Large number of changes to the same node can fill observation queue
Merge EventIterable and JcrListener to EventGenerator to simplify the code
and to use just a single list of events as the event queue.
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/VisibleFilter.java
(with props)
Removed:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/EventIterable.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/JcrListener.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/ChangeProcessor.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/EventContext.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/EventGenerator.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/EventFilter.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/ChangeProcessor.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/ChangeProcessor.java?rev=1561710&r1=1561709&r2=1561710&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/ChangeProcessor.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/ChangeProcessor.java
Mon Jan 27 15:24:51 2014
@@ -20,13 +20,11 @@ package org.apache.jackrabbit.oak.plugin
import static com.google.common.base.Preconditions.checkState;
-import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import javax.jcr.observation.Event;
import javax.jcr.observation.EventListener;
import com.google.common.util.concurrent.Monitor;
@@ -163,15 +161,12 @@ public class ChangeProcessor implements
EventFilter acFilter = new ACFilter(previousRoot, root,
permissionProvider, basePath);
ImmutableTree beforeTree = getTree(previousRoot, basePath);
ImmutableTree afterTree = getTree(root, basePath);
- EventContext context = new EventContext(namePathMapper,
info);
- EventIterable<Event> events = new EventIterable<Event>(
- beforeTree.getNodeState(),
afterTree.getNodeState(),
- Filters.all(userFilter, acFilter),
- new JcrListener(context, beforeTree, afterTree));
- Iterator<Event> iterator = events.iterator();
- if (iterator.hasNext() && runningMonitor.enterIf(running))
{
+ EventGenerator events = new EventGenerator(
+ namePathMapper, info, beforeTree, afterTree,
+ Filters.all(userFilter, acFilter));
+ if (events.hasNext() && runningMonitor.enterIf(running)) {
try {
- eventListener.onEvent(new
EventIteratorAdapter(iterator));
+ eventListener.onEvent(new
EventIteratorAdapter(events));
} finally {
runningMonitor.leave();
}
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/EventContext.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/EventContext.java?rev=1561710&r1=1561709&r2=1561710&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/EventContext.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/EventContext.java
Mon Jan 27 15:24:51 2014
@@ -92,4 +92,4 @@ final class EventContext {
return info.toString();
}
-}
\ No newline at end of file
+}
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/EventGenerator.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/EventGenerator.java?rev=1561710&r1=1561709&r2=1561710&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/EventGenerator.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/EventGenerator.java
Mon Jan 27 15:24:51 2014
@@ -19,174 +19,279 @@
package org.apache.jackrabbit.oak.plugins.observation;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Lists.newLinkedList;
+import static javax.jcr.observation.Event.NODE_ADDED;
+import static javax.jcr.observation.Event.NODE_MOVED;
+import static javax.jcr.observation.Event.NODE_REMOVED;
+import static javax.jcr.observation.Event.PROPERTY_ADDED;
+import static javax.jcr.observation.Event.PROPERTY_CHANGED;
+import static javax.jcr.observation.Event.PROPERTY_REMOVED;
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static org.apache.jackrabbit.oak.core.AbstractTree.OAK_CHILD_ORDER;
import static
org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE;
+import static org.apache.jackrabbit.oak.spi.state.MoveDetector.SOURCE_PATH;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
import javax.annotation.Nonnull;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
-import org.apache.jackrabbit.oak.api.CommitFailedException;
import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.core.ImmutableTree;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
import org.apache.jackrabbit.oak.plugins.observation.filter.EventFilter;
-import org.apache.jackrabbit.oak.spi.state.MoveValidator;
+import org.apache.jackrabbit.oak.plugins.observation.filter.Filters;
+import org.apache.jackrabbit.oak.plugins.observation.filter.VisibleFilter;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
+
+import com.google.common.collect.ImmutableMap;
/**
- * {@link EventFilter filter} and report changes between node states to the
{@link Listener}.
+ * Generator of a traversable view of events.
*/
-public class EventGenerator implements MoveValidator {
- private final EventFilter filter;
- private final Listener listener;
+public class EventGenerator implements EventIterator {
+
+ private final EventContext context;
+
+ private final LinkedList<Event> events = newLinkedList();
+ private final LinkedList<Runnable> generators = newLinkedList();
+
+ private long position = 0;
/**
- * Listener for listening to changes.
+ * Create a new instance of a {@code EventGenerator} reporting events to
the
+ * passed {@code listener} after filtering with the passed {@code filter}.
+ *
+ * @param filter filter for filtering changes
*/
- public interface Listener {
+ public EventGenerator(
+ @Nonnull NamePathMapper namePathMapper, CommitInfo info,
+ @Nonnull ImmutableTree before, @Nonnull ImmutableTree after,
+ @Nonnull EventFilter filter) {
+ this.context = new EventContext(namePathMapper, info);
+
+ filter = Filters.all(new VisibleFilter(), checkNotNull(filter));
+ new EventDiff(before, after, filter).run();
+ }
+
+ private class EventDiff implements NodeStateDiff, Runnable {
+
+ private final EventFilter filter;
+ private final NodeState before;
+ private final NodeState after;
+ private final ImmutableTree beforeTree;
+ private final ImmutableTree afterTree;
+
+ EventDiff(ImmutableTree before, ImmutableTree after,
+ EventFilter filter) {
+ this.before = before.getNodeState();
+ this.after = after.getNodeState();
+ this.filter = filter;
+ this.beforeTree = before;
+ this.afterTree = after;
+ }
- /**
- * Notification for an added property
- * @param after added property
- */
- void propertyAdded(PropertyState after);
+ public EventDiff(
+ EventDiff parent, String name,
+ NodeState before, NodeState after, EventFilter filter) {
+ this.before = before;
+ this.after = after;
+ this.filter = filter;
+ this.beforeTree = new ImmutableTree(parent.beforeTree, name,
before);
+ this.afterTree = new ImmutableTree(parent.afterTree, name, after);
+ }
- /**
- * Notification for a changed property
- * @param before property before the change
- * @param after property after the change
- */
- void propertyChanged(PropertyState before, PropertyState after);
+ //------------------------------------------------------< Runnable >--
- /**
- * Notification for a deleted property
- * @param before deleted property
- */
- void propertyDeleted(PropertyState before);
+ @Override
+ public void run() {
+ after.compareAgainstBaseState(before, this);
+ }
- /**
- * Notification for an added node
- * @param name name of the node
- * @param after added node
- */
- void childNodeAdded(String name, NodeState after);
+ @Override
+ public boolean propertyAdded(PropertyState after) {
+ if (filter.includeAdd(after)) {
+ events.add(new EventImpl(
+ context, PROPERTY_ADDED, afterTree, after.getName()));
+ }
+ return true;
+ }
- /**
- * Notification for a changed node
- * @param name name of the node
- * @param before node before the change
- * @param after node after the change
- */
- void childNodeChanged(String name, NodeState before, NodeState after);
+ @Override
+ public boolean propertyChanged(
+ PropertyState before, PropertyState after) {
+ if (filter.includeChange(before, after)) {
+ events.add(new EventImpl(
+ context, PROPERTY_CHANGED, afterTree,
after.getName()));
+ }
+ return true;
+ }
- /**
- * Notification for a deleted node
- * @param name name of the deleted node
- * @param before deleted node
- */
- void childNodeDeleted(String name, NodeState before);
+ @Override
+ public boolean propertyDeleted(PropertyState before) {
+ if (filter.includeDelete(before)) {
+ events.add(new EventImpl(
+ context, PROPERTY_REMOVED, beforeTree,
before.getName()));
+ }
+ return true;
+ }
- /**
- * Notification for a moved node
- * @param sourcePath source of the moved node
- * @param name name of the moved node
- * @param moved moved node
- */
- void nodeMoved(String sourcePath, String name, NodeState moved);
+ @Override
+ public boolean childNodeAdded(String name, NodeState after) {
+ PropertyState sourceProperty = after.getProperty(SOURCE_PATH);
+ if (sourceProperty != null) {
+ String sourcePath = sourceProperty.getValue(STRING);
+ if (filter.includeMove(sourcePath, name, after)) {
+ String destPath = PathUtils.concat(afterTree.getPath(),
name);
+ Map<String, String> info = ImmutableMap.of(
+ "srcAbsPath", context.getJcrPath(sourcePath),
+ "destAbsPath", context.getJcrPath(destPath));
+ ImmutableTree tree = new ImmutableTree(afterTree, name,
after);
+ events.add(new EventImpl(context, NODE_MOVED, tree, info));
+ }
+ }
+ if (filter.includeAdd(name, after)) {
+ ImmutableTree tree = new ImmutableTree(afterTree, name, after);
+ events.add(new EventImpl(context, NODE_ADDED, tree));
+ }
+ addChildGenerator(name, MISSING_NODE, after);
+ return true;
+ }
+
+ @Override
+ public boolean childNodeChanged(
+ String name, NodeState before, NodeState after) {
+ if (filter.includeChange(name, before, after)) {
+ detectReorder(name, before, after);
+ }
+ addChildGenerator(name, before, after);
+ return true;
+ }
+
+ @Override
+ public boolean childNodeDeleted(String name, NodeState before) {
+ if (filter.includeDelete(name, before)) {
+ ImmutableTree tree = new ImmutableTree(beforeTree, name,
before);
+ events.add(new EventImpl(context, NODE_REMOVED, tree));
+ }
+ addChildGenerator(name, before, MISSING_NODE);
+ return true;
+ }
+
+ //------------------------------------------------------------<
private >---
+
+ private void detectReorder(String name, NodeState before, NodeState
after) {
+ List<String> afterNames =
newArrayList(after.getNames(OAK_CHILD_ORDER));
+ List<String> beforeNames =
newArrayList(before.getNames(OAK_CHILD_ORDER));
+
+ afterNames.retainAll(beforeNames);
+ beforeNames.retainAll(afterNames);
+
+ // Selection sort beforeNames into afterNames recording the swaps
as we go
+ ImmutableTree parent = new ImmutableTree(afterTree, name, after);
+ for (int a = 0; a < afterNames.size(); a++) {
+ String afterName = afterNames.get(a);
+ for (int b = a; b < beforeNames.size(); b++) {
+ String beforeName = beforeNames.get(b);
+ if (a != b && beforeName.equals(afterName)) {
+ beforeNames.set(b, beforeNames.get(a));
+ beforeNames.set(a, beforeName);
+ Map<String, String> info = ImmutableMap.of(
+ "srcChildRelPath", beforeNames.get(a),
+ "destChildRelPath", beforeNames.get(a + 1));
+ ImmutableTree tree = parent.getChild(afterName);
+ events.add(new EventImpl(context, NODE_MOVED, tree,
info));
+ }
+ }
+ }
+ }
/**
- * Factory for creating a filter instance for the given child node
- * @param name name of the child node
+ * Factory method for creating {@code EventGenerator} instances of
child nodes.
+ * @param name name of the child node
* @param before before state of the child node
* @param after after state of the child node
- * @return listener for the child node
*/
- @Nonnull
- Listener create(String name, NodeState before, NodeState after);
- }
-
- /**
- * Create a new instance of a {@code EventGenerator} reporting events to
the
- * passed {@code listener} after filtering with the passed {@code filter}.
- * @param filter filter for filtering changes
- * @param listener listener for listening to the filtered changes
- */
- public EventGenerator(@Nonnull EventFilter filter, @Nonnull Listener
listener) {
- this.filter = checkNotNull(filter);
- this.listener = checkNotNull(listener);
- }
-
- @Override
- public void move(String name, String sourcePath, NodeState moved) throws
CommitFailedException {
- if (filter.includeMove(sourcePath, name, moved)) {
- listener.nodeMoved(sourcePath, name, moved);
+ private void addChildGenerator(
+ String name, NodeState before, NodeState after) {
+ EventFilter childFilter = filter.create(name, before, after);
+ if (childFilter != null) {
+ generators.add(
+ new EventDiff(this, name, before, after, childFilter));
+ }
}
- }
- @Override
- public void enter(NodeState before, NodeState after) throws
CommitFailedException {
}
- @Override
- public void leave(NodeState before, NodeState after) throws
CommitFailedException {
- }
+ //-----------------------------------------------------< EventIterator >--
@Override
- public void propertyAdded(PropertyState after) throws
CommitFailedException {
- if (filter.includeAdd(after)) {
- listener.propertyAdded(after);
+ public long getSize() {
+ if (generators.isEmpty()) {
+ return position + events.size();
+ } else {
+ return -1;
}
}
@Override
- public void propertyChanged(PropertyState before, PropertyState after)
throws CommitFailedException {
- if (filter.includeChange(before, after)) {
- listener.propertyChanged(before, after);
- }
+ public long getPosition() {
+ return position;
}
@Override
- public void propertyDeleted(PropertyState before) throws
CommitFailedException {
- if (filter.includeDelete(before)) {
- listener.propertyDeleted(before);
+ public boolean hasNext() {
+ while (events.isEmpty()) {
+ if (generators.isEmpty()) {
+ return false;
+ } else {
+ generators.removeFirst().run();
+ }
}
+ return true;
}
@Override
- public MoveValidator childNodeAdded(String name, NodeState after) throws
CommitFailedException {
- if (filter.includeAdd(name, after)) {
- listener.childNodeAdded(name, after);
+ public void skip(long skipNum) {
+ while (skipNum > events.size()) {
+ position += events.size();
+ skipNum -= events.size();
+ events.clear();
+ // the remove below throws NoSuchElementException if there
+ // are no more generators, which is correct as then we can't
+ // skip over enough events
+ generators.removeFirst().run();
}
- return createChildGenerator(name, MISSING_NODE, after);
+ position += skipNum;
+ events.subList(0, (int) skipNum).clear();
}
@Override
- public MoveValidator childNodeChanged(String name, NodeState before,
NodeState after) throws CommitFailedException {
- if (filter.includeChange(name, before, after)) {
- listener.childNodeChanged(name, before, after);
+ public Event nextEvent() {
+ if (hasNext()) {
+ position++;
+ return events.removeFirst();
+ } else {
+ throw new NoSuchElementException();
}
- return createChildGenerator(name, before, after);
}
@Override
- public MoveValidator childNodeDeleted(String name, NodeState before)
throws CommitFailedException {
- if (filter.includeDelete(name, before)) {
- listener.childNodeDeleted(name, before);
- }
- return createChildGenerator(name, before, MISSING_NODE);
+ public Event next() {
+ return nextEvent();
}
- /**
- * Factory method for creating {@code EventGenerator} instances of child
nodes.
- * @param name name of the child node
- * @param before before state of the child node
- * @param after after state of the child node
- * @return {@code EventGenerator} for a child node
- */
- protected EventGenerator createChildGenerator(String name, NodeState
before, NodeState after) {
- EventFilter childFilter = filter.create(name, before, after);
- if (childFilter != null) {
- return new EventGenerator(
- childFilter,
- listener.create(name, before, after));
- } else {
- return null;
- }
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
}
+
}
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/EventFilter.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/EventFilter.java?rev=1561710&r1=1561709&r2=1561710&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/EventFilter.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/EventFilter.java
Mon Jan 27 15:24:51 2014
@@ -21,11 +21,10 @@ package org.apache.jackrabbit.oak.plugin
import javax.annotation.CheckForNull;
import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.plugins.observation.EventGenerator.Listener;
import org.apache.jackrabbit.oak.spi.state.NodeState;
/**
- * Filter for determining what changes to report the the {@link Listener}.
+ * Filter for determining what changes to report the the event listener.
*/
public interface EventFilter {
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/VisibleFilter.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/VisibleFilter.java?rev=1561710&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/VisibleFilter.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/VisibleFilter.java
Mon Jan 27 15:24:51 2014
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.oak.plugins.observation.filter;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+/**
+ * Event filter that hides all non-visible content.
+ */
+public class VisibleFilter implements EventFilter {
+
+ private boolean isVisible(String name) {
+ return !name.startsWith(":");
+ }
+
+ @Override
+ public boolean includeAdd(PropertyState after) {
+ return isVisible(after.getName());
+ }
+
+ @Override
+ public boolean includeChange(PropertyState before, PropertyState after) {
+ return isVisible(after.getName());
+ }
+
+ @Override
+ public boolean includeDelete(PropertyState before) {
+ return isVisible(before.getName());
+ }
+
+ @Override
+ public boolean includeAdd(String name, NodeState after) {
+ return isVisible(name);
+ }
+
+ @Override
+ public boolean includeChange(String name, NodeState before, NodeState
after) {
+ return isVisible(name);
+ }
+
+ @Override
+ public boolean includeDelete(String name, NodeState before) {
+ return isVisible(name);
+ }
+
+ @Override
+ public boolean includeMove(String sourcePath, String name, NodeState
moved) {
+ return isVisible(name);
+ }
+
+ @Override
+ public EventFilter create(String name, NodeState before, NodeState after) {
+ if (isVisible(name)) {
+ return this;
+ } else {
+ return null;
+ }
+ }
+
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/VisibleFilter.java
------------------------------------------------------------------------------
svn:eol-style = native