Skip to content

Commit

Permalink
OAK-6548: Composite node builder/state keeps references to all the an…
Browse files Browse the repository at this point in the history
…cestor states

git-svn-id: https://svn.apache.org/repos/asf/jackrabbit/oak/trunk@1804757 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
trekawek committed Aug 11, 2017
1 parent a5918fd commit 8122c3c
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 119 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,56 +22,46 @@
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.composite.util.Memoizer;
import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
import org.apache.jackrabbit.oak.spi.state.MoveDetector;
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 java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Maps.newHashMap;
import static java.lang.Long.MAX_VALUE;
import static java.util.Collections.singleton;
import static org.apache.jackrabbit.oak.composite.CompositeNodeState.STOP_COUNTING_CHILDREN;
import static org.apache.jackrabbit.oak.composite.CompositeNodeState.accumulateChildSizes;
import static org.apache.jackrabbit.oak.composite.CompositeNodeState.wrapWithNullCheck;
import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE;
import static org.apache.jackrabbit.oak.spi.state.AbstractNodeState.checkValidName;

class CompositeNodeBuilder implements NodeBuilder {

private static final Logger LOG = LoggerFactory.getLogger(CompositeNodeBuilder.class);

private final String path;

private final CompositionContext ctx;

private Function<MountedNodeStore, NodeBuilder> nodeBuilders;
private NodeMap<NodeBuilder> nodeBuilders;

private final MountedNodeStore owningStore;

private final CompositeNodeBuilder parent;

private final CompositeNodeBuilder rootBuilder;

CompositeNodeBuilder(String path, Function<MountedNodeStore, NodeBuilder> nodeBuilders, CompositionContext ctx) {
CompositeNodeBuilder(String path, NodeMap<NodeBuilder> nodeBuilders, CompositionContext ctx) {
this(path, nodeBuilders, ctx, null);
}

private CompositeNodeBuilder(String path, Function<MountedNodeStore, NodeBuilder> nodeBuilders, CompositionContext ctx, CompositeNodeBuilder parent) {
private CompositeNodeBuilder(String path, NodeMap<NodeBuilder> nodeBuilders, CompositionContext ctx, CompositeNodeBuilder parent) {
this.path = path;
this.ctx = ctx;
this.nodeBuilders = wrapWithNullCheck(Memoizer.memoize(nodeBuilders), LOG, path);
this.nodeBuilders = nodeBuilders;
this.owningStore = ctx.getOwningStore(path);
this.parent = parent;
if (parent == null) {
Expand All @@ -82,23 +72,17 @@ private CompositeNodeBuilder(String path, Function<MountedNodeStore, NodeBuilder
}

NodeBuilder getNodeBuilder(MountedNodeStore mns) {
return nodeBuilders.apply(mns);
return nodeBuilders.get(mns);
}

@Override
public CompositeNodeState getNodeState() {
Map<MountedNodeStore, NodeState> states = ctx.getAllMountedNodeStores().stream().collect(Collectors.toMap(Function.identity(),
nodeBuilders
.andThen(n -> n.exists() ? n.getNodeState() : MISSING_NODE)));
return new CompositeNodeState(path, states, ctx);
return new CompositeNodeState(path, nodeBuilders.getAndApply(n -> n.exists() ? n.getNodeState() : MISSING_NODE), ctx);
}

@Override
public CompositeNodeState getBaseState() {
Map<MountedNodeStore, NodeState> states = ctx.getAllMountedNodeStores().stream().collect(Collectors.toMap(Function.identity(),
nodeBuilders
.andThen(NodeBuilder::getBaseState)));
return new CompositeNodeState(path, states, ctx);
return new CompositeNodeState(path, nodeBuilders.getAndApply(NodeBuilder::getBaseState), ctx);
}

// node or property-related methods ; directly delegate to wrapped builder
Expand Down Expand Up @@ -208,7 +192,7 @@ public long getChildNodeCount(final long max) {
// Count the children in each contributing store.
return accumulateChildSizes(FluentIterable.from(contributingStores)
.transformAndConcat(mns -> {
NodeBuilder node = nodeBuilders.apply(mns);
NodeBuilder node = nodeBuilders.get(mns);
if (node.getChildNodeCount(max) == MAX_VALUE) {
return singleton(STOP_COUNTING_CHILDREN);
} else {
Expand All @@ -222,15 +206,15 @@ public long getChildNodeCount(final long max) {
public Iterable<String> getChildNodeNames() {
return FluentIterable.from(ctx.getContributingStoresForBuilders(path, nodeBuilders))
.transformAndConcat(mns -> FluentIterable
.from(nodeBuilders.apply(mns).getChildNodeNames())
.from(nodeBuilders.get(mns).getChildNodeNames())
.filter(e -> belongsToStore(mns, e)));
}

@Override
public boolean hasChildNode(String name) {
String childPath = simpleConcat(path, name);
MountedNodeStore mountedStore = ctx.getOwningStore(childPath);
return nodeBuilders.apply(mountedStore).hasChildNode(name);
return nodeBuilders.get(mountedStore).hasChildNode(name);
}

@Override
Expand All @@ -243,24 +227,22 @@ public NodeBuilder child(String name) {
}

private void createAncestors(MountedNodeStore mountedNodeStore) {
NodeBuilder builder = rootBuilder.nodeBuilders.apply(mountedNodeStore);
NodeBuilder builder = rootBuilder.nodeBuilders.get(mountedNodeStore);
for (String element : PathUtils.elements(path)) {
builder = builder.child(element);
}

// the nodeBuilders function should be updated, to return the new node builder
Map<MountedNodeStore, NodeBuilder> map = newHashMap(ctx.getAllMountedNodeStores().stream().collect(Collectors.toMap(Function.identity(), nodeBuilders)));
map.put(mountedNodeStore, builder);
nodeBuilders = wrapWithNullCheck(m -> map.get(m), LOG, path);
synchronized(this) {
nodeBuilders = nodeBuilders.replaceNode(mountedNodeStore, builder);
}
}

@Override
public NodeBuilder getChildNode(final String name) {
String childPath = simpleConcat(path, name);
if (!ctx.shouldBeComposite(childPath)) {
return nodeBuilders.apply(ctx.getOwningStore(childPath)).getChildNode(name);
return nodeBuilders.get(ctx.getOwningStore(childPath)).getChildNode(name);
}
return new CompositeNodeBuilder(childPath, nodeBuilders.andThen(b -> b.getChildNode(name)), ctx, this);
return new CompositeNodeBuilder(childPath, nodeBuilders.lazyApply(b -> b.getChildNode(name)), ctx, this);
}

@Override
Expand All @@ -273,15 +255,14 @@ public NodeBuilder setChildNode(final String name, NodeState nodeState) {
checkState(exists(), "This builder does not exist: " + PathUtils.getName(path));
String childPath = simpleConcat(path, name);
final MountedNodeStore childStore = ctx.getOwningStore(childPath);
if (childStore != owningStore && !nodeBuilders.apply(childStore).exists()) {
if (childStore != owningStore && !nodeBuilders.get(childStore).exists()) {
createAncestors(childStore);
}
final NodeBuilder childBuilder = nodeBuilders.apply(childStore).setChildNode(name, nodeState);
final NodeBuilder childBuilder = nodeBuilders.get(childStore).setChildNode(name, nodeState);
if (!ctx.shouldBeComposite(childPath)) {
return childBuilder;
}

return new CompositeNodeBuilder(childPath, m -> m == childStore ? childBuilder : nodeBuilders.apply(m).getChildNode(name), ctx, this);
return new CompositeNodeBuilder(childPath, nodeBuilders.lazyApply(b -> b.getChildNode(name)).replaceNode(childStore, childBuilder), ctx, this);
}

@Override
Expand Down Expand Up @@ -314,7 +295,7 @@ public Blob createBlob(InputStream stream) throws IOException {
}

private NodeBuilder getWrappedNodeBuilder() {
return nodeBuilders.apply (owningStore);
return nodeBuilders.get(owningStore);
}

private void annotateSourcePath() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,20 @@
import com.google.common.collect.FluentIterable;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.composite.util.Memoizer;
import org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry;
import org.apache.jackrabbit.oak.spi.state.AbstractNodeState;
import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;

import static java.lang.Long.MAX_VALUE;
import static java.util.Collections.singleton;
import static org.apache.jackrabbit.oak.composite.CompositeNodeBuilder.simpleConcat;

class CompositeNodeState extends AbstractNodeState {

private static final Logger LOG = LoggerFactory.getLogger(CompositeNodeState.class);

// A note on content held by node stores which is outside the mount boundaries
//
// As a matter of design, mounted stores will definitely hold information _above_
Expand All @@ -60,40 +52,21 @@ class CompositeNodeState extends AbstractNodeState {

private final String path;

private final NodeMap<NodeState> nodeStates;

private final CompositionContext ctx;

private final MountedNodeStore owningStore;

private final Function<MountedNodeStore, NodeState> nodeStates;

CompositeNodeState(String path, Map<MountedNodeStore, NodeState> nodeStates, CompositionContext ctx) {
CompositeNodeState(String path, NodeMap<NodeState> nodeStates, CompositionContext ctx) {
this.path = path;
this.ctx = ctx;
this.nodeStates = wrapWithNullCheck(mns -> nodeStates.get(mns), LOG, path);
this.nodeStates = nodeStates;
this.owningStore = ctx.getOwningStore(path);
}

CompositeNodeState(String path, Function<MountedNodeStore, NodeState> nodeStates, CompositionContext ctx) {
this.path = path;
this.ctx = ctx;
this.nodeStates = wrapWithNullCheck(Memoizer.memoize(nodeStates), LOG, path);
this.owningStore = ctx.getOwningStore(path);
}

static <N> Function<MountedNodeStore, N> wrapWithNullCheck(Function<MountedNodeStore, N> f, Logger log, String path) {
return mns -> {
N nodeState = f.apply(mns);
if (nodeState == null) {
// this shouldn't happen, so we need to log some more debug info
log.warn("Can't find node state for path {} and mount {}.", path, mns);
throw new IllegalStateException("Can't find the node state for mount " + mns);
}
return nodeState;
};
}

NodeState getNodeState(MountedNodeStore mns) {
return nodeStates.apply(mns);
return nodeStates.get(mns);
}

@Override
Expand Down Expand Up @@ -127,16 +100,16 @@ public Iterable<? extends PropertyState> getProperties() {
public boolean hasChildNode(String name) {
String childPath = simpleConcat(path, name);
MountedNodeStore mountedStore = ctx.getOwningStore(childPath);
return nodeStates.apply(mountedStore).hasChildNode(name);
return nodeStates.get(mountedStore).hasChildNode(name);
}

@Override
public NodeState getChildNode(final String name) {
String childPath = simpleConcat(path, name);
if (!ctx.shouldBeComposite(childPath)) {
return nodeStates.apply(ctx.getOwningStore(childPath)).getChildNode(name);
return nodeStates.get(ctx.getOwningStore(childPath)).getChildNode(name);
}
Function<MountedNodeStore, NodeState> newNodeStates = nodeStates.andThen(n -> n.getChildNode(name));
NodeMap<NodeState> newNodeStates = nodeStates.lazyApply(n -> n.getChildNode(name));
return new CompositeNodeState(childPath, newNodeStates, ctx);
}

Expand All @@ -151,7 +124,7 @@ public long getChildNodeCount(final long max) {
// Count the children in each contributing store.
return accumulateChildSizes(FluentIterable.from(contributingStores)
.transformAndConcat(mns -> {
NodeState node = nodeStates.apply(mns);
NodeState node = nodeStates.get(mns);
if (node.getChildNodeCount(max) == MAX_VALUE) {
return singleton(STOP_COUNTING_CHILDREN);
} else {
Expand All @@ -176,7 +149,7 @@ static long accumulateChildSizes(Iterable<String> nodeNames, long max) {
public Iterable<? extends ChildNodeEntry> getChildNodeEntries() {
return FluentIterable.from(ctx.getContributingStoresForNodes(path, nodeStates))
.transformAndConcat(mns -> FluentIterable
.from(nodeStates.apply(mns).getChildNodeNames())
.from(nodeStates.get(mns).getChildNodeNames())
.filter(n -> belongsToStore(mns, n)))
.transform(n -> new MemoryChildNodeEntry(n, getChildNode(n)));
}
Expand All @@ -192,8 +165,8 @@ public boolean compareAgainstBaseState(NodeState base, NodeStateDiff diff) {
continue;
}
NodeStateDiff childrenDiffFilter = new ChildrenDiffFilter(wrappingDiff, mns, false);
NodeState contributing = nodeStates.apply(mns);
NodeState contributingBase = multiBase.nodeStates.apply(mns);
NodeState contributing = nodeStates.get(mns);
NodeState contributingBase = multiBase.nodeStates.get(mns);
full = full && contributing.compareAgainstBaseState(contributingBase, childrenDiffFilter);
}
return full;
Expand All @@ -205,11 +178,11 @@ public boolean compareAgainstBaseState(NodeState base, NodeStateDiff diff) {
// write operations
@Override
public CompositeNodeBuilder builder() {
return new CompositeNodeBuilder(path, nodeStates.andThen(NodeState::builder), ctx);
return new CompositeNodeBuilder(path, nodeStates.lazyApply(NodeState::builder), ctx);
}

private NodeState getWrappedNodeState() {
return nodeStates.apply(owningStore);
return nodeStates.get(owningStore);
}

private boolean belongsToStore(MountedNodeStore mns, String childName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@ MountedNodeStore getOwningStore(String path) {
}
}

List<MountedNodeStore> getContributingStoresForNodes(String path, final Function<MountedNodeStore, NodeState> nodeStates) {
return getContributingStores(path, mns -> nodeStates.apply(mns).getChildNodeNames());
List<MountedNodeStore> getContributingStoresForNodes(String path, final NodeMap<NodeState> nodeStates) {
return getContributingStores(path, mns -> nodeStates.get(mns).getChildNodeNames());
}

List<MountedNodeStore> getContributingStoresForBuilders(String path, final Function<MountedNodeStore, NodeBuilder> nodeBuilders) {
return getContributingStores(path, mns -> nodeBuilders.apply(mns).getChildNodeNames());
List<MountedNodeStore> getContributingStoresForBuilders(String path, final NodeMap<NodeBuilder> nodeBuilders) {
return getContributingStores(path, mns -> nodeBuilders.get(mns).getChildNodeNames());
}

boolean shouldBeComposite(final String path) {
Expand Down Expand Up @@ -171,7 +171,7 @@ CompositeNodeState createRootNodeState(Map<MountedNodeStore, NodeState> rootStat
if (rootStates.size() != nonDefaultStores.size() + 1) {
throw new IllegalArgumentException("Too many root states passed: " + rootStates.size());
}
return new CompositeNodeState("/", rootStates, this);
return new CompositeNodeState("/", NodeMap.create(rootStates), this);
}

}
Loading

0 comments on commit 8122c3c

Please sign in to comment.