Skip to content

Commit 122a5da

Browse files
committed
JCR-3997 TraversingItemVisitor causes stackoverflowexception with breadth first traversal
1 parent b52518c commit 122a5da

File tree

8 files changed

+401
-20
lines changed

8 files changed

+401
-20
lines changed

jackrabbit-jcr-commons/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,18 @@
128128
<version>0.11.4.1</version>
129129
<scope>test</scope>
130130
</dependency>
131+
<dependency>
132+
<groupId>org.apache.jackrabbit</groupId>
133+
<artifactId>jackrabbit-jcr-tests</artifactId>
134+
<version>${project.version}</version>
135+
<optional>true</optional>
136+
</dependency>
137+
<dependency>
138+
<groupId>org.apache.jackrabbit</groupId>
139+
<artifactId>jackrabbit-core</artifactId>
140+
<version>${project.version}</version>
141+
<scope>test</scope>
142+
</dependency>
131143
</dependencies>
132144

133145
</project>

jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/visitor/FilteringItemVisitor.java

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -200,31 +200,39 @@ public void visit(Node node)
200200
leaving(node, currentLevel);
201201
} else {
202202
// breadth-first traversal
203-
entering(node, currentLevel);
204-
leaving(node, currentLevel);
203+
currentQueue.addLast(node);
205204

206-
if (maxLevel == -1 || currentLevel < maxLevel) {
207-
if ( this.walkProperties ) {
208-
PropertyIterator propIter = node.getProperties();
209-
while (propIter.hasNext()) {
210-
nextQueue.addLast(propIter.nextProperty());
211-
}
205+
while(true) {
206+
int currentLevelItems = currentQueue.size();
207+
if (currentLevelItems == 0 || (maxLevel != -1 && currentLevel > maxLevel)) {
208+
break;
212209
}
213-
NodeIterator nodeIter = node.getNodes();
214-
while (nodeIter.hasNext()) {
215-
nextQueue.addLast(nodeIter.nextNode());
216-
}
217-
}
210+
while (currentLevelItems > 0 ) {
211+
Item currItem = (Item) currentQueue.removeFirst();
212+
if (currItem instanceof Property) {
213+
Property currProperty = (Property)currItem;
214+
visit(currProperty);
215+
} else if (currItem instanceof Node) {
216+
Node currNode = (Node) currItem;
217+
entering(currNode, currentLevel);
218+
if ( this.walkProperties ) {
219+
PropertyIterator propIter = currNode.getProperties();
220+
while (propIter.hasNext()) {
221+
currentQueue.addLast(propIter.nextProperty());
222+
}
223+
}
224+
NodeIterator nodeIter = currNode.getNodes();
225+
while (nodeIter.hasNext()) {
226+
currentQueue.addLast(nodeIter.nextNode());
227+
}
228+
leaving(currNode, currentLevel);
218229

219-
while (!currentQueue.isEmpty() || !nextQueue.isEmpty()) {
220-
if (currentQueue.isEmpty()) {
221-
currentLevel++;
222-
currentQueue = nextQueue;
223-
nextQueue = new LinkedList();
230+
}
231+
currentLevelItems--;
224232
}
225-
Item e = (Item) currentQueue.removeFirst();
226-
e.accept(this);
233+
currentLevel++;
227234
}
235+
228236
currentLevel = 0;
229237
}
230238
} catch (RepositoryException re) {
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package org.apache.jackrabbit.commons.visitor;
2+
3+
4+
import org.junit.Assert;
5+
6+
import javax.jcr.Node;
7+
import javax.jcr.Property;
8+
import javax.jcr.RepositoryException;
9+
import org.apache.jackrabbit.test.AbstractJCRTest;
10+
11+
import java.util.ArrayList;
12+
import java.util.Iterator;
13+
14+
public class FilteringItemVisitorTest extends AbstractJCRTest {
15+
16+
final private String TEST_ROOT_NODE = "testNode";
17+
18+
public void testFilteringVisitor() throws RepositoryException {
19+
20+
ArrayList<Node> insertedNodeList = new ArrayList<Node>();
21+
22+
Node root = superuser.getRootNode();
23+
Node testNode = root.addNode(TEST_ROOT_NODE);
24+
insertedNodeList.add(testNode);
25+
26+
for (int i = 1; i < 101; i++) {
27+
Node firstLevelChildNode = testNode.addNode("a"+i);
28+
insertedNodeList.add(i, firstLevelChildNode);
29+
30+
Node secondLevelChildNode = firstLevelChildNode.addNode("b"+i);
31+
insertedNodeList.add(2*i, secondLevelChildNode);
32+
33+
Node thirdLevelChildNode = secondLevelChildNode.addNode("c"+i);
34+
insertedNodeList.add(3*i, thirdLevelChildNode);
35+
36+
}
37+
38+
39+
testNode.getSession().save();
40+
41+
final Iterator<Node> insertedNodes = insertedNodeList.iterator();
42+
43+
FilteringItemVisitor v = new FilteringItemVisitor() {
44+
45+
@Override
46+
protected void entering(Property property, int level) throws RepositoryException {
47+
48+
49+
}
50+
51+
@Override
52+
protected void entering(Node node, int level) throws RepositoryException {
53+
int currentDepth = Thread.currentThread().getStackTrace().length;
54+
if (currentDepth > 200){
55+
Assert.fail("Stack depth is more than 200");
56+
}
57+
58+
if (insertedNodes.hasNext()) {
59+
Node currNode= insertedNodes.next();
60+
61+
if (currNode.getPath().equals(node.getPath())) {
62+
return;
63+
}
64+
65+
}
66+
Assert.fail("Traversal in not Breath First Traversal");
67+
}
68+
69+
@Override
70+
protected void leaving(Property property, int level) throws RepositoryException {
71+
72+
}
73+
74+
@Override
75+
protected void leaving(Node node, int level) throws RepositoryException {
76+
77+
}
78+
};
79+
80+
v.setBreadthFirst(true);
81+
testNode.accept(v);
82+
83+
}
84+
85+
@Override
86+
protected void tearDown() throws Exception {
87+
superuser.getRootNode().getNode(TEST_ROOT_NODE).remove();
88+
superuser.save();
89+
super.tearDown();
90+
91+
}
92+
}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<?xml version="1.0"?>
2+
<!--
3+
Licensed to the Apache Software Foundation (ASF) under one or more
4+
contributor license agreements. See the NOTICE file distributed with
5+
this work for additional information regarding copyright ownership.
6+
The ASF licenses this file to You under the Apache License, Version 2.0
7+
(the "License"); you may not use this file except in compliance with
8+
the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
-->
18+
<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
19+
"http://jackrabbit.apache.org/dtd/repository-1.6.dtd">
20+
<!-- Example Repository Configuration File -->
21+
<Repository>
22+
<!--
23+
virtual file system where the repository stores global state
24+
(e.g. registered namespaces, custom node types, etc.)
25+
-->
26+
<FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
27+
<param name="path" value="${rep.home}/repository"/>
28+
</FileSystem>
29+
30+
<!--
31+
data store configuration
32+
-->
33+
<DataStore class="org.apache.jackrabbit.core.data.FileDataStore"/>
34+
<!--
35+
sample database data store configuration
36+
<DataStore class="org.apache.jackrabbit.core.data.db.DbDataStore">
37+
<param name="url" value="jdbc:h2:~/test"/>
38+
<param name="user" value="sa"/>
39+
<param name="password" value="sa"/>
40+
</DataStore>
41+
-->
42+
43+
<!--
44+
repository lock mechanism configuration
45+
<RepositoryLockMechanism class="org.apache.jackrabbit.core.util.CooperativeFileLock"/>
46+
-->
47+
48+
<!--
49+
security configuration
50+
-->
51+
<Security appName="Jackrabbit">
52+
<!--
53+
security manager:
54+
class: FQN of class implementing the JackrabbitSecurityManager interface
55+
-->
56+
<SecurityManager class="org.apache.jackrabbit.core.DefaultSecurityManager" workspaceName="security">
57+
<!-- <param name="config" value="${rep.home}/security.xml"/> -->
58+
</SecurityManager>
59+
60+
<!--
61+
access manager:
62+
class: FQN of class implementing the AccessManager interface
63+
-->
64+
<AccessManager class="org.apache.jackrabbit.core.security.DefaultAccessManager">
65+
<!-- <param name="config" value="${rep.home}/access.xml"/> -->
66+
</AccessManager>
67+
68+
<LoginModule class="org.apache.jackrabbit.core.security.authentication.DefaultLoginModule">
69+
<!--
70+
anonymous user name ('anonymous' is the default value)
71+
-->
72+
<param name="anonymousId" value="anonymous"/>
73+
<!--
74+
administrator user id (default value if param is missing is 'admin')
75+
-->
76+
<param name="adminId" value="admin"/>
77+
<!--
78+
optional parameter 'principalProvider'.
79+
the value refers to the class name of the PrincipalProvider implementation.
80+
-->
81+
<!-- <param name="principalProvider" value="..."/> -->
82+
</LoginModule>
83+
</Security>
84+
85+
<!--
86+
location of workspaces root directory and name of default workspace
87+
-->
88+
<Workspaces rootPath="${rep.home}/workspaces" defaultWorkspace="default" maxIdleTime="2"/>
89+
<!--
90+
workspace configuration template:
91+
used to create the initial workspace if there's no workspace yet
92+
-->
93+
<Workspace name="${wsp.name}">
94+
<!--
95+
virtual file system of the workspace:
96+
class: FQN of class implementing the FileSystem interface
97+
-->
98+
<FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
99+
<param name="path" value="${wsp.home}"/>
100+
</FileSystem>
101+
<!--
102+
persistence manager of the workspace:
103+
class: FQN of class implementing the PersistenceManager interface
104+
-->
105+
<PersistenceManager class="org.apache.jackrabbit.core.persistence.pool.DerbyPersistenceManager">
106+
<param name="url" value="jdbc:derby:${wsp.home}/db;create=true"/>
107+
<param name="schemaObjectPrefix" value="${wsp.name}_"/>
108+
</PersistenceManager>
109+
<!--
110+
Search index and the file system it uses.
111+
class: FQN of class implementing the QueryHandler interface
112+
-->
113+
<SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
114+
<param name="path" value="${wsp.home}/index"/>
115+
</SearchIndex>
116+
</Workspace>
117+
118+
<!--
119+
Configures the versioning
120+
-->
121+
<Versioning rootPath="${rep.home}/version">
122+
<!--
123+
Configures the filesystem to use for versioning for the respective
124+
persistence manager
125+
-->
126+
<FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
127+
<param name="path" value="${rep.home}/version" />
128+
</FileSystem>
129+
130+
<!--
131+
Configures the persistence manager to be used for persisting version state.
132+
Please note that the current versioning implementation is based on
133+
a 'normal' persistence manager, but this could change in future
134+
implementations.
135+
-->
136+
<PersistenceManager class="org.apache.jackrabbit.core.persistence.pool.DerbyPersistenceManager">
137+
<param name="url" value="jdbc:derby:${rep.home}/version/db;create=true"/>
138+
<param name="schemaObjectPrefix" value="version_"/>
139+
</PersistenceManager>
140+
</Versioning>
141+
142+
<!--
143+
Search index for content that is shared repository wide
144+
(/jcr:system tree, contains mainly versions)
145+
-->
146+
<SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
147+
<param name="path" value="${rep.home}/repository/index"/>
148+
</SearchIndex>
149+
150+
<!--
151+
Run with a cluster journal
152+
-->
153+
<Cluster id="node1">
154+
<Journal class="org.apache.jackrabbit.core.journal.MemoryJournal"/>
155+
</Cluster>
156+
</Repository>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one or more
2+
# contributor license agreements. See the NOTICE file distributed with
3+
# this work for additional information regarding copyright ownership.
4+
# The ASF licenses this file to You under the Apache License, Version 2.0
5+
# (the "License"); you may not use this file except in compliance with
6+
# the License. You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
# this is a synonym definition file for PropertiesSynonymProvider
17+
ASF=Apache Software Foundation
18+
quick=fast
19+
sluggish=lazy
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?xml version="1.0"?>
2+
<!--
3+
Licensed to the Apache Software Foundation (ASF) under one or more
4+
contributor license agreements. See the NOTICE file distributed with
5+
this work for additional information regarding copyright ownership.
6+
The ASF licenses this file to You under the Apache License, Version 2.0
7+
(the "License"); you may not use this file except in compliance with
8+
the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
-->
18+
<Workspace name="default" defaultLockTimeout="86400">
19+
<!--
20+
virtual file system of the workspace:
21+
class: FQN of class implementing FileSystem interface
22+
-->
23+
<FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
24+
<param name="path" value="${wsp.home}" />
25+
</FileSystem>
26+
<!--
27+
persistence of the workspace:
28+
class: FQN of class implementing PersistenceManager interface
29+
-->
30+
<PersistenceManager class="org.apache.jackrabbit.core.persistence.pool.DerbyPersistenceManager">
31+
<param name="url" value="jdbc:derby:${wsp.home}/db;create=true"/>
32+
<param name="schemaObjectPrefix" value="${wsp.name}_"/>
33+
</PersistenceManager>
34+
<!--
35+
Search index and the file system it uses.
36+
-->
37+
<SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
38+
<param name="path" value="${wsp.home}/index" />
39+
<param name="respectDocumentOrder" value="true" />
40+
<param name="synonymProviderClass" value="org.apache.jackrabbit.core.query.lucene.PropertiesSynonymProvider"/>
41+
<param name="synonymProviderConfigPath" value="../synonyms.properties"/>
42+
<param name="supportHighlighting" value="true"/>
43+
<param name="excerptProviderClass" value="org.apache.jackrabbit.core.query.lucene.WeightedHTMLExcerpt"/>
44+
</SearchIndex>
45+
</Workspace>
46+

0 commit comments

Comments
 (0)