| /* |
| * 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.document.secondary; |
| |
| import java.util.List; |
| |
| import org.apache.jackrabbit.oak.api.PropertyState; |
| import org.apache.jackrabbit.oak.plugins.document.AbstractDocumentNodeState; |
| import org.apache.jackrabbit.oak.plugins.document.RevisionVector; |
| import org.apache.jackrabbit.oak.spi.filter.PathFilter; |
| import org.apache.jackrabbit.oak.spi.state.ApplyDiff; |
| import org.apache.jackrabbit.oak.spi.state.NodeBuilder; |
| import org.apache.jackrabbit.oak.spi.state.NodeState; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import static com.google.common.base.Preconditions.checkArgument; |
| import static org.apache.jackrabbit.oak.plugins.document.secondary.DelegatingDocumentNodeState.PROP_LAST_REV; |
| import static org.apache.jackrabbit.oak.plugins.document.secondary.DelegatingDocumentNodeState.PROP_REVISION; |
| import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE; |
| import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty; |
| |
| class PathFilteringDiff extends ApplyDiff { |
| private static final Logger logger = LoggerFactory.getLogger(PathFilteringDiff.class); |
| private final DiffContext ctx; |
| private final AbstractDocumentNodeState parent; |
| |
| public PathFilteringDiff(NodeBuilder builder, PathFilter pathFilter, List<String> metaPropNames, AbstractDocumentNodeState parent) { |
| this(builder, new DiffContext(pathFilter, metaPropNames), parent); |
| } |
| |
| private PathFilteringDiff(NodeBuilder builder, DiffContext ctx, AbstractDocumentNodeState parent) { |
| super(builder); |
| this.ctx = ctx; |
| this.parent = parent; |
| } |
| |
| @Override |
| public boolean childNodeAdded(String name, NodeState after) { |
| AbstractDocumentNodeState afterDoc = asDocumentState(after, name); |
| String nextPath = afterDoc.getPath().toString(); |
| PathFilter.Result result = ctx.pathFilter.filter(nextPath); |
| if (result == PathFilter.Result.EXCLUDE){ |
| return true; |
| } |
| |
| ctx.traversingNode(nextPath); |
| //We avoid this as we need to copy meta properties |
| //super.childNodeAdded(name, after); |
| |
| NodeBuilder childBuilder = builder.child(name); |
| copyMetaProperties(afterDoc, childBuilder, ctx.metaPropNames); |
| return after.compareAgainstBaseState(EMPTY_NODE, |
| new PathFilteringDiff(childBuilder, ctx, afterDoc)); |
| } |
| |
| @Override |
| public boolean childNodeChanged(String name, NodeState before, NodeState after) { |
| AbstractDocumentNodeState afterDoc = asDocumentState(after, name); |
| String nextPath = afterDoc.getPath().toString(); |
| if (ctx.pathFilter.filter(nextPath) != PathFilter.Result.EXCLUDE) { |
| ctx.traversingNode(nextPath); |
| NodeBuilder childBuilder = builder.getChildNode(name); |
| copyMetaProperties(afterDoc, childBuilder, ctx.metaPropNames); |
| return after.compareAgainstBaseState( |
| before, new PathFilteringDiff(builder.getChildNode(name), ctx, afterDoc)); |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean childNodeDeleted(String name, NodeState before) { |
| String path = asDocumentState(before, name).getPath().toString(); |
| if (ctx.pathFilter.filter(path) != PathFilter.Result.EXCLUDE) { |
| return super.childNodeDeleted(name, before); |
| } |
| return true; |
| } |
| |
| private AbstractDocumentNodeState asDocumentState(NodeState state, String name){ |
| checkArgument(state instanceof AbstractDocumentNodeState, "Node %s (%s) at [%s/%s] is not" + |
| " of expected type i.e. AbstractDocumentNodeState. Parent %s (%s)", |
| state, state.getClass(), parent.getPath(), name, parent, parent.getClass()); |
| return (AbstractDocumentNodeState) state; |
| } |
| |
| static void copyMetaProperties(AbstractDocumentNodeState state, NodeBuilder builder, List<String> metaPropNames) { |
| //Only set root revision on root node |
| if (state.getPath().isRoot()) { |
| builder.setProperty(asPropertyState(PROP_REVISION, state.getRootRevision())); |
| } |
| |
| //LastRev would be set on each node |
| builder.setProperty(asPropertyState(PROP_LAST_REV, state.getLastRevision())); |
| |
| for (String metaProp : metaPropNames){ |
| PropertyState ps = state.getProperty(metaProp); |
| if (ps != null){ |
| builder.setProperty(ps); |
| } |
| } |
| } |
| |
| private static PropertyState asPropertyState(String name, RevisionVector revision) { |
| return createProperty(name, revision.asString()); |
| } |
| |
| private static class DiffContext { |
| private long count; |
| final PathFilter pathFilter; |
| final List<String> metaPropNames; |
| |
| public DiffContext(PathFilter filter, List<String> metaPropNames) { |
| this.pathFilter = filter; |
| this.metaPropNames = metaPropNames; |
| } |
| |
| public void traversingNode(String path){ |
| if (++count % 10000 == 0) { |
| logger.info("Updating Secondary Store. Traversed #{} - {}", count, path); |
| } |
| } |
| } |
| } |