Skip to content
Permalink
Browse files
CURATOR-549
The next phase of this issue will implement a bridge cache that bridges TreeCache for pre 3.6 SK and CuratorCache for ZK 3.6+. That bridge will need this TreeCache iterator.
  • Loading branch information
randgalt authored and Randgalt committed Apr 9, 2020
1 parent 7f9e9cc commit 844c0ad36340b695b2784489c078cfd78522143c
Show file tree
Hide file tree
Showing 6 changed files with 393 additions and 8 deletions.
@@ -47,6 +47,7 @@
import java.io.Closeable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -217,7 +218,7 @@ public static Builder newBuilder(CuratorFramework client, String path)

private static final ChildData DEAD = new ChildData("/", null, null);

private static boolean isLive(ChildData cd)
static boolean isLive(ChildData cd)
{
return cd != null && cd != DEAD;
}
@@ -226,7 +227,7 @@ private static boolean isLive(ChildData cd)

private static final AtomicReferenceFieldUpdater<TreeNode, ConcurrentMap<String, TreeNode>> childrenUpdater = (AtomicReferenceFieldUpdater)AtomicReferenceFieldUpdater.newUpdater(TreeNode.class, ConcurrentMap.class, "children");

private final class TreeNode implements Watcher, BackgroundCallback
final class TreeNode implements Watcher, BackgroundCallback
{
volatile ChildData childData;
final TreeNode parent;
@@ -343,7 +344,7 @@ void wasDeleted() throws Exception

if ( isLive(oldChildData) )
{
publishEvent(TreeCacheEvent.Type.NODE_REMOVED, oldChildData);
publishEvent(TreeCacheEvent.Type.NODE_REMOVED, oldChildData, null);
}

if ( parent == null )
@@ -482,7 +483,14 @@ else if ( event.getResultCode() == KeeperException.Code.NONODE.intValue() )
}
if ( childDataUpdater.compareAndSet(this, oldChildData, toUpdate) )
{
publishEvent(isLive(oldChildData) ? TreeCacheEvent.Type.NODE_UPDATED : TreeCacheEvent.Type.NODE_ADDED, toPublish);
if ( isLive(oldChildData) )
{
publishEvent(TreeCacheEvent.Type.NODE_UPDATED, toPublish, oldChildData);
}
else
{
publishEvent(TreeCacheEvent.Type.NODE_ADDED, toPublish, null);
}
break;
}
}
@@ -750,6 +758,49 @@ public ChildData getCurrentData(String fullPath)
return isLive(result) ? result : null;
}

/**
* Return an iterator over all nodes in the cache. There are no
* guarantees of accuracy; this is merely the most recent view of the data.
*
* @return a possibly-empty iterator of nodes in the cache
*/
public Iterator<ChildData> iterator()
{
return new TreeCacheIterator(root);
}

/**
* Return the number of nodes in the cache. There are no
* guarantees of accuracy; this is merely the most recent view of the data.
*
* @return size
*/
public int size()
{
return size(root);
}

private int size(TreeNode node)
{
int size;
if ( isLive(node.childData) )
{
size = 1;
if ( node.children != null )
{
for ( TreeNode child : node.children.values() )
{
size += size(child);
}
}
}
else
{
size = 0;
}
return size;
}

private void callListeners(final TreeCacheEvent event)
{
listeners.forEach(listener ->
@@ -837,9 +888,9 @@ private void publishEvent(TreeCacheEvent.Type type)
publishEvent(new TreeCacheEvent(type, null));
}

private void publishEvent(TreeCacheEvent.Type type, ChildData data)
private void publishEvent(TreeCacheEvent.Type type, ChildData data, ChildData oldData)
{
publishEvent(new TreeCacheEvent(type, data));
publishEvent(new TreeCacheEvent(type, data, oldData));
}

private void publishEvent(final TreeCacheEvent event)
@@ -26,6 +26,7 @@
{
private final Type type;
private final ChildData data;
private final ChildData oldData;

/**
* Type of change
@@ -112,9 +113,20 @@
* @param data event data or null
*/
public TreeCacheEvent(Type type, ChildData data)
{
this(type, data, null);
}

/**
* @param type event type
* @param data event data or null
* @param oldData event oldData or null
*/
public TreeCacheEvent(Type type, ChildData data, ChildData oldData)
{
this.type = type;
this.data = data;
this.oldData = oldData;
}

/**
@@ -133,6 +145,14 @@ public ChildData getData()
return data;
}

/**
* @return the node's old data when the type is {@link org.apache.curator.framework.recipes.cache.TreeCacheEvent.Type#NODE_UPDATED}
*/
public ChildData getOldData()
{
return oldData;
}

@Override
public String toString()
{
@@ -0,0 +1,101 @@
/**
* 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.curator.framework.recipes.cache;

import com.google.common.collect.Iterators;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.NoSuchElementException;

// depth first iterator over tree cache nodes
class TreeCacheIterator implements Iterator<ChildData>
{
private final LinkedList<Current> stack = new LinkedList<>();
private Current current;

private static class Current
{
final Iterator<TreeCache.TreeNode> iterator;
TreeCache.TreeNode node;

Current(Iterator<TreeCache.TreeNode> iterator)
{
this.iterator = iterator;
node = iterator.next();
}
}

TreeCacheIterator(TreeCache.TreeNode root)
{
current = new Current(Iterators.forArray(root));
stack.push(current);
}

@Override
public boolean hasNext()
{
return (current != null) && TreeCache.isLive(current.node.childData);
}

@Override
public ChildData next()
{
if ( current == null )
{
throw new NoSuchElementException();
}

ChildData result = current.node.childData; // result of next iteration is current node's data

// set the next node for the next iteration (or note completion)

do
{
setNext();
} while ( (current != null) && !TreeCache.isLive(current.node.childData) );

return result;
}

private void setNext()
{
if ( current.node.children != null )
{
stack.push(current);
current = new Current(current.node.children.values().iterator());
}
else while ( true )
{
if ( current.iterator.hasNext() )
{
current.node = current.iterator.next();
break;
}
else if ( stack.size() > 0 )
{
current = stack.pop();
}
else
{
current = null; // done
break;
}
}
}
}
@@ -193,6 +193,16 @@ TreeCacheEvent assertEvent(TreeCacheEvent.Type expectedType, String expectedPath
{
Assert.assertEquals(event.getData().getData(), expectedData, message);
}

if ( event.getType() == TreeCacheEvent.Type.NODE_UPDATED)
{
Assert.assertNotNull(event.getOldData());
}
else
{
Assert.assertNull(event.getOldData());
}

return event;
}
}

0 comments on commit 844c0ad

Please sign in to comment.