Author: chetanm
Date: Tue Jun 21 06:18:07 2016
New Revision: 1749426
URL: http://svn.apache.org/viewvc?rev=1749426&view=rev
Log:
OAK-4180 - Use another NodeStore as a local cache for a remote Document store
Implementation for DocumentNodeStateCache which makes use of a secondary
NodeStore to lookup paths which are configured for caching
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeState.java
(with props)
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/PathFilteringDiff.java
(with props)
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCache.java
(with props)
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheService.java
(with props)
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreObserver.java
(with props)
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreRootObserver.java
(with props)
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeStateTest.java
(with props)
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheServiceTest.java
(with props)
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheTest.java
(with props)
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreObserverTest.java
(with props)
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeState.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeState.java?rev=1749426&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeState.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeState.java
Tue Jun 21 06:18:07 2016
@@ -0,0 +1,219 @@
+/*
+ * 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 javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.plugins.document.AbstractDocumentNodeState;
+import org.apache.jackrabbit.oak.plugins.document.NodeStateDiffer;
+import org.apache.jackrabbit.oak.plugins.document.RevisionVector;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static org.apache.jackrabbit.oak.commons.PathUtils.denotesRoot;
+
+/**
+ * NodeState wrapper which wraps another NodeState (mostly SegmentNodeState)
+ * so as to expose it as an {@link AbstractDocumentNodeState} by extracting
+ * the meta properties which are stored as hidden properties
+ */
+class DelegatingDocumentNodeState extends AbstractDocumentNodeState {
+ //Hidden props holding DocumentNodeState meta properties
+ static final String PROP_PATH = ":doc-path";
+ static final String PROP_REVISION = ":doc-rev";
+ static final String PROP_LAST_REV = ":doc-lastRev";
+
+ private static final Predicate<PropertyState> NOT_META_PROPS = new
Predicate<PropertyState>() {
+ @Override
+ public boolean apply(@Nullable PropertyState input) {
+ return !input.getName().startsWith(":doc-");
+ }
+ };
+
+ private final NodeState delegate;
+ private final RevisionVector rootRevision;
+ private final boolean fromExternalChange;
+ private final RevisionVector lastRevision;
+ private final RevisionVector readRevision;
+ private final String path;
+
+ /**
+ * Wraps a given NodeState as a {@link DelegatingDocumentNodeState} if
+ * it has required meta properties otherwise just returns the passed
NodeState
+ *
+ * @param delegate nodeState to wrap
+ * @return wrapped state or original state
+ */
+ public static NodeState wrapIfPossible(NodeState delegate, NodeStateDiffer
differ) {
+ if (hasMetaProps(delegate)) {
+ String revVector = getRequiredProp(delegate, PROP_REVISION);
+ return new DelegatingDocumentNodeState(delegate,
RevisionVector.fromString(revVector), differ);
+ }
+ return delegate;
+ }
+
+ public static boolean hasMetaProps(NodeState delegate) {
+ return delegate.hasProperty(PROP_REVISION);
+ }
+
+ public static AbstractDocumentNodeState wrap(NodeState delegate,
NodeStateDiffer differ) {
+ String revVector = getRequiredProp(delegate, PROP_REVISION);
+ return new DelegatingDocumentNodeState(delegate,
RevisionVector.fromString(revVector), differ);
+ }
+
+ public DelegatingDocumentNodeState(NodeState delegate, RevisionVector
rootRevision, NodeStateDiffer differ) {
+ this(delegate, rootRevision, false, differ);
+ }
+
+ public DelegatingDocumentNodeState(NodeState delegate, RevisionVector
rootRevision,
+ boolean fromExternalChange,
NodeStateDiffer differ) {
+ super(differ);
+ this.delegate = delegate;
+ this.rootRevision = rootRevision;
+ this.fromExternalChange = fromExternalChange;
+ this.path = getRequiredProp(PROP_PATH);
+ this.readRevision =
RevisionVector.fromString(getRequiredProp(PROP_REVISION));
+ this.lastRevision =
RevisionVector.fromString(getRequiredProp(PROP_LAST_REV));
+ }
+
+ private DelegatingDocumentNodeState(DelegatingDocumentNodeState original,
+ RevisionVector rootRevision, boolean
fromExternalChange) {
+ super(original.differ);
+ this.delegate = original.delegate;
+ this.rootRevision = rootRevision;
+ this.fromExternalChange = fromExternalChange;
+ this.path = original.path;
+ this.readRevision = original.readRevision;
+ this.lastRevision = original.lastRevision;
+ }
+
+ //~----------------------------------< AbstractDocumentNodeState >
+
+ @Override
+ public String getPath() {
+ return path;
+ }
+
+ @Override
+ public RevisionVector getRevision() {
+ return readRevision;
+ }
+
+ @Override
+ public RevisionVector getLastRevision() {
+ return lastRevision;
+ }
+
+ @Override
+ public RevisionVector getRootRevision() {
+ return rootRevision;
+ }
+
+ @Override
+ public boolean isFromExternalChange() {
+ return fromExternalChange;
+ }
+
+ @Override
+ public AbstractDocumentNodeState withRootRevision(@Nonnull RevisionVector
root, boolean externalChange) {
+ if (rootRevision.equals(root) && fromExternalChange == externalChange)
{
+ return this;
+ } else {
+ return new DelegatingDocumentNodeState(this, root, externalChange);
+ }
+ }
+
+ @Override
+ public boolean hasNoChildren() {
+ //Passing max as 1 so as to minimize any overhead.
+ return delegate.getChildNodeCount(1) == 0;
+ }
+
+ //~----------------------------------< NodeState >
+
+ @Override
+ public boolean exists() {
+ return true;
+ }
+
+ @Nonnull
+ @Override
+ public Iterable<? extends PropertyState> getProperties() {
+ return Iterables.filter(delegate.getProperties(), NOT_META_PROPS);
+ }
+
+ @Override
+ public boolean hasChildNode(@Nonnull String name) {
+ return delegate.hasChildNode(name);
+ }
+
+ @Nonnull
+ @Override
+ public NodeState getChildNode(@Nonnull String name) throws
IllegalArgumentException {
+ return decorate(delegate.getChildNode(name));
+ }
+
+ @Nonnull
+ @Override
+ public Iterable<? extends ChildNodeEntry> getChildNodeEntries() {
+ return Iterables.transform(delegate.getChildNodeEntries(), new
Function<ChildNodeEntry, ChildNodeEntry>() {
+ @Nullable
+ @Override
+ public ChildNodeEntry apply(@Nullable ChildNodeEntry input) {
+ return new MemoryChildNodeEntry(input.getName(),
decorate(input.getNodeState()));
+ }
+ });
+ }
+
+ @Nonnull
+ @Override
+ public NodeBuilder builder() {
+ checkState(!denotesRoot(getPath()), "Builder cannot be opened for root
" +
+ "path for state of type [%s]", delegate.getClass());
+ return new MemoryNodeBuilder(this);
+ }
+
+ //~--------------------------------------------< internal >
+
+ private NodeState decorate(NodeState childNode) {
+ if (childNode.exists()) {
+ return new DelegatingDocumentNodeState(childNode, rootRevision,
fromExternalChange, differ);
+ }
+ return childNode;
+ }
+
+ private String getRequiredProp(String name){
+ return getRequiredProp(delegate, name);
+ }
+
+ private static String getRequiredProp(NodeState state, String name){
+ return checkNotNull(state.getString(name), "No property [%s] found in
[%s]", name, state);
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeState.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/PathFilteringDiff.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/PathFilteringDiff.java?rev=1749426&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/PathFilteringDiff.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/PathFilteringDiff.java
Tue Jun 21 06:18:07 2016
@@ -0,0 +1,97 @@
+/*
+ * 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 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.plugins.index.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 static
org.apache.jackrabbit.oak.plugins.document.secondary.DelegatingDocumentNodeState.PROP_LAST_REV;
+import static
org.apache.jackrabbit.oak.plugins.document.secondary.DelegatingDocumentNodeState.PROP_PATH;
+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 final PathFilter pathFilter;
+
+ public PathFilteringDiff(NodeBuilder builder, PathFilter pathFilter) {
+ super(builder);
+ this.pathFilter = pathFilter;
+ }
+
+ @Override
+ public boolean childNodeAdded(String name, NodeState after) {
+ AbstractDocumentNodeState afterDoc = asDocumentState(after);
+ String nextPath = afterDoc.getPath();
+ PathFilter.Result result = pathFilter.filter(nextPath);
+ if (result == PathFilter.Result.EXCLUDE){
+ return true;
+ }
+
+ //We avoid this as we need to copy meta properties
+ //super.childNodeAdded(name, after);
+
+ NodeBuilder childBuilder = builder.child(name);
+ copyMetaProperties(afterDoc, childBuilder);
+ return after.compareAgainstBaseState(EMPTY_NODE,
+ new PathFilteringDiff(childBuilder, pathFilter));
+ }
+
+ @Override
+ public boolean childNodeChanged(String name, NodeState before, NodeState
after) {
+ AbstractDocumentNodeState afterDoc = asDocumentState(after);
+ String nextPath = afterDoc.getPath();
+ if (pathFilter.filter(nextPath) != PathFilter.Result.EXCLUDE) {
+ NodeBuilder childBuilder = builder.getChildNode(name);
+ copyMetaProperties(afterDoc, childBuilder);
+ return after.compareAgainstBaseState(
+ before, new PathFilteringDiff(builder.getChildNode(name),
pathFilter));
+ }
+ return true;
+ }
+
+ @Override
+ public boolean childNodeDeleted(String name, NodeState before) {
+ String path = asDocumentState(before).getPath();
+ if (pathFilter.filter(path) != PathFilter.Result.EXCLUDE) {
+ return super.childNodeDeleted(name, before);
+ }
+ return true;
+ }
+
+ static void copyMetaProperties(AbstractDocumentNodeState state,
NodeBuilder builder) {
+ builder.setProperty(asPropertyState(PROP_REVISION,
state.getRevision()));
+ builder.setProperty(asPropertyState(PROP_LAST_REV,
state.getLastRevision()));
+ builder.setProperty(createProperty(PROP_PATH, state.getPath()));
+ }
+
+ private static PropertyState asPropertyState(String name, RevisionVector
revision) {
+ return createProperty(name, revision.asString());
+ }
+
+ private static AbstractDocumentNodeState asDocumentState(NodeState state){
+ return (AbstractDocumentNodeState) state;
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/PathFilteringDiff.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCache.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCache.java?rev=1749426&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCache.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCache.java
Tue Jun 21 06:18:07 2016
@@ -0,0 +1,172 @@
+/*
+ * 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 javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+import com.google.common.collect.EvictingQueue;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.document.AbstractDocumentNodeState;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStateCache;
+import org.apache.jackrabbit.oak.plugins.document.NodeStateDiffer;
+import org.apache.jackrabbit.oak.plugins.document.RevisionVector;
+import org.apache.jackrabbit.oak.plugins.index.PathFilter;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.stats.MeterStats;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.apache.jackrabbit.oak.stats.StatsOptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+class SecondaryStoreCache implements DocumentNodeStateCache,
SecondaryStoreRootObserver {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ private static final AbstractDocumentNodeState[] EMPTY = new
AbstractDocumentNodeState[0];
+ private final NodeStore store;
+ private final PathFilter pathFilter;
+ private final NodeStateDiffer differ;
+ private final MeterStats unknownPaths;
+ private final MeterStats knownMissed;
+ private final MeterStats headRevMatched;
+ private final MeterStats prevRevMatched;
+ private final int maxSize = 10000;
+ private final EvictingQueue<AbstractDocumentNodeState> queue;
+ private volatile AbstractDocumentNodeState[] previousRoots = EMPTY;
+
+ public SecondaryStoreCache(NodeStore store, PathFilter pathFilter,
NodeStateDiffer differ) {
+ this(store, pathFilter, StatisticsProvider.NOOP, differ);
+ }
+
+ public SecondaryStoreCache(NodeStore store, PathFilter pathFilter,
StatisticsProvider statisticsProvider,
+ NodeStateDiffer differ) {
+ this.differ = differ;
+ this.store = store;
+ this.pathFilter = pathFilter;
+ this.unknownPaths =
statisticsProvider.getMeter("DOCUMENT_CACHE_SEC_UNKNOWN", StatsOptions.DEFAULT);
+ this.knownMissed =
statisticsProvider.getMeter("DOCUMENT_CACHE_SEC_KNOWN_MISSED",
StatsOptions.DEFAULT);
+ this.headRevMatched =
statisticsProvider.getMeter("DOCUMENT_CACHE_SEC_HEAD", StatsOptions.DEFAULT);
+ this.prevRevMatched =
statisticsProvider.getMeter("DOCUMENT_CACHE_SEC_OLD", StatsOptions.DEFAULT);
+ this.queue = EvictingQueue.create(maxSize);
+ }
+
+ @Nonnull
+ @Override
+ public NodeStateCacheEntry getDocumentNodeState(String path,
RevisionVector rootRevision,
+ RevisionVector
parentLastRev) {
+ //TODO We might need skip the calls if they occur due to
SecondaryStoreObserver
+ //doing the diff or in the startup when we try to sync the state
+ PathFilter.Result result = pathFilter.filter(path);
+ if (result != PathFilter.Result.INCLUDE) {
+ unknownPaths.mark();
+ return DocumentNodeStateCache.UNKNOWN;
+ }
+
+ if (!DelegatingDocumentNodeState.hasMetaProps(store.getRoot())){
+ return DocumentNodeStateCache.UNKNOWN;
+ }
+
+ AbstractDocumentNodeState currentRoot =
DelegatingDocumentNodeState.wrap(store.getRoot(), differ);
+
+ NodeStateCacheEntry cacheEntryResult =
findByMatchingParentLastRev(currentRoot, path,
+ rootRevision, parentLastRev);
+ if (cacheEntryResult != DocumentNodeStateCache.UNKNOWN){
+ headRevMatched.mark();
+ return cacheEntryResult;
+ }
+
+ AbstractDocumentNodeState matchingRoot =
findMatchingRoot(rootRevision);
+ if (matchingRoot != null){
+ NodeState state = NodeStateUtils.getNode(matchingRoot, path);
+ if (state.exists()){
+ AbstractDocumentNodeState docState =
(AbstractDocumentNodeState) state;
+ prevRevMatched.mark();
+ return new NodeStateCacheEntry(docState);
+ } else {
+ return DocumentNodeStateCache.MISSING;
+ }
+ }
+
+ //TODO Check in tail if rootRevision is not in our maintained list of
root
+ knownMissed.mark();
+ return DocumentNodeStateCache.UNKNOWN;
+ }
+
+ @Nonnull
+ private NodeStateCacheEntry
findByMatchingParentLastRev(AbstractDocumentNodeState root, String path,
+ RevisionVector
rootRevision, RevisionVector parentLastRev){
+ NodeState parentNodeState = root;
+ NodeState state = root;
+
+ //Get the parent node state
+ for (String name : PathUtils.elements(checkNotNull(path))) {
+ parentNodeState = state;
+ state = state.getChildNode(checkNotNull(name));
+ }
+
+ if (parentNodeState.exists()) {
+ AbstractDocumentNodeState parentDocState =
(AbstractDocumentNodeState) parentNodeState;
+ //So parent state exists and matches the expected revision
+ if (parentLastRev.equals(parentDocState.getLastRevision())) {
+ headRevMatched.mark();
+ if (state.exists()) {
+ AbstractDocumentNodeState stateAtExpectedRootRev =
+ ((AbstractDocumentNodeState)
state).withRootRevision(rootRevision, false);
+ return new NodeStateCacheEntry(stateAtExpectedRootRev);
+ } else {
+ return DocumentNodeStateCache.MISSING;
+ }
+ }
+ }
+
+ return DocumentNodeStateCache.UNKNOWN;
+ }
+
+ @CheckForNull
+ private AbstractDocumentNodeState findMatchingRoot(RevisionVector rr) {
+ if (previousRoots.length == 0){
+ return null;
+ }
+ //TODO Binary search
+ AbstractDocumentNodeState latest = previousRoots[previousRoots.length
- 1];
+ AbstractDocumentNodeState oldest = previousRoots[0];
+ if (rr.compareTo(latest.getRootRevision()) <= 0
+ && rr.compareTo(oldest.getRootRevision()) >= 0){
+ for (AbstractDocumentNodeState s : previousRoots){
+ if (s.getRootRevision().equals(rr)){
+ return s;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void contentChanged(@Nonnull AbstractDocumentNodeState root) {
+ synchronized (queue){
+ //TODO Possibly can be improved
+ queue.add(root);
+ previousRoots = queue.toArray(EMPTY);
+ }
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCache.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheService.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheService.java?rev=1749426&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheService.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheService.java
Tue Jun 21 06:18:07 2016
@@ -0,0 +1,220 @@
+/*
+ * 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.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+import javax.annotation.Nonnull;
+
+import com.google.common.collect.Lists;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.ConfigurationPolicy;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.PropertyUnbounded;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.ReferencePolicy;
+import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard;
+import org.apache.jackrabbit.oak.plugins.document.AbstractDocumentNodeState;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStateCache;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore;
+import org.apache.jackrabbit.oak.plugins.document.NodeStateDiffer;
+import org.apache.jackrabbit.oak.plugins.index.PathFilter;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.spi.commit.BackgroundObserver;
+import org.apache.jackrabbit.oak.spi.commit.BackgroundObserverMBean;
+import org.apache.jackrabbit.oak.spi.commit.Observer;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.state.SecondaryNodeStoreProvider;
+import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
+import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.util.Arrays.asList;
+import static org.apache.jackrabbit.oak.commons.PropertiesUtil.toBoolean;
+import static org.apache.jackrabbit.oak.commons.PropertiesUtil.toInteger;
+import static org.apache.jackrabbit.oak.commons.PropertiesUtil.toStringArray;
+import static
org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean;
+
+@Component(label = "Apache Jackrabbit Oak DocumentNodeStateCache Provider",
+ metatype = true,
+ policy = ConfigurationPolicy.REQUIRE,
+ description = "Configures a DocumentNodeStateCache based on a
secondary NodeStore"
+)
+public class SecondaryStoreCacheService {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ /**
+ * Having a reference to BlobStore ensures that DocumentNodeStoreService
does register a BlobStore
+ */
+ @Reference
+ private BlobStore blobStore;
+
+ @Reference
+ private SecondaryNodeStoreProvider secondaryNodeStoreProvider;
+
+ @Reference
+ private Executor executor;
+
+ @Reference
+ private StatisticsProvider statisticsProvider;
+
+ /*
+ * Have an optional dependency on DocumentNodeStore such that we do not
have hard dependency
+ * on it and DocumentNodeStore can make use of this service even after it
has unregistered
+ */
+ @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY,
+ policy = ReferencePolicy.DYNAMIC)
+ private DocumentNodeStore documentNodeStore;
+
+ @Property(unbounded = PropertyUnbounded.ARRAY,
+ label = "Included Paths",
+ description = "List of paths which are to be included in the
secondary store",
+ value = {"/"}
+ )
+ private static final String PROP_INCLUDES = "includedPaths";
+
+ @Property(unbounded = PropertyUnbounded.ARRAY,
+ label = "Excluded Paths",
+ description = "List of paths which are to be excluded in the
secondary store",
+ value = {}
+ )
+ private static final String PROP_EXCLUDES = "excludedPaths";
+
+
+ private static final boolean PROP_ASYNC_OBSERVER_DEFAULT = true;
+ @Property(
+ boolValue = PROP_ASYNC_OBSERVER_DEFAULT,
+ label = "Async Observation",
+ description = "Enable async observation processing"
+ )
+ private static final String PROP_ASYNC_OBSERVER = "enableAsyncObserver";
+
+ private static final int PROP_OBSERVER_QUEUE_SIZE_DEFAULT = 1000;
+ @Property(
+ intValue = PROP_OBSERVER_QUEUE_SIZE_DEFAULT,
+ label = "Observer queue size",
+ description = "Observer queue size. Used if 'enableAsyncObserver'
is set to true"
+ )
+ private static final String PROP_OBSERVER_QUEUE_SIZE = "observerQueueSize";
+
+ private final List<Registration> oakRegs = Lists.newArrayList();
+
+ private final List<ServiceRegistration> regs = Lists.newArrayList();
+
+ private Whiteboard whiteboard;
+
+ private BundleContext bundleContext;
+
+ private PathFilter pathFilter;
+
+ private final MultiplexingNodeStateDiffer differ = new
MultiplexingNodeStateDiffer();
+
+ @Activate
+ private void activate(BundleContext context, Map<String, Object> config){
+ bundleContext = context;
+ whiteboard = new OsgiWhiteboard(context);
+ String[] includedPaths = toStringArray(config.get(PROP_INCLUDES), new
String[]{"/"});
+ String[] excludedPaths = toStringArray(config.get(PROP_EXCLUDES), new
String[]{""});
+
+ pathFilter = new PathFilter(asList(includedPaths),
asList(excludedPaths));
+ NodeStore segStore = secondaryNodeStoreProvider.getSecondaryStore();
+
+ SecondaryStoreCache cache = new SecondaryStoreCache(segStore,
pathFilter, statisticsProvider, differ);
+ SecondaryStoreObserver observer = new SecondaryStoreObserver(segStore,
pathFilter,
+ cache, differ, statisticsProvider);
+ registerObserver(observer, config);
+
+
regs.add(bundleContext.registerService(DocumentNodeStateCache.class.getName(),
cache, null));
+
+ //TODO Need to see OSGi dynamics. Its possible that DocumentNodeStore
works after the cache
+ //gets deregistered but the SegmentNodeState instances might still be
in use and that would cause
+ //failure
+ }
+
+ @Deactivate
+ private void deactivate(){
+ for (Registration r : oakRegs){
+ r.unregister();
+ }
+ for (ServiceRegistration r : regs){
+ r.unregister();
+ }
+ }
+
+ PathFilter getPathFilter() {
+ return pathFilter;
+ }
+
+ protected void bindDocumentNodeStore(DocumentNodeStore documentNodeStore){
+ log.info("Registering DocumentNodeStore as the differ");
+ differ.setDelegate(documentNodeStore);
+ }
+
+ protected void unbindDocumentNodeStore(DocumentNodeStore
documentNodeStore){
+ differ.setDelegate(NodeStateDiffer.DEFAULT_DIFFER);
+ }
+
+ //~----------------------------------------------------< internal >
+
+ private void registerObserver(Observer observer, Map<String, Object>
config) {
+ boolean enableAsyncObserver =
toBoolean(config.get(PROP_ASYNC_OBSERVER), PROP_ASYNC_OBSERVER_DEFAULT);
+ int queueSize = toInteger(config.get(PROP_OBSERVER_QUEUE_SIZE),
PROP_OBSERVER_QUEUE_SIZE_DEFAULT);
+ if (enableAsyncObserver){
+ BackgroundObserver bgObserver = new BackgroundObserver(observer,
executor, queueSize);
+ oakRegs.add(registerMBean(whiteboard,
+ BackgroundObserverMBean.class,
+ bgObserver.getMBean(),
+ BackgroundObserverMBean.TYPE,
+ "Secondary NodeStore observer stats"));
+ observer = bgObserver;
+ log.info("Configuring the observer for secondary NodeStore as " +
+ "Background Observer with queue size {}", queueSize);
+ }
+
+ //Ensure that our observer comes first in processing
+ Hashtable<String, Object> props = new Hashtable<>();
+ props.put(Constants.SERVICE_RANKING, 10000);
+ regs.add(bundleContext.registerService(Observer.class.getName(),
observer, props));
+ }
+
+ private static class MultiplexingNodeStateDiffer implements
NodeStateDiffer {
+ private volatile NodeStateDiffer delegate =
NodeStateDiffer.DEFAULT_DIFFER;
+ @Override
+ public boolean compare(@Nonnull AbstractDocumentNodeState node,
+ @Nonnull AbstractDocumentNodeState base,
@Nonnull NodeStateDiff diff) {
+ return delegate.compare(node, base, diff);
+ }
+
+ public void setDelegate(NodeStateDiffer delegate) {
+ this.delegate = delegate;
+ }
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheService.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreObserver.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreObserver.java?rev=1749426&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreObserver.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreObserver.java
Tue Jun 21 06:18:07 2016
@@ -0,0 +1,107 @@
+/*
+ * 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.concurrent.TimeUnit;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.google.common.base.Stopwatch;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.plugins.document.AbstractDocumentNodeState;
+import org.apache.jackrabbit.oak.plugins.document.NodeStateDiffer;
+import org.apache.jackrabbit.oak.plugins.index.PathFilter;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
+import org.apache.jackrabbit.oak.spi.commit.Observer;
+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.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.apache.jackrabbit.oak.stats.StatsOptions;
+import org.apache.jackrabbit.oak.stats.TimerStats;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class SecondaryStoreObserver implements Observer {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ private final NodeStore nodeStore;
+ private final PathFilter pathFilter;
+ private final SecondaryStoreRootObserver secondaryObserver;
+ private final NodeStateDiffer differ;
+ private final TimerStats local;
+ private final TimerStats external;
+ private boolean firstEventProcessed;
+
+ public SecondaryStoreObserver(NodeStore nodeStore, PathFilter pathFilter,
NodeStateDiffer differ) {
+ this(nodeStore, pathFilter, SecondaryStoreRootObserver.NOOP, differ,
StatisticsProvider.NOOP);
+ }
+
+ public SecondaryStoreObserver(NodeStore nodeStore, PathFilter pathFilter,
+ SecondaryStoreRootObserver secondaryObserver,
+ NodeStateDiffer differ, StatisticsProvider
statisticsProvider) {
+ this.nodeStore = nodeStore;
+ this.pathFilter = pathFilter;
+ this.secondaryObserver = secondaryObserver;
+ this.differ = differ;
+ this.local = statisticsProvider.getTimer("DOCUMENT_CACHE_SEC_LOCAL",
StatsOptions.DEFAULT);
+ this.external =
statisticsProvider.getTimer("DOCUMENT_CACHE_SEC_EXTERNAL",
StatsOptions.DEFAULT);
+ }
+
+ @Override
+ public void contentChanged(@Nonnull NodeState root, @Nullable CommitInfo
info) {
+ //Diff here would also be traversing non visible areas and there
+ //diffManyChildren might pose problem for e.g. data under uuid index
+ if (!firstEventProcessed){
+ log.info("Starting initial sync");
+ }
+
+ Stopwatch w = Stopwatch.createStarted();
+ NodeState target = root;
+ NodeState secondaryRoot = nodeStore.getRoot();
+ NodeState base =
DelegatingDocumentNodeState.wrapIfPossible(secondaryRoot, differ);
+ NodeBuilder builder = secondaryRoot.builder();
+ ApplyDiff diff = new PathFilteringDiff(builder, pathFilter);
+
+ //Copy the root node meta properties
+ PathFilteringDiff.copyMetaProperties((AbstractDocumentNodeState)
target, builder);
+
+ //Apply the rest of properties
+ target.compareAgainstBaseState(base, diff);
+ try {
+ NodeState updatedSecondaryRoot = nodeStore.merge(builder,
EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
secondaryObserver.contentChanged(DelegatingDocumentNodeState.wrap(updatedSecondaryRoot,
differ));
+
+ TimerStats timer = info == null ? external : local;
+ timer.update(w.elapsed(TimeUnit.NANOSECONDS),
TimeUnit.NANOSECONDS);
+
+ if (!firstEventProcessed){
+ log.info("Time taken for initial sync {}", w);
+ firstEventProcessed = true;
+ }
+ } catch (CommitFailedException e) {
+ //TODO
+ log.warn("Commit to secondary store failed", e);
+ }
+ }
+
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreObserver.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreRootObserver.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreRootObserver.java?rev=1749426&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreRootObserver.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreRootObserver.java
Tue Jun 21 06:18:07 2016
@@ -0,0 +1,35 @@
+/*
+ * 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 javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.plugins.document.AbstractDocumentNodeState;
+
+interface SecondaryStoreRootObserver {
+ SecondaryStoreRootObserver NOOP = new SecondaryStoreRootObserver(){
+ @Override
+ public void contentChanged(@Nonnull AbstractDocumentNodeState root) {
+
+ }
+ };
+
+ void contentChanged(@Nonnull AbstractDocumentNodeState root);
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreRootObserver.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeStateTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeStateTest.java?rev=1749426&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeStateTest.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeStateTest.java
Tue Jun 21 06:18:07 2016
@@ -0,0 +1,146 @@
+/*
+ * 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 com.google.common.collect.Iterables;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.plugins.document.AbstractDocumentNodeState;
+import org.apache.jackrabbit.oak.plugins.document.NodeStateDiffer;
+import org.apache.jackrabbit.oak.plugins.document.Revision;
+import org.apache.jackrabbit.oak.plugins.document.RevisionVector;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.EqualsDiff;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.junit.Test;
+
+import static
org.apache.jackrabbit.oak.plugins.document.secondary.DelegatingDocumentNodeState.PROP_LAST_REV;
+import static
org.apache.jackrabbit.oak.plugins.document.secondary.DelegatingDocumentNodeState.PROP_PATH;
+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;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+public class DelegatingDocumentNodeStateTest {
+ private NodeBuilder builder = EMPTY_NODE.builder();
+
+ @Test
+ public void basicWorking() throws Exception{
+ RevisionVector rv1 = new RevisionVector(new Revision(1,0,1));
+ RevisionVector rv2 = new RevisionVector(new Revision(1,0,3));
+ builder.setProperty(asPropertyState(PROP_REVISION, rv1));
+ builder.setProperty(asPropertyState(PROP_LAST_REV, rv2));
+ builder.setProperty(createProperty(PROP_PATH, "foo"));
+ AbstractDocumentNodeState state =
DelegatingDocumentNodeState.wrap(builder.getNodeState(),
NodeStateDiffer.DEFAULT_DIFFER);
+
+ assertEquals(rv1, state.getRevision());
+ assertEquals(rv1, state.getRootRevision());
+ assertEquals(rv2, state.getLastRevision());
+ assertEquals("foo", state.getPath());
+ assertTrue(state.hasNoChildren());
+ assertTrue(state.exists());
+ assertFalse(state.isFromExternalChange());
+ }
+
+ @Test
+ public void metaPropertiesFilteredOut() throws Exception{
+ setMetaProps(builder);
+ builder.setProperty("foo", "bar");
+
+ AbstractDocumentNodeState state =
DelegatingDocumentNodeState.wrap(builder.getNodeState(),
NodeStateDiffer.DEFAULT_DIFFER);
+ assertEquals(1, Iterables.size(state.getProperties()));
+ }
+
+ @Test
+ public void childNodeDecorated() throws Exception{
+ setMetaProps(builder);
+ setMetaProps(builder.child("a"));
+ setMetaProps(builder.child("b"));
+
+ AbstractDocumentNodeState state =
DelegatingDocumentNodeState.wrap(builder.getNodeState(),
NodeStateDiffer.DEFAULT_DIFFER);
+ assertTrue(state.getChildNode("a") instanceof
AbstractDocumentNodeState);
+ assertTrue(state.getChildNode("b") instanceof
AbstractDocumentNodeState);
+ assertFalse(state.hasChildNode("c"));
+ assertFalse(state.getChildNode("c").exists());
+
+ assertFalse(state.hasNoChildren());
+
+ for(ChildNodeEntry cne : state.getChildNodeEntries()){
+ assertTrue(cne.getNodeState() instanceof
AbstractDocumentNodeState);
+ }
+
+ assertEquals(2, state.getChildNodeCount(100));
+ }
+
+ @Test
+ public void withRootRevision() throws Exception{
+ RevisionVector rv1 = new RevisionVector(new Revision(1,0,1));
+ RevisionVector rv2 = new RevisionVector(new Revision(1,0,3));
+ builder.setProperty(asPropertyState(PROP_REVISION, rv1));
+ builder.setProperty(asPropertyState(PROP_LAST_REV, rv2));
+ builder.setProperty(createProperty(PROP_PATH, "foo"));
+ AbstractDocumentNodeState state =
DelegatingDocumentNodeState.wrap(builder.getNodeState(),
NodeStateDiffer.DEFAULT_DIFFER);
+
+ AbstractDocumentNodeState state2 = state.withRootRevision(rv1, false);
+ assertSame(state, state2);
+
+ RevisionVector rv4 = new RevisionVector(new Revision(1,0,4));
+ AbstractDocumentNodeState state3 = state.withRootRevision(rv4, true);
+ assertEquals(rv4, state3.getRootRevision());
+ assertTrue(state3.isFromExternalChange());
+ }
+
+ @Test
+ public void wrapIfPossible() throws Exception{
+ assertFalse(DelegatingDocumentNodeState.wrapIfPossible(EMPTY_NODE,
NodeStateDiffer.DEFAULT_DIFFER)
+ instanceof AbstractDocumentNodeState);
+
+ setMetaProps(builder);
+
assertTrue(DelegatingDocumentNodeState.wrapIfPossible(builder.getNodeState(),
NodeStateDiffer.DEFAULT_DIFFER) instanceof
+ AbstractDocumentNodeState);
+ }
+
+ @Test
+ public void equals1() throws Exception{
+ setMetaProps(builder);
+ builder.setProperty("foo", "bar");
+
+ NodeBuilder b2 = EMPTY_NODE.builder();
+ b2.setProperty("foo", "bar");
+
+
assertTrue(EqualsDiff.equals(DelegatingDocumentNodeState.wrap(builder.getNodeState(),
NodeStateDiffer.DEFAULT_DIFFER),
+ b2.getNodeState()));
+ assertTrue(EqualsDiff.equals(b2.getNodeState(),
+ DelegatingDocumentNodeState.wrap(builder.getNodeState(),
NodeStateDiffer.DEFAULT_DIFFER)));
+ }
+
+ private static void setMetaProps(NodeBuilder nb){
+ nb.setProperty(asPropertyState(PROP_REVISION, new RevisionVector(new
Revision(1,0,1))));
+ nb.setProperty(asPropertyState(PROP_LAST_REV, new RevisionVector(new
Revision(1,0,1))));
+ nb.setProperty(createProperty(PROP_PATH, "foo"));
+ }
+
+ private static PropertyState asPropertyState(String name, RevisionVector
revision) {
+ return createProperty(name, revision.asString());
+ }
+
+}
\ No newline at end of file
Propchange:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/DelegatingDocumentNodeStateTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheServiceTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheServiceTest.java?rev=1749426&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheServiceTest.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheServiceTest.java
Tue Jun 21 06:18:07 2016
@@ -0,0 +1,109 @@
+/*
+ * 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.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import org.apache.jackrabbit.oak.plugins.document.DocumentMKBuilderProvider;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStateCache;
+import org.apache.jackrabbit.oak.plugins.index.PathFilter;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.spi.blob.MemoryBlobStore;
+import org.apache.jackrabbit.oak.spi.commit.BackgroundObserverMBean;
+import org.apache.jackrabbit.oak.spi.commit.Observer;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.state.SecondaryNodeStoreProvider;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.apache.sling.testing.mock.osgi.MockOsgi;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+public class SecondaryStoreCacheServiceTest {
+ @Rule
+ public final OsgiContext context = new OsgiContext();
+
+ @Rule
+ public DocumentMKBuilderProvider builderProvider = new
DocumentMKBuilderProvider();
+
+ private SecondaryStoreCacheService cacheService = new
SecondaryStoreCacheService();
+ private NodeStore secondaryStore = new MemoryNodeStore();
+
+ @Before
+ public void configureDefaultServices(){
+ context.registerService(BlobStore.class, new MemoryBlobStore());
+ context.registerService(SecondaryNodeStoreProvider.class, new
SecondaryNodeStoreProvider() {
+ @Override
+ public NodeStore getSecondaryStore() {
+ return secondaryStore;
+ }
+ });
+ context.registerService(Executor.class,
Executors.newSingleThreadExecutor());
+ context.registerService(StatisticsProvider.class,
StatisticsProvider.NOOP);
+ MockOsgi.injectServices(cacheService, context.bundleContext());
+ }
+
+ @Test
+ public void defaultSetup() throws Exception{
+ MockOsgi.activate(cacheService, context.bundleContext(), new
HashMap<String, Object>());
+
+ assertNotNull(context.getService(Observer.class));
+ assertNotNull(context.getService(BackgroundObserverMBean.class));
+ assertNotNull(context.getService(DocumentNodeStateCache.class));
+
+ MockOsgi.deactivate(cacheService);
+
+ assertNull(context.getService(Observer.class));
+ assertNull(context.getService(BackgroundObserverMBean.class));
+ assertNull(context.getService(DocumentNodeStateCache.class));
+ }
+
+ @Test
+ public void disableBackground() throws Exception{
+ Map<String, Object> config = new HashMap<>();
+ config.put("enableAsyncObserver", "false");
+ MockOsgi.activate(cacheService, context.bundleContext(), config);
+
+ assertNotNull(context.getService(Observer.class));
+ assertNull(context.getService(BackgroundObserverMBean.class));
+ assertNotNull(context.getService(DocumentNodeStateCache.class));
+ }
+
+ @Test
+ public void configurePathFilter() throws Exception{
+ Map<String, Object> config = new HashMap<>();
+ config.put("includedPaths", new String[] {"/a"});
+ config.put("excludedPaths", new String[] {"/a/b"});
+ MockOsgi.activate(cacheService, context.bundleContext(), config);
+
+ assertEquals(PathFilter.Result.INCLUDE,
cacheService.getPathFilter().filter("/a"));
+ assertEquals(PathFilter.Result.EXCLUDE,
cacheService.getPathFilter().filter("/a/b/c"));
+ }
+
+}
\ No newline at end of file
Propchange:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheServiceTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheTest.java?rev=1749426&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheTest.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheTest.java
Tue Jun 21 06:18:07 2016
@@ -0,0 +1,151 @@
+/*
+ * 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.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.jackrabbit.oak.plugins.document.AbstractDocumentNodeState;
+import org.apache.jackrabbit.oak.plugins.document.DocumentMKBuilderProvider;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStateCache;
+import
org.apache.jackrabbit.oak.plugins.document.DocumentNodeStateCache.NodeStateCacheEntry;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore;
+import org.apache.jackrabbit.oak.plugins.document.NodeStateDiffer;
+import org.apache.jackrabbit.oak.plugins.document.Revision;
+import org.apache.jackrabbit.oak.plugins.document.RevisionVector;
+import org.apache.jackrabbit.oak.plugins.index.PathFilter;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
+import org.apache.jackrabbit.oak.spi.state.EqualsDiff;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static com.google.common.collect.ImmutableList.of;
+import static
org.apache.jackrabbit.oak.plugins.document.NodeStateDiffer.DEFAULT_DIFFER;
+import static
org.apache.jackrabbit.oak.plugins.document.secondary.SecondaryStoreObserverTest.create;
+import static
org.apache.jackrabbit.oak.plugins.document.secondary.SecondaryStoreObserverTest.documentState;
+import static org.junit.Assert.*;
+
+public class SecondaryStoreCacheTest {
+ private final List<String> empty = Collections.emptyList();
+ @Rule
+ public DocumentMKBuilderProvider builderProvider = new
DocumentMKBuilderProvider();
+
+ private DocumentNodeStore primary;
+ private NodeStore secondary;
+
+ @Before
+ public void setUp() throws IOException {
+ primary = builderProvider.newBuilder().getNodeStore();
+ secondary = new MemoryNodeStore();
+ }
+
+ @Test
+ public void basicTest() throws Exception{
+ PathFilter pathFilter = new PathFilter(of("/a"), empty);
+ SecondaryStoreObserver observer = new
SecondaryStoreObserver(secondary, pathFilter, DEFAULT_DIFFER);
+ primary.addObserver(observer);
+
+ SecondaryStoreCache cache = new SecondaryStoreCache(secondary,
pathFilter, DEFAULT_DIFFER);
+
+ NodeBuilder nb = primary.getRoot().builder();
+ create(nb, "/a/b", "/a/c", "/x/y/z");
+ primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+ RevisionVector rv1 = new RevisionVector(new Revision(1,0,1));
+ RevisionVector rv2 = new RevisionVector(new Revision(1,0,3));
+ assertSame(DocumentNodeStateCache.UNKNOWN,
cache.getDocumentNodeState("/a/b", rv1, rv2));
+ assertSame(DocumentNodeStateCache.UNKNOWN,
cache.getDocumentNodeState("/x", rv1, rv2));
+ }
+
+ @Test
+ public void updateAndReadAtReadRev() throws Exception{
+ PathFilter pathFilter = new PathFilter(of("/a"), empty);
+ SecondaryStoreObserver observer = new
SecondaryStoreObserver(secondary, pathFilter, DEFAULT_DIFFER);
+ primary.addObserver(observer);
+
+ SecondaryStoreCache cache = new SecondaryStoreCache(secondary,
pathFilter, DEFAULT_DIFFER);
+
+ NodeBuilder nb = primary.getRoot().builder();
+ create(nb, "/a/b", "/a/c", "/x/y/z");
+ AbstractDocumentNodeState r1 =
+ (AbstractDocumentNodeState) primary.merge(nb,
EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+ //Update some other part of tree i.e. which does not change lastRev
for /a/c
+ nb = primary.getRoot().builder();
+ create(nb, "/a/e/d");
+ AbstractDocumentNodeState r2 =
+ (AbstractDocumentNodeState)primary.merge(nb,
EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+ //Lookup should work fine
+ AbstractDocumentNodeState a_r2 = documentState(r2, "/a");
+ NodeStateCacheEntry result
+ = cache.getDocumentNodeState("/a/c", r2.getRootRevision(),
a_r2.getLastRevision());
+ assertTrue(EqualsDiff.equals(a_r2.getChildNode("c"),
result.getState()));
+
+ nb = primary.getRoot().builder();
+ nb.child("a").child("c").remove();
+ primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+ //Now look from older revision
+ result = cache.getDocumentNodeState("/a/c", r1.getRootRevision(),
a_r2.getLastRevision());
+
+ //now as its not visible from head it would not be visible
+ assertSame(DocumentNodeStateCache.UNKNOWN, result);
+ }
+
+ @Test
+ public void updateAndReadAtPrevRevision() throws Exception {
+ PathFilter pathFilter = new PathFilter(of("/a"), empty);
+ SecondaryStoreCache cache = new SecondaryStoreCache(secondary,
pathFilter, DEFAULT_DIFFER);
+ SecondaryStoreObserver observer = new
SecondaryStoreObserver(secondary, pathFilter, cache,
+ DEFAULT_DIFFER, StatisticsProvider.NOOP);
+ primary.addObserver(observer);
+
+ NodeBuilder nb = primary.getRoot().builder();
+ create(nb, "/a/b", "/a/c");
+ AbstractDocumentNodeState r0 =
+ (AbstractDocumentNodeState) primary.merge(nb,
EmptyHook.INSTANCE, CommitInfo.EMPTY);
+ AbstractDocumentNodeState a_c_0 = documentState(primary.getRoot(),
"/a/c");
+
+ //Update some other part of tree i.e. which does not change lastRev
for /a/c
+ nb = primary.getRoot().builder();
+ create(nb, "/a/c/d");
+ AbstractDocumentNodeState r1 =
+ (AbstractDocumentNodeState)primary.merge(nb,
EmptyHook.INSTANCE, CommitInfo.EMPTY);
+ AbstractDocumentNodeState a_c_1 = documentState(primary.getRoot(),
"/a/c");
+
+ NodeStateCacheEntry result
+ = cache.getDocumentNodeState("/a/c", r1.getRootRevision(),
a_c_1.getLastRevision());
+ assertTrue(EqualsDiff.equals(a_c_1, result.getState()));
+
+ //Read from older revision
+ result
+ = cache.getDocumentNodeState("/a/c", r0.getRootRevision(),
a_c_0.getLastRevision());
+ assertTrue(EqualsDiff.equals(a_c_0, result.getState()));
+ }
+
+}
\ No newline at end of file
Propchange:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreObserverTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreObserverTest.java?rev=1749426&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreObserverTest.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreObserverTest.java
Tue Jun 21 06:18:07 2016
@@ -0,0 +1,163 @@
+/*
+ * 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.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.document.AbstractDocumentNodeState;
+import org.apache.jackrabbit.oak.plugins.document.DocumentMKBuilderProvider;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore;
+import org.apache.jackrabbit.oak.plugins.document.NodeStateDiffer;
+import org.apache.jackrabbit.oak.plugins.index.PathFilter;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static com.google.common.collect.ImmutableList.of;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+public class SecondaryStoreObserverTest {
+ private final List<String> empty = Collections.emptyList();
+
+ @Rule
+ public DocumentMKBuilderProvider builderProvider = new
DocumentMKBuilderProvider();
+
+ private DocumentNodeStore primary;
+ private NodeStore secondary;
+
+ @Before
+ public void setUp() throws IOException {
+ primary = builderProvider.newBuilder().getNodeStore();
+ secondary = new MemoryNodeStore();
+ }
+
+ @Test
+ public void basicSetup() throws Exception{
+ PathFilter pathFilter = new PathFilter(of("/a"), empty);
+ SecondaryStoreObserver observer = new
SecondaryStoreObserver(secondary, pathFilter, NodeStateDiffer.DEFAULT_DIFFER);
+ primary.addObserver(observer);
+
+ NodeBuilder nb = primary.getRoot().builder();
+ create(nb, "/a/b", "/a/c", "/x/y/z");
+ primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+ dump(secondaryRoot(), "/a");
+ dump(primary.getRoot(), "/a");
+ assertEquals(secondaryRoot().getChildNode("a"),
+ primary.getRoot().getChildNode("a"));
+ }
+
+ @Test
+ public void childNodeAdded() throws Exception{
+ PathFilter pathFilter = new PathFilter(of("/a"), empty);
+ SecondaryStoreObserver observer = new
SecondaryStoreObserver(secondary, pathFilter, NodeStateDiffer.DEFAULT_DIFFER);
+ primary.addObserver(observer);
+
+ NodeBuilder nb = primary.getRoot().builder();
+ create(nb, "/a/b", "/a/c", "/x/y/z");
+ primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+ nb = primary.getRoot().builder();
+ create(nb, "/a/d");
+ primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+ assertMetaState(primary.getRoot(), secondaryRoot(), "/a/d");
+ assertMetaState(primary.getRoot(), secondaryRoot(), "/a");
+ }
+
+ @Test
+ public void childNodeChangedAndExclude() throws Exception{
+ PathFilter pathFilter = new PathFilter(of("/a"), of("a/b"));
+ SecondaryStoreObserver observer = new
SecondaryStoreObserver(secondary, pathFilter, NodeStateDiffer.DEFAULT_DIFFER);
+ primary.addObserver(observer);
+
+ NodeBuilder nb = primary.getRoot().builder();
+ create(nb, "/a/b", "/a/c", "/x/y/z");
+ primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+ nb = primary.getRoot().builder();
+ create(nb, "/a/d", "/a/b/e");
+ primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+ assertMetaState(primary.getRoot(), secondaryRoot(), "/a/d");
+ }
+
+ @Test
+ public void childNodeDeleted() throws Exception{
+ PathFilter pathFilter = new PathFilter(of("/a"), empty);
+ SecondaryStoreObserver observer = new
SecondaryStoreObserver(secondary, pathFilter, NodeStateDiffer.DEFAULT_DIFFER);
+ primary.addObserver(observer);
+
+ NodeBuilder nb = primary.getRoot().builder();
+ create(nb, "/a/b", "/a/c", "/x/y/z");
+ primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+ nb = primary.getRoot().builder();
+ nb.child("a").child("c").remove();
+ primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+ assertFalse(NodeStateUtils.getNode(secondaryRoot(), "/a/c").exists());
+ }
+
+ private NodeState secondaryRoot() {
+ return DelegatingDocumentNodeState.wrap(secondary.getRoot(),
NodeStateDiffer.DEFAULT_DIFFER);
+ }
+
+ private static void assertMetaState(NodeState root1, NodeState root2,
String path){
+ assertMetaState(documentState(root1, path), documentState(root2,
path));
+ }
+
+ private static void assertMetaState(AbstractDocumentNodeState a,
AbstractDocumentNodeState b){
+ assertEquals(a.getRevision(), b.getRevision());
+ assertEquals(a.getRootRevision(), b.getRootRevision());
+ assertEquals(a.getPath(), b.getPath());
+ }
+
+ static AbstractDocumentNodeState documentState(NodeState root, String
path){
+ return (AbstractDocumentNodeState) NodeStateUtils.getNode(root, path);
+ }
+
+ private static void dump(NodeState root, String path){
+ NodeState state = NodeStateUtils.getNode(root, path);
+ System.out.println(NodeStateUtils.toString(state));
+ }
+
+ static NodeState create(NodeBuilder b, String ... paths){
+ for (String path : paths){
+ NodeBuilder cb = b;
+ for (String pathElement : PathUtils.elements(path)){
+ cb = cb.child(pathElement);
+ }
+ }
+ return b.getNodeState();
+ }
+
+}
\ No newline at end of file
Propchange:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreObserverTest.java
------------------------------------------------------------------------------
svn:eol-style = native