| /* |
| * 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; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| |
| import java.io.Closeable; |
| import java.util.concurrent.Executor; |
| |
| import org.apache.jackrabbit.oak.spi.commit.BackgroundObserver; |
| import org.apache.jackrabbit.oak.spi.commit.CommitInfo; |
| import org.apache.jackrabbit.oak.spi.commit.Observer; |
| import org.apache.jackrabbit.oak.spi.state.NodeState; |
| import org.jetbrains.annotations.NotNull; |
| |
| /** |
| * An observer that implements filtering of content changes |
| * while at the same time supporting (wrapping) a BackgroundObserver |
| * underneath. |
| * <p> |
| * The FilteringObserver uses an explicit Filter to decide whether |
| * or not to forward a content change to the BackgroundObserver. |
| * If the Filter decides to include the change things happen as usual. |
| * If the Filter decides to exclude the change, this FilteringObserver |
| * does not forward the change, but remembers the fact that the last |
| * change was filtered. The first included change after excluded ones |
| * will cause a NOOP_CHANGE commitInfo to be passed along to the |
| * BackgroundObserver. That NOOP_CHANGE is then used by the |
| * FilteringDispatcher: if a CommitInfo is a NOOP_CHANGE then the |
| * FilteringDispatcher will not forward anything to the FilteringAwareObserver |
| * and only adjust the 'before' state accordingly (which it does also |
| * for a NOOP_CHANGE, to exactly achieve the skipping effect). |
| */ |
| public class FilteringObserver implements Observer, Closeable { |
| |
| /** package protected CommitInfo used between FilteringObserver and FilteringDispatcher **/ |
| final static CommitInfo NOOP_CHANGE = new CommitInfo(CommitInfo.OAK_UNKNOWN, CommitInfo.OAK_UNKNOWN); |
| |
| private final BackgroundObserver backgroundObserver; |
| |
| private final Filter filter; |
| |
| private NodeState lastNoop; |
| |
| /** |
| * Default constructor which creates a BackgroundObserver automatically, including |
| * creating a FilteringDispatcher. |
| * @param executor the executor that should be used for the BackgroundObserver |
| * @param queueLength the queue length of the BackgroundObserver |
| * @param filter the Filter to be used for filtering |
| * @param observer the FilteringAwareObserver to which content changes ultimately |
| * are delivered after going through a chain of |
| * FilteringObserver->BackgroundObserver->FilteringDispatcher. |
| */ |
| public FilteringObserver(@NotNull Executor executor, int queueLength, @NotNull Filter filter, |
| @NotNull FilteringAwareObserver observer) { |
| this(new BackgroundObserver(new FilteringDispatcher(checkNotNull(observer)), checkNotNull(executor), |
| queueLength), filter); |
| } |
| |
| /** |
| * Alternative constructor where the BackgroundObserver is created elsewhere |
| * @param backgroundObserver the BackgroundObserver to be used by this FilteringObserver |
| * @param filter the Filter to be used for filtering |
| */ |
| public FilteringObserver(@NotNull BackgroundObserver backgroundObserver, @NotNull Filter filter) { |
| this.backgroundObserver = backgroundObserver; |
| this.filter = checkNotNull(filter); |
| } |
| |
| public BackgroundObserver getBackgroundObserver() { |
| return backgroundObserver; |
| } |
| |
| @Override |
| public final void contentChanged(@NotNull NodeState root, @NotNull CommitInfo info) { |
| if (filter.excludes(root, info)) { |
| lastNoop = root; |
| return; |
| } |
| // current change is not an noop |
| if (lastNoop != null) { |
| // report up to previous noop |
| backgroundObserver.contentChanged(lastNoop, NOOP_CHANGE); |
| lastNoop = null; |
| } |
| backgroundObserver.contentChanged(root, info); |
| } |
| |
| @Override |
| public void close() { |
| backgroundObserver.close(); |
| } |
| |
| } |