You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by md...@apache.org on 2010/11/05 18:53:57 UTC
svn commit: r1031680 - in /jackrabbit/trunk:
jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/
jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/
jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/...
Author: mduerig
Date: Fri Nov 5 17:53:57 2010
New Revision: 1031680
URL: http://svn.apache.org/viewvc?rev=1031680&view=rev
Log:
JCR-2800: Implement search facility for users and groups
work in progress
Added:
jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/Query.java
jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java
jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/iterator/Predicates.java
Modified:
jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/UserManager.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java
jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/TestAll.java
jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java
Added: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/Query.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/Query.java?rev=1031680&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/Query.java (added)
+++ jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/Query.java Fri Nov 5 17:53:57 2010
@@ -0,0 +1,50 @@
+/*
+ * 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.api.security.user;
+
+/**
+ * A query to match {@link Authorizable}s. Pass an instance of this interface to
+ * {@link UserManager#findAuthorizables(Query)}.
+ *
+ * The following query finds all users named 'Bob' which have the word
+ * 'engineer' in its description and returns them in ascending order wrt. to
+ * the name.
+ *
+ * <pre>
+ * Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ * public <T> void build(QueryBuilder<T> builder) {
+ * builder.setCondition(builder.
+ * and(builder.
+ * property("@name", RelationOp.EQ, valueFactory.createValue("Bob")), builder.
+ * contains("@description", "engineer")));
+ *
+ * builder.setSortOrder("@name", Direction.ASCENDING);
+ * builder.setSelector(Selector.USER);
+ * }
+ * });
+ * </pre>
+ */
+public interface Query {
+
+ /**
+ * Build the query using a {@link QueryBuilder}.
+ * @param builder A query builder for building the query.
+ * @param <T> Opaque type of the query builder.
+ */
+ <T> void build(QueryBuilder<T> builder);
+}
Added: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java?rev=1031680&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java (added)
+++ jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java Fri Nov 5 17:53:57 2010
@@ -0,0 +1,207 @@
+/*
+ * 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.api.security.user;
+
+import javax.jcr.Value;
+
+public interface QueryBuilder<T> {
+
+ /**
+ * The sort order of the result set of a query.
+ */
+ enum Direction {
+ ASCENDING("ascending"),
+ DESCENDING("descending");
+
+ private final String direction;
+
+ Direction(String direction) {
+ this.direction = direction;
+ }
+
+ public String getDirection() {
+ return direction;
+ }
+ }
+
+ /**
+ * The selectors for a query.
+ */
+ enum Selector {
+ AUTHORIZABLE("rep:Authorizable"),
+ USER("rep:User"),
+ GROUP("rep:Group");
+
+ private final String ntName;
+
+ Selector(String ntName) {
+ this.ntName = ntName;
+ }
+
+ public String getNtName() {
+ return ntName;
+ }
+ }
+
+ /**
+ * Relational operators for comparing a property to a value. Correspond
+ * to the general comparison operators as define in JSR-170.
+ * The {@link #EX} tests for existence of a property.
+ */
+ enum RelationOp {
+ NE("!="),
+ EQ("="),
+ LT("<"),
+ LE("<="),
+ GT(">"),
+ GE("=>"),
+ EX("");
+
+ private final String op;
+
+ RelationOp(String op) {
+ this.op = op;
+ }
+
+ public String getOp() {
+ return op;
+ }
+ }
+
+ /**
+ * Set the selector for the query.
+ *
+ * @param selector One of {@link Selector#AUTHORIZABLE}, {@link Selector#USER} or {@link Selector#GROUP}
+ */
+ void setSelector(Selector selector);
+
+ /**
+ * Set the scope for the query. If set, the query will only return members of a specific group.
+ *
+ * @param groupName Name of the group to restrict the query to.
+ * @param declaredOnly If <code>true</code> only declared members of the groups are returned.
+ * Otherwise indirect memberships are also considered.
+ */
+ void setScope(String groupName, boolean declaredOnly);
+
+ /**
+ * Set the condition for the query. The query only includes {@link Authorizable}s
+ * for which this condition holds.
+ *
+ * @param condition Condition upon which <code>Authorizables</code> are included in the query result
+ */
+ void setCondition(T condition);
+
+ /**
+ * Set the sort order of the {@link Authorizable}s returned by the query.
+ * The format of the <code>propertyName</code> is the same as in XPath:
+ * <code>@propertyName</code> sorts on a property of the current node.
+ * <code>relative/path/@propertyName</code> sorts on a property of a
+ * descendant node.
+ *
+ * @param propertyName The name of the property to sort on
+ * @param direction Direction to sort. Either {@link Direction#ASCENDING} or {@link Direction#DESCENDING}
+ */
+ void setSortOrder(String propertyName, Direction direction);
+
+ /**
+ * Set the limit of the query. A limit consists of an offset, a maximal number of results
+ * to include and a direction. The offset refers to the value of the sort order property.
+ * If <code>forward</code> is <code>true</code> (<code>false</code>) the query returns
+ * at most the <code>maxCount</code> {@link Authorizable}s whose value of the sort order
+ * property follows (precedes)the specified <code>offset</code> in the given sort
+ * {@link Direction direction}. This method has no effect if the
+ * {@link #setSortOrder(String, Direction) sort order} is not also specified.
+ * @see #setSortOrder(String, Direction)
+ *
+ * @param offset Offset from where to start returning results. <code>null</code> for no offset
+ * @param maxCount Maximal number of results to return. -1 for all.
+ * @param forward If <code>true</code> return the following <code>count</code> records.
+ * Otherwise return the preceding <code>count</code> records wrt. <code>offset</code>.
+ */
+ void setLimit(String offset, int maxCount, boolean forward);
+
+ /**
+ * Create a condition which holds iff the node of an {@link Authorizable} has a
+ * property at <code>relPath</code> which relates to <code>value</code> through
+ * <code>op</<code>. The format of the <code>relPath</code> argument is the same
+ * as in XPath: <code>@attributeName</code> for an attribute on this node and
+ * <code>relative/path/@attributeName</code> for an attribute of a descendant node.
+ * {@link RelationOp#EX} tests for existence of a property. In this case the
+ * <code>value</code> argument is ignored.
+ *
+ * @param relPath Relative path from the authorizable's node to the property
+ * @param op Comparison operator
+ * @param value Value to compare the property at <code>relPath</code> to
+ * @return A condition
+ */
+ T property(String relPath, RelationOp op, Value value);
+
+ /**
+ * Create a full text search condition. The condition holds iff the node of an
+ * {@link Authorizable} has a property at <code>relPath</code> for which
+ * <code>searchExpr</code> yields results.
+ * The format of the <code>relPath</code> argument is the same as in XPath:
+ * <code>.</code> searches all properties of the current node, <code>@attributeName</code>
+ * searches the attributeName property of the current node, <code>relative/path/.</code>
+ * searches all properties of the descendant node at relative/path and
+ * <code>relative/path/@attributeName</code> searches the attributeName property
+ * of the descendant node at relative/path.
+ * The syntax of <code>searchExpr</code> is <pre>[-]value { [OR] [-]value }</pre>.
+ *
+ * @param relPath Relative path from the authorizable's node to the property
+ * @param searchExpr A full text search expression
+ * @return A condition
+ */
+ T contains(String relPath, String searchExpr);
+
+ /**
+ * Create a condition which holds for {@link Authorizable}s which can impersonate as
+ * <code>name</code>.
+ *
+ * @param name Name of an authorizable
+ * @return A condition
+ */
+ T impersonates(String name);
+
+ /**
+ * Return a condition which holds iff <code>condition</code> does not hold.
+ *
+ * @param condition Condition to negate
+ * @return A condition
+ */
+ T not(T condition);
+
+ /**
+ * Return a condition which holds iff both sub conditions hold.
+ *
+ * @param condition1 first sub condition
+ * @param condition2 second sub condition
+ * @return A condition
+ */
+ T and(T condition1, T condition2);
+
+ /**
+ * Return a condition which holds iff any of the two sub conditions hold.
+ *
+ * @param condition1 first sub condition
+ * @param condition2 second sub condition
+ * @return A condition
+ */
+ T or(T condition1, T condition2);
+}
Modified: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/UserManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/UserManager.java?rev=1031680&r1=1031679&r2=1031680&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/UserManager.java (original)
+++ jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/UserManager.java Fri Nov 5 17:53:57 2010
@@ -111,6 +111,15 @@ public interface UserManager {
Iterator<Authorizable> findAuthorizables(String relPath, String value, int searchType) throws RepositoryException;
/**
+ * Return {@link Authorizable}s that match a specific {@link Query}.
+ *
+ * @param query A query
+ * @return Iterator of authorizables witch match the <code>query</code>.
+ * @throws RepositoryException If an error occurs.
+ */
+ Iterator<Authorizable> findAuthorizables(Query query) throws RepositoryException;
+
+ /**
* Creates an User for the given userID / password pair; neither of the
* specified parameters can be <code>null</code>.<br>
* Same as {@link #createUser(String,String,Principal,String)} where
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java?rev=1031680&r1=1031679&r2=1031680&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java Fri Nov 5 17:53:57 2010
@@ -17,16 +17,8 @@
package org.apache.jackrabbit.core.security.user;
import org.apache.jackrabbit.api.security.principal.ItemBasedPrincipal;
-import org.apache.jackrabbit.api.security.user.Authorizable;
-import org.apache.jackrabbit.api.security.user.AuthorizableExistsException;
-import org.apache.jackrabbit.api.security.user.Group;
-import org.apache.jackrabbit.api.security.user.User;
-import org.apache.jackrabbit.api.security.user.UserManager;
-import org.apache.jackrabbit.core.ItemImpl;
-import org.apache.jackrabbit.core.NodeImpl;
-import org.apache.jackrabbit.core.ProtectedItemModifier;
-import org.apache.jackrabbit.core.SessionImpl;
-import org.apache.jackrabbit.core.SessionListener;
+import org.apache.jackrabbit.api.security.user.*;
+import org.apache.jackrabbit.core.*;
import org.apache.jackrabbit.core.id.NodeId;
import org.apache.jackrabbit.core.security.SystemPrincipal;
import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
@@ -37,26 +29,13 @@ import org.apache.jackrabbit.util.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.jcr.AccessDeniedException;
-import javax.jcr.ItemExistsException;
-import javax.jcr.ItemNotFoundException;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.RepositoryException;
-import javax.jcr.UnsupportedRepositoryOperationException;
-import javax.jcr.Value;
+import javax.jcr.*;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.version.VersionException;
-
import java.io.UnsupportedEncodingException;
import java.security.Principal;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-import java.util.Properties;
-import java.util.Set;
-import java.util.UUID;
+import java.util.*;
/**
* Default implementation of the <code>UserManager</code> interface with the
@@ -487,6 +466,12 @@ public class UserManagerImpl extends Pro
return new AuthorizableIterator(nodes);
}
+ public Iterator<Authorizable> findAuthorizables(Query query) throws RepositoryException {
+ XPathQueryBuilder builder = new XPathQueryBuilder();
+ query.build(builder);
+ return new XPathQueryEvaluator(builder, this, session.getWorkspace().getQueryManager()).eval();
+ }
+
/**
* @see UserManager#createUser(String,String)
*/
Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java?rev=1031680&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java Fri Nov 5 17:53:57 2010
@@ -0,0 +1,264 @@
+/*
+ * 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.security.user;
+
+import org.apache.jackrabbit.api.security.user.QueryBuilder;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class XPathQueryBuilder implements QueryBuilder<XPathQueryBuilder.Condition> {
+
+ interface Condition {
+ void accept(ConditionVisitor visitor) throws RepositoryException;
+ }
+
+ interface ConditionVisitor {
+ void visit(PropertyCondition condition) throws RepositoryException;
+ void visit(ContainsCondition condition);
+ void visit(ImpersonationCondition condition);
+ void visit(NotCondition condition) throws RepositoryException;
+ void visit(AndCondition condition) throws RepositoryException;
+ void visit(OrCondition condition) throws RepositoryException;
+ }
+
+ private Selector selector = Selector.AUTHORIZABLE;
+ private String groupName;
+ private boolean declaredMembersOnly;
+ private Condition condition;
+ private String sortProperty;
+ private Direction sortDirection = Direction.ASCENDING;
+ private String offset;
+ private int maxCount = -1;
+ private boolean forward = true;
+
+ Selector getSelector() {
+ return selector;
+ }
+
+ public String getGroupName() {
+ return groupName;
+ }
+
+ public boolean isDeclaredMembersOnly() {
+ return declaredMembersOnly;
+ }
+
+ Condition getCondition() {
+ return condition;
+ }
+
+ String getSortProperty() {
+ return sortProperty;
+ }
+
+ Direction getSortDirection() {
+ return sortDirection;
+ }
+
+ String getOffset() {
+ return offset;
+ }
+
+ int getMaxCount() {
+ return maxCount;
+ }
+
+ boolean isForward() {
+ return forward;
+ }
+
+ //------------------------------------------< QueryBuilder >---
+
+ public void setSelector(Selector selector) {
+ this.selector = selector;
+ }
+
+ public void setScope(String groupName, boolean declaredOnly) {
+ this.groupName = groupName;
+ declaredMembersOnly = declaredOnly;
+ }
+
+ public void setCondition(Condition condition) {
+ this.condition = condition;
+ }
+
+ public void setSortOrder(String propertyName, Direction direction) {
+ sortProperty = propertyName;
+ sortDirection = direction;
+ }
+
+ public void setLimit(String offset, int maxCount, boolean forward) {
+ this.offset = offset;
+ this.maxCount = maxCount;
+ this.forward = true;
+ throw new UnsupportedOperationException("limit is not yet supported"); // todo implement: limit
+ }
+
+ public Condition property(String relPath, RelationOp op, Value value) {
+ return new PropertyCondition(relPath, op, value);
+ }
+
+ public Condition contains(String relPath, String searchExpr) {
+ return new ContainsCondition(relPath, searchExpr);
+ }
+
+ public Condition impersonates(String name) {
+ return new ImpersonationCondition(name);
+ }
+
+ public Condition not(Condition condition) {
+ return new NotCondition(condition);
+ }
+
+ public Condition and(Condition condition1, Condition condition2) {
+ return new AndCondition(condition1, condition2);
+ }
+
+ public Condition or(Condition condition1, Condition condition2) {
+ return new OrCondition(condition1, condition2);
+ }
+
+ //------------------------------------------< private >---
+
+ static class PropertyCondition implements Condition {
+ private final String relPath;
+ private final RelationOp op;
+ private final Value value;
+
+ public PropertyCondition(String relPath, RelationOp op, Value value) {
+ this.relPath = relPath;
+ this.op = op;
+ this.value = value;
+ }
+
+ public String getRelPath() {
+ return relPath;
+ }
+
+ public RelationOp getOp() {
+ return op;
+ }
+
+ public Value getValue() {
+ return value;
+ }
+
+ public void accept(ConditionVisitor visitor) throws RepositoryException {
+ visitor.visit(this);
+ }
+ }
+
+ static class ContainsCondition implements Condition {
+ private final String relPath;
+ private final String searchExpr;
+
+ public ContainsCondition(String relPath, String searchExpr) {
+ this.relPath = relPath;
+ this.searchExpr = searchExpr;
+ }
+
+ public String getRelPath() {
+ return relPath;
+ }
+
+ public String getSearchExpr() {
+ return searchExpr;
+ }
+
+ public void accept(ConditionVisitor visitor) {
+ visitor.visit(this);
+ }
+ }
+
+ static class ImpersonationCondition implements Condition {
+ private final String name;
+
+ public ImpersonationCondition(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void accept(ConditionVisitor visitor) {
+ visitor.visit(this);
+ }
+ }
+
+ static class NotCondition implements Condition {
+ private final Condition condition;
+
+ public NotCondition(Condition condition) {
+ this.condition = condition;
+ }
+
+ public Condition getCondition() {
+ return condition;
+ }
+
+ public void accept(ConditionVisitor visitor) throws RepositoryException {
+ visitor.visit(this);
+ }
+ }
+
+ abstract static class CompoundCondition implements Condition, Iterable<Condition> {
+ private final List<Condition> conditions = new ArrayList<Condition>();
+
+ public CompoundCondition() {
+ super();
+ }
+
+ public CompoundCondition(Condition condition1, Condition condition2) {
+ conditions.add(condition1);
+ conditions.add(condition2);
+ }
+
+ public void addCondition(Condition condition) {
+ conditions.add(condition);
+ }
+
+ public Iterator<Condition> iterator() {
+ return conditions.iterator();
+ }
+ }
+
+ static class AndCondition extends CompoundCondition {
+ public AndCondition(Condition condition1, Condition condition2) {
+ super(condition1, condition2);
+ }
+
+ public void accept(ConditionVisitor visitor) throws RepositoryException {
+ visitor.visit(this);
+ }
+ }
+
+ static class OrCondition extends CompoundCondition {
+ public OrCondition(Condition condition1, Condition condition2) {
+ super(condition1, condition2);
+ }
+
+ public void accept(ConditionVisitor visitor) throws RepositoryException {
+ visitor.visit(this);
+ }
+ }
+}
Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java?rev=1031680&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java Fri Nov 5 17:53:57 2010
@@ -0,0 +1,237 @@
+/*
+ * 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.security.user;
+
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.QueryBuilder.RelationOp;
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.spi.commons.iterator.Iterators;
+import org.apache.jackrabbit.spi.commons.iterator.Predicate;
+import org.apache.jackrabbit.spi.commons.iterator.Predicates;
+import org.apache.jackrabbit.spi.commons.iterator.Transformer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.Node;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
+import java.util.Iterator;
+
+/**
+ * This evaluator for {@link org.apache.jackrabbit.api.security.user.Query}s use XPath
+ * and some minimal client side filtering.
+ */
+public class XPathQueryEvaluator implements XPathQueryBuilder.ConditionVisitor {
+ static final Logger log = LoggerFactory.getLogger(XPathQueryEvaluator.class);
+
+ private final XPathQueryBuilder builder;
+ private final UserManagerImpl userManager;
+ private final QueryManager queryManager;
+ private final StringBuilder xPath = new StringBuilder();
+
+ public XPathQueryEvaluator(XPathQueryBuilder builder, UserManagerImpl userManager, QueryManager queryManager) {
+ this.builder = builder;
+ this.userManager = userManager;
+ this.queryManager = queryManager;
+ }
+
+ public Iterator<Authorizable> eval() throws RepositoryException {
+ xPath.append("//element(*,")
+ .append(builder.getSelector().getNtName())
+ .append(')');
+
+ XPathQueryBuilder.Condition condition = builder.getCondition();
+ if (condition != null) {
+ xPath.append('[');
+ condition.accept(this);
+ xPath.append(']');
+ }
+
+ String sortCol = builder.getSortProperty();
+ if (sortCol != null) {
+ xPath.append(" order by ")
+ .append(sortCol)
+ .append(' ')
+ .append(builder.getSortDirection().getDirection());
+ }
+
+ Query query = queryManager.createQuery(xPath.toString(), Query.XPATH);
+ int count = builder.getMaxCount();
+ if (count == 0) {
+ return Iterators.empty();
+ }
+
+ if (count > 0) {
+ query.setLimit(count);
+ }
+
+ return filter(toAuthorizables(execute(query)), builder.getGroupName(), builder.isDeclaredMembersOnly());
+ }
+
+ //------------------------------------------< ConditionVisitor >---
+
+ public void visit(XPathQueryBuilder.PropertyCondition condition) throws RepositoryException {
+ RelationOp relOp = condition.getOp();
+ if (relOp == RelationOp.EX) {
+ xPath.append(condition.getRelPath());
+ }
+ else {
+ xPath.append(condition.getRelPath())
+ .append(condition.getOp().getOp())
+ .append(format(condition.getValue()));
+ }
+ }
+
+ public void visit(XPathQueryBuilder.ContainsCondition condition) {
+ xPath.append("jcr:contains(")
+ .append(condition.getRelPath())
+ .append(",'")
+ .append(condition.getSearchExpr())
+ .append("')");
+ }
+
+ public void visit(XPathQueryBuilder.ImpersonationCondition condition) {
+ xPath.append("@rep:impersonators='")
+ .append(condition.getName())
+ .append('\'');
+ }
+
+ public void visit(XPathQueryBuilder.NotCondition condition) throws RepositoryException {
+ xPath.append("not(");
+ condition.getCondition().accept(this);
+ xPath.append(')');
+ }
+
+ public void visit(XPathQueryBuilder.AndCondition condition) throws RepositoryException {
+ int count = 0;
+ for (XPathQueryBuilder.Condition c : condition) {
+ xPath.append(count++ > 0 ? " and " : "");
+ c.accept(this);
+ }
+ }
+
+ public void visit(XPathQueryBuilder.OrCondition condition) throws RepositoryException {
+ int pos = xPath.length();
+
+ int count = 0;
+ for (XPathQueryBuilder.Condition c : condition) {
+ xPath.append(count++ > 0 ? " or " : "");
+ c.accept(this);
+ }
+
+ // Surround or clause with parentheses if it contains more than one term
+ if (count > 1) {
+ xPath.insert(pos, '(');
+ xPath.append(')');
+ }
+ }
+
+ //------------------------------------------< private >---
+
+ private static String format(Value value) throws RepositoryException {
+ switch (value.getType()) {
+ case PropertyType.STRING:
+ case PropertyType.BOOLEAN:
+ return '\'' + value.getString() + '\'';
+
+ case PropertyType.LONG:
+ case PropertyType.DOUBLE:
+ return value.getString();
+
+ case PropertyType.DATE:
+ return "xs:dateTime('" + value.getString() + "')";
+
+ default:
+ throw new RepositoryException("Property of type " + PropertyType.nameFromValue(value.getType()) +
+ " not supported");
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Iterator<Node> execute(Query query) throws RepositoryException {
+ return query.execute().getNodes();
+ }
+
+ private Iterator<Authorizable> toAuthorizables(Iterator<Node> nodes) {
+ Transformer<Node, Authorizable> transformer = new Transformer<Node, Authorizable>() {
+ public Authorizable transform(Node node) {
+ try {
+ return userManager.getAuthorizable((NodeImpl) node);
+ } catch (RepositoryException e) {
+ log.warn("Cannot create authorizable from node {}", node);
+ log.debug(e.getMessage(), e);
+ return null;
+ }
+ }
+ };
+
+ return Iterators.transformIterator(nodes, transformer);
+ }
+
+ private Iterator<Authorizable> filter(Iterator<Authorizable> authorizables, String groupName,
+ boolean declaredMembersOnly) throws RepositoryException {
+
+ Predicate<Authorizable> predicate;
+ if (groupName == null) {
+ predicate = Predicates.TRUE();
+ }
+ else {
+ Authorizable groupAuth = userManager.getAuthorizable(groupName);
+ if (groupAuth == null || !groupAuth.isGroup()) {
+ predicate = Predicates.FALSE();
+ }
+ else {
+ final Group group = (Group) groupAuth;
+ if (declaredMembersOnly) {
+ predicate = new Predicate<Authorizable>() {
+ public boolean evaluate(Authorizable authorizable) {
+ try {
+ return authorizable != null && group.isDeclaredMember(authorizable);
+ } catch (RepositoryException e) {
+ log.warn("Cannot determine whether {} is member of group {}", authorizable, group);
+ log.debug(e.getMessage(), e);
+ return false;
+ }
+ }
+ };
+
+ }
+ else {
+ predicate = new Predicate<Authorizable>() {
+ public boolean evaluate(Authorizable authorizable) {
+ try {
+ return authorizable != null && group.isMember(authorizable);
+ } catch (RepositoryException e) {
+ log.warn("Cannot determine whether {} is member of group {}", authorizable, group);
+ log.debug(e.getMessage(), e);
+ return false;
+ }
+ }
+ };
+ }
+ }
+ }
+
+ return Iterators.filterIterator(authorizables, predicate);
+ }
+
+}
Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/TestAll.java?rev=1031680&r1=1031679&r2=1031680&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/TestAll.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/TestAll.java Fri Nov 5 17:53:57 2010
@@ -41,6 +41,7 @@ public class TestAll extends TestCase {
suite.addTestSuite(GroupTest.class);
suite.addTestSuite(NestedGroupTest.class);
suite.addTestSuite(ImpersonationTest.class);
+ suite.addTestSuite(UserManagerSearchTest.class);
return suite;
}
Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java?rev=1031680&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java Fri Nov 5 17:53:57 2010
@@ -0,0 +1,628 @@
+/*
+ * 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.api.security.user;
+
+import org.apache.jackrabbit.api.security.user.QueryBuilder.RelationOp;
+import org.apache.jackrabbit.api.security.user.QueryBuilder.Selector;
+import org.apache.jackrabbit.api.security.user.QueryBuilder.Direction;
+import org.apache.jackrabbit.spi.commons.iterator.Iterators;
+import org.apache.jackrabbit.spi.commons.iterator.Predicate;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+public class UserManagerSearchTest extends AbstractUserTest {
+
+ // users
+ private User blackWidow;
+ private User gardenSpider;
+ private User jumpingSpider;
+ private User ant;
+ private User bee;
+ private User fly;
+ private User jackrabbit;
+ private User deer;
+ private User opposum;
+ private User kangaroo;
+ private User elephant;
+ private User lemur;
+ private User gibbon;
+ private User crocodile;
+ private User turtle;
+ private User lizard;
+ private User kestrel;
+ private User goose;
+ private User pelican;
+ private User dove;
+ private User salamander;
+ private User goldenToad;
+ private User poisonDartFrog;
+
+ private final Set<User> users = new HashSet<User>();
+
+ // groups
+ private Group animals;
+ private Group invertebrates;
+ private Group arachnids;
+ private Group insects;
+ private Group vertebrates;
+ private Group mammals;
+ private Group apes;
+ private Group reptiles;
+ private Group birds;
+ private Group amphibians;
+
+ private final Set<Group> groups = new HashSet<Group>();
+ private final Set<Authorizable> authorizables = new HashSet<Authorizable>();
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // Create a zoo. Please excuse my ignorance in zoology ;-)
+ animals = createGroup("animals");
+ invertebrates = createGroup("invertebrates");
+ arachnids = createGroup("arachnids");
+ insects = createGroup("insects");
+ vertebrates = createGroup("vertebrates");
+ mammals = createGroup("mammals");
+ apes = createGroup("apes");
+ reptiles = createGroup("reptiles");
+ birds = createGroup("birds");
+ amphibians = createGroup("amphibians");
+
+ animals.addMember(invertebrates);
+ animals.addMember(vertebrates);
+ invertebrates.addMember(arachnids);
+ invertebrates.addMember(insects);
+ vertebrates.addMember(mammals);
+ vertebrates.addMember(reptiles);
+ vertebrates.addMember(birds);
+ vertebrates.addMember(amphibians);
+ mammals.addMember(apes);
+
+ blackWidow = createUser("black widow", "flies", 2, false);
+ gardenSpider = createUser("garden spider", "flies", 2, false);
+ jumpingSpider = createUser("jumping spider", "insects", 1, false);
+ addMembers(arachnids, blackWidow, gardenSpider, jumpingSpider);
+
+ ant = createUser("ant", "leaves", 0.5, false);
+ bee = createUser("bee", "honey", 2.5, true);
+ fly = createUser("fly", "dirt", 1.3, false);
+ addMembers(insects, ant, bee, fly);
+
+ jackrabbit = createUser("jackrabbit", "carrots", 2500, true);
+ deer = createUser("deer", "leaves", 120000, true);
+ opposum = createUser("opposum", "fruit", 1200, true);
+ kangaroo = createUser("kangaroo", "grass", 80000, true);
+ elephant = createUser("elephant", "leaves", 5000000, true);
+ addMembers(mammals, jackrabbit, deer, opposum, kangaroo, elephant);
+
+ lemur = createUser("lemur", "nectar", 1100, true);
+ gibbon = createUser("gibbon", "meat", 20000, true);
+ addMembers(apes, lemur, gibbon);
+
+ crocodile = createUser("crocodile", "meat", 80000, false);
+ turtle = createUser("turtle", "leaves", 10000, true);
+ lizard = createUser("lizard", "leaves", 2000, false);
+ addMembers(reptiles, crocodile, turtle, lizard);
+
+ kestrel = createUser("kestrel", "mice", 2000, false);
+ goose = createUser("goose", "snails", 10000, true);
+ pelican = createUser("pelican", "fish", 15000, true);
+ dove = createUser("dove", "insects", 1600, false);
+ addMembers(birds, kestrel, goose, pelican, dove);
+
+ salamander = createUser("salamander", "insects", 800, true);
+ goldenToad = createUser("golden toad", "insects", 700, false);
+ poisonDartFrog = createUser("poison dart frog", "insects", 40, false);
+ addMembers(amphibians, salamander, goldenToad, poisonDartFrog);
+
+ setProperty("canFly", vf.createValue(true), bee, fly, kestrel, goose, pelican, dove);
+ setProperty("poisonous",vf.createValue(true), blackWidow, bee, poisonDartFrog );
+ setProperty("poisonous", vf.createValue(false), turtle, lemur);
+ setProperty("hasWings", vf.createValue(false), blackWidow, gardenSpider, jumpingSpider, ant,
+ jackrabbit, deer, opposum, kangaroo, elephant, lemur, gibbon, crocodile, turtle, lizard,
+ salamander, goldenToad, poisonDartFrog);
+ setProperty("color", vf.createValue("black"), blackWidow, gardenSpider, ant, fly, lizard, salamander);
+ setProperty("color", vf.createValue("white"), opposum, goose, pelican, dove);
+ setProperty("color", vf.createValue("gold"), goldenToad);
+ setProperty("numberOfLegs", vf.createValue(2), kangaroo, gibbon, kestrel, goose, dove);
+ setProperty("numberOfLegs", vf.createValue(4), jackrabbit, deer, opposum, elephant, lemur, crocodile,
+ turtle, lizard, salamander, goldenToad, poisonDartFrog);
+ setProperty("numberOfLegs", vf.createValue(6), ant, bee, fly);
+ setProperty("numberOfLegs", vf.createValue(8), blackWidow, gardenSpider, jumpingSpider);
+
+ elephant.getImpersonation().grantImpersonation(jackrabbit.getPrincipal());
+
+ authorizables.addAll(users);
+ authorizables.addAll(groups);
+
+ if (!userMgr.isAutoSave()) {
+ superuser.save();
+ }
+
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ for (Authorizable authorizable : authorizables) {
+ authorizable.remove();
+ }
+ authorizables.clear();
+ groups.clear();
+ users.clear();
+
+ if (!userMgr.isAutoSave()) {
+ superuser.save();
+ }
+
+ super.tearDown();
+ }
+
+ public void testAny() throws RepositoryException {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) { /* any */ }
+ });
+
+ assertContainsAll(result, authorizables.iterator());
+ }
+
+ public void testSelector() throws RepositoryException {
+ for (final Selector s : Selector.values()) {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setSelector(s);
+ }
+ });
+
+ switch (s) {
+ case AUTHORIZABLE:
+ assertContainsAll(result, authorizables.iterator());
+ break;
+
+ case USER:
+ assertContainsAll(result, users.iterator());
+ break;
+
+ case GROUP:
+ assertContainsAll(result, groups.iterator());
+ break;
+
+ default:
+ fail("Fall through in switch");
+ }
+
+ }
+ }
+
+ public void testDirectScope() throws RepositoryException {
+ Group[] groups = new Group[]{mammals, vertebrates, apes};
+ for (final Group g : groups) {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ try {
+ builder.setScope(g.getID(), true);
+ } catch (RepositoryException e) {
+ fail(e.getMessage());
+ }
+ }
+ });
+
+ Iterator<Authorizable> members = g.getDeclaredMembers();
+ assertSameElements(result, members);
+ }
+ }
+
+ public void testIndirectScope() throws RepositoryException {
+ Group[] groups = new Group[]{mammals, vertebrates, apes};
+ for (final Group g : groups) {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ try {
+ builder.setScope(g.getID(), false);
+ } catch (RepositoryException e) {
+ fail(e.getMessage());
+ }
+ }
+ });
+
+ Iterator<Authorizable> members = g.getMembers();
+ assertSameElements(result, members);
+ }
+ }
+
+ public void testFindUsersInGroup() throws RepositoryException {
+ Group[] groups = new Group[]{mammals, vertebrates, apes};
+ for (final Group g : groups) {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ try {
+ builder.setSelector(Selector.USER);
+ builder.setScope(g.getID(), false);
+ } catch (RepositoryException e) {
+ fail(e.getMessage());
+ }
+ }
+ });
+
+ Iterator<Authorizable> members = g.getMembers();
+ Iterator<Authorizable> users = Iterators.filterIterator(members, new Predicate<Authorizable>() {
+ public boolean evaluate(Authorizable authorizable) {
+ return !authorizable.isGroup();
+ }
+ });
+ assertSameElements(result, users);
+ }
+ }
+
+ public void testFindGroupsInGroup() throws RepositoryException {
+ Group[] groups = new Group[]{mammals, vertebrates, apes};
+ for (final Group g : groups) {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ try {
+ builder.setSelector(Selector.GROUP);
+ builder.setScope(g.getID(), true);
+ } catch (RepositoryException e) {
+ fail(e.getMessage());
+ }
+ }
+ });
+
+ Iterator<Authorizable> members = g.getDeclaredMembers();
+ Iterator<Authorizable> users = Iterators.filterIterator(members, new Predicate<Authorizable>() {
+ public boolean evaluate(Authorizable authorizable) {
+ return authorizable.isGroup();
+ }
+ });
+ assertSameElements(result, users);
+ }
+ }
+
+ public void testFindProperty1() throws RepositoryException {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setCondition(builder.
+ property("@canFly", RelationOp.EQ, vf.createValue(true)));
+ }
+ });
+
+ Iterator<User> expected = Iterators.filterIterator(users.iterator(), new Predicate<User>() {
+ public boolean evaluate(User user) {
+ try {
+ Value[] canFly = user.getProperty("canFly");
+ return canFly != null && canFly.length == 1 && canFly[0].getBoolean();
+ } catch (RepositoryException e) {
+ fail(e.getMessage());
+ }
+ return false;
+ }
+ });
+
+ assertTrue(result.hasNext());
+ assertSameElements(result, expected);
+ }
+
+ public void testFindProperty2() throws RepositoryException {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setCondition(builder.
+ property("profile/@weight", RelationOp.GT, vf.createValue(2000.0)));
+ }
+ });
+
+ Iterator<User> expected = Iterators.filterIterator(users.iterator(), new Predicate<User>() {
+ public boolean evaluate(User user) {
+ try {
+ Value[] weight = user.getProperty("profile/weight");
+ return weight != null && weight.length == 1 && weight[0].getDouble() > 2000.0;
+ } catch (RepositoryException e) {
+ fail(e.getMessage());
+ }
+ return false;
+ }
+ });
+
+ assertTrue(result.hasNext());
+ assertSameElements(result, expected);
+ }
+
+ public void testFindProperty3() throws RepositoryException {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setCondition(builder.
+ property("@numberOfLegs", RelationOp.EQ, vf.createValue(8)));
+ }
+ });
+
+ Iterator<User> expected = Iterators.filterIterator(users.iterator(), new Predicate<User>() {
+ public boolean evaluate(User user) {
+ try {
+ Value[] numberOfLegs = user.getProperty("numberOfLegs");
+ return numberOfLegs != null && numberOfLegs.length == 1 && numberOfLegs[0].getLong() == 8;
+ } catch (RepositoryException e) {
+ fail(e.getMessage());
+ }
+ return false;
+ }
+ });
+
+ assertTrue(result.hasNext());
+ assertSameElements(result, expected);
+ }
+
+ public void testPropertyExistence() throws RepositoryException {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setCondition(builder.
+ property("@poisonous", RelationOp.EX, null));
+ }
+ });
+
+ Iterator<User> expected = Iterators.filterIterator(users.iterator(), new Predicate<User>() {
+ public boolean evaluate(User user) {
+ try {
+ Value[] poisonous = user.getProperty("poisonous");
+ return poisonous != null && poisonous.length == 1;
+ } catch (RepositoryException e) {
+ fail(e.getMessage());
+ }
+ return false;
+ }
+ });
+
+ assertTrue(result.hasNext());
+ assertSameElements(result, expected);
+ }
+
+ public void testContains1() throws RepositoryException {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setCondition(builder.
+ contains(".", "gold"));
+ }
+ });
+
+ Iterator<User> expected = Iterators.singleton(goldenToad);
+ assertTrue(result.hasNext());
+ assertSameElements(result, expected);
+ }
+
+ public void testContains2() throws RepositoryException {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setCondition(builder.
+ contains("@color", "gold"));
+ }
+ });
+
+ Iterator<User> expected = Iterators.singleton(goldenToad);
+ assertTrue(result.hasNext());
+ assertSameElements(result, expected);
+ }
+
+ public void testContains3() throws RepositoryException {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setCondition(builder.
+ contains("profile/.", "grass"));
+ }
+ });
+
+ Iterator<User> expected = Iterators.singleton(kangaroo);
+ assertTrue(result.hasNext());
+ assertSameElements(result, expected);
+ }
+
+ public void testContains4() throws RepositoryException {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setCondition(builder.
+ contains("profile/@food", "grass"));
+ }
+ });
+
+ Iterator<User> expected = Iterators.singleton(kangaroo);
+ assertTrue(result.hasNext());
+ assertSameElements(result, expected);
+ }
+
+ public void testCondition1() throws RepositoryException {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setCondition(builder.
+ and(builder.
+ property("profile/@cute", RelationOp.EQ, vf.createValue(true)), builder.
+ not(builder.
+ property("@color", RelationOp.EQ, vf.createValue("black")))));
+ }
+ });
+
+ Iterator<User> expected = Iterators.filterIterator(users.iterator(), new Predicate<User>() {
+ public boolean evaluate(User user) {
+ try {
+ Value[] cute = user.getProperty("profile/cute");
+ Value[] black = user.getProperty("color");
+ return cute != null && cute.length == 1 && cute[0].getBoolean() &&
+ !(black != null && black.length == 1 && black[0].getString().equals("black"));
+ } catch (RepositoryException e) {
+ fail(e.getMessage());
+ }
+ return false;
+ }
+ });
+
+ assertTrue(result.hasNext());
+ assertSameElements(result, expected);
+ }
+
+ public void testCondition2() throws RepositoryException {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setCondition(builder.
+ or(builder.
+ property("profile/@food", RelationOp.EQ, vf.createValue("mice")), builder.
+ property("profile/@food", RelationOp.EQ, vf.createValue("nectar"))));
+ }
+ });
+
+ Iterator<User> expected = Iterators.filterIterator(users.iterator(), new Predicate<User>() {
+ public boolean evaluate(User user) {
+ try {
+ Value[] food = user.getProperty("profile/food");
+ return food != null && food.length == 1 &&
+ (food[0].getString().equals("mice") || food[0].getString().equals("nectar"));
+ } catch (RepositoryException e) {
+ fail(e.getMessage());
+ }
+ return false;
+ }
+ });
+
+ assertTrue(result.hasNext());
+ assertSameElements(result, expected);
+ }
+
+ public void testImpersonation() throws RepositoryException {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setCondition(builder.
+ impersonates("jackrabbit"));
+ }
+ });
+
+ Iterator<User> expected = Iterators.singleton(elephant);
+ assertTrue(result.hasNext());
+ assertSameElements(result, expected);
+ }
+
+ public void testSortOrder1() throws RepositoryException {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setCondition(
+ builder.property("@color", RelationOp.EX, null));
+ builder.setSortOrder("@color", Direction.DESCENDING);
+ }
+ });
+
+ assertTrue(result.hasNext());
+ String prev = null;
+ while (result.hasNext()) {
+ Authorizable authorizable = result.next();
+ Value[] color = authorizable.getProperty("color");
+ assertNotNull(color);
+ assertEquals(1, color.length);
+ assertTrue(prev == null || prev.compareTo(color[0].getString()) >= 0);
+ prev = color[0].getString();
+ }
+ }
+
+ public void testSortOrder2() throws RepositoryException {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setCondition(
+ builder.property("profile/@weight", RelationOp.EX, null));
+ builder.setSortOrder("profile/@weight", Direction.ASCENDING);
+ }
+ });
+
+ assertTrue(result.hasNext());
+ double prev = Double.MIN_VALUE;
+ while (result.hasNext()) {
+ Authorizable authorizable = result.next();
+ Value[] weight = authorizable.getProperty("profile/weight");
+ assertNotNull(weight);
+ assertEquals(1, weight.length);
+ assertTrue(prev <= weight[0].getDouble());
+ prev = weight[0].getDouble();
+ }
+ }
+
+ //------------------------------------------< private >---
+
+ private static void addMembers(Group group, Authorizable... authorizables) throws RepositoryException {
+ for (Authorizable authorizable : authorizables) {
+ group.addMember(authorizable);
+ }
+ }
+
+ private Group createGroup(String name) throws RepositoryException {
+ Group group = userMgr.createGroup(name);
+ groups.add(group);
+ return group;
+ }
+
+ private User createUser(String name, String food, double weight, boolean cute) throws RepositoryException {
+ User user = userMgr.createUser(name, "");
+ user.setProperty("profile/food", vf.createValue(food));
+ user.setProperty("profile/weight", vf.createValue(weight));
+ user.setProperty("profile/cute", vf.createValue(cute));
+ users.add(user);
+ return user;
+ }
+
+ private static void setProperty(String relPath, Value value, Authorizable... authorizables) throws RepositoryException {
+ for (Authorizable authorizable : authorizables) {
+ authorizable.setProperty(relPath, value);
+ }
+ }
+
+ private static <T> void assertContainsAll(Iterator<? extends T> it1, Iterator<? extends T> it2) {
+ Set<? extends T> set1 = toSet(it1);
+ Set<? extends T> set2 = toSet(it2);
+ set2.removeAll(set1);
+ if (!set2.isEmpty()) {
+ fail("Missing elements in query result: " + set2);
+ }
+ }
+
+ private static <T> void assertSameElements(Iterator<? extends T> it1, Iterator<? extends T> it2) {
+ Set<? extends T> set1 = toSet(it1);
+ Set<? extends T> set2 = toSet(it2);
+
+ Set<? super T> missing = new HashSet<T>();
+ missing.addAll(set2);
+ missing.removeAll(set1);
+
+ Set<? super T> excess = new HashSet<T>();
+ excess.addAll(set1);
+ excess.removeAll(set2);
+
+ if (!missing.isEmpty()) {
+ fail("Missing elements in query result: " + missing);
+ }
+
+ if (!excess.isEmpty()) {
+ fail("Excess elements in query result: " + excess);
+ }
+ }
+
+ private static <T> Set<T> toSet(Iterator<T> it) {
+ Set<T> set = new HashSet<T>();
+ while (it.hasNext()) {
+ set.add(it.next());
+ }
+ return set;
+ }
+
+
+}
Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java?rev=1031680&r1=1031679&r2=1031680&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java Fri Nov 5 17:53:57 2010
@@ -19,12 +19,7 @@ package org.apache.jackrabbit.core.secur
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.principal.PrincipalIterator;
import org.apache.jackrabbit.api.security.principal.PrincipalManager;
-import org.apache.jackrabbit.api.security.user.Authorizable;
-import org.apache.jackrabbit.api.security.user.AuthorizableExistsException;
-import org.apache.jackrabbit.api.security.user.Group;
-import org.apache.jackrabbit.api.security.user.Impersonation;
-import org.apache.jackrabbit.api.security.user.User;
-import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.api.security.user.*;
import org.apache.jackrabbit.commons.xml.ParsingContentHandler;
import org.apache.jackrabbit.core.NodeImpl;
import org.apache.jackrabbit.core.SessionImpl;
@@ -41,29 +36,7 @@ import org.apache.jackrabbit.test.NotExe
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
-import javax.jcr.AccessDeniedException;
-import javax.jcr.Credentials;
-import javax.jcr.ImportUUIDBehavior;
-import javax.jcr.InvalidItemStateException;
-import javax.jcr.InvalidSerializedDataException;
-import javax.jcr.Item;
-import javax.jcr.ItemExistsException;
-import javax.jcr.ItemNotFoundException;
-import javax.jcr.LoginException;
-import javax.jcr.NamespaceException;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.PathNotFoundException;
-import javax.jcr.Property;
-import javax.jcr.PropertyType;
-import javax.jcr.ReferentialIntegrityException;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.UnsupportedRepositoryOperationException;
-import javax.jcr.Value;
-import javax.jcr.ValueFactory;
-import javax.jcr.Workspace;
+import javax.jcr.*;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
@@ -71,19 +44,13 @@ import javax.jcr.retention.RetentionMana
import javax.jcr.security.AccessControlManager;
import javax.jcr.version.VersionException;
import javax.security.auth.Subject;
-
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.AccessControlException;
import java.security.Principal;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.UUID;
+import java.util.*;
/**
* <code>UserImporterTest</code>...
@@ -1405,6 +1372,10 @@ public class UserImporterTest extends Ab
return null;
}
+ public Iterator<Authorizable> findAuthorizables(Query query) throws RepositoryException {
+ return null;
+ }
+
public User createUser(String userID, String password) throws AuthorizableExistsException, RepositoryException {
return null;
}
Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/iterator/Predicates.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/iterator/Predicates.java?rev=1031680&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/iterator/Predicates.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/iterator/Predicates.java Fri Nov 5 17:53:57 2010
@@ -0,0 +1,67 @@
+/*
+ * 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.spi.commons.iterator;
+
+/**
+ * Utility class containing pre defined {@link Predicate}s
+ */
+public final class Predicates {
+
+ /**
+ * A predicate which is always true
+ */
+ public static final Predicate TRUE = new Predicate() {
+ public boolean evaluate(Object arg) {
+ return true;
+ }
+ };
+
+ /**
+ * A predicate which is always false
+ */
+ public static final Predicate FALSE = new Predicate() {
+ public boolean evaluate(Object arg) {
+ return true;
+ }
+ };
+
+ private Predicates() {
+ // no instances allowed
+ }
+
+ /**
+ * A predicate which is always true
+ * @param <T>
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> Predicate<T> TRUE() {
+ return TRUE;
+ }
+
+ /**
+ * A predicate which is always false
+ * @param <T>
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> Predicate<T> FALSE() {
+ return FALSE;
+ }
+
+}