mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Jean-Noel Rouvignac
06.00.2014 e67f90aeff42363b892ae478f935b00c7e64e0a8
OPENDJ-1308 (CR-3138) Migrate schema support


Added the index query factory support code.
This copies little piece of code from OpenDJ server code base, namely JE backend and makes it more abstract so it can be reused.



Assertion.java:
Added createIndexQuery(IndexQueryFactory).

IndexQueryFactory.java, Indexer.java, IndexingOptions.java: ADDED


MatchingRuleImpl.java:
Added getIndexer() and isIndexingSupported().

MatchingRule.java:
Added getIndexer().

AbstractMatchingRuleImpl.java:
Added inner class DefaultIndexer.
Implemented createIndexQuery() for inner classes DefaultAssertion, and anonymous inner class UNDEFINED_ASSERTION.
Implemented isIndexingSupported().

AbstractApproximateMatchingRuleImpl.java, AbstractEqualityMatchingRuleImpl.java:
Implemented getIndexer().

AbstractOrderingMatchingRuleImpl.java:
Implemented getIndexer().
Implemented createIndexQuery() for anonymous inner classes.

AbstractSubstringMatchingRuleImpl.java:
Implemented getIndexer().
Implemented createIndexQuery() for inner class DefaultSubstringAssertion.
Added SubstringIndexer inner class.
Added javadocs.


CertificateExactMatchingRuleImpl.java:
Now extends AbstractEqualityMatchingRuleImpl.

EqualLengthApproximateMatchingRuleImpl.java:
Implemented createIndexQuery() for anonymous inner class.

KeywordEqualityMatchingRuleImpl.java
Implemented createIndexQuery() for anonymous inner class.
Implemented getIndexer().


clirr-ignored-api-changes.xml:
Ignored changes due to methods added to interfaces.
12 files modified
3 files added
546 ■■■■■ changed files
opendj-sdk/opendj-core/clirr-ignored-api-changes.xml 20 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/Assertion.java 19 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractApproximateMatchingRuleImpl.java 8 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractEqualityMatchingRuleImpl.java 8 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractMatchingRuleImpl.java 44 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractOrderingMatchingRuleImpl.java 26 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractSubstringMatchingRuleImpl.java 145 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CertificateExactMatchingRuleImpl.java 5 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/EqualLengthApproximateMatchingRuleImpl.java 6 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/KeywordEqualityMatchingRuleImpl.java 13 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRule.java 10 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleImpl.java 15 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/IndexQueryFactory.java 117 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/Indexer.java 69 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/IndexingOptions.java 41 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/clirr-ignored-api-changes.xml
@@ -178,4 +178,24 @@
    <method>org.forgerock.opendj.ldap.LDAPOptions setDecodeOptions(org.forgerock.opendj.ldap.DecodeOptions)</method>
    <justification>OPENDJ-1197: Method return type has changed due to reification</justification>
  </difference>
  <difference>
    <className>org/forgerock/opendj/ldap/Assertion</className>
    <differenceType>7012</differenceType>
    <method>java.lang.Object createIndexQuery(org.forgerock.opendj.ldap.spi.IndexQueryFactory)</method>
    <justification>OPENDJ-1308 Migrate schema support: allows decoupling indexing from a specific backend</justification>
  </difference>
  <difference>
    <className>org/forgerock/opendj/ldap/schema/MatchingRuleImpl</className>
    <differenceType>7012</differenceType>
    <method>org.forgerock.opendj.ldap.spi.Indexer getIndexer()</method>
    <justification>OPENDJ-1308 Migrate schema support: allows decoupling indexing from a specific backend</justification>
  </difference>
  <difference>
    <className>org/forgerock/opendj/ldap/schema/MatchingRuleImpl</className>
    <differenceType>7012</differenceType>
    <method>boolean isIndexingSupported()</method>
    <justification>OPENDJ-1308 Migrate schema support: allows decoupling indexing from a specific backend</justification>
  </difference>
</differences>
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/Assertion.java
@@ -26,10 +26,13 @@
 */
package org.forgerock.opendj.ldap;
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
/**
 * A compiled attribute value assertion.
 */
