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 );
+            }
+        }
+    }
+
+}