Date: Wed Aug 15 13:24:21 2012
New Revision: 1373392
URL:
http://svn.apache.org/viewvc?**rev=1373392&view=rev<http://svn.apache.org/viewvc?rev=1373392&view=rev>
Log:
OAK-50 : Implement User Management (WIP)
Added:
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/Condition.java
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/ConditionVisitor.**java
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/RelationOp.java
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/ResultIterator.java
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/XPathQueryBuilder.**java
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/**XPathQueryEvaluator.java
Modified:
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/UserManagerImpl.java
Modified:
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/UserManagerImpl.java
URL:
http://svn.apache.org/viewvc/**jackrabbit/oak/trunk/oak-jcr/**
src/main/java/org/apache/**jackrabbit/oak/jcr/security/**
user/UserManagerImpl.java?rev=**1373392&r1=1373391&r2=1373392&**
view=diff<http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerImpl.java?rev=1373392&r1=1373391&r2=1373392&view=diff>
==============================**==============================**
==================
---
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/UserManagerImpl.java
(original)
+++
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/UserManagerImpl.java
Wed Aug 15 13:24:21 2012
@@ -41,6 +41,8 @@ import
org.apache.jackrabbit.oak.api.**Pro<http://org.apache.jackrabbit.oak.api.Pro>
import org.apache.jackrabbit.oak.api.**Root;
import org.apache.jackrabbit.oak.api.**Tree;
import org.apache.jackrabbit.oak.jcr.**SessionDelegate;
+import
org.apache.jackrabbit.oak.jcr.**security.user.query.**XPathQueryBuilder;
+import
org.apache.jackrabbit.oak.jcr.**security.user.query.**
XPathQueryEvaluator;
import org.apache.jackrabbit.oak.jcr.**value.ValueConverter;
import org.apache.jackrabbit.oak.**security.user.**UserProviderImpl;
import org.apache.jackrabbit.oak.spi.**security.principal.**
EveryonePrincipal;
@@ -142,8 +144,9 @@ public class UserManagerImpl implements
@Override
public Iterator<Authorizable> findAuthorizables(Query query)
throws
RepositoryException {
- // TODO : execute the specified query
- throw new UnsupportedOperationException(**"Not Implemented");
+ XPathQueryBuilder builder = new XPathQueryBuilder();
+ query.build(builder);
+ return new XPathQueryEvaluator(builder, this,
sessionDelegate.**getQueryManager(),
sessionDelegate.**getNamePathMapper()).eval();
}
@Override
Added:
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/Condition.java
URL:
http://svn.apache.org/viewvc/**jackrabbit/oak/trunk/oak-jcr/**
src/main/java/org/apache/**jackrabbit/oak/jcr/security/**
user/query/Condition.java?rev=**1373392&view=auto<http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/query/Condition.java?rev=1373392&view=auto>
==============================**==============================**
==================
---
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/Condition.java
(added)
+++
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/Condition.java
Wed Aug 15 13:24:21 2012
@@ -0,0 +1,190 @@
+/*
+ * 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<http://www.apache.org/licenses/LICENSE-2.0>
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.jcr.**security.user.query;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+
+interface Condition {
+
+ void accept(ConditionVisitor visitor) throws RepositoryException;
+
+ //----------------------------**--------------< Condition
implementations>---
+
+ static class Node implements Condition {
+ private final String pattern;
+
+ public Node(String pattern) {
+ this.pattern = pattern;
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ public void accept(ConditionVisitor visitor) throws
RepositoryException {
+ visitor.visit(this);
+ }
+ }
+
+ static class Property implements Condition {
+ private final String relPath;
+ private final RelationOp op;
+ private final Value value;
+ private final String pattern;
+
+ public Property(String relPath, RelationOp op, Value value) {
+ this.relPath = relPath;
+ this.op = op;
+ this.value = value;
+ pattern = null;
+ }
+
+ public Property(String relPath, RelationOp op, String pattern) {
+ this.relPath = relPath;
+ this.op = op;
+ value = null;
+ this.pattern = pattern;
+ }
+
+ public Property(String relPath, RelationOp op) {
+ this.relPath = relPath;
+ this.op = op;
+ value = null;
+ pattern = null;
+ }
+
+ public String getRelPath() {
+ return relPath;
+ }
+
+ public RelationOp getOp() {
+ return op;
+ }
+
+ public Value getValue() {
+ return value;
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ public void accept(ConditionVisitor visitor) throws
RepositoryException {
+ visitor.visit(this);
+ }
+ }
+
+ static class Contains implements Condition {
+ private final String relPath;
+ private final String searchExpr;
+
+ public Contains(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 Impersonation implements Condition {
+ private final String name;
+
+ public Impersonation(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void accept(ConditionVisitor visitor) {
+ visitor.visit(this);
+ }
+ }
+
+ static class Not implements Condition {
+ private final Condition condition;
+
+ public Not(Condition condition) {
+ this.condition = condition;
+ }
+
+ public Condition getCondition() {
+ return condition;
+ }
+
+ public void accept(ConditionVisitor visitor) throws
RepositoryException {
+ visitor.visit(this);
+ }
+ }
+
+ abstract static class Compound implements Condition,
Iterable<Condition> {
+ private final List<Condition> conditions = new
ArrayList<Condition>();
+
+ public Compound() {
+ super();
+ }
+
+ public Compound(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 And extends Compound {
+ public And(Condition condition1, Condition condition2) {
+ super(condition1, condition2);
+ }
+
+ public void accept(ConditionVisitor visitor) throws
RepositoryException {
+ visitor.visit(this);
+ }
+ }
+
+ static class Or extends Compound {
+ public Or(Condition condition1, Condition condition2) {
+ super(condition1, condition2);
+ }
+
+ public void accept(ConditionVisitor visitor) throws
RepositoryException {
+ visitor.visit(this);
+ }
+ }
+}
\ No newline at end of file
Added:
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/ConditionVisitor.**java
URL:
http://svn.apache.org/viewvc/**jackrabbit/oak/trunk/oak-jcr/**
src/main/java/org/apache/**jackrabbit/oak/jcr/security/**
user/query/ConditionVisitor.**java?rev=1373392&view=auto<http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/query/ConditionVisitor.java?rev=1373392&view=auto>
==============================**==============================**
==================
---
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/ConditionVisitor.**java
(added)
+++
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/ConditionVisitor.**java
Wed Aug 15 13:24:21 2012
@@ -0,0 +1,36 @@
+/*
+ * 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<http://www.apache.org/licenses/LICENSE-2.0>
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.jcr.**security.user.query;
+
+import javax.jcr.RepositoryException;
+
+interface ConditionVisitor {
+
+ void visit(Condition.Node node) throws RepositoryException;
+
+ void visit(Condition.Property condition) throws RepositoryException;
+
+ void visit(Condition.Contains condition);
+
+ void visit(Condition.Impersonation condition);
+
+ void visit(Condition.Not condition) throws RepositoryException;
+
+ void visit(Condition.And condition) throws RepositoryException;
+
+ void visit(Condition.Or condition) throws RepositoryException;
+}
\ No newline at end of file
Added:
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/RelationOp.java
URL:
http://svn.apache.org/viewvc/**jackrabbit/oak/trunk/oak-jcr/**
src/main/java/org/apache/**jackrabbit/oak/jcr/security/**
user/query/RelationOp.java?**rev=1373392&view=auto<http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/query/RelationOp.java?rev=1373392&view=auto>
==============================**==============================**
==================
---
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/RelationOp.java
(added)
+++
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/RelationOp.java
Wed Aug 15 13:24:21 2012
@@ -0,0 +1,28 @@
+package org.apache.jackrabbit.oak.jcr.**security.user.query;
+
+/**
+ * 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(""),
+ LIKE("like");
+
+ private final String op;
+
+ RelationOp(String op) {
+ this.op = op;
+ }
+
+ String getOp() {
+ return op;
+ }
+}
Added:
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/ResultIterator.java
URL:
http://svn.apache.org/viewvc/**jackrabbit/oak/trunk/oak-jcr/**
src/main/java/org/apache/**jackrabbit/oak/jcr/security/**
user/query/ResultIterator.**java?rev=1373392&view=auto<http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/query/ResultIterator.java?rev=1373392&view=auto>
==============================**==============================**
==================
---
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/ResultIterator.java
(added)
+++
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/ResultIterator.java
Wed Aug 15 13:24:21 2012
@@ -0,0 +1,120 @@
+/*
+ * 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<http://www.apache.org/licenses/LICENSE-2.0>
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.jcr.**security.user.query;
+
+import java.util.Iterator;
+import java.util.**NoSuchElementException;
+
+/**
+ * Implements a query result iterator which only returns a maximum
number
of
+ * element from an underlying iterator starting at a given offset.
+ *
+ * @param<T> element type of the query results
+ *
+ * TODO move to query-commons ?
+ */
+public class ResultIterator<T> implements Iterator<T> {
+
+ public final static int OFFSET_NONE = 0;
+ public final static int MAX_ALL = -1;
+
+ private final Iterator<T> iterator;
+ private final long offset;
+ private final long max;
+ private int pos;
+ private T next;
+
+ /**
+ * Create a new {@code ResultIterator} with a given offset and
maximum
+ *
+ * @param offset Offset to start iteration at. Must be non negative
+ * @param max Maximum elements this iterator should return.
+ * Set to {@link #MAX_ALL} for all results.
+ * @param iterator the underlying iterator
+ * @throws IllegalArgumentException if offset is negative
+ */
+ private ResultIterator(long offset, long max, Iterator<T>
iterator) {
+ if (offset< OFFSET_NONE) {
+ throw new IllegalArgumentException("**Offset must not be
negative");
+ }
+ this.iterator = iterator;
+ this.offset = offset;
+ this.max = max;
+ }
+
+ /**
+ * Returns an iterator respecting the specified {@code offset} and
{@code max}.
+ *
+ * @param offset offset to start iteration at. Must be non
negative
+ * @param max maximum elements this iterator should return. Set
to
+ * {@link #MAX_ALL} for all
+ * @param iterator the underlying iterator
+ * @param<T> element type
+ * @return an iterator which only returns the elements in the given
bounds
+ */
+ public static<T> Iterator<T> create(long offset, long max,
Iterator<T> iterator) {
+ if (offset == OFFSET_NONE&& max == MAX_ALL) {
+ // no constraints on offset nor max -> return the original
iterator.
+ return iterator;
+ } else {
+ return new ResultIterator<T>(offset, max, iterator);
+ }
+ }
+
+ //----------------------------**------------------------------**-<
Iterator>---
+ @Override
+ public boolean hasNext() {
+ if (next == null) {
+ fetchNext();
+ }
+ return next != null;
+ }
+
+ @Override
+ public T next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ return consumeNext();
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException(**);
+ }
+
+ //----------------------------**------------------------------**--<
private>---
+
+ private void fetchNext() {
+ for (; pos< offset&& iterator.hasNext(); pos++) {
+ next = iterator.next();
+ }
+
+ if (pos< offset || !iterator.hasNext() || max>= 0&& pos -
offset + 1> max) {
+ next = null;
+ } else {
+ next = iterator.next();
+ pos++;
+ }
+ }
+
+ private T consumeNext() {
+ T element = next;
+ next = null;
+ return element;
+ }
+}
\ No newline at end of file
Added:
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/XPathQueryBuilder.**java
URL:
http://svn.apache.org/viewvc/**jackrabbit/oak/trunk/oak-jcr/**
src/main/java/org/apache/**jackrabbit/oak/jcr/security/**
user/query/XPathQueryBuilder.**java?rev=1373392&view=auto<http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/query/XPathQueryBuilder.java?rev=1373392&view=auto>
==============================**==============================**
==================
---
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/XPathQueryBuilder.**java
(added)
+++
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/XPathQueryBuilder.**java
Wed Aug 15 13:24:21 2012
@@ -0,0 +1,195 @@
+/*
+ * 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<http://www.apache.org/licenses/LICENSE-2.0>
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.jcr.**security.user.query;
+
+import javax.jcr.Value;
+
+import org.apache.jackrabbit.api.**security.user.Authorizable;
+import org.apache.jackrabbit.api.**security.user.QueryBuilder;
+
+public class XPathQueryBuilder implements QueryBuilder<Condition> {
+
+ private Class<? extends Authorizable> selector =
Authorizable.class;
+ private String groupName;
+ private boolean declaredMembersOnly;
+ private Condition condition;
+ private String sortProperty;
+ private Direction sortDirection = Direction.ASCENDING;
+ private boolean sortIgnoreCase;
+ private Value bound;
+ private long offset;
+ private long maxCount = -1;
+
+ //----------------------------**---------------------------<
QueryBuilder>---
+ @Override
+ public void setSelector(Class<? extends Authorizable> selector) {
+ this.selector = selector;
+ }
+
+ @Override
+ public void setScope(String groupName, boolean declaredOnly) {
+ this.groupName = groupName;
+ declaredMembersOnly = declaredOnly;
+ }
+
+ @Override
+ public void setCondition(Condition condition) {
+ this.condition = condition;
+ }
+
+ @Override
+ public void setSortOrder(String propertyName, Direction direction,
boolean ignoreCase) {
+ sortProperty = propertyName;
+ sortDirection = direction;
+ sortIgnoreCase = ignoreCase;
+ }
+
+ @Override
+ public void setSortOrder(String propertyName, Direction direction) {
+ setSortOrder(propertyName, direction, false);
+ }
+
+ @Override
+ public void setLimit(Value bound, long maxCount) {
+ offset = 0; // Unset any previously set offset
+ this.bound = bound;
+ this.maxCount = maxCount;
+ }
+
+ @Override
+ public void setLimit(long offset, long maxCount) {
+ bound = null; // Unset any previously set bound
+ this.offset = offset;
+ this.maxCount = maxCount;
+ }
+
+ @Override
+ public Condition nameMatches(String pattern) {
+ return new Condition.Node(pattern);
+ }
+
+ @Override
+ public Condition neq(String relPath, Value value) {
+ return new Condition.Property(relPath, RelationOp.NE, value);
+ }
+
+ @Override
+ public Condition eq(String relPath, Value value) {
+ return new Condition.Property(relPath, RelationOp.EQ, value);
+ }
+
+ @Override
+ public Condition lt(String relPath, Value value) {
+ return new Condition.Property(relPath, RelationOp.LT, value);
+ }
+
+ @Override
+ public Condition le(String relPath, Value value) {
+ return new Condition.Property(relPath, RelationOp.LE, value);
+ }
+
+ @Override
+ public Condition gt(String relPath, Value value) {
+ return new Condition.Property(relPath, RelationOp.GT, value);
+ }
+
+ @Override
+ public Condition ge(String relPath, Value value) {
+ return new Condition.Property(relPath, RelationOp.GE, value);
+ }
+
+ @Override
+ public Condition exists(String relPath) {
+ return new Condition.Property(relPath, RelationOp.EX);
+ }
+
+ @Override
+ public Condition like(String relPath, String pattern) {
+ return new Condition.Property(relPath, RelationOp.LIKE,
pattern);
+ }
+
+ @Override
+ public Condition contains(String relPath, String searchExpr) {
+ return new Condition.Contains(relPath, searchExpr);
+ }
+
+ @Override
+ public Condition impersonates(String name) {
+ return new Condition.Impersonation(name);
+ }
+
+ @Override
+ public Condition not(Condition condition) {
+ return new Condition.Not(condition);
+ }
+
+ @Override
+ public Condition and(Condition condition1, Condition condition2) {
+ return new Condition.And(condition1, condition2);
+ }
+
+ @Override
+ public Condition or(Condition condition1, Condition condition2) {
+ return new Condition.Or(condition1, condition2);
+ }
+
+ //----------------------------**------------------------------**-<
internal>---
+
+ Condition property(String relPath, RelationOp op, Value value) {
+ return new Condition.Property(relPath, op, value);
+ }
+
+ Class<? extends Authorizable> getSelector() {
+ return selector;
+ }
+
+ String getGroupName() {
+ return groupName;
+ }
+
+ boolean isDeclaredMembersOnly() {
+ return declaredMembersOnly;
+ }
+
+ Condition getCondition() {
+ return condition;
+ }
+
+ String getSortProperty() {
+ return sortProperty;
+ }
+
+ Direction getSortDirection() {
+ return sortDirection;
+ }
+
+ boolean getSortIgnoreCase() {
+ return sortIgnoreCase;
+ }
+
+ Value getBound() {
+ return bound;
+ }
+
+ long getOffset() {
+ return offset;
+ }
+
+ long getMaxCount() {
+ return maxCount;
+ }
+}
\ No newline at end of file
Added:
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/**XPathQueryEvaluator.java
URL:
http://svn.apache.org/viewvc/**jackrabbit/oak/trunk/oak-jcr/**
src/main/java/org/apache/**jackrabbit/oak/jcr/security/**user/query/**
XPathQueryEvaluator.java?rev=**1373392&view=auto<http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/query/XPathQueryEvaluator.java?rev=1373392&view=auto>
==============================**==============================**
==================
---
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/**XPathQueryEvaluator.java
(added)
+++
jackrabbit/oak/trunk/oak-jcr/**src/main/java/org/apache/**
jackrabbit/oak/jcr/security/**user/query/**XPathQueryEvaluator.java
Wed Aug 15 13:24:21 2012
@@ -0,0 +1,340 @@
+/*
+ * 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<http://www.apache.org/licenses/LICENSE-2.0>
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.jcr.**security.user.query;
+
+import java.util.Iterator;
+import javax.annotation.Nonnull;
+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 com.google.common.base.**Function;
+import com.google.common.base.**Predicate;
+import com.google.common.base.**Predicates;
+import com.google.common.collect.**Iterators;
+import org.apache.jackrabbit.api.**security.user.Authorizable;
+import org.apache.jackrabbit.api.**security.user.Group;
+import org.apache.jackrabbit.api.**security.user.QueryBuilder;
+import org.apache.jackrabbit.api.**security.user.User;
+import org.apache.jackrabbit.api.**security.user.UserManager;
+import org.apache.jackrabbit.oak.jcr.**security.user.UserManagerImpl;
+import org.apache.jackrabbit.oak.**namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.spi.**security.user.UserConstants;
+import org.apache.jackrabbit.util.**Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This evaluator for {@link
org.apache.jackrabbit.api.**security.user.Query}s use XPath
+ * and some minimal client side filtering.
+ */
+public class XPathQueryEvaluator implements ConditionVisitor {
+ static final Logger log =
LoggerFactory.getLogger(**XPathQueryEvaluator.class);
+
+ private final XPathQueryBuilder builder;
+ private final UserManager userManager;
+ private final QueryManager queryManager;
+ private final NamePathMapper namePathMapper;
+
+ private final StringBuilder xPath = new StringBuilder();
+
+ public XPathQueryEvaluator(**XPathQueryBuilder builder,
UserManagerImpl
userManager,
+ QueryManager queryManager, NamePathMapper
namePathMapper) {
+ this.builder = builder;
+ this.userManager = userManager;
+ this.queryManager = queryManager;
+ this.namePathMapper = namePathMapper;
+ }
+
+ public Iterator<Authorizable> eval() throws RepositoryException {
+ xPath.append("//element(*,")
+ .append(getNtName(builder.**getSelector()))
+ .append(')');
+
+ Value bound = builder.getBound();
+ long offset = builder.getOffset();
+ if (bound != null&& offset> 0) {
+ log.warn("Found bound {} and offset {} in limit. Discarding
offset.", bound, offset);
+ offset = 0;
+ }
+
+ Condition condition = builder.getCondition();
+ String sortCol = builder.getSortProperty();
+ QueryBuilder.Direction sortDir = builder.getSortDirection();
+ if (bound != null) {
+ if (sortCol == null) {
+ log.warn("Ignoring bound {} since no sort order is
specified");
+ } else {
+ Condition boundCondition = builder.property(sortCol,
getCollation(sortDir), bound);
+ condition = condition == null
+ ? boundCondition
+ : builder.and(condition, boundCondition);
+ }
+ }
+
+ if (condition != null) {
+ xPath.append('[');
+ condition.accept(this);
+ xPath.append(']');
+ }
+
+ if (sortCol != null) {
+ boolean ignoreCase = builder.getSortIgnoreCase();
+ xPath.append(" order by ")
+ .append(ignoreCase ? "" : "fn:lower-case(")
+ .append(sortCol)
+ .append(ignoreCase ? " " : ") ")
+ .append(sortDir.getDirection()**);
+ }
+
+ Query query = queryManager.createQuery(**xPath.toString(),
Query.XPATH);
+ long maxCount = builder.getMaxCount();
+ if (maxCount == 0) {
+ return Iterators.emptyIterator();
+ }
+
+ // If we are scoped to a group and have a limit, we have to
apply
the limit
+ // here (inefficient!) otherwise we can apply the limit in the
query
+ if (builder.getGroupName() == null) {
+ if (offset> 0) {
+ query.setOffset(offset);
+ }
+ if (maxCount> 0) {
+ query.setLimit(maxCount);
+ }
+ return toAuthorizables(execute(query)**);
+ } else {
+ Iterator<Authorizable> result =
toAuthorizables(execute(query)**);
+ Iterator<Authorizable> filtered = filter(result,
builder.getGroupName(), builder.isDeclaredMembersOnly(**));
+ return ResultIterator.create(offset, maxCount, filtered);
+ }
+ }
+
+ //----------------------------**-----------------------<
ConditionVisitor>---
+ @Override
+ public void visit(Condition.Node condition) throws
RepositoryException {
+ xPath.append('(')
+ .append("jcr:like(")
+
.append(namePathMapper.**getJcrName(UserConstants.REP_**
PRINCIPAL_NAME))
+ .append(",'")
+ .append(condition.getPattern()**)
+ .append("')")
+ .append(" or ")
+ .append("jcr:like(fn:name(.),'**")
+ .append(escape(condition.**getPattern()))
+ .append("')")
+ .append(')');
+ }
+
+ @Override
+ public void visit(Condition.Property condition) throws
RepositoryException {
+ RelationOp relOp = condition.getOp();
+ if (relOp == RelationOp.EX) {
+ xPath.append(condition.**getRelPath());
+ } else if (relOp == RelationOp.LIKE) {
+ xPath.append("jcr:like(")
+ .append(condition.getRelPath()**)
+ .append(",'")
+ .append(condition.getPattern()**)
+ .append("')");
+ } else {
+ xPath.append(condition.**getRelPath())
+ .append(condition.getOp().**getOp())
+ .append(format(condition.**getValue()));
+ }
+ }
+
+ @Override
+ public void visit(Condition.Contains condition) {
+ xPath.append("jcr:contains(")
+ .append(condition.getRelPath()**)
+ .append(",'")
+ .append(condition.**getSearchExpr())
+ .append("')");
+ }
+
+ @Override
+ public void visit(Condition.Impersonation condition) {
+ xPath.append("@rep:**impersonators='")
+ .append(condition.getName())
+ .append('\'');
+ }
+
+ @Override
+ public void visit(Condition.Not condition) throws
RepositoryException
{
+ xPath.append("not(");
+ condition.getCondition().**accept(this);
+ xPath.append(')');
+ }
+
+ @Override
+ public void visit(Condition.And condition) throws
RepositoryException
{
+ int count = 0;
+ for (Condition c : condition) {
+ xPath.append(count++> 0 ? " and " : "");
+ c.accept(this);
+ }
+ }
+
+ @Override
+ public void visit(Condition.Or condition) throws
RepositoryException {
+ int pos = xPath.length();
+
+ int count = 0;
+ for (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>---
+ /**
+ * Escape {@code string} for matching in jcr escaped node names
+ *
+ * @param string string to escape
+ * @return escaped string
+ */
+ @Nonnull
+ public static String escape(String string) {
+ StringBuilder result = new StringBuilder();
+
+ int k = 0;
+ int j;
+ do {
+ j = string.indexOf('%', k); // split on %
+ if (j< 0) {
+ // jcr escape trail
+
result.append(Text.**escapeIllegalJcrChars(string.**substring(k)));
+ } else if (j> 0&& string.charAt(j - 1) == '\\') {
+ // literal occurrence of % -> jcr escape
+
result.append(Text.**escapeIllegalJcrChars(string.**substring(k, j) +
'%'));
+ } else {
+ // wildcard occurrence of % -> jcr escape all but %
+
result.append(Text.**escapeIllegalJcrChars(string.**substring(k,
j))).append('%');
+ }
+
+ k = j + 1;
+ } while (j>= 0);
+
+ return result.toString();
+ }
+
+ @Nonnull
+ private String getNtName(Class<? extends Authorizable> selector) {
+ String ntName;
+ if (User.class.isAssignableFrom(**selector)) {
+ ntName = namePathMapper.getJcrName(**
UserConstants.NT_REP_USER);
+ } else if (Group.class.isAssignableFrom(**selector)) {
+ ntName =
namePathMapper.getJcrName(**UserConstants.NT_REP_GROUP);
+ } else {
+ ntName =
namePathMapper.getJcrName(**UserConstants.NT_REP_**AUTHORIZABLE);
+ }
+ if (ntName == null) {
+ log.warn("Failed to retrieve JCR name for authorizable node
type.");
+ ntName = UserConstants.NT_REP_**AUTHORIZABLE;
+ }
+ return ntName;
+ }
+
+ @Nonnull
+ 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");
+ }
+ }
+
+ @Nonnull
+ private static RelationOp getCollation(QueryBuilder.**Direction
direction) throws RepositoryException {
+ switch (direction) {
+ case ASCENDING:
+ return RelationOp.GT;
+ case DESCENDING:
+ return RelationOp.LT;
+ default:
+ throw new RepositoryException("Unknown sort order " +
direction);
+ }
+ }
+
+ @Nonnull
+ @SuppressWarnings("unchecked")
+ private static Iterator<Node> execute(Query query) throws
RepositoryException {
+ return query.execute().getNodes();
+ }
+
+ @Nonnull
+ private Iterator<Authorizable> toAuthorizables(Iterator<Node>
nodes) {
+ Function<Node, Authorizable> transformer = new Function<Node,
Authorizable>() {
+ public Authorizable apply(Node node) {
+ try {
+ return
userManager.**getAuthorizableByPath(node.**getPath());
+ } catch (RepositoryException e) {
+ log.warn("Cannot create authorizable from node {}",
node);
+ log.debug(e.getMessage(), e);
+ return null;
+ }
+ }
+ };
+
+ return Iterators.transform(nodes, transformer);
+ }
+
+ @Nonnull
+ private Iterator<Authorizable> filter(Iterator<Authorizable>
authorizables,
+ String groupName,
+ final boolean
declaredMembersOnly) throws RepositoryException {
+ Predicate<Authorizable> predicate;
+ Authorizable authorizable =
userManager.getAuthorizable(**groupName);
+ if (authorizable == null || !authorizable.isGroup()) {
+ predicate = Predicates.alwaysFalse();
+ } else {
+ final Group group = (Group) authorizable;
+ predicate = new Predicate<Authorizable>() {
+ public boolean apply(Authorizable authorizable) {
+ try {
+ return (declaredMembersOnly) ?
group.isDeclaredMember(**authorizable) : group.isMember(authorizable);
+ } catch (RepositoryException e) {
+ log.debug("Cannot determine group membership for
{}", authorizable, e.getMessage());
+ return false;
+ }
+ }
+ };
+ }
+ return Iterators.filter(**authorizables, predicate);
+ }
+}
\ No newline at end of file