jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ju...@apache.org
Subject svn commit: r1561710 - in /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation: ./ filter/
Date Mon, 27 Jan 2014 15:24:51 GMT
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



Mime
View raw message