You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by md...@apache.org on 2013/11/18 17:46:34 UTC

svn commit: r1543077 - in /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter: ./ EventGenerator.java EventIterator.java Filters.java

Author: mduerig
Date: Mon Nov 18 16:46:33 2013
New Revision: 1543077

URL: http://svn.apache.org/r1543077
Log:
OAK-1133: Observation listener PLUS
Add generic classes and interfaces for event filtering and generation

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/EventGenerator.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/EventIterator.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/Filters.java

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/EventGenerator.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/EventGenerator.java?rev=1543077&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/EventGenerator.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/EventGenerator.java Mon Nov 18 16:46:33 2013
@@ -0,0 +1,265 @@
+/*
+ * 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 static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.spi.state.MoveValidator;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+/**
+ * {@link Filter filter} and report changes between node states to the {@link Listener}.
+ */
+public class EventGenerator implements MoveValidator {
+    private final Filter filter;
+    private final Listener listener;
+
+    /**
+     * Filter for determining what changes to report the the {@link Listener}.
+     */
+    public interface Filter {
+
+        /**
+         * Include an added property
+         * @param after  added property
+         * @return  {@code true} if the property should be included
+         */
+        boolean includeAdd(PropertyState after);
+
+        /**
+         * Include a changed property
+         * @param before  property before the change
+         * @param after  property after the change
+         * @return  {@code true} if the property should be included
+         */
+        boolean includeChange(PropertyState before, PropertyState after);
+
+        /**
+         * Include a deleted property
+         * @param before  deleted property
+         * @return  {@code true} if the property should be included
+         */
+        boolean includeDelete(PropertyState before);
+
+        /**
+         * Include an added node
+         * @param name name of the node
+         * @param after  added node
+         * @return  {@code true} if the node should be included
+         */
+        boolean includeAdd(String name, NodeState after);
+
+        /**
+         * Include a changed node
+         * @param name name of the node
+         * @param before node before the change
+         * @param after  node after the change
+         * @return  {@code true} if the node should be included
+         */
+        boolean includeChange(String name, NodeState before, NodeState after);
+
+        /**
+         * Include a deleted node
+         * @param name name of the node
+         * @param before deleted node
+         * @return  {@code true} if the node should be included
+         */
+        boolean includeDelete(String name, NodeState before);
+
+        /**
+         * Include a moved node
+         * @param sourcePath  source path of the move operation
+         * @param destPath  destination path of the move operation
+         * @param moved the moved node
+         * @return  {@code true} if the node should be included
+         */
+        boolean includeMove(String sourcePath, String destPath, NodeState moved);
+
+        /**
+         * Factory for creating a filter instance for the given child node
+         * @param name  name of the child node
+         * @param before  before state of the child node
+         * @param after  after state of the child node
+         * @return  filter instance for filtering the child node or {@code null} to
+         *          exclude the sub tree rooted at this child node.
+         */
+        @CheckForNull
+        Filter create(String name, NodeState before, NodeState after);
+    }
+
+    /**
+     * Listener for listening to changes.
+     */
+    public interface Listener {
+
+        /**
+         * Notification for an added property
+         * @param after  added property
+         */
+        void propertyAdded(PropertyState after);
+
+        /**
+         * Notification for a changed property
+         * @param before  property before the change
+         * @param after  property after the change
+         */
+        void propertyChanged(PropertyState before, PropertyState after);
+
+        /**
+         * Notification for a deleted property
+         * @param before  deleted property
+         */
+        void propertyDeleted(PropertyState before);
+
+        /**
+         * Notification for an added node
+         * @param name  name of the node
+         * @param after  added node
+         */
+        void childNodeAdded(String name, NodeState after);
+
+        /**
+         * 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);
+
+        /**
+         * Notification for a deleted node
+         * @param name  name of the deleted node
+         * @param before  deleted node
+         */
+        void childNodeDeleted(String name, NodeState before);
+
+        /**
+         * Notification for a moved node
+         * @param sourcePath  source of the moved node
+         * @param destPath  destination of the moved node
+         * @param moved  moved node
+         */
+        void nodeMoved(String sourcePath, String destPath, NodeState moved);
+
+        /**
+         * Factory for creating a filter instance for the given child node
+         * @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(Filter filter, Listener listener) {
+        this.filter = filter;
+        this.listener = listener;
+    }
+
+    @Override
+    public void move(String sourcePath, String destPath, NodeState moved) throws CommitFailedException {
+        if (filter.includeMove(sourcePath, destPath, moved)) {
+            listener.nodeMoved(sourcePath, destPath, moved);
+        }
+    }
+
+    @Override
+    public void enter(NodeState before, NodeState after) throws CommitFailedException {
+    }
+
+    @Override
+    public void leave(NodeState before, NodeState after) throws CommitFailedException {
+    }
+
+    @Override
+    public void propertyAdded(PropertyState after) throws CommitFailedException {
+        if (filter.includeAdd(after)) {
+            listener.propertyAdded(after);
+        }
+    }
+
+    @Override
+    public void propertyChanged(PropertyState before, PropertyState after) throws CommitFailedException {
+        if (filter.includeChange(before, after)) {
+            listener.propertyChanged(before, after);
+        }
+    }
+
+    @Override
+    public void propertyDeleted(PropertyState before) throws CommitFailedException {
+        if (filter.includeDelete(before)) {
+            listener.propertyDeleted(before);
+        }
+    }
+
+    @Override
+    public MoveValidator childNodeAdded(String name, NodeState after) throws CommitFailedException {
+        if (filter.includeAdd(name, after)) {
+            listener.childNodeAdded(name, after);
+        }
+        return createChildGenerator(name, MISSING_NODE, after);
+    }
+
+    @Override
+    public MoveValidator childNodeChanged(String name, NodeState before, NodeState after) throws CommitFailedException {
+        if (filter.includeChange(name, before, after)) {
+            listener.childNodeChanged(name, before, after);
+        }
+        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);
+    }
+
+    /**
+     * 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) {
+        Filter childFilter = filter.create(name, before, after);
+        if (childFilter != null) {
+            return new EventGenerator(
+                    childFilter,
+                    listener.create(name, before, after));
+        } else {
+            return null;
+        }
+    }
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/EventIterator.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/EventIterator.java?rev=1543077&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/EventIterator.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/EventIterator.java Mon Nov 18 16:46:33 2013
@@ -0,0 +1,142 @@
+/*
+ * 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 static com.google.common.collect.Iterators.concat;
+import static com.google.common.collect.Lists.newArrayList;
+
+import java.util.Iterator;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.core.ImmutableTree;
+import org.apache.jackrabbit.oak.spi.commit.EditorDiff;
+import org.apache.jackrabbit.oak.spi.commit.VisibleEditor;
+import org.apache.jackrabbit.oak.spi.state.MoveDetector;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.util.LazyValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This {@link EventGenerator} implementation provides a traversable view for
+ * events.
+ * @param <T> type of the event returned by this iterator
+ */
+public class EventIterator<T> extends EventGenerator implements Iterator<T> {
+    private static final Logger LOG = LoggerFactory.getLogger(EventIterator.class);
+
+    private final ImmutableTree beforeTree;
+    private final ImmutableTree afterTree;
+
+    private final Filter filter;
+    private final IterableListener<T> listener;
+
+    private final List<Iterator<T>> childEvents = newArrayList();
+
+    private final LazyValue<Iterator<T>> eventIterator = new LazyValue<Iterator<T>>() {
+        @Override
+        protected Iterator<T> createValue() {
+            CommitFailedException e = EditorDiff.process(
+                    new VisibleEditor(
+                        new MoveDetector(EventIterator.this, afterTree.getPath())),
+                    beforeTree.getNodeState(), afterTree.getNodeState());
+
+            if (e != null) {
+                LOG.error("Error while extracting observation events", e);
+            }
+
+            return concat(listener.iterator(), concat(childEvents.iterator()));
+        }
+    };
+
+    /**
+     * Specialisation of {@link Listener} that provides the events reported
+     * to it as an iterator.
+     *
+     * @param <S> type of the events in the iterator
+     */
+    public interface IterableListener<S> extends Listener, Iterable<S> {
+        @Override
+        @Nonnull
+        IterableListener<S> create(String name, NodeState before, NodeState after);
+    }
+
+    /**
+     * Create a new instance of a {@code EventIterator} reporting events to the
+     * passed {@code listener} after filtering with the passed {@code filter}.
+     *
+     * @param before  before state
+     * @param after   after state
+     * @param filter  filter for filtering changes
+     * @param listener  listener for listening to the filtered changes
+     */
+    public EventIterator(NodeState before, NodeState after, Filter filter,
+            IterableListener<T> listener) {
+        super(filter, listener);
+        this.beforeTree = new ImmutableTree(before);
+        this.afterTree = new ImmutableTree(after);
+        this.filter = filter;
+        this.listener = listener;
+    }
+
+    private EventIterator(ImmutableTree before, ImmutableTree after, Filter filter,
+            IterableListener<T> listener) {
+        super(filter, listener);
+        this.beforeTree = before;
+        this.afterTree = after;
+        this.filter = filter;
+        this.listener = listener;
+    }
+
+    //------------------------------------------------------------< EventGenerator >---
+
+    @Override
+    protected EventGenerator createChildGenerator(String name, NodeState before, NodeState after) {
+        Filter childFilter = filter.create(name, before, after);
+        if (childFilter != null) {
+            childEvents.add(new EventIterator<T>(
+                    beforeTree.getChild(name), afterTree.getChild(name),
+                    childFilter,
+                    listener.create(name, before, after)));
+        }
+        return null;
+    }
+
+    //------------------------------------------------------------< Iterator >---
+
+    @Override
+    public boolean hasNext() {
+        return eventIterator.get().hasNext();
+    }
+
+    @Override
+    public T next() {
+        return eventIterator.get().next();
+    }
+
+    @Override
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/Filters.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/Filters.java?rev=1543077&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/Filters.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/filter/Filters.java Mon Nov 18 16:46:33 2013
@@ -0,0 +1,231 @@
+/*
+ * 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 static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import com.google.common.collect.Lists;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.plugins.observation.filter.EventGenerator.Filter;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+/**
+ * This utility class provides common {@link Filter} instances
+ */
+public final class Filters {
+    private Filters() {
+    }
+
+    /**
+     * A filter that matches if and only if any of the filters passed to this
+     * method matches.
+     * @param filters  filters of which any must match
+     * @return {@code true} if any of {@code filters} match.
+     */
+    public static Filter any(@Nonnull final Filter... filters) {
+        return any(Lists.newArrayList(checkNotNull(filters)));
+    }
+
+    /**
+     * A filter that matches if and only if all of the filters passed to this
+     * method matches.
+     * @param filters  filters of which all must match
+     * @return {@code true} if all of {@code filters} match.
+     */
+    public static Filter all(@Nonnull final Filter... filters) {
+        return all(Lists.newArrayList(checkNotNull(filters)));
+    }
+
+    private static Filter any(final List<Filter> filters) {
+        return new Filter() {
+            @Override
+            public boolean includeAdd(PropertyState after) {
+                for (Filter filter : filters) {
+                    if (filter.includeAdd(after)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            public boolean includeChange(PropertyState before, PropertyState after) {
+                for (Filter filter : filters) {
+                    if (filter.includeChange(before, after)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            public boolean includeDelete(PropertyState before) {
+                for (Filter filter : filters) {
+                    if (filter.includeDelete(before)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            public boolean includeAdd(String name, NodeState after) {
+                for (Filter filter : filters) {
+                    if (filter.includeAdd(name, after)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            public boolean includeChange(String name, NodeState before, NodeState after) {
+                for (Filter filter : filters) {
+                    if (filter.includeChange(name, before, after)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            public boolean includeDelete(String name, NodeState before) {
+                for (Filter filter : filters) {
+                    if (filter.includeDelete(name, before)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            public boolean includeMove(String sourcePath, String destPath, NodeState moved) {
+                for (Filter filter : filters) {
+                    if (filter.includeMove(sourcePath, destPath, moved)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            public Filter create(String name, NodeState before, NodeState after) {
+                List<Filter> childFilters = Lists.newArrayList();
+                for (Filter filter : filters) {
+                    Filter childFilter = filter.create(name, before, after);
+                    if (childFilter != null) {
+                        childFilters.add(childFilter);
+                    }
+                }
+                return any(childFilters);
+            }
+        };
+    }
+
+    private static Filter all(final List<Filter> filters) {
+        return new Filter() {
+            @Override
+            public boolean includeAdd(PropertyState after) {
+                for (Filter filter : filters) {
+                    if (!filter.includeAdd(after)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            @Override
+            public boolean includeChange(PropertyState before, PropertyState after) {
+                for (Filter filter : filters) {
+                    if (!filter.includeChange(before, after)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            @Override
+            public boolean includeDelete(PropertyState before) {
+                for (Filter filter : filters) {
+                    if (!filter.includeDelete(before)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            @Override
+            public boolean includeAdd(String name, NodeState after) {
+                for (Filter filter : filters) {
+                    if (!filter.includeAdd(name, after)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            @Override
+            public boolean includeChange(String name, NodeState before, NodeState after) {
+                for (Filter filter : filters) {
+                    if (!filter.includeChange(name, before, after)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            @Override
+            public boolean includeDelete(String name, NodeState before) {
+                for (Filter filter : filters) {
+                    if (!filter.includeDelete(name, before)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            @Override
+            public boolean includeMove(String sourcePath, String destPath, NodeState moved) {
+                for (Filter filter : filters) {
+                    if (!filter.includeMove(sourcePath, destPath, moved)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            @Override
+            public Filter create(String name, NodeState before, NodeState after) {
+                List<Filter> childFilters = Lists.newArrayList();
+                for (Filter filter : filters) {
+                    Filter childFilter = filter.create(name, before, after);
+                    if (childFilter != null) {
+                        childFilters.add(childFilter);
+                    }
+                }
+                return all(childFilters);
+            }
+        };
+    }
+}