/* * 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.core.integration; import java.io.ByteArrayInputStream; import java.io.UnsupportedEncodingException; import java.util.Calendar; import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.jcr.query.Row; import javax.jcr.query.RowIterator; import org.apache.jackrabbit.commons.query.GQL; import org.apache.jackrabbit.core.query.AbstractQueryTest; import junit.framework.AssertionFailedError; /** * <code>GQLTest</code> performs tests on {@link GQL}. */ public class GQLTest extends AbstractQueryTest { private static final String SAMPLE_CONTENT = "the quick brown fox jumps over the lazy dog."; public void testPath() throws RepositoryException { Node n1 = testRootNode.addNode("node1"); Node n2 = testRootNode.addNode("node2"); Node n3 = testRootNode.addNode("node3"); superuser.save(); RowIterator rows = GQL.execute(createStatement(""), superuser); checkResult(rows, new Node[]{n1, n2, n3}); } public void testOrder() throws RepositoryException { Node n1 = testRootNode.addNode("node1"); n1.setProperty("p", 1); Node n2 = testRootNode.addNode("node2"); n2.setProperty("p", 2); Node n3 = testRootNode.addNode("node3"); n3.setProperty("p", 3); superuser.save(); // default: ascending String stmt = createStatement("order:p"); RowIterator rows = GQL.execute(stmt, superuser); checkResultSequence(rows, new Node[]{n1, n2, n3}); // explicit ascending stmt = createStatement("order:+p"); rows = GQL.execute(stmt, superuser); checkResultSequence(rows, new Node[]{n1, n2, n3}); // explicit descending stmt = createStatement("order:-p"); rows = GQL.execute(stmt, superuser); checkResultSequence(rows, new Node[]{n3, n2, n1}); } public void testOrderDeep() throws RepositoryException { Node n1 = testRootNode.addNode("node1"); n1.setProperty("prop", "value"); n1.addNode("sub").setProperty("p", 1); Node n2 = testRootNode.addNode("node2"); n2.setProperty("prop", "value"); n2.addNode("sub").setProperty("p", 2); Node n3 = testRootNode.addNode("node3"); n3.setProperty("prop", "value"); n3.addNode("sub").setProperty("p", 3); superuser.save(); // default: ascending String stmt = createStatement("prop:value order:sub/p"); RowIterator rows = GQL.execute(stmt, superuser); checkResultSequence(rows, new Node[]{n1, n2, n3}); // explicit ascending stmt = createStatement("prop:value order:+sub/p"); rows = GQL.execute(stmt, superuser); checkResultSequence(rows, new Node[]{n1, n2, n3}); // explicit descending stmt = createStatement("prop:value order:-sub/p"); rows = GQL.execute(stmt, superuser); checkResultSequence(rows, new Node[]{n3, n2, n1}); } public void testLimit() throws RepositoryException { Node n1 = testRootNode.addNode("node1"); n1.setProperty("p", 1); Node n2 = testRootNode.addNode("node2"); n2.setProperty("p", 2); Node n3 = testRootNode.addNode("node3"); n3.setProperty("p", 3); superuser.save(); // only 2 results String stmt = createStatement("order:p limit:2"); RowIterator rows = GQL.execute(stmt, superuser); checkResultSequence(rows, new Node[]{n1, n2}); // range with open start stmt = createStatement("order:p limit:..2"); rows = GQL.execute(stmt, superuser); checkResultSequence(rows, new Node[]{n1, n2}); // range with open end stmt = createStatement("order:p limit:1.."); rows = GQL.execute(stmt, superuser); checkResultSequence(rows, new Node[]{n2, n3}); // range stmt = createStatement("order:p limit:1..2"); rows = GQL.execute(stmt, superuser); checkResultSequence(rows, new Node[]{n2}); // range with end larger than max results stmt = createStatement("order:p limit:1..7"); rows = GQL.execute(stmt, superuser); checkResultSequence(rows, new Node[]{n2, n3}); // range start larger than end // end is ignored in that case stmt = createStatement("order:p limit:2..1"); rows = GQL.execute(stmt, superuser); checkResultSequence(rows, new Node[]{n3}); // range with start larger than max results stmt = createStatement("order:p limit:6..10"); rows = GQL.execute(stmt, superuser); checkResultSequence(rows, new Node[]{}); } public void testPhrase() throws RepositoryException { Node file1 = addFile(testRootNode, "file1.txt", SAMPLE_CONTENT); superuser.save(); String stmt = createStatement("\"quick brown\""); checkResultWithRetries(stmt, "jcr:content", new Node[]{file1}); } /** * Test for JCR-3157 */ public void testApostrophe() throws RepositoryException { Node n1 = testRootNode.addNode("node1"); n1.setProperty("text", "let's go"); superuser.save(); String stmt = createStatement("\"let's go\""); RowIterator rows = GQL.execute(stmt, superuser); checkResult(rows, new Node[]{n1}); } public void testExcludeTerm() throws RepositoryException { Node n1 = testRootNode.addNode("node1"); n1.setProperty("text", "foo"); Node n2 = testRootNode.addNode("node2"); n2.setProperty("text", "bar"); Node n3 = testRootNode.addNode("node3"); n3.setProperty("text", "foo bar"); superuser.save(); String stmt = createStatement("foo -bar"); RowIterator rows = GQL.execute(stmt, superuser); checkResult(rows, new Node[]{n1}); } public void testOptionalTerm() throws RepositoryException { Node n1 = testRootNode.addNode("node1"); n1.setProperty("text", "apache jackrabbit"); Node n2 = testRootNode.addNode("node2"); n2.setProperty("text", "apache jcr"); Node n3 = testRootNode.addNode("node3"); n3.setProperty("text", "jackrabbit jcr"); superuser.save(); String stmt = createStatement("apache jackrabbit OR jcr"); RowIterator rows = GQL.execute(stmt, superuser); checkResult(rows, new Node[]{n1, n2}); } public void testType() throws RepositoryException { Node file = addFile(testRootNode, "file1.txt", SAMPLE_CONTENT); Node node = testRootNode.addNode("node1"); node.setProperty("text", SAMPLE_CONTENT); superuser.save(); // only nt:resource String stmt = createStatement("quick type:\"nt:resource\""); checkResultWithRetries(stmt, null, new Node[]{file.getNode("jcr:content")}); // only nt:unstructured stmt = createStatement("quick type:\"nt:unstructured\""); RowIterator rows = GQL.execute(stmt, superuser); checkResult(rows, new Node[]{node}); } public void testMixinType() throws RepositoryException { Node node = testRootNode.addNode("node1"); node.setProperty("text", SAMPLE_CONTENT); node.addMixin(mixReferenceable); superuser.save(); String stmt = createStatement("quick type:referenceable"); RowIterator rows = GQL.execute(stmt, superuser); checkResult(rows, new Node[]{node}); } public void testTypeInheritance() throws RepositoryException { Node file = addFile(testRootNode, "file1.txt", SAMPLE_CONTENT); superuser.save(); // nt:hierarchyNode and sub types String stmt = createStatement("quick type:hierarchyNode"); checkResultWithRetries(stmt, "jcr:content", new Node[]{file}); } public void testAutoPrefixType() throws RepositoryException { Node file = addFile(testRootNode, "file1.txt", SAMPLE_CONTENT); Node node = testRootNode.addNode("node1"); node.setProperty("text", SAMPLE_CONTENT); superuser.save(); // only nt:resource String stmt = createStatement("quick type:resource"); checkResultWithRetries(stmt, null, new Node[]{file.getNode("jcr:content")}); // only nt:unstructured stmt = createStatement("quick type:unstructured"); RowIterator rows = GQL.execute(stmt, superuser); checkResult(rows, new Node[]{node}); } public void testQuotedProperty() throws RepositoryException { Node file1 = addFile(testRootNode, "file1.txt", SAMPLE_CONTENT); superuser.save(); String stmt = createStatement("\"jcr:mimeType\":text/plain"); checkResultWithRetries(stmt, "jcr:content", new Node[]{file1}); } public void testAutoPrefix() throws RepositoryException { Node file1 = addFile(testRootNode, "file1.txt", SAMPLE_CONTENT); superuser.save(); String stmt = createStatement("mimeType:text/plain"); checkResultWithRetries(stmt, "jcr:content", new Node[]{file1}); } public void testCommonPathPrefix() throws RepositoryException { Node file1 = addFile(testRootNode, "file1.txt", SAMPLE_CONTENT); Node file2 = addFile(testRootNode, "file2.txt", SAMPLE_CONTENT); Node file3 = addFile(testRootNode, "file3.txt", SAMPLE_CONTENT); superuser.save(); String stmt = createStatement("quick"); checkResultWithRetries(stmt, "jcr:content", new Node[]{file1, file2, file3}); } public void testExcerpt() throws RepositoryException { Node file = addFile(testRootNode, "file1.txt", SAMPLE_CONTENT); superuser.save(); String stmt = createStatement("quick"); checkResultWithRetries(stmt, "jcr:content", new Node[]{file}); RowIterator rows = GQL.execute(stmt, superuser, "jcr:content"); assertTrue("Expected result", rows.hasNext()); String excerpt = rows.nextRow().getValue("rep:excerpt()").getString(); assertTrue("No excerpt returned", excerpt.startsWith("<div><span>")); stmt = createStatement("type:resource quick"); rows = GQL.execute(stmt, superuser); assertTrue("Expected result", rows.hasNext()); excerpt = rows.nextRow().getValue("rep:excerpt()").getString(); assertTrue("No excerpt returned", excerpt.startsWith("<div><span>")); } public void testPrefixedValue() throws RepositoryException { Node n1 = testRootNode.addNode("node1"); n1.setProperty("jcr:title", "a"); Node n2 = testRootNode.addNode("node2"); n2.setProperty("jcr:title", "b"); Node n3 = testRootNode.addNode("node3"); n3.setProperty("jcr:title", "c"); superuser.save(); String stmt = createStatement("order:jcr:title"); RowIterator rows = GQL.execute(stmt, superuser); checkResultSequence(rows, new Node[]{n1, n2, n3}); } public void testFilter() throws RepositoryException { final Node n1 = testRootNode.addNode("node1"); n1.setProperty("jcr:title", "a"); Node n2 = testRootNode.addNode("node2"); n2.setProperty("jcr:title", "b"); Node n3 = testRootNode.addNode("node3"); n3.setProperty("jcr:title", "c"); superuser.save(); String stmt = createStatement("order:jcr:title"); RowIterator rows = GQL.execute(stmt, superuser, null, new GQL.Filter() { public boolean include(Row row) throws RepositoryException { return !n1.getPath().equals(row.getValue("jcr:path").getString()); } }); checkResultSequence(rows, new Node[]{n2, n3}); } public void testFilterLimit() throws RepositoryException { final Node n1 = testRootNode.addNode("node1"); n1.setProperty("jcr:title", "a"); Node n2 = testRootNode.addNode("node2"); n2.setProperty("jcr:title", "b"); Node n3 = testRootNode.addNode("node3"); n3.setProperty("jcr:title", "c"); superuser.save(); String stmt = createStatement("order:jcr:title limit:1"); RowIterator rows = GQL.execute(stmt, superuser, null, new GQL.Filter() { public boolean include(Row row) throws RepositoryException { return !n1.getPath().equals(row.getValue("jcr:path").getString()); } }); checkResultSequence(rows, new Node[]{n2}); } public void testName() throws RepositoryException { Node file1 = addFile(testRootNode, "file1.txt", SAMPLE_CONTENT); superuser.save(); String stmt = createStatement("\"quick brown\" name:file1.txt"); checkResultWithRetries(stmt, "jcr:content", new Node[]{file1}); stmt = createStatement("\"quick brown\" name:file?.txt"); checkResultWithRetries(stmt, "jcr:content", new Node[]{file1}); stmt = createStatement("\"quick brown\" name:?ile1.txt"); checkResultWithRetries(stmt, "jcr:content", new Node[]{file1}); stmt = createStatement("\"quick brown\" name:file1.tx?"); checkResultWithRetries(stmt, "jcr:content", new Node[]{file1}); stmt = createStatement("\"quick brown\" name:file1.???"); checkResultWithRetries(stmt, "jcr:content", new Node[]{file1}); stmt = createStatement("\"quick brown\" name:fil*xt"); checkResultWithRetries(stmt, "jcr:content", new Node[]{file1}); stmt = createStatement("\"quick brown\" name:*.txt"); checkResultWithRetries(stmt, "jcr:content", new Node[]{file1}); stmt = createStatement("\"quick brown\" name:file1.*"); checkResultWithRetries(stmt, "jcr:content", new Node[]{file1}); stmt = createStatement("\"quick brown\" name:*"); checkResultWithRetries(stmt, "jcr:content", new Node[]{file1}); stmt = createStatement("\"quick brown\" name:fIlE1.*"); checkResultWithRetries(stmt, "jcr:content", new Node[]{file1}); stmt = createStatement("\"quick brown\" name:file2.txt"); checkResultWithRetries(stmt, "jcr:content", new Node[]{}); stmt = createStatement("\"quick brown\" name:file1.t?"); checkResultWithRetries(stmt, "jcr:content", new Node[]{}); stmt = createStatement("\"quick brown\" name:?le1.txt"); checkResultWithRetries(stmt, "jcr:content", new Node[]{}); } public void XXXtestQueryDestruction() throws RepositoryException { char[] stmt = createStatement("title:jackrabbit \"apache software\" type:file order:+title limit:10..20").toCharArray(); for (char c = 0; c < 255; c++) { for (int i = 0; i < stmt.length; i++) { char orig = stmt[i]; stmt[i] = c; try { GQL.execute(new String(stmt), superuser); } finally { stmt[i] = orig; } } } } protected static Node addFile(Node folder, String name, String contents) throws RepositoryException { Node file = folder.addNode(name, "nt:file"); Node resource = file.addNode("jcr:content", "nt:resource"); resource.setProperty("jcr:lastModified", Calendar.getInstance()); resource.setProperty("jcr:mimeType", "text/plain"); resource.setProperty("jcr:encoding", "UTF-8"); try { resource.setProperty("jcr:data", new ByteArrayInputStream( contents.getBytes("UTF-8"))); } catch (UnsupportedEncodingException e) { // will never happen } return file; } protected String createStatement(String stmt) { return "path:" + testRoot + " " + stmt; } /** * Checks if the result contains exactly the <code>nodes</code>. If the * result does not contain the * * @param gql the gql statement. * @param cpp the common path prefix or <code>null</code>. * @param nodes the expected nodes in the result set. * @throws RepositoryException if an error occurs while reading from the result. */ protected void checkResultWithRetries(String gql, String cpp, Node[] nodes) throws RepositoryException { int retries = 10; for (int i = 0; i < retries; i++) { try { checkResult(GQL.execute(gql, superuser, cpp), nodes); break; } catch (AssertionFailedError e) { if (i + 1 == retries) { throw e; } try { // sleep for a second and retry Thread.sleep(1000); } catch (InterruptedException e1) { // ignore } } } } }