public interface Assertion {
    /**
     * Indicates whether the provided attribute value should be considered a
     * match for this assertion value according to the matching rule.
@@ -41,4 +44,20 @@
     *         match, or {@code UNDEFINED} if the result is undefined.
     */
    ConditionResult matches(ByteSequence normalizedAttributeValue);
    /**
     * Returns an index query appropriate for the provided attribute
     * value assertion.
     *
     * @param <T>
     *          The type of index query created by the {@code factory}.
     * @param factory
     *          The index query factory which should be used to
     *          construct the index query.
     * @return The index query appropriate for the provided attribute
     *         value assertion.
     * @throws DecodeException
     *           If an error occurs while generating the index query.
     */
    <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException;
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractApproximateMatchingRuleImpl.java
@@ -28,6 +28,7 @@
import org.forgerock.opendj.ldap.Assertion;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.Indexer;
/**
 * This class implements an approximate matching rule that matches normalized
@@ -35,6 +36,8 @@
 */
abstract class AbstractApproximateMatchingRuleImpl extends AbstractMatchingRuleImpl {
    private final Indexer indexer = new DefaultIndexer("approximate");
    AbstractApproximateMatchingRuleImpl() {
        // Nothing to do.
    }
@@ -45,4 +48,9 @@
        return DefaultAssertion.approximate(normalizeAttributeValue(schema, assertionValue));
    }
    /** {@inheritDoc} */
    @Override
    public Indexer getIndexer() {
        return indexer;
    }
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractEqualityMatchingRuleImpl.java
@@ -28,6 +28,7 @@
import org.forgerock.opendj.ldap.Assertion;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.Indexer;
/**
 * This class implements an equality matching rule that matches normalized
@@ -35,6 +36,8 @@
 */
abstract class AbstractEqualityMatchingRuleImpl extends AbstractMatchingRuleImpl {
    private final Indexer indexer = new DefaultIndexer("equality");
    AbstractEqualityMatchingRuleImpl() {
        // Nothing to do.
    }
@@ -45,4 +48,9 @@
        return DefaultAssertion.equality(normalizeAttributeValue(schema, assertionValue));
    }
    /** {@inheritDoc} */
    @Override
    public Indexer getIndexer() {
        return indexer;
    }
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractMatchingRuleImpl.java
@@ -26,13 +26,18 @@
 */
package org.forgerock.opendj.ldap.schema;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import org.forgerock.opendj.ldap.Assertion;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
import org.forgerock.opendj.ldap.spi.Indexer;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
/**
 * This class implements a default equality or approximate matching rule that
@@ -41,7 +46,7 @@
abstract class AbstractMatchingRuleImpl implements MatchingRuleImpl {
    static final class DefaultAssertion implements Assertion {
        /** The ID of the DB index that to use with this assertion.*/
        /** The ID of the DB index to use with this assertion.*/
        private final String indexID;
        private final ByteSequence normalizedAssertionValue;
@@ -61,12 +66,42 @@
        public ConditionResult matches(final ByteSequence attributeValue) {
            return ConditionResult.valueOf(normalizedAssertionValue.equals(attributeValue));
        }
        public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException {
            return factory.createExactMatchQuery(indexID, normalizedAssertionValue);
    }
    }
    final class DefaultIndexer implements Indexer {
        /** The ID of the DB index to use with this indexer.*/
        private final String indexID;
        DefaultIndexer(String indexID) {
            this.indexID = indexID;
        }
        @Override
        public void createKeys(Schema schema, ByteSequence value, IndexingOptions options, Collection<ByteString> keys)
                throws DecodeException {
            keys.add(normalizeAttributeValue(schema, value));
        }
        @Override
        public String getIndexID() {
            return indexID;
        }
    };
    private static final Assertion UNDEFINED_ASSERTION = new Assertion() {
        public ConditionResult matches(final ByteSequence attributeValue) {
            return ConditionResult.UNDEFINED;
        }
        public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException {
            // Subclassing this class will always work, albeit inefficiently.
            // This is better than throwing an exception for no good reason.
            return factory.createMatchAllQuery();
        }
    };
    private static final Comparator<ByteSequence> DEFAULT_COMPARATOR =
@@ -104,4 +139,11 @@
            throws DecodeException {
        return UNDEFINED_ASSERTION;
    }
    /** {@inheritDoc} */
    @Override
    public boolean isIndexingSupported() {
        return getIndexer() != null;
    }
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractOrderingMatchingRuleImpl.java
@@ -31,12 +31,17 @@
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
import org.forgerock.opendj.ldap.spi.Indexer;
/**
 * This class implements a default ordering matching rule that matches
 * normalized values in byte order.
 */
abstract class AbstractOrderingMatchingRuleImpl extends AbstractMatchingRuleImpl {
    private final Indexer indexer = new DefaultIndexer("ordering");
    AbstractOrderingMatchingRuleImpl() {
        // Nothing to do.
    }
@@ -48,6 +53,11 @@
            public ConditionResult matches(final ByteSequence attributeValue) {
                return ConditionResult.valueOf(attributeValue.compareTo(normAssertion) < 0);
            }
            @Override
            public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException {
                return factory.createRangeMatchQuery("ordering", ByteString.empty(), normAssertion, false, false);
            }
        };
    }
@@ -59,6 +69,11 @@
            public ConditionResult matches(final ByteSequence attributeValue) {
                return ConditionResult.valueOf(attributeValue.compareTo(normAssertion) >= 0);
            }
            @Override
            public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException {
                return factory.createRangeMatchQuery("ordering", normAssertion, ByteString.empty(), true, false);
            }
        };
    }
@@ -70,6 +85,17 @@
            public ConditionResult matches(final ByteSequence attributeValue) {
                return ConditionResult.valueOf(attributeValue.compareTo(normAssertion) <= 0);
            }
            @Override
            public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException {
                return factory.createRangeMatchQuery("ordering", ByteString.empty(), normAssertion, false, true);
            }
        };
    }
    /** {@inheritDoc} */
    @Override
    public Indexer getIndexer() {
        return indexer;
    }
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractSubstringMatchingRuleImpl.java
@@ -28,8 +28,10 @@
import static com.forgerock.opendj.ldap.CoreMessages.*;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeSet;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.ldap.Assertion;
@@ -38,6 +40,9 @@
import org.forgerock.opendj.ldap.ByteStringBuilder;
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
import org.forgerock.opendj.ldap.spi.Indexer;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import com.forgerock.opendj.util.SubstringReader;
@@ -46,18 +51,34 @@
 * normalized substring assertion values in byte order.
 */
abstract class AbstractSubstringMatchingRuleImpl extends AbstractMatchingRuleImpl {
    static class DefaultSubstringAssertion implements Assertion {
    /**
     * Default assertion implementation for substring matching rules.
     * For example, with the assertion value "initial*any1*any2*any3*final",
     * the assertion will be decomposed like this:
     * <ul>
     * <li>normInitial will contain "initial"</li>
     * <li>normAnys will contain [ "any1", "any2", "any3" ]</li>
     * <li>normFinal will contain "final"</li>
     * </ul>
     */
    static final class DefaultSubstringAssertion implements Assertion {
        /** Normalized substring for the text before the first '*' character. */
        private final ByteString normInitial;
        /** Normalized substrings for all text chunks in between '*' characters. */
        private final ByteString[] normAnys;
        /** Normalized substring for the text after the last '*' character. */
        private final ByteString normFinal;
        protected DefaultSubstringAssertion(final ByteString normInitial,
        private DefaultSubstringAssertion(final ByteString normInitial,
                final ByteString[] normAnys, final ByteString normFinal) {
            this.normInitial = normInitial;
            this.normAnys = normAnys;
            this.normFinal = normFinal;
        }
        /** {@inheritDoc} */
        @Override
        public ConditionResult matches(final ByteSequence attributeValue) {
            final int valueLength = attributeValue.length();
@@ -125,12 +146,118 @@
            return ConditionResult.TRUE;
        }
        /** {@inheritDoc} */
        @Override
        public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException {
            final Collection<T> subqueries = new LinkedList<T>();
            if (normInitial != null) {
                // relies on the fact that equality indexes are also ordered
                subqueries.add(rangeMatch(factory, "equality", normInitial));
    }
            if (normAnys != null) {
                for (ByteString normAny : normAnys) {
                    substringMatch(factory, normAny, subqueries);
                }
            }
            if (normFinal != null) {
                substringMatch(factory, normFinal, subqueries);
            }
            if (normInitial != null) {
                // Add this one last to minimize the risk to run the same search twice
                // (possible overlapping with the use of equality index at the start of this method)
                substringMatch(factory, normInitial, subqueries);
            }
            return factory.createIntersectionQuery(subqueries);
        }
        private <T> T rangeMatch(IndexQueryFactory<T> factory, String indexID, ByteSequence lower) {
            // Iterate through all the keys that have this value as the prefix.
            // Set the upper bound for a range search.
            // We need a key for the upper bound that is of equal length
            // but slightly greater than the lower bound.
            final ByteStringBuilder upper = new ByteStringBuilder(lower);
            for (int i = upper.length() - 1; i >= 0; i--) {
                if (upper.byteAt(i) == 0xFF) {
                    // We have to carry the overflow to the more significant byte.
                    upper.setByte(i, (byte) 0);
                } else {
                    // No overflow, we can stop.
                    upper.setByte(i, (byte) (upper.byteAt(i) + 1));
                    break;
                }
            }
            // Read the range: lower <= keys < upper.
            return factory.createRangeMatchQuery(indexID, lower, upper, true, false);
        }
        private <T> void substringMatch(final IndexQueryFactory<T> factory, final ByteString normSubstring,
                final Collection<T> subqueries) {
            int substrLength = factory.getIndexingOptions().substringKeySize();
            // There are two cases, depending on whether the user-provided
            // substring is smaller than the configured index substring length or not.
            if (normSubstring.length() < substrLength) {
                subqueries.add(rangeMatch(factory, "substring", normSubstring));
            } else {
                // Break the value up into fragments of length equal to the
                // index substring length, and read those keys.
                // Eliminate duplicates by putting the keys into a set.
                final TreeSet<ByteSequence> substringKeys = new TreeSet<ByteSequence>();
                // Example: The value is ABCDE and the substring length is 3.
                // We produce the keys ABC BCD CDE.
                for (int first = 0, last = substrLength;
                     last <= normSubstring.length(); first++, last++) {
                    substringKeys.add(normSubstring.subSequence(first, first + substrLength));
                }
                for (ByteSequence key : substringKeys) {
                    subqueries.add(factory.createExactMatchQuery("substring", key));
                }
            }
        }
    }
    final class SubstringIndexer implements Indexer {
        /** {@inheritDoc} */
        @Override
        public void createKeys(Schema schema, ByteSequence value, IndexingOptions options, Collection<ByteString> keys)
                throws DecodeException {
            final ByteString normValue = normalizeAttributeValue(schema, value);
            final int substringKeySize = options.substringKeySize();
            // Example: The value is ABCDE and the substring length is 3.
            // We produce the keys ABC BCD CDE DE E
            // To find values containing a short substring such as DE,
            // iterate through keys with prefix DE. To find values
            // containing a longer substring such as BCDE, read keys BCD and CDE.
            for (int i = 0, remain = normValue.length(); remain > 0; i++, remain--) {
                int len = Math.min(substringKeySize, remain);
                keys.add(normValue.subSequence(i, i  + len));
            }
        }
        /** {@inheritDoc} */
        @Override
        public String getIndexID() {
            return "substring";
        }
    }
    private SubstringIndexer substringIndexer = new SubstringIndexer();
    AbstractSubstringMatchingRuleImpl() {
        // Nothing to do.
    }
    /** {@inheritDoc} */
    @Override
    public Assertion getAssertion(final Schema schema, final ByteSequence assertionValue)
            throws DecodeException {
@@ -143,7 +270,6 @@
        List<ByteSequence> anyStrings = null;
        final String valueString = assertionValue.toString();
        if (valueString.length() == 1 && valueString.charAt(0) == '*') {
            return getSubstringAssertion(schema, initialString, anyStrings, finalString);
        }
@@ -156,8 +282,7 @@
            initialString = normalizeSubString(schema, bytes);
        }
        if (reader.remaining() == 0) {
            throw DecodeException.error(WARN_ATTR_SYNTAX_SUBSTRING_NO_WILDCARDS.get(assertionValue
                    .toString()));
            throw DecodeException.error(WARN_ATTR_SYNTAX_SUBSTRING_NO_WILDCARDS.get(assertionValue));
        }
        while (true) {
            reader.read();
@@ -165,7 +290,7 @@
            if (reader.remaining() > 0) {
                if (bytes.length() == 0) {
                    throw DecodeException.error(WARN_ATTR_SYNTAX_SUBSTRING_CONSECUTIVE_WILDCARDS
                            .get(assertionValue.toString(), reader.pos()));
                            .get(assertionValue, reader.pos()));
                }
                if (anyStrings == null) {
                    anyStrings = new LinkedList<ByteSequence>();
@@ -182,6 +307,7 @@
        return getSubstringAssertion(schema, initialString, anyStrings, finalString);
    }
    /** {@inheritDoc} */
    @Override
    public Assertion getSubstringAssertion(final Schema schema, final ByteSequence subInitial,
            final List<? extends ByteSequence> subAnyElements, final ByteSequence subFinal)
@@ -436,4 +562,11 @@
            return ByteString.empty();
        }
    }
    /** {@inheritDoc} */
    @Override
    public Indexer getIndexer() {
        return substringIndexer;
    }
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CertificateExactMatchingRuleImpl.java
@@ -23,7 +23,6 @@
 *
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 *      Portions Copyright 2013-2014 Manuel Gaupp
 *      Copyright 2014 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -43,8 +42,8 @@
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ByteStringBuilder;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.GSERParser;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.GSERParser;
import com.forgerock.opendj.util.StaticUtils;
@@ -55,7 +54,7 @@
 * X.509 and referenced in RFC 4523.
 */
final class CertificateExactMatchingRuleImpl
        extends AbstractMatchingRuleImpl {
        extends AbstractEqualityMatchingRuleImpl {
    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/EqualLengthApproximateMatchingRuleImpl.java
@@ -31,6 +31,7 @@
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
/**
 * This class implements an extremely simple approximate matching rule that will
@@ -46,6 +47,11 @@
            public ConditionResult matches(final ByteSequence attributeValue) {
                return ConditionResult.valueOf(attributeValue.length() == assertionValue.length());
            }
            @Override
            public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException {
                return factory.createExactMatchQuery("approximate", ByteString.valueOf(assertionValue.length()));
            }
        };
    }
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/KeywordEqualityMatchingRuleImpl.java
@@ -34,6 +34,8 @@
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
import org.forgerock.opendj.ldap.spi.Indexer;
/**
 * This class implements the keywordMatch matching rule defined in X.520. That
@@ -107,9 +109,20 @@
                    return false;
                }
            }
            @Override
            public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException {
                return factory.createMatchAllQuery();
            }
        };
    }
    /** {@inheritDoc} */
    @Override
    public Indexer getIndexer() {
        return null;
    }
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        return ByteString.valueOf(normalize(value));
    }
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRule.java
@@ -43,6 +43,7 @@
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.Indexer;
/**
@@ -361,6 +362,15 @@
    }
    /**
     * Returns the indexer for this matching rule.
     *
     * @return the indexer for this matching rule.
     */
    public Indexer getIndexer() {
        return impl.getIndexer();
    }
    /**
     * Returns the name or OID for this schema definition. If it has one or more
     * names, then the primary name will be returned. If it does not have any
     * names, then the OID will be returned.
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleImpl.java
@@ -33,6 +33,7 @@
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.Indexer;
/**
 * This interface defines the set of methods that must be implemented to define
@@ -139,4 +140,18 @@
    ByteString normalizeAttributeValue(Schema schema, ByteSequence value)
            throws DecodeException;
    /**
     * Returns the indexer for this matching rule.
     *
     * @return the indexer for this matching rule.
     */
    Indexer getIndexer();
    /**
     * Returns whether a backend can build an index for this matching rule.
     *
     * @return true a backend can build an index for this matching rule,
     *         false otherwise.
     */
    boolean isIndexingSupported();
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/IndexQueryFactory.java
New file
@@ -0,0 +1,117 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions Copyright 2014 ForgeRock AS
 */
package org.forgerock.opendj.ldap.spi;
import java.util.Collection;
import org.forgerock.opendj.ldap.ByteSequence;
/**
 * A factory for creating arbitrarily complex index queries. This
 * interface is implemented by the underlying backend implementation
 * and passed to extensible matching rules so that they can construct
 * arbitrarily complex index queries.
 *
 * @param <T>
 *          The type of query created by this factory.
 */
public interface IndexQueryFactory<T> {
    /**
     * Returns a query requesting an index record matching the provided key.
     *
     * @param indexID
     *          An identifier of the index type.
     * @param key
     *          A byte sequence containing the key.
     * @return A query requesting the index record matching the key.
     */
    T createExactMatchQuery(String indexID, ByteSequence key);
    /**
     * Returns a query requesting all index records. A backend implementation
     * may choose to return all or no records as part of the optimization.
     *
     * @return A query requesting all index records.
     */
    T createMatchAllQuery();
    /**
     * Returns a query requesting all index records in the specified range.
     *
     * @param indexID
     *          An identifier of the index type.
     * @param lower
     *          The lower bound of the range. A 0 length byte array indicates no
     *          lower bound and the range will start from the smallest key.
     * @param upper
     *          The upper bound of the range. A 0 length byte array indicates no
     *          upper bound and the range will end at the largest key.
     * @param lowerIncluded
     *          true if a key exactly matching the lower bound is included in
     *          the range, false if only keys strictly greater than the lower
     *          bound are included. This value is ignored if the lower bound is
     *          not specified.
     * @param upperIncluded
     *          true if a key exactly matching the upper bound is included in
     *          the range, false if only keys strictly less than the upper bound
     *          are included. This value is ignored if the upper bound is not
     *          specified.
     * @return A query requesting all index records in the specified range.
     */
    T createRangeMatchQuery(String indexID, ByteSequence lower,
            ByteSequence upper, boolean lowerIncluded, boolean upperIncluded);
    /**
     * Returns a query which returns the intersection of a collection of
     * sub-queries.
     *
     * @param subqueries
     *          A collection of sub-queries.
     * @return A query which returns the intersection of a collection of
     *         sub-queries.
     */
    T createIntersectionQuery(Collection<T> subqueries);
    /**
     * Returns a query which combines the results of a collection of
     * sub-queries.
     *
     * @param subqueries
     *          A collection of sub-queries.
     * @return A query which combines the results of a collection of
     *         sub-queries.
     */
    T createUnionQuery(Collection<T> subqueries);
    /**
     * Returns the indexing options for this factory.
     *
     * @return the indexing options for this factory.
     */
    IndexingOptions getIndexingOptions();
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/Indexer.java
New file
@@ -0,0 +1,69 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.forgerock.opendj.ldap.spi;
import java.util.Collection;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.schema.Schema;
/**
 * This class is registered with a Backend and it provides call- backs
 * for indexing attribute values. An index implementation will use
 * this interface to create the keys for an attribute value.
 */
public interface Indexer {
    /**
     * Returns an index identifier associated with this indexer. An identifier
     * should be selected based on the matching rule type. A unique identifier
     * will map to a unique index database in the backend implementation. If
     * multiple matching rules need to share the index database, the
     * corresponding indexers should always use the same identifier.
     *
     * @return index ID A String containing the ID associated with this indexer.
     */
    String getIndexID();
    /**
     * Generates the set of index keys for an attribute.
     *
     * @param schema
     *          The schema in which the associated matching rule is defined.
     * @param value
     *          The attribute value for which keys are required.
     * @param options
     *          The indexing options
     * @param keys
     *          A collection where to add the created keys.
     * @throws DecodeException if an error occurs while normalizing the value
     */
    void createKeys(Schema schema, ByteSequence value, IndexingOptions options,
            Collection<ByteString> keys) throws DecodeException;
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/IndexingOptions.java
New file
@@ -0,0 +1,41 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *      Copyright 2014 ForgeRock AS
 */
package org.forgerock.opendj.ldap.spi;
/**
 * Contains options indicating how indexing must be performed.
 */
public interface IndexingOptions {
    /**
     * Returns the maximum size to use when building the keys for the
     * "substring" index.
     *
     * @return the maximum size to use when building the keys for the
     *         "substring" index.
     */
    int substringKeySize();
}