You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by se...@apache.org on 2010/01/24 20:04:39 UTC
svn commit: r902620 [4/6] - in /directory/sandbox/seelmann/hbase-partition:
./ src/ src/main/ src/main/java/ src/main/java/org/
src/main/java/org/apache/ src/main/java/org/apache/directory/
src/main/java/org/apache/directory/server/ src/main/java/org/a...
Added: directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedOneLevelScopeCursor.java
URL: http://svn.apache.org/viewvc/directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedOneLevelScopeCursor.java?rev=902620&view=auto
==============================================================================
--- directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedOneLevelScopeCursor.java (added)
+++ directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedOneLevelScopeCursor.java Sun Jan 24 19:04:37 2010
@@ -0,0 +1,322 @@
+/*
+ * 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.directory.server.core.partition.hbase.xdbmext;
+
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.xdbm.AbstractIndexCursor;
+import org.apache.directory.server.xdbm.Index;
+import org.apache.directory.server.xdbm.IndexCursor;
+import org.apache.directory.server.xdbm.IndexEntry;
+import org.apache.directory.server.xdbm.Store;
+import org.apache.directory.server.xdbm.search.impl.OneLevelScopeEvaluator;
+import org.apache.directory.shared.ldap.cursor.Cursor;
+import org.apache.directory.shared.ldap.cursor.InvalidCursorPositionException;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+
+
+/**
+ * A Cursor over entries satisfying one level scope constraints with alias
+ * dereferencing considerations when enabled during search.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ExtendedOneLevelScopeCursor extends AbstractIndexCursor<Long, ServerEntry>
+{
+ /** Error message for unsupported operations */
+ private static final String UNSUPPORTED_MSG = "Scope Cursors are not ordered and do not support positioning by element.";
+
+ /** The entry database/store */
+ private final Store<ServerEntry> db;
+
+ /** A onelevel ScopeNode Evaluator */
+ @SuppressWarnings("unchecked")
+ private final OneLevelScopeEvaluator evaluator;
+
+ /** A Cursor over the entries in the scope of the search base */
+ private final IndexCursor<Long, ServerEntry> scopeCursor;
+
+ /** A Cursor over entries brought into scope by alias dereferencing */
+ private final Cursor<IndexEntry<Long, ServerEntry>> dereferencedCursor;
+
+ /** Currently active Cursor: we switch between two cursors */
+ private Cursor<IndexEntry<Long, ServerEntry>> cursor;
+
+ /** Whether or not this Cursor is positioned so an entry is available */
+ private boolean available = false;
+
+
+ /**
+ * Creates a Cursor over entries satisfying one level scope criteria.
+ *
+ * @param db the entry store
+ * @param filter
+ * @param evaluator an IndexEntry (candidate) evaluator
+ * @throws Exception on db access failures
+ */
+ @SuppressWarnings("unchecked")
+ public ExtendedOneLevelScopeCursor( Store<ServerEntry> db, ExprNode filter, OneLevelScopeEvaluator evaluator )
+ throws Exception
+ {
+ this.db = db;
+ this.evaluator = evaluator;
+
+ Index<Long, ServerEntry> oneLevelIndex = db.getOneLevelIndex();
+ if ( oneLevelIndex instanceof IndexFilteringExtension<?, ?> )
+ {
+ IndexFilteringExtension<Long, ServerEntry> filteringIndex = ( IndexFilteringExtension<Long, ServerEntry> ) oneLevelIndex;
+ scopeCursor = filteringIndex.forwardFilteringCursor( evaluator.getBaseId(), filter );
+ }
+ else
+ {
+ scopeCursor = oneLevelIndex.forwardCursor( evaluator.getBaseId() );
+ }
+
+ if ( evaluator.isDereferencing() )
+ {
+ dereferencedCursor = db.getOneAliasIndex().forwardCursor( evaluator.getBaseId() );
+ }
+ else
+ {
+ dereferencedCursor = null;
+ }
+ }
+
+
+ public boolean available()
+ {
+ return available;
+ }
+
+
+ public void beforeValue( Long id, Long value ) throws Exception
+ {
+ throw new UnsupportedOperationException( UNSUPPORTED_MSG );
+ }
+
+
+ public void afterValue( Long id, Long value ) throws Exception
+ {
+ throw new UnsupportedOperationException( UNSUPPORTED_MSG );
+ }
+
+
+ public void before( IndexEntry<Long, ServerEntry> element ) throws Exception
+ {
+ throw new UnsupportedOperationException( UNSUPPORTED_MSG );
+ }
+
+
+ public void after( IndexEntry<Long, ServerEntry> element ) throws Exception
+ {
+ throw new UnsupportedOperationException( UNSUPPORTED_MSG );
+ }
+
+
+ public void beforeFirst() throws Exception
+ {
+ checkNotClosed( "beforeFirst()" );
+ cursor = scopeCursor;
+ cursor.beforeFirst();
+ available = false;
+ }
+
+
+ public void afterLast() throws Exception
+ {
+ checkNotClosed( "afterLast()" );
+ if ( evaluator.isDereferencing() )
+ {
+ cursor = dereferencedCursor;
+ }
+ else
+ {
+ cursor = scopeCursor;
+ }
+
+ cursor.afterLast();
+ available = false;
+ }
+
+
+ public boolean first() throws Exception
+ {
+ beforeFirst();
+ return next();
+ }
+
+
+ public boolean last() throws Exception
+ {
+ afterLast();
+ return previous();
+ }
+
+
+ public boolean previous() throws Exception
+ {
+ checkNotClosed( "previous()" );
+ // if the cursor has not been set - position it after last element
+ if ( cursor == null )
+ {
+ afterLast();
+ }
+
+ // if we're using the scopeCursor (1st Cursor) then return result as is
+ if ( cursor == scopeCursor )
+ {
+ /*
+ * If dereferencing is enabled then we must ignore alias entries, not
+ * returning them as part of the results.
+ */
+ if ( evaluator.isDereferencing() )
+ {
+ // advance until nothing is available or until we find a non-alias
+ do
+ {
+ checkNotClosed( "previous()" );
+ available = cursor.previous();
+
+ if ( available && db.getAliasIndex().reverseLookup( cursor.get().getId() ) == null )
+ {
+ break;
+ }
+ }
+ while ( available );
+ }
+ else
+ {
+ available = cursor.previous();
+ }
+
+ return available;
+ }
+
+ /*
+ * Below here we are using the dereferencedCursor so if nothing is
+ * available after an advance backwards we need to switch to the
+ * scopeCursor and try a previous call after positioning past it's
+ * last element.
+ */
+ available = cursor.previous();
+ if ( !available )
+ {
+ cursor = scopeCursor;
+ cursor.afterLast();
+
+ // advance until nothing is available or until we find a non-alias
+ do
+ {
+ checkNotClosed( "previous()" );
+ available = cursor.previous();
+
+ if ( available && db.getAliasIndex().reverseLookup( cursor.get().getId() ) == null )
+ {
+ break;
+ }
+ }
+ while ( available );
+
+ return available;
+ }
+
+ return true;
+ }
+
+
+ public boolean next() throws Exception
+ {
+ checkNotClosed( "next()" );
+ // if the cursor hasn't been set position it before the first element
+ if ( cursor == null )
+ {
+ beforeFirst();
+ }
+
+ /*
+ * If dereferencing is enabled then we must ignore alias entries, not
+ * returning them as part of the results.
+ */
+ if ( evaluator.isDereferencing() )
+ {
+ // advance until nothing is available or until we find a non-alias
+ do
+ {
+ checkNotClosed( "next()" );
+ available = cursor.next();
+
+ if ( available && db.getAliasIndex().reverseLookup( cursor.get().getId() ) == null )
+ {
+ break;
+ }
+ }
+ while ( available );
+ }
+ else
+ {
+ available = cursor.next();
+ }
+
+ // if we're using dereferencedCursor (2nd) then we return the result
+ if ( cursor == dereferencedCursor )
+ {
+ return available;
+ }
+
+ /*
+ * Below here we are using the scopeCursor so if nothing is
+ * available after an advance forward we need to switch to the
+ * dereferencedCursor and try a previous call after positioning past
+ * it's last element.
+ */
+ if ( !available )
+ {
+ if ( dereferencedCursor != null )
+ {
+ cursor = dereferencedCursor;
+ cursor.beforeFirst();
+ return available = cursor.next();
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+
+ public IndexEntry<Long, ServerEntry> get() throws Exception
+ {
+ checkNotClosed( "get()" );
+ if ( available )
+ {
+ return cursor.get();
+ }
+
+ throw new InvalidCursorPositionException( "Cursor has not been positioned yet." );
+ }
+
+
+ public boolean isElementReused()
+ {
+ return scopeCursor.isElementReused() || ( dereferencedCursor != null && dereferencedCursor.isElementReused() );
+ }
+}
\ No newline at end of file
Added: directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedOptimizer.java
URL: http://svn.apache.org/viewvc/directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedOptimizer.java?rev=902620&view=auto
==============================================================================
--- directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedOptimizer.java (added)
+++ directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedOptimizer.java Sun Jan 24 19:04:37 2010
@@ -0,0 +1,109 @@
+/*
+ * 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.directory.server.core.partition.hbase.xdbmext;
+
+
+import org.apache.directory.server.xdbm.Index;
+import org.apache.directory.server.xdbm.Store;
+import org.apache.directory.server.xdbm.search.impl.DefaultOptimizer;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.SubstringNode;
+
+
+/**
+ * Optimizer that annotates the filter using scan counts.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 777037 $
+ */
+public class ExtendedOptimizer<E> extends DefaultOptimizer<E>
+{
+ /** the database this optimizer operates on */
+ private final Store<E> db;
+ /** creates cursors over entries satisfying filter expressions */
+ private final ExtendedCursorBuilder cursorBuilder;
+ /** creates evaluators which check to see if candidates satisfy a filter expression */
+ private final ExtendedEvaluatorBuilder evaluatorBuilder;
+
+
+ /**
+ * Creates an optimizer on a database.
+ *
+ * @param db the database this optimizer works for.
+ */
+ public ExtendedOptimizer( Store<E> db, ExtendedCursorBuilder cursorBuilder,
+ ExtendedEvaluatorBuilder evaluatorBuilder ) throws Exception
+ {
+ super( db );
+ this.db = db;
+ this.cursorBuilder = cursorBuilder;
+ this.evaluatorBuilder = evaluatorBuilder;
+ }
+
+
+ /**
+ * Annotates the expression tree to determine optimal evaluation order based
+ * on the scan count for indices that exist for each expression node. If an
+ * index on the attribute does not exist an IndexNotFoundException will be
+ * thrown.
+ *
+ * @see org.apache.directory.server.xdbm.search.Optimizer#annotate(ExprNode)
+ */
+ public Long annotate( ExprNode node ) throws Exception
+ {
+ if ( node instanceof SubstringNode )
+ {
+ // Start off with the worst case unless scan count says otherwise.
+ Long count = Long.MAX_VALUE;
+
+ count = getSubstringScan( ( SubstringNode ) node );
+
+ // Protect against overflow when counting.
+ if ( count < 0L )
+ {
+ count = Long.MAX_VALUE;
+ }
+
+ node.set( "count", count );
+ return count;
+ }
+ else
+ {
+ return super.annotate( node );
+ }
+ }
+
+
+ private long getSubstringScan( SubstringNode node ) throws Exception
+ {
+ if ( db.hasUserIndexOn( node.getAttribute() ) )
+ {
+ Index<?, E> idx = db.getUserIndex( node.getAttribute() );
+ if ( idx instanceof IndexSubstringExtension<?, ?> )
+ {
+ IndexSubstringExtension<?, E> substringIdx = ( IndexSubstringExtension<?, E> ) idx;
+ return substringIdx.substringCount( node );
+ }
+ }
+
+ // count for non-indexed attribute is unknown so we presume da worst
+ return Long.MAX_VALUE;
+ }
+}
Added: directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedSubstringCursor.java
URL: http://svn.apache.org/viewvc/directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedSubstringCursor.java?rev=902620&view=auto
==============================================================================
--- directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedSubstringCursor.java (added)
+++ directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedSubstringCursor.java Sun Jan 24 19:04:37 2010
@@ -0,0 +1,252 @@
+/*
+ * 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.directory.server.core.partition.hbase.xdbmext;
+
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.xdbm.AbstractIndexCursor;
+import org.apache.directory.server.xdbm.ForwardIndexEntry;
+import org.apache.directory.server.xdbm.Index;
+import org.apache.directory.server.xdbm.IndexCursor;
+import org.apache.directory.server.xdbm.IndexEntry;
+import org.apache.directory.server.xdbm.Store;
+import org.apache.directory.shared.ldap.cursor.InvalidCursorPositionException;
+
+
+/**
+ * A Cursor traversing candidates matching a Substring assertion expression.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ExtendedSubstringCursor extends AbstractIndexCursor<String, ServerEntry>
+{
+ private static final String UNSUPPORTED_MSG = "SubstringCursors may not be ordered and do not support positioning by element.";
+ private final boolean hasIndex;
+ private final IndexCursor<String, ServerEntry> wrapped;
+ private final ExtendedSubstringEvaluator evaluator;
+ private final ForwardIndexEntry<String, ServerEntry> indexEntry = new ForwardIndexEntry<String, ServerEntry>();
+ private boolean available = false;
+
+
+ @SuppressWarnings("unchecked")
+ public ExtendedSubstringCursor( Store<ServerEntry> db, final ExtendedSubstringEvaluator substringEvaluator )
+ throws Exception
+ {
+ evaluator = substringEvaluator;
+ hasIndex = db.hasUserIndexOn( evaluator.getExpression().getAttribute() );
+
+ if ( hasIndex )
+ {
+ Index<String, ServerEntry> idx = ( Index<String, ServerEntry> ) db.getUserIndex( evaluator.getExpression()
+ .getAttribute() );
+ if ( idx instanceof IndexSubstringExtension<?, ?> )
+ {
+ wrapped = ( ( IndexSubstringExtension ) idx ).forwardSubstringCursor( evaluator.getExpression() );
+ }
+ else
+ {
+ wrapped = idx.forwardCursor();
+ }
+ // explicit move to before first because there is some optimization
+ // when the substring filter initial pattern is set
+ beforeFirst();
+ }
+ else
+ {
+ /*
+ * There is no index on the attribute here. We have no choice but
+ * to perform a full table scan but need to leverage an index for the
+ * wrapped Cursor. We know that all entries are listed under
+ * the ndn index and so this will enumerate over all entries. The
+ * substringEvaluator is used in an assertion to constrain the
+ * result set to only those entries matching the pattern. The
+ * substringEvaluator handles all the details of normalization and
+ * knows to use it, when it itself detects the lack of an index on
+ * the node's attribute.
+ */
+ wrapped = db.getNdnIndex().forwardCursor();
+ }
+ }
+
+
+ public boolean available()
+ {
+ return available;
+ }
+
+
+ public void beforeValue( Long id, String value ) throws Exception
+ {
+ throw new UnsupportedOperationException( UNSUPPORTED_MSG );
+ }
+
+
+ public void afterValue( Long id, String value ) throws Exception
+ {
+ throw new UnsupportedOperationException( UNSUPPORTED_MSG );
+ }
+
+
+ public void before( IndexEntry<String, ServerEntry> element ) throws Exception
+ {
+ throw new UnsupportedOperationException( UNSUPPORTED_MSG );
+ }
+
+
+ public void after( IndexEntry<String, ServerEntry> element ) throws Exception
+ {
+ throw new UnsupportedOperationException( UNSUPPORTED_MSG );
+ }
+
+
+ public void beforeFirst() throws Exception
+ {
+ checkNotClosed( "beforeFirst()" );
+ if ( evaluator.getExpression().getInitial() != null && hasIndex )
+ {
+ ForwardIndexEntry<String, ServerEntry> indexEntry = new ForwardIndexEntry<String, ServerEntry>();
+ indexEntry.setValue( evaluator.getExpression().getInitial() );
+ wrapped.before( indexEntry );
+ }
+ else
+ {
+ wrapped.beforeFirst();
+ }
+
+ clear();
+ }
+
+
+ private void clear()
+ {
+ available = false;
+ indexEntry.setObject( null );
+ indexEntry.setId( null );
+ indexEntry.setValue( null );
+ }
+
+
+ public void afterLast() throws Exception
+ {
+ checkNotClosed( "afterLast()" );
+
+ // to keep the cursor always *after* the last matched tuple
+ // This fixes an issue if the last matched tuple is also the last record present in the
+ // index. In this case the wrapped cursor is positioning on the last tuple instead of positioning after that
+ wrapped.afterLast();
+ clear();
+ }
+
+
+ public boolean first() throws Exception
+ {
+ beforeFirst();
+ return next();
+ }
+
+
+ private boolean evaluateCandidate( IndexEntry<String, ServerEntry> indexEntry ) throws Exception
+ {
+ if ( hasIndex )
+ {
+ return evaluator.getPattern().matcher( indexEntry.getValue() ).matches();
+ }
+ else
+ {
+ return evaluator.evaluate( indexEntry );
+ }
+ }
+
+
+ public boolean last() throws Exception
+ {
+ afterLast();
+ return previous();
+ }
+
+
+ public boolean previous() throws Exception
+ {
+ while ( wrapped.previous() )
+ {
+ checkNotClosed( "previous()" );
+ IndexEntry<String, ServerEntry> entry = wrapped.get();
+ if ( evaluateCandidate( entry ) )
+ {
+ available = true;
+ this.indexEntry.setId( entry.getId() );
+ this.indexEntry.setValue( entry.getValue() );
+ this.indexEntry.setObject( entry.getObject() );
+ return true;
+ }
+ }
+
+ clear();
+ return false;
+ }
+
+
+ public boolean next() throws Exception
+ {
+ while ( wrapped.next() )
+ {
+ checkNotClosed( "next()" );
+ IndexEntry<String, ServerEntry> entry = wrapped.get();
+ if ( evaluateCandidate( entry ) )
+ {
+ available = true;
+ this.indexEntry.setId( entry.getId() );
+ this.indexEntry.setValue( entry.getValue() );
+ this.indexEntry.setObject( entry.getObject() );
+ return true;
+ }
+ }
+
+ clear();
+ return false;
+ }
+
+
+ public IndexEntry<String, ServerEntry> get() throws Exception
+ {
+ checkNotClosed( "get()" );
+ if ( available )
+ {
+ return indexEntry;
+ }
+
+ throw new InvalidCursorPositionException( "Cursor has yet to be positioned." );
+ }
+
+
+ public boolean isElementReused()
+ {
+ return wrapped.isElementReused();
+ }
+
+
+ public void close() throws Exception
+ {
+ super.close();
+ wrapped.close();
+ clear();
+ }
+}
Added: directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedSubstringEvaluator.java
URL: http://svn.apache.org/viewvc/directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedSubstringEvaluator.java?rev=902620&view=auto
==============================================================================
--- directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedSubstringEvaluator.java (added)
+++ directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedSubstringEvaluator.java Sun Jan 24 19:04:37 2010
@@ -0,0 +1,418 @@
+/*
+ * 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.directory.server.core.partition.hbase.xdbmext;
+
+
+import java.util.Iterator;
+import java.util.regex.Pattern;
+
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.xdbm.Index;
+import org.apache.directory.server.xdbm.IndexEntry;
+import org.apache.directory.server.xdbm.Store;
+import org.apache.directory.server.xdbm.search.Evaluator;
+import org.apache.directory.shared.ldap.cursor.Cursor;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.filter.SubstringNode;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.SchemaManager;
+import org.apache.directory.shared.ldap.schema.normalizers.NoOpNormalizer;
+
+
+/**
+ * Evaluates substring filter assertions on an entry.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 764131 $
+ */
+public class ExtendedSubstringEvaluator implements Evaluator<SubstringNode, ServerEntry>
+{
+ /** Database used while evaluating candidates */
+ private final Store<ServerEntry> db;
+
+ /** Schema manager used to translate attributeIds to OIDs */
+ private final SchemaManager schemaManager;
+
+ /** The Substring expression */
+ private final SubstringNode node;
+
+ /** The regular expression generated for the SubstringNode pattern */
+ private final Pattern regex;
+
+ private final AttributeType type;
+
+ private final Normalizer normalizer;
+
+ private final Index<String, ServerEntry> idx;
+
+
+ /**
+ * Creates a new SubstringEvaluator for substring expressions.
+ *
+ * @param node the substring expression node
+ * @param db the database this evaluator uses
+ * @param registries the set of registries
+ * @throws Exception if there are failures accessing resources and the db
+ */
+ public ExtendedSubstringEvaluator( SubstringNode node, Store<ServerEntry> db, SchemaManager schemaManager )
+ throws Exception
+ {
+ this.db = db;
+ this.node = node;
+ this.schemaManager = schemaManager;
+
+ //String oid = schemaManager.getAttributeTypeRegistry().getOidByName( node.getAttribute() );
+ type = schemaManager.getAttributeTypeRegistry().lookup( node.getAttribute() );
+
+ MatchingRule rule = type.getSubstring();
+
+ if ( rule == null )
+ {
+ rule = type.getEquality();
+ }
+
+ if ( rule != null )
+ {
+ normalizer = rule.getNormalizer();
+ }
+ else
+ {
+ normalizer = new NoOpNormalizer();
+ }
+
+ // compile the regular expression to search for a matching attribute
+ regex = node.getRegex( normalizer );
+
+ if ( db.hasUserIndexOn( node.getAttribute() ) )
+ {
+ //noinspection unchecked
+ idx = ( Index<String, ServerEntry> ) db.getUserIndex( node.getAttribute() );
+ }
+ else
+ {
+ idx = null;
+ }
+ }
+
+
+ public boolean evaluate( IndexEntry<?, ServerEntry> indexEntry ) throws Exception
+ {
+
+ if ( idx == null || indexEntry.getObject() != null )
+ {
+ //noinspection unchecked
+ return evaluateWithoutIndex( ( IndexEntry<String, ServerEntry> ) indexEntry );
+ }
+ else
+ {
+ return evaluateWithIndex( indexEntry );
+ }
+ }
+
+
+ public boolean evaluate( Long id ) throws Exception
+ {
+
+ if ( idx == null )
+ {
+ //noinspection unchecked
+ return evaluateWithoutIndex( id );
+ }
+ else
+ {
+ return evaluateWithIndex( id );
+ }
+ }
+
+
+ public boolean evaluate( ServerEntry entry ) throws Exception
+ {
+
+ if ( idx == null )
+ {
+ //noinspection unchecked
+ return evaluateWithoutIndex( entry );
+ }
+ else
+ {
+ return evaluateWithIndex( entry );
+ }
+ }
+
+
+ public Pattern getPattern()
+ {
+ return regex;
+ }
+
+
+ public SubstringNode getExpression()
+ {
+ return node;
+ }
+
+
+ private boolean evaluateWithIndex( IndexEntry<?, ServerEntry> indexEntry ) throws Exception
+ {
+ /*
+ * Note that this is using the reverse half of the index giving a
+ * considerable performance improvement on this kind of operation.
+ * Otherwise we would have to scan the entire index if there were
+ * no reverse lookups.
+ */
+ Cursor<IndexEntry<String, ServerEntry>> entries = idx.reverseCursor( indexEntry.getId() );
+
+ // cycle through the attribute values testing for a match
+ while ( entries.next() )
+ {
+ IndexEntry rec = entries.get();
+
+ // once match is found cleanup and return true
+ if ( regex.matcher( ( String ) rec.getValue() ).matches() )
+ {
+ entries.close();
+ return true;
+ }
+ }
+
+ // we fell through so a match was not found - assertion was false.
+ return false;
+ }
+
+
+ @SuppressWarnings(
+ { "UnusedDeclaration" })
+ private boolean evaluateWithIndex( ServerEntry entry ) throws Exception
+ {
+ throw new UnsupportedOperationException( "This is too inefficient without getId() on ServerEntry" );
+ }
+
+
+ private boolean evaluateWithIndex( Long id ) throws Exception
+ {
+ /*
+ * Note that this is using the reverse half of the index giving a
+ * considerable performance improvement on this kind of operation.
+ * Otherwise we would have to scan the entire index if there were
+ * no reverse lookups.
+ */
+ Cursor<IndexEntry<String, ServerEntry>> entries = idx.reverseCursor( id );
+
+ // cycle through the attribute values testing for a match
+ while ( entries.next() )
+ {
+ IndexEntry rec = entries.get();
+
+ // once match is found cleanup and return true
+ if ( regex.matcher( ( String ) rec.getValue() ).matches() )
+ {
+ entries.close();
+ return true;
+ }
+ }
+
+ // we fell through so a match was not found - assertion was false.
+ return false;
+ }
+
+
+ // TODO - determine if comaparator and index entry should have the Value
+ // wrapper or the raw normalized value
+ private boolean evaluateWithoutIndex( Long id ) throws Exception
+ {
+ return evaluateWithoutIndex( db.lookup( id ) );
+ }
+
+
+ // TODO - determine if comaparator and index entry should have the Value
+ // wrapper or the raw normalized value
+ private boolean evaluateWithoutIndex( ServerEntry entry ) throws Exception
+ {
+ // get the attribute
+ ServerAttribute attr = ( ServerAttribute ) entry.get( type );
+
+ // if the attribute exists and the pattern matches return true
+ if ( attr != null )
+ {
+ /*
+ * Cycle through the attribute values testing normalized version
+ * obtained from using the substring matching rule's normalizer.
+ * The test uses the comparator obtained from the appropriate
+ * substring matching rule.
+ */
+ for ( Value value : attr )
+ {
+ value.normalize( normalizer );
+ String strValue = ( String ) value.getNormalizedValue();
+
+ // Once match is found cleanup and return true
+ if ( regex.matcher( strValue ).matches() )
+ {
+ return true;
+ }
+ }
+
+ // Fall through as we didn't find any matching value for this attribute.
+ // We will have to check in the potential descendant, if any.
+ }
+
+ // If we do not have the attribute, loop through the descendant
+ // May be the node Attribute has descendant ?
+ if ( schemaManager.getAttributeTypeRegistry().hasDescendants( node.getAttribute() ) )
+ {
+ // TODO check to see if descendant handling is necessary for the
+ // index so we can match properly even when for example a name
+ // attribute is used instead of more specific commonName
+ Iterator<AttributeType> descendants = schemaManager.getAttributeTypeRegistry().descendants(
+ node.getAttribute() );
+
+ while ( descendants.hasNext() )
+ {
+ AttributeType descendant = descendants.next();
+
+ attr = ( ServerAttribute ) entry.get( descendant );
+
+ if ( null != attr )
+ {
+
+ /*
+ * Cycle through the attribute values testing normalized version
+ * obtained from using the substring matching rule's normalizer.
+ * The test uses the comparator obtained from the appropriate
+ * substring matching rule.
+ */
+ for ( Value value : attr )
+ {
+ value.normalize( normalizer );
+ String strValue = ( String ) value.getNormalizedValue();
+
+ // Once match is found cleanup and return true
+ if ( regex.matcher( strValue ).matches() )
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ // we fell through so a match was not found - assertion was false.
+ return false;
+ }
+
+
+ // TODO - determine if comaparator and index entry should have the Value
+ // wrapper or the raw normalized value
+ private boolean evaluateWithoutIndex( IndexEntry<String, ServerEntry> indexEntry ) throws Exception
+ {
+ ServerEntry entry = indexEntry.getObject();
+
+ // resuscitate the entry if it has not been and set entry in IndexEntry
+ if ( null == entry )
+ {
+ entry = db.lookup( indexEntry.getId() );
+ indexEntry.setObject( entry );
+ }
+
+ /*
+ * Don't make a call here to evaluateWithoutIndex( ServerEntry ) for
+ * code reuse since we do want to set the value on the indexEntry on
+ * matches.
+ */
+
+ // get the attribute
+ ServerAttribute attr = ( ServerAttribute ) entry.get( type );
+
+ // if the attribute exists and the pattern matches return true
+ if ( attr != null )
+ {
+ /*
+ * Cycle through the attribute values testing normalized version
+ * obtained from using the substring matching rule's normalizer.
+ * The test uses the comparator obtained from the appropriate
+ * substring matching rule.
+ */
+ for ( Value value : attr )
+ {
+ value.normalize( normalizer );
+ String strValue = ( String ) value.getNormalizedValue();
+
+ // Once match is found cleanup and return true
+ if ( regex.matcher( strValue ).matches() )
+ {
+ // before returning we set the normalized value
+ indexEntry.setValue( strValue );
+ return true;
+ }
+ }
+
+ // Fall through as we didn't find any matching value for this attribute.
+ // We will have to check in the potential descendant, if any.
+ }
+
+ // If we do not have the attribute, loop through the descendant
+ // May be the node Attribute has descendant ?
+ if ( schemaManager.getAttributeTypeRegistry().hasDescendants( node.getAttribute() ) )
+ {
+ // TODO check to see if descendant handling is necessary for the
+ // index so we can match properly even when for example a name
+ // attribute is used instead of more specific commonName
+ Iterator<AttributeType> descendants = schemaManager.getAttributeTypeRegistry().descendants(
+ node.getAttribute() );
+
+ while ( descendants.hasNext() )
+ {
+ AttributeType descendant = descendants.next();
+
+ attr = ( ServerAttribute ) entry.get( descendant );
+
+ if ( null != attr )
+ {
+
+ /*
+ * Cycle through the attribute values testing normalized version
+ * obtained from using the substring matching rule's normalizer.
+ * The test uses the comparator obtained from the appropriate
+ * substring matching rule.
+ */
+ for ( Value value : attr )
+ {
+ value.normalize( normalizer );
+ String strValue = ( String ) value.getNormalizedValue();
+
+ // Once match is found cleanup and return true
+ if ( regex.matcher( strValue ).matches() )
+ {
+ // before returning we set the normalized value
+ indexEntry.setValue( strValue );
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ // we fell through so a match was not found - assertion was false.
+ return false;
+ }
+}
\ No newline at end of file
Added: directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedSubtreeScopeCursor.java
URL: http://svn.apache.org/viewvc/directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedSubtreeScopeCursor.java?rev=902620&view=auto
==============================================================================
--- directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedSubtreeScopeCursor.java (added)
+++ directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/ExtendedSubtreeScopeCursor.java Sun Jan 24 19:04:37 2010
@@ -0,0 +1,348 @@
+/*
+ * 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.directory.server.core.partition.hbase.xdbmext;
+
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.xdbm.AbstractIndexCursor;
+import org.apache.directory.server.xdbm.Index;
+import org.apache.directory.server.xdbm.IndexCursor;
+import org.apache.directory.server.xdbm.IndexEntry;
+import org.apache.directory.server.xdbm.Store;
+import org.apache.directory.server.xdbm.search.impl.AllEntriesCursor;
+import org.apache.directory.server.xdbm.search.impl.SubtreeScopeEvaluator;
+import org.apache.directory.shared.ldap.cursor.InvalidCursorPositionException;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+
+
+/**
+ * A Cursor over entries satisfying scope constraints with alias dereferencing
+ * considerations.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ExtendedSubtreeScopeCursor extends AbstractIndexCursor<Long, ServerEntry>
+{
+ private static final String UNSUPPORTED_MSG = "Scope Cursors are not ordered and do not support positioning by element.";
+
+ /** The Entry database/store */
+ private final Store<ServerEntry> db;
+
+ /** A ScopeNode Evaluator */
+ private final SubtreeScopeEvaluator<ServerEntry> evaluator;
+
+ /** A Cursor over the entries in the scope of the search base */
+ private final IndexCursor<Long, ServerEntry> scopeCursor;
+
+ /** A Cursor over entries brought into scope by alias dereferencing */
+ private final IndexCursor<Long, ServerEntry> dereferencedCursor;
+
+ /** Currently active Cursor: we switch between two cursors */
+ private IndexCursor<Long, ServerEntry> cursor;
+
+ /** Whether or not this Cursor is positioned so an entry is available */
+ private boolean available = false;
+
+ private Long contextEntryId;
+
+
+ /**
+ * Creates a Cursor over entries satisfying subtree level scope criteria.
+ *
+ * @param db the entry store
+ * @param filter
+ * @param evaluator an IndexEntry (candidate) evaluator
+ * @throws Exception on db access failures
+ */
+ public ExtendedSubtreeScopeCursor( Store<ServerEntry> db, ExprNode filter,
+ SubtreeScopeEvaluator<ServerEntry> evaluator ) throws Exception
+ {
+ this.db = db;
+ this.evaluator = evaluator;
+
+ Index<Long, ServerEntry> subLevelIndex = db.getSubLevelIndex();
+ if ( subLevelIndex instanceof IndexFilteringExtension<?, ?> )
+ {
+ IndexFilteringExtension<Long, ServerEntry> filteringIndex = ( IndexFilteringExtension<Long, ServerEntry> ) subLevelIndex;
+ scopeCursor = filteringIndex.forwardFilteringCursor( evaluator.getBaseId(), filter );
+ }
+ else if ( evaluator.getBaseId() == getContextEntryId() )
+ {
+ scopeCursor = new AllEntriesCursor( db );
+ }
+ else
+ {
+ scopeCursor = subLevelIndex.forwardCursor( evaluator.getBaseId() );
+ }
+
+ if ( evaluator.isDereferencing() )
+ {
+ dereferencedCursor = db.getSubAliasIndex().forwardCursor( evaluator.getBaseId() );
+ }
+ else
+ {
+ dereferencedCursor = null;
+ }
+ }
+
+
+ private Long getContextEntryId()
+ {
+ if ( contextEntryId == null )
+ {
+ try
+ {
+ this.contextEntryId = db.getEntryId( db.getSuffix().getNormName() );
+ }
+ catch ( Exception e )
+ {
+ // might not have been created
+ // might not have been created
+ }
+ }
+
+ if ( contextEntryId == null )
+ {
+ return 1L;
+ }
+
+ return contextEntryId;
+ }
+
+
+ public boolean available()
+ {
+ return available;
+ }
+
+
+ public void beforeValue( Long id, Long value ) throws Exception
+ {
+ throw new UnsupportedOperationException( UNSUPPORTED_MSG );
+ }
+
+
+ public void before( IndexEntry<Long, ServerEntry> element ) throws Exception
+ {
+ throw new UnsupportedOperationException( UNSUPPORTED_MSG );
+ }
+
+
+ public void afterValue( Long id, Long value ) throws Exception
+ {
+ throw new UnsupportedOperationException( UNSUPPORTED_MSG );
+ }
+
+
+ public void after( IndexEntry<Long, ServerEntry> element ) throws Exception
+ {
+ throw new UnsupportedOperationException( UNSUPPORTED_MSG );
+ }
+
+
+ public void beforeFirst() throws Exception
+ {
+ checkNotClosed( "beforeFirst()" );
+ cursor = scopeCursor;
+ cursor.beforeFirst();
+ available = false;
+ }
+
+
+ public void afterLast() throws Exception
+ {
+ checkNotClosed( "afterLast()" );
+ if ( evaluator.isDereferencing() )
+ {
+ cursor = dereferencedCursor;
+ }
+ else
+ {
+ cursor = scopeCursor;
+ }
+
+ cursor.afterLast();
+ available = false;
+ }
+
+
+ public boolean first() throws Exception
+ {
+ beforeFirst();
+ return next();
+ }
+
+
+ public boolean last() throws Exception
+ {
+ afterLast();
+ return previous();
+ }
+
+
+ public boolean previous() throws Exception
+ {
+ checkNotClosed( "previous()" );
+ // if the cursor has not been set - position it after last element
+ if ( cursor == null )
+ {
+ afterLast();
+ }
+
+ // if we're using the scopeCursor (1st Cursor) then return result as is
+ if ( cursor == scopeCursor )
+ {
+ /*
+ * If dereferencing is enabled then we must ignore alias entries, not
+ * returning them as part of the results.
+ */
+ if ( evaluator.isDereferencing() )
+ {
+ // advance until nothing is available or until we find a non-alias
+ do
+ {
+ checkNotClosed( "previous()" );
+ available = cursor.previous();
+ if ( available && db.getAliasIndex().reverseLookup( cursor.get().getId() ) == null )
+ {
+ break;
+ }
+ }
+ while ( available );
+ }
+ else
+ {
+ available = cursor.previous();
+ }
+
+ return available;
+ }
+
+ /*
+ * Below here we are using the dereferencedCursor so if nothing is
+ * available after an advance backwards we need to switch to the
+ * scopeCursor and try a previous call after positioning past it's
+ * last element.
+ */
+ available = cursor.previous();
+ if ( !available )
+ {
+ cursor = scopeCursor;
+ cursor.afterLast();
+
+ // advance until nothing is available or until we find a non-alias
+ do
+ {
+ checkNotClosed( "previous()" );
+ available = cursor.previous();
+
+ if ( available && db.getAliasIndex().reverseLookup( cursor.get().getId() ) == null )
+ {
+ break;
+ }
+ }
+ while ( available );
+
+ return available;
+ }
+
+ return true;
+ }
+
+
+ public boolean next() throws Exception
+ {
+ checkNotClosed( "next()" );
+ // if the cursor hasn't been set position it before the first element
+ if ( cursor == null )
+ {
+ beforeFirst();
+ }
+
+ /*
+ * If dereferencing is enabled then we must ignore alias entries, not
+ * returning them as part of the results.
+ */
+ if ( evaluator.isDereferencing() )
+ {
+ // advance until nothing is available or until we find a non-alias
+ do
+ {
+ checkNotClosed( "next()" );
+ available = cursor.next();
+
+ if ( available && db.getAliasIndex().reverseLookup( cursor.get().getId() ) == null )
+ {
+ break;
+ }
+ }
+ while ( available );
+ }
+ else
+ {
+ available = cursor.next();
+ }
+
+ // if we're using dereferencedCursor (2nd) then we return the result
+ if ( cursor == dereferencedCursor )
+ {
+ return available;
+ }
+
+ /*
+ * Below here we are using the scopeCursor so if nothing is
+ * available after an advance forward we need to switch to the
+ * dereferencedCursor and try a previous call after positioning past
+ * it's last element.
+ */
+ if ( !available )
+ {
+ if ( dereferencedCursor != null )
+ {
+ cursor = dereferencedCursor;
+ cursor.beforeFirst();
+ return available = cursor.next();
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+
+ public IndexEntry<Long, ServerEntry> get() throws Exception
+ {
+ checkNotClosed( "get()" );
+ if ( available )
+ {
+ return cursor.get();
+ }
+
+ throw new InvalidCursorPositionException( "Cursor has not been positioned yet." );
+ }
+
+
+ public boolean isElementReused()
+ {
+ return scopeCursor.isElementReused() || ( dereferencedCursor != null && dereferencedCursor.isElementReused() );
+ }
+}
Added: directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/IndexFilteringExtension.java
URL: http://svn.apache.org/viewvc/directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/IndexFilteringExtension.java?rev=902620&view=auto
==============================================================================
--- directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/IndexFilteringExtension.java (added)
+++ directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/IndexFilteringExtension.java Sun Jan 24 19:04:37 2010
@@ -0,0 +1,40 @@
+/*
+ * 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.directory.server.core.partition.hbase.xdbmext;
+
+
+import org.apache.directory.server.xdbm.Index;
+import org.apache.directory.server.xdbm.IndexCursor;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+
+
+/**
+ * An index that supports filtering of candidates.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 787602 $
+ */
+public interface IndexFilteringExtension<K, O> extends Index<K, O>
+{
+
+ IndexCursor<K, O> forwardFilteringCursor( Long id, ExprNode filter ) throws Exception;
+ //IndexCursor<K, O> reverseCursor( Long id, ExprNode filter ) throws Exception;
+
+}
Added: directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/IndexSubstringExtension.java
URL: http://svn.apache.org/viewvc/directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/IndexSubstringExtension.java?rev=902620&view=auto
==============================================================================
--- directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/IndexSubstringExtension.java (added)
+++ directory/sandbox/seelmann/hbase-partition/src/main/java/org/apache/directory/server/core/partition/hbase/xdbmext/IndexSubstringExtension.java Sun Jan 24 19:04:37 2010
@@ -0,0 +1,54 @@
+/*
+ * 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.directory.server.core.partition.hbase.xdbmext;
+
+
+import java.util.regex.Pattern;
+
+import org.apache.directory.server.xdbm.Index;
+import org.apache.directory.server.xdbm.IndexCursor;
+import org.apache.directory.shared.ldap.filter.SubstringNode;
+
+
+/**
+ * An index that supports substring operations.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 787602 $
+ */
+public interface IndexSubstringExtension<K, O> extends Index<K, O>
+{
+
+ int substringCount( SubstringNode node ) throws Exception;
+
+
+ boolean forwardSubstring( SubstringNode node, Pattern valuePattern ) throws Exception;
+
+
+ boolean forwardSubstring( SubstringNode node, Pattern valuePattern, Long id ) throws Exception;
+
+
+ IndexCursor<Object, O> forwardSubstringCursor( SubstringNode node ) throws Exception;
+
+ //boolean reverseGreaterOrEq( Long id ) throws Exception;
+ //boolean reverseGreaterOrEq( Long id, K attrVal ) throws Exception;
+ //IndexCursor<K, O> reverseCursor( Long id ) throws Exception;
+
+}
Added: directory/sandbox/seelmann/hbase-partition/src/test/java/org/apache/directory/server/core/partition/hbase/HBaseClusterTestCaseAdapter.java
URL: http://svn.apache.org/viewvc/directory/sandbox/seelmann/hbase-partition/src/test/java/org/apache/directory/server/core/partition/hbase/HBaseClusterTestCaseAdapter.java?rev=902620&view=auto
==============================================================================
--- directory/sandbox/seelmann/hbase-partition/src/test/java/org/apache/directory/server/core/partition/hbase/HBaseClusterTestCaseAdapter.java (added)
+++ directory/sandbox/seelmann/hbase-partition/src/test/java/org/apache/directory/server/core/partition/hbase/HBaseClusterTestCaseAdapter.java Sun Jan 24 19:04:37 2010
@@ -0,0 +1,76 @@
+/*
+ * 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.directory.server.core.partition.hbase;
+
+
+import org.apache.hadoop.hbase.HBaseClusterTestCase;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.junit.Ignore;
+
+
+/**
+ * Adapter for {@link HBaseClusterTestCase}. The setup() method
+ * starts up a Mini DFS and HBase cluster, ready to be used for
+ * unit tests.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+@Ignore
+public class HBaseClusterTestCaseAdapter extends HBaseClusterTestCase
+{
+
+ /**
+ * Instantiates a new HBaseClusterTestCase.
+ *
+ * @param clazz the clazz
+ */
+ public HBaseClusterTestCaseAdapter( Class<?> clazz )
+ {
+ super();
+ setName( clazz.getName() );
+ }
+
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ }
+
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+
+ /**
+ * Gets the HBase configurtion.
+ *
+ * @return the HBase configurtion
+ */
+ public HBaseConfiguration getHBaseConfigurtion()
+ {
+ return conf;
+ }
+
+}
Added: directory/sandbox/seelmann/hbase-partition/src/test/java/org/apache/directory/server/core/partition/hbase/HBaseRunner.java
URL: http://svn.apache.org/viewvc/directory/sandbox/seelmann/hbase-partition/src/test/java/org/apache/directory/server/core/partition/hbase/HBaseRunner.java?rev=902620&view=auto
==============================================================================
--- directory/sandbox/seelmann/hbase-partition/src/test/java/org/apache/directory/server/core/partition/hbase/HBaseRunner.java (added)
+++ directory/sandbox/seelmann/hbase-partition/src/test/java/org/apache/directory/server/core/partition/hbase/HBaseRunner.java Sun Jan 24 19:04:37 2010
@@ -0,0 +1,71 @@
+/*
+ * 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.directory.server.core.partition.hbase;
+
+import org.apache.directory.server.annotations.CreateLdapServer;
+import org.apache.directory.server.annotations.CreateTransport;
+import org.apache.directory.server.core.annotations.CreateDS;
+import org.apache.directory.server.core.annotations.CreatePartition;
+import org.apache.directory.server.core.integ.AbstractLdapTestUnit;
+import org.apache.directory.server.core.integ.FrameworkRunner;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Starts up an ApacheDS LDAP server with an HBase partition.
+ * A running HBase instance is required and must be configured
+ * in hbase-site.xml.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+@RunWith(FrameworkRunner.class)
+@CreateDS(
+ name = "hbase",
+ enableChangeLog = false,
+ partitions =
+ {
+ @CreatePartition(
+ name = "hbase",
+ suffix = "o=hbase",
+ type = HBasePartition.class
+ ),
+ @CreatePartition(
+ name = "sevenSeas",
+ suffix = "o=sevenSeas",
+ type = HBasePartition.class
+ )
+ })
+@CreateLdapServer(transports =
+ { @CreateTransport(protocol = "LDAP", port = 10389) })
+@Ignore
+public class HBaseRunner extends AbstractLdapTestUnit
+{
+
+ @Test
+ public void runServer() throws Exception
+ {
+ System.out.println( "ApacheDS started on port " + ldapServer.getPort() + ", press any key to shutdown..." );
+ System.in.read();
+ }
+
+}
\ No newline at end of file
Added: directory/sandbox/seelmann/hbase-partition/src/test/java/org/apache/directory/server/core/partition/hbase/JdbmRunner.java
URL: http://svn.apache.org/viewvc/directory/sandbox/seelmann/hbase-partition/src/test/java/org/apache/directory/server/core/partition/hbase/JdbmRunner.java?rev=902620&view=auto
==============================================================================
--- directory/sandbox/seelmann/hbase-partition/src/test/java/org/apache/directory/server/core/partition/hbase/JdbmRunner.java (added)
+++ directory/sandbox/seelmann/hbase-partition/src/test/java/org/apache/directory/server/core/partition/hbase/JdbmRunner.java Sun Jan 24 19:04:37 2010
@@ -0,0 +1,128 @@
+/*
+ * 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.directory.server.core.partition.hbase;
+
+import static org.apache.directory.server.integ.ServerIntegrationUtils.getWiredContext;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.annotations.CreateLdapServer;
+import org.apache.directory.server.annotations.CreateTransport;
+import org.apache.directory.server.core.annotations.ApplyLdifFiles;
+import org.apache.directory.server.core.annotations.CreateDS;
+import org.apache.directory.server.core.annotations.CreateIndex;
+import org.apache.directory.server.core.annotations.CreatePartition;
+import org.apache.directory.server.core.integ.AbstractLdapTestUnit;
+import org.apache.directory.server.core.integ.FrameworkRunner;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+@RunWith(FrameworkRunner.class)
+@CreateDS(
+ name = "example",
+ enableChangeLog = false,
+ partitions =
+ {
+ @CreatePartition(
+ name = "example",
+ suffix = "dc=example,dc=com",
+ cacheSize = 11000,
+ indexes =
+ {
+ @CreateIndex(attribute="ou", cacheSize=100),
+ @CreateIndex(attribute="uid", cacheSize=11000),
+ @CreateIndex(attribute="objectClass", cacheSize=11000)
+ }
+ )
+ })
+@CreateLdapServer(transports =
+ { @CreateTransport(protocol = "LDAP", port = 10389) })
+@ApplyLdifFiles("jdbm.ldif")
+@Ignore
+public class JdbmRunner extends AbstractLdapTestUnit
+{
+
+ @Test
+ public void runServer() throws Exception
+ {
+ System.out.println( "ApacheDS started on port " + ldapServer.getPort() + ", press any key to shutdown..." );
+ System.in.read();
+ }
+
+
+
+ //@Test
+ public void testIndexPerformance() throws Exception
+ {
+ LdapContext ctx = getWiredContext( ldapServer );
+
+ for(int run=0; run<5; run++)
+ {
+ long t0 = System.currentTimeMillis();
+ for ( int i = 1; i < 10000; i++ )
+ {
+ SearchControls searchControls = new SearchControls();
+ NamingEnumeration<SearchResult> results = ctx.search( "ou=test10000,dc=example,dc=com", "(uid=user." + i + ")",
+ searchControls );
+ assertTrue( results.hasMore() );
+ SearchResult next = results.next();
+ assertNotNull( next );
+ assertFalse( results.hasMore() );
+ results.close();
+ }
+ long t1 = System.currentTimeMillis();
+ long t = t1 - t0;
+ System.out.println("indexed: " + t);
+
+
+ t0 = System.currentTimeMillis();
+ for ( int i = 1; i < 10000; i++ )
+ {
+ SearchControls searchControls = new SearchControls();
+ NamingEnumeration<SearchResult> results = ctx.search( "ou=test10000,dc=example,dc=com", "(employeeNumber=" + i + ")",
+ searchControls );
+ assertTrue( results.hasMore() );
+ SearchResult next = results.next();
+ assertNotNull( next );
+ assertFalse( results.hasMore() );
+ results.close();
+ }
+ t1 = System.currentTimeMillis();
+ t = t1 - t0;
+ System.out.println("unindexed: " + t);
+
+ }
+ }
+
+
+}
\ No newline at end of file
Added: directory/sandbox/seelmann/hbase-partition/src/test/java/org/apache/directory/server/core/partition/hbase/it/AbstractHBasePartitionIT.java
URL: http://svn.apache.org/viewvc/directory/sandbox/seelmann/hbase-partition/src/test/java/org/apache/directory/server/core/partition/hbase/it/AbstractHBasePartitionIT.java?rev=902620&view=auto
==============================================================================
--- directory/sandbox/seelmann/hbase-partition/src/test/java/org/apache/directory/server/core/partition/hbase/it/AbstractHBasePartitionIT.java (added)
+++ directory/sandbox/seelmann/hbase-partition/src/test/java/org/apache/directory/server/core/partition/hbase/it/AbstractHBasePartitionIT.java Sun Jan 24 19:04:37 2010
@@ -0,0 +1,189 @@
+/*
+ * 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.directory.server.core.partition.hbase.it;
+
+
+import java.text.DecimalFormat;
+
+import org.apache.commons.lang.RandomStringUtils;
+import org.apache.directory.server.core.CoreSession;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.integ.AbstractLdapTestUnit;
+import org.apache.directory.server.core.partition.hbase.HBaseClusterTestCaseAdapter;
+import org.apache.directory.server.core.partition.hbase.table.HBaseTableHelper;
+import org.apache.directory.server.ldap.LdapServer;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.SchemaManager;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public abstract class AbstractHBasePartitionIT extends AbstractLdapTestUnit
+{
+ private static final Logger LOG = LoggerFactory.getLogger( AbstractHBasePartitionIT.class );
+
+ protected static HBaseClusterTestCaseAdapter adapter;
+ protected CoreSession session;
+
+
+ @BeforeClass
+ public static void before() throws Exception
+ {
+ adapter = new HBaseClusterTestCaseAdapter( AbstractHBasePartitionIT.class );
+ adapter.setUp();
+ }
+
+
+ @AfterClass
+ public static void after() throws Exception
+ {
+ adapter.tearDown();
+ }
+
+
+ @Before
+ public void setUp() throws Exception
+ {
+ session = ldapServer.getDirectoryService().getAdminSession();
+ }
+
+ public static String[] TABLES =
+ { "apacheds_hbase_master", "apacheds_hbase_tree", "apacheds_hbase_index_objectClass",
+ "apacheds_hbase_index_dc", "apacheds_hbase_index_o", "apacheds_hbase_index_ou", "apacheds_hbase_index_uid",
+ "apacheds_hbase_index_cn" };
+
+
+ public void createBasicTestData( LdapServer ldapServer ) throws Exception
+ {
+ CoreSession session = ldapServer.getDirectoryService().getAdminSession();
+ SchemaManager schemaManager = ldapServer.getDirectoryService().getSchemaManager();
+
+ LOG.debug( "Adding basic test data..." );
+
+ if ( !session.exists( new LdapDN( "o=hbase" ) ) )
+ {
+ ServerEntry entry = new DefaultServerEntry( schemaManager, new LdapDN( "o=hbase" ) );
+ entry.add( "objectClass", "top", "organization" );
+ entry.add( "o", "hbase" );
+ session.add( entry );
+ LOG.debug( " added " + entry.getDn().getName() );
+ }
+
+ if ( !session.exists( new LdapDN( "ou=test-ou,o=hbase" ) ) )
+ {
+ ServerEntry entry = new DefaultServerEntry( schemaManager, new LdapDN( "ou=test-ou,o=hbase" ) );
+ entry.add( "objectClass", "top", "organizationalUnit" );
+ entry.add( "ou", "test-ou" );
+ session.add( entry );
+ LOG.debug( " added " + entry.getDn().getName() );
+ }
+
+ if ( !session.exists( new LdapDN( "cn=test-person,ou=test-ou,o=hbase" ) ) )
+ {
+ ServerEntry entry = new DefaultServerEntry( schemaManager, new LdapDN( "cn=test-person,ou=test-ou,o=hbase" ) );
+ entry.add( "objectClass", "top", "person" );
+ entry.add( "cn", "test-person" );
+ entry.add( "Sn", "test-person" );
+ entry.add( "userPassword", "secret" );
+ session.add( entry );
+ LOG.debug( " added " + entry.getDn().getName() );
+ }
+
+ LOG.debug( "...done" );
+ }
+
+
+ public void createTestData( int number, String pattern, LdapServer ldapServer ) throws Exception
+ {
+ createTestData( number, 0, number, pattern, ldapServer );
+ }
+
+
+ public void createTestData( int number, int start, int stop, String pattern, LdapServer ldapServer )
+ throws Exception
+ {
+ CoreSession session = ldapServer.getDirectoryService().getAdminSession();
+ SchemaManager schemaManager = ldapServer.getDirectoryService().getSchemaManager();
+
+ LOG.debug( "Adding test data " + number + "..." );
+
+ LdapDN dn = new LdapDN( "ou=test" + number + ",o=hbase" );
+ if ( !session.exists( dn ) )
+ {
+ ServerEntry entry = new DefaultServerEntry( schemaManager, dn );
+ entry.add( "objectClass", "top", "organizationalUnit" );
+ entry.add( "ou", "test" + number );
+ session.add( entry );
+ }
+
+ DecimalFormat df = new DecimalFormat( pattern );
+ for ( int i = start; i < stop; i++ )
+ {
+ String s = df.format( i );
+
+ dn = new LdapDN( "cn=test" + s + ",ou=test" + number + ",o=hbase" );
+ if ( session.exists( dn ) )
+ {
+ continue;
+ }
+
+ ServerEntry entry = new DefaultServerEntry( schemaManager, dn );
+ entry.add( "objectClass", "top", "person", "organizationalPerson", "inetOrgPerson" );
+ entry.add( "cn", "test" + s );
+ entry.add( "Sn", "test" + s );
+ entry.add( "telephoneNumber", RandomStringUtils.randomNumeric( 13 ) );
+ entry.add( "mail", "test" + s + "@example.com" );
+ entry.add( "userPassword", "secret" );
+
+ session.add( entry );
+
+ if ( i > start && i % 1000 == 0 )
+ {
+ LOG.debug( " " + System.currentTimeMillis() + " -> created " + s + " entries." );
+ }
+ }
+
+ LOG.debug( "...done" );
+ }
+
+
+ public void compactDatabase() throws Exception
+ {
+ HBaseAdmin admin = new HBaseAdmin( HBaseTableHelper.getHBaseConfiguration() );
+ for ( String table : TABLES )
+ {
+ if ( admin.tableExists( table ) )
+ {
+ admin.flush( table );
+ Thread.sleep( 10000 );
+ // admin.compact( table );
+ // Thread.sleep( 10000 );
+ // admin.majorCompact( table );
+ // Thread.sleep( 10000 );
+ }
+ }
+ }
+
+}