blob: 91aecc9b2c1e1702e765c268a9995b75c9ec0f60 [file] [log] [blame]
/*
* 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-&gt;BackgroundObserver-&gt;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();
}
}