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

sin
31.06.2009 7ddf5a923866031a7034f457715d6a5e4ecbca67
Integrating the changes related to Collation indexing support
8 files added
16 files modified
3681 ■■■■ changed files
opends/resource/admin/admin.xsd 8 ●●●●● patch | view | raw | blame | history
opends/resource/admin/property-types.xsl 3 ●●●● patch | view | raw | blame | history
opends/resource/admin/property-types/extensible-matching-rule-type.xsl 42 ●●●●● patch | view | raw | blame | history
opends/resource/schema/02-config.ldif 7 ●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/LocalDBIndexConfiguration.xml 39 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/ExtensibleMatchingRuleTypePropertyDefinition.java 185 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/PropertyDefinitionUsageBuilder.java 13 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/PropertyDefinitionVisitor.java 19 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/PropertyValueVisitor.java 22 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/api/ExtensibleIndexer.java 92 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/api/ExtensibleMatchingRule.java 34 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/api/IndexQueryFactory.java 118 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java 709 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/BackendImpl.java 24 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/IndexFilter.java 33 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/IndexQuery.java 209 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/IndexQueryFactoryImpl.java 152 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/JEExtensibleIndexer.java 221 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/importLDIF/WorkThread.java 31 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/DirectoryServer.java 38 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java 1424 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/IndexConfig.java 41 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/Schema.java 199 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/ServerConstants.java 18 ●●●●● patch | view | raw | blame | history
opends/resource/admin/admin.xsd
@@ -1330,6 +1330,14 @@
        </xsd:annotation>
        <xsd:complexType />
      </xsd:element>
      <xsd:element name="extensible-matching-rule-type">
       <xsd:annotation>
          <xsd:documentation>
            Used for properties which contain Extensible matching rule type names.
          </xsd:documentation>
        </xsd:annotation>
        <xsd:complexType />
      </xsd:element>
      <xsd:element name="boolean">
        <xsd:annotation>
          <xsd:documentation>
opends/resource/admin/property-types.xsl
@@ -22,7 +22,7 @@
  ! CDDL HEADER END
  !
  !
  !      Copyright 2008 Sun Microsystems, Inc.
  !      Copyright 2008-2009 Sun Microsystems, Inc.
  ! -->
<xsl:stylesheet version="1.0" xmlns:adm="http://www.opends.org/admin"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
@@ -54,6 +54,7 @@
  <xsl:include href="property-types/password.xsl" />
  <xsl:include href="property-types/size.xsl" />
  <xsl:include href="property-types/string.xsl" />
  <xsl:include href="property-types/extensible-matching-rule-type.xsl" />
  <!--
    
    
opends/resource/admin/property-types/extensible-matching-rule-type.xsl
New file
@@ -0,0 +1,42 @@
<!--
  ! 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
  ! trunk/opends/resource/legal-notices/OpenDS.LICENSE
  ! or https://OpenDS.dev.java.net/OpenDS.LICENSE.
  ! 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
  ! trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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.
  ! -->
<xsl:stylesheet version="1.0" xmlns:adm="http://www.opends.org/admin"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <!--
    Templates for processing extensible matching rule type properties.
  -->
  <xsl:template match="adm:extensible-matching-rule-type" mode="java-value-imports">
    <import>org.opends.server.api.ExtensibleMatchingRule</import>
  </xsl:template>
  <xsl:template match="adm:extensible-matching-rule-type" mode="java-value-type">
    <xsl:value-of select="'ExtensibleMatchingRule'" />
  </xsl:template>
  <xsl:template match="adm:extensible-matching-rule-type"
    mode="java-definition-type">
    <xsl:value-of select="'ExtensibleMatchingRuleTypePropertyDefinition'" />
  </xsl:template>
</xsl:stylesheet>
opends/resource/schema/02-config.ldif
@@ -2387,6 +2387,10 @@
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.518
  NAME 'ds-cfg-index-extensible-matching-rule'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
  NAME 'ds-cfg-access-control-handler'
  SUP top
@@ -2465,7 +2469,8 @@
  MUST ( ds-cfg-attribute $
         ds-cfg-index-type )
  MAY ( ds-cfg-index-entry-limit $
        ds-cfg-substring-length )
              ds-cfg-substring-length $
              ds-cfg-index-extensible-matching-rule )
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.8
  NAME 'ds-cfg-schema-backend'
opends/src/admin/defn/org/opends/server/admin/std/LocalDBIndexConfiguration.xml
@@ -23,7 +23,7 @@
  ! CDDL HEADER END
  !
  !
  !      Copyright 2007-2008 Sun Microsystems, Inc.
  !      Copyright 2007-2009 Sun Microsystems, Inc.
  ! -->
<adm:managed-object name="local-db-index" plural-name="local-db-indexes"
  package="org.opends.server.admin.std"
@@ -150,6 +150,12 @@
            of searches using approximate matching search filters.
          </adm:synopsis>
        </adm:value>
        <adm:value name="extensible">
         <adm:synopsis>
            This index type is used to improve the efficiency
            of searches using extensible matching search filters.
         </adm:synopsis>
      </adm:value>
      </adm:enumeration>
    </adm:syntax>
    <adm:profile name="ldap">
@@ -184,4 +190,35 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="index-extensible-matching-rule" multi-valued="true">
    <adm:synopsis>
      The extensible matching rule in an extensible index.
    </adm:synopsis>
    <adm:description>
      An extensible matching rule must be specified using either LOCALE or OID of the matching rule.
    </adm:description>
    <adm:requires-admin-action>
      <adm:other>
        <adm:synopsis>
          The index must be rebuilt before it will reflect the
          new value.
        </adm:synopsis>
      </adm:other>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          No extensible matching rules will be indexed.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:extensible-matching-rule-type/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-index-extensible-matching-rule</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/server/org/opends/server/admin/ExtensibleMatchingRuleTypePropertyDefinition.java
New file
@@ -0,0 +1,185 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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.opends.server.admin;
import java.util.EnumSet;
import org.opends.server.api.ExtensibleMatchingRule;
import org.opends.server.core.DirectoryServer;
import static org.opends.server.util.Validator.ensureNotNull;
/**
 * Extensible Matching Rule Type Propertiy Definition.
 */
public final class ExtensibleMatchingRuleTypePropertyDefinition
        extends PropertyDefinition<ExtensibleMatchingRule>
{
  /**
   * An interface for incrementally constructing attribute type
   * property definitions.
   */
  public static class Builder extends
      AbstractBuilder<ExtensibleMatchingRule,
                                ExtensibleMatchingRuleTypePropertyDefinition> {
    // Private constructor
    private Builder(AbstractManagedObjectDefinition<?, ?> d,
        String propertyName) {
      super(d, propertyName);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    protected ExtensibleMatchingRuleTypePropertyDefinition buildInstance(
        AbstractManagedObjectDefinition<?, ?> d, String propertyName,
        EnumSet<PropertyOption> options,
        AdministratorAction adminAction,
        DefaultBehaviorProvider<ExtensibleMatchingRule> defaultBehavior) {
      return new ExtensibleMatchingRuleTypePropertyDefinition(d, propertyName,
          options, adminAction, defaultBehavior);
    }
  }
  /**
   * Create an extensible matching rule type property definition builder.
   *
   * @param d
   *          The managed object definition associated with this
   *          property definition.
   * @param propertyName
   *          The property name.
   * @return Returns the new attribute type property definition
   *         builder.
   */
  public static Builder createBuilder(
      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
    return new Builder(d, propertyName);
  }
  /**
   * Creates a new insantce of this class.
   */
  private ExtensibleMatchingRuleTypePropertyDefinition(
      AbstractManagedObjectDefinition<?, ?> d, String propertyName,
      EnumSet<PropertyOption> options,
      AdministratorAction adminAction,
      DefaultBehaviorProvider<ExtensibleMatchingRule> defaultBehavior) {
    super(d, ExtensibleMatchingRule.class, propertyName, options,
        adminAction, defaultBehavior);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
    return v.visitExtensibleMatchingRuleType(this, p);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public <R, P> R accept(PropertyValueVisitor<R, P> v,
      ExtensibleMatchingRule value, P p) {
    return v.visitExtensibleMatchingRuleType(this, value, p);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public int compare(ExtensibleMatchingRule o1, ExtensibleMatchingRule o2) {
    return o1.getOID().compareToIgnoreCase(o2.getOID());
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ExtensibleMatchingRule decodeValue(String value)
      throws IllegalPropertyValueStringException {
    ensureNotNull(value);
    String name = value.trim().toLowerCase();
    //Check if the name is a valid Matching rule OID or a Locale value.
    ExtensibleMatchingRule rule =
            DirectoryServer.getExtensibleMatchingRule(name);
    if (rule == null) {
      throw new IllegalPropertyValueStringException(this, value);
    } else {
      try {
        validateValue(rule);
        return rule;
      } catch (IllegalPropertyValueException e) {
        throw new IllegalPropertyValueStringException(this, value);
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String encodeValue(ExtensibleMatchingRule value)
      throws IllegalPropertyValueException {
    return value.getNameOrOID();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void validateValue(ExtensibleMatchingRule value)
      throws IllegalPropertyValueException {
    ensureNotNull(value);
    // No implementation required.
  }
}
opends/src/server/org/opends/server/admin/PropertyDefinitionUsageBuilder.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin;
import org.opends.messages.Message;
@@ -338,6 +338,17 @@
     * {@inheritDoc}
     */
    @Override
     public Message visitExtensibleMatchingRuleType(
            ExtensibleMatchingRuleTypePropertyDefinition d, Void p) {
        return Message.raw("LOCALE | OID");
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <T> Message visitUnknown(PropertyDefinition<T> d, Void p)
        throws UnknownPropertyDefinitionException {
      return Message.raw("?");
opends/src/server/org/opends/server/admin/PropertyDefinitionVisitor.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin;
@@ -269,6 +269,23 @@
  /**
   * Visit an extensible matching rule property definition.
   *
   * @param pd
   *          The string property definition to visit.
   * @param p
   *          A visitor specified parameter.
   * @return Returns a visitor specified result.
   */
  public R visitExtensibleMatchingRuleType(
          ExtensibleMatchingRuleTypePropertyDefinition pd, P p) {
    return visitUnknown(pd, p);
  }
  /**
   * Visit an unknown type of property definition. Implementations of
   * this method can provide default behavior for unknown property
   * definition types.
opends/src/server/org/opends/server/admin/PropertyValueVisitor.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin;
@@ -35,6 +35,7 @@
import org.opends.server.types.AttributeType;
import org.opends.server.types.DN;
import org.opends.server.authorization.dseecompat.Aci;
import org.opends.server.api.ExtensibleMatchingRule;
/**
@@ -308,6 +309,25 @@
  /**
   * Visit an extensible matching rule type.
   *
   * @param pd
   *          The extensible matching rule type property definition.
   * @param v
   *          The property value to visit.
   * @param p
   *          A visitor specified parameter.
   * @return Returns a visitor specified result.
   */
  public R visitExtensibleMatchingRuleType(
          ExtensibleMatchingRuleTypePropertyDefinition pd,
          ExtensibleMatchingRule v, P p) {
    return visitUnknown(pd, v, p);
  }
  /**
   * Visit an unknown type of property value. Implementations of this
   * method can provide default behavior for unknown types of
   * property.
opends/src/server/org/opends/server/api/ExtensibleIndexer.java
New file
@@ -0,0 +1,92 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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.opends.server.api;
import java.util.Map;
import java.util.Set;
import org.opends.server.types.AttributeValue;
/**
 * 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.
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.VOLATILE,
     mayInstantiate=false,
     mayExtend=true,
     mayInvoke=false)
public abstract class ExtensibleIndexer
{
  /**
   * Returns the index name preferred by this indexer. This name
   * appended with the identifier returned from
   * {@link #getExtensibleIndexID()} will be used as the index
   * database name.
   * @return  The name of the index for this indexer.
   */
  public abstract String getPreferredIndexName();
  /**
   * 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.
   */
  public abstract String getExtensibleIndexID();
  /**
   * Generates the set of index keys for an attribute.
   * @param value The attribute value  for which  keys are required.
   * @param keys The set into which the generated keys will be
   *                          inserted.
   */
  public abstract void getKeys(AttributeValue value,
                                                  Set<byte[]> keys);
  /**
   * Generates a map of index keys and a boolean flag indicating
   * whether the corresponding key will be inserted or deleted.
   * @param value The attribute for which keys are required.
   * @param modifiedKeys A map containing the keys and a boolean.
   *              Keys corresponding to the boolean value <code>true
   *              </code> should be inserted and <code>false</code>
   *              should be deleted.
   * @param insert <code>true</code> if generated keys should
   *            be inserted or <code>false</code> otherwise.
   */
  public abstract void getKeys(AttributeValue value,
                                  Map<byte[], Boolean> modifiedKeys,
                                  Boolean insert);
}
opends/src/server/org/opends/server/api/ExtensibleMatchingRule.java
@@ -22,10 +22,14 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.api;
import java.util.Collection;
import org.opends.server.types.ByteString;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.IndexConfig;
/**
@@ -40,7 +44,33 @@
     mayInvoke=false)
public abstract class ExtensibleMatchingRule extends MatchingRule
{
  /**
  * Returns a collection of extensible indexers associated with this
  * matching rule.
  * @param config The index configuration to be used by this
  *                      matching rule.
  * @return ExtensibleIndexer associated with this matching rule.
  */
  public  abstract Collection<ExtensibleIndexer> getIndexers(
          IndexConfig config);
  //TODO: Index Implementation.
  /**
   * Queries the index using factory of type T and returns
   * a query of type T for the provided assertion value.
   * @param  <T>  The type of IndexQueryFactory.
   * @param  assertionValue  An assertion value which needs to be
   *                                               queried.
   * @param factory  An IndexQueryFactory which will be used for
   *                                creating  queries.
   * @return T  The generated index query.
   * @throws DirectoryException  If an  error occurs while generating
   *                the query.
   */
  public  abstract <T> T createIndexQuery(
                   ByteString assertionValue,
                   IndexQueryFactory<T> factory)
                                          throws DirectoryException;
}
opends/src/server/org/opends/server/api/IndexQueryFactory.java
New file
@@ -0,0 +1,118 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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.opends.server.api;
import java.util.Collection;
/**
 * This class acts as a factory for creating 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 Results  returned by the factory.
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.VOLATILE,
     mayInstantiate=false,
     mayExtend=true,
     mayInvoke=false)
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 array containing the key.
   * @return A query requesting the index record matching the key.
   */
  T createExactMatchQuery(String indexID,byte[] 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();
  /**
   * Rreturns 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,
                              byte[]  lower,
                              byte[] upper,
                              boolean lowerIncluded,
                              boolean upperIncluded);
  /**
   * Returns a query requesting  intersection from a Collection of
   * sub-queries.
   *@param  subquery  A Collection of sub-queries.
   *@return A query requesting intersection of  the records.
   */
  T createIntersectionQuery(Collection<T> subquery);
  /**
   * Returns a query requesting union from a Collection of
   * sub-queries.
   * @param  subquery  A Collection of sub-queries.
   * @return A query requesting union of the records.
   */
  T createUnionQuery(Collection<T> subquery);
}
opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.backends.jeb;
import org.opends.messages.Message;
@@ -42,8 +42,12 @@
import org.opends.server.admin.std.server.LocalDBIndexCfg;
import org.opends.server.admin.std.meta.LocalDBIndexCfgDefn;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.api.ExtensibleMatchingRule;
import org.opends.server.api.IndexQueryFactory;
import org.opends.server.config.ConfigException;
import static org.opends.messages.JebMessages.*;
import static org.opends.server.util.ServerConstants.*;
import org.opends.server.core.DirectoryServer;
import org.opends.server.util.StaticUtils;
@@ -113,6 +117,12 @@
   */
  Index approximateIndex = null;
  /**
   * The ExtensibleMatchingRuleIndex instance for ExtensibleMatchingRule
   * indexes.
   */
  private  ExtensibleMatchingRuleIndex extensibleIndexes = null;
  private State state;
  private int cursorEntryLimit = 100000;
@@ -238,7 +248,61 @@
                                        env,
                                        entryContainer);
    }
    if (indexConfig.getIndexType().contains(
        LocalDBIndexCfgDefn.IndexType.EXTENSIBLE))
    {
      Set<ExtensibleMatchingRule> extensibleRules =
              indexConfig.getIndexExtensibleMatchingRule();
      if(extensibleRules == null || extensibleRules.size() == 0)
      {
        Message message = ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(
            String.valueOf(attrType), "extensible");
        throw new ConfigException(message);
      }
      extensibleIndexes = new ExtensibleMatchingRuleIndex();
      //Iterate through the Set and create the index only if necessary.
      //Collation equality and Ordering matching rules share the same
      //indexer and index. A Collation substring matching rule is treated
      // differently as it uses a separate indexer and index.
      IndexConfig config = new JEIndexConfig(indexConfig.getSubstringLength());
      for(ExtensibleMatchingRule rule:extensibleRules)
      {
        Map<String,Index> indexMap = new HashMap<String,Index>();
        for(ExtensibleIndexer indexer : rule.getIndexers(config))
        {
          String indexerId = indexer.getExtensibleIndexID();
          String indexID =
                  attrType.getNameOrOID()  + "."
                    + indexer.getPreferredIndexName()
                    + "." + indexerId;
          if(!extensibleIndexes.isIndexPresent(indexID))
          {
            //There is no index available for this index id. Create a new index.
            Indexer extensibleIndexer =
                    new JEExtensibleIndexer(attrType,
                                               rule,
                                               indexer);
            String indexName = entryContainer.getDatabasePrefix() + "_"
                                                  + indexID;
            Index extensibleIndex = new Index(indexName,
                                      extensibleIndexer,
                                      state,
                                      indexEntryLimit,
                                      cursorEntryLimit,
                                      false,
                                      env,
                                      entryContainer);
              extensibleIndexes.addIndex(extensibleIndex,indexID);
          }
        extensibleIndexes.addRule(indexID, rule);
        indexMap.put(indexer.getExtensibleIndexID(),
                extensibleIndexes.getIndex(indexID));
      }
      IndexQueryFactory<IndexQuery> factory =
              new IndexQueryFactoryImpl(indexMap);
      extensibleIndexes.addQueryFactory(rule, factory);
      }
    }
    this.indexConfig.addChangeListener(this);
  }
@@ -274,6 +338,14 @@
    {
      approximateIndex.open();
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.open();
      }
    }
  }
  /**
@@ -309,6 +381,14 @@
      approximateIndex.close();
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.close();
      }
    }
    indexConfig.removeChangeListener(this);
    // The entryContainer is responsible for closing the JE databases.
  }
@@ -388,6 +468,17 @@
      }
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        if(!extensibleIndex.addEntry(buffer, entryID,entry))
        {
          success = false;
        }
      }
    }
    return success;
  }
@@ -448,6 +539,17 @@
      }
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        if(!extensibleIndex.addEntry(txn, entryID,entry))
        {
          success = false;
        }
      }
    }
    return success;
  }
@@ -488,6 +590,14 @@
    {
      approximateIndex.removeEntry(buffer, entryID, entry);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.removeEntry(buffer, entryID, entry);
      }
    }
  }
  /**
@@ -526,6 +636,14 @@
    {
      approximateIndex.removeEntry(txn, entryID, entry);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.removeEntry(txn, entryID, entry);
      }
    }
  }
  /**
@@ -571,6 +689,14 @@
    {
      approximateIndex.modifyEntry(txn, entryID, oldEntry, newEntry, mods);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.modifyEntry(txn, entryID, oldEntry, newEntry, mods);
      }
    }
  }
  /**
@@ -616,6 +742,14 @@
    {
      approximateIndex.modifyEntry(buffer, entryID, oldEntry, newEntry, mods);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.modifyEntry(buffer, entryID, oldEntry, newEntry, mods);
      }
    }
  }
  /**
@@ -1249,6 +1383,14 @@
    {
      approximateIndex.closeCursor();
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.closeCursor();
      }
    }
  }
  /**
@@ -1287,6 +1429,14 @@
          approximateIndex.getEntryLimitExceededCount();
    }
     if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        entryLimitExceededCount +=
                extensibleIndex.getEntryLimitExceededCount();
      }
    }
    return entryLimitExceededCount;
  }
@@ -1320,6 +1470,14 @@
    {
      dbList.add(approximateIndex);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        dbList.add(extensibleIndex);
      }
    }
  }
  /**
@@ -1384,6 +1542,18 @@
        return false;
      }
    }
    if (cfg.getIndexType().contains(LocalDBIndexCfgDefn.IndexType.EXTENSIBLE))
    {
      Set<ExtensibleMatchingRule> newRules =
              cfg.getIndexExtensibleMatchingRule();
      if (newRules == null || newRules.size() == 0)
      {
        Message message = ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(
                String.valueOf(attrType), "extensible");
        unacceptableReasons.add(message);
        return false;
      }
    }
    return true;
  }
@@ -1721,6 +1891,171 @@
        }
      }
      if (cfg.getIndexType().contains(
              LocalDBIndexCfgDefn.IndexType.EXTENSIBLE))
      {
        Set<ExtensibleMatchingRule> extensibleRules =
            cfg.getIndexExtensibleMatchingRule();
        if(extensibleIndexes == null)
        {
          extensibleIndexes = new ExtensibleMatchingRuleIndex();
        }
        IndexConfig config = new JEIndexConfig(cfg.getSubstringLength());
        for(ExtensibleMatchingRule rule:extensibleRules)
        {
          Map<String,Index> indexMap = new HashMap<String,Index>();
          for(ExtensibleIndexer indexer: rule.getIndexers(config))
          {
            String indexerId = indexer.getExtensibleIndexID();
            String indexID =
                  attrType.getNameOrOID()  + "."
                   + indexer.getPreferredIndexName()
                   + "." + indexerId;
            if(!extensibleIndexes.isIndexPresent(indexID))
            {
              Indexer extensibleIndexer =
                      new JEExtensibleIndexer(attrType,
                                                 rule,
                                                 indexer);
              String indexName =  entryContainer.getDatabasePrefix() + "_"
                      + indexID;
              Index extensibleIndex = new Index(indexName,
                                        extensibleIndexer,
                                        state,
                                        indexEntryLimit,
                                        cursorEntryLimit,
                                        false,
                                        env,
                                        entryContainer);
              extensibleIndexes.addIndex(extensibleIndex,indexID);
            }
            else
            {
              Index extensibleIndex = extensibleIndexes.getIndex(indexID);
              if(extensibleIndex.setIndexEntryLimit(indexEntryLimit))
              {
                adminActionRequired = true;
                Message message =
                      NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(
                              extensibleIndex.getName());
                messages.add(message);
              }
              if(indexConfig.getSubstringLength() !=
              cfg.getSubstringLength())
              {
                Indexer extensibleIndexer =
                      new JEExtensibleIndexer(attrType,
                                                 rule,
                                                 indexer);
                extensibleIndex.setIndexer(extensibleIndexer);
              }
            }
            extensibleIndexes.addRule(indexID, rule);
            indexMap.put(indexerId,extensibleIndexes.getIndex(indexID));
          }
          IndexQueryFactory<IndexQuery> factory =
                  new IndexQueryFactoryImpl(indexMap);
          extensibleIndexes.addQueryFactory(rule, factory);
        }
        //Some rules might have been removed from the configuration.
        Set<ExtensibleMatchingRule> deletedRules =
                new HashSet<ExtensibleMatchingRule>();
        for(ExtensibleMatchingRule r:extensibleIndexes.getRules())
        {
          if(!extensibleRules.contains(r))
          {
            deletedRules.add(r);
          }
        }
        if(deletedRules.size() > 0)
        {
          entryContainer.exclusiveLock.lock();
          try
          {
            for(ExtensibleMatchingRule rule:deletedRules)
            {
              Set<ExtensibleMatchingRule> rules =
                      new HashSet<ExtensibleMatchingRule>();
              List<String> ids = new ArrayList<String>();
              for(ExtensibleIndexer indexer: rule.getIndexers(config))
              {
                String id = attrType.getNameOrOID()  + "."
                 + indexer.getPreferredIndexName()
                 + "." + indexer.getExtensibleIndexID();
                rules.addAll(extensibleIndexes.getRules(id));
                ids.add(id);
              }
              if(rules.isEmpty())
              {
                //Rule has been already deleted.
                continue;
              }
              //If all the rules are part of the deletedRules, delete
              //this index.
              if(deletedRules.containsAll(rules))
              {
                //it is safe to delete this index as it is not shared.
                for(String indexID : ids)
                {
                  Index extensibleIndex = extensibleIndexes.getIndex(indexID);
                  entryContainer.deleteDatabase(extensibleIndex);
                  extensibleIndex = null;
                  extensibleIndexes.deleteIndex(indexID);
                  extensibleIndexes.deleteRule(indexID);
                }
              }
              else
              {
                for(String indexID : ids)
                {
                  extensibleIndexes.deleteRule(rule, indexID);
                }
              }
            }
          }
          catch(DatabaseException de)
          {
            messages.add(
                  Message.raw(StaticUtils.stackTraceToSingleLineString(de)));
            ccr = new ConfigChangeResult(
              DirectoryServer.getServerErrorResultCode(), false, messages);
            return ccr;
          }
          finally
          {
            entryContainer.exclusiveLock.unlock();
          }
        }
      }
      else
      {
        if(extensibleIndexes != null)
        {
          entryContainer.exclusiveLock.lock();
          try
          {
            for(Index extensibleIndex:extensibleIndexes.getIndexes())
            {
              entryContainer.deleteDatabase(extensibleIndex);
              extensibleIndex =  null;
            }
            extensibleIndexes.deleteAll();
          }
          catch(DatabaseException de)
          {
            messages.add(
                  Message.raw(StaticUtils.stackTraceToSingleLineString(de)));
            ccr = new ConfigChangeResult(
              DirectoryServer.getServerErrorResultCode(), false, messages);
            return ccr;
          }
          finally
          {
            entryContainer.exclusiveLock.unlock();
          }
        }
      }
      indexConfig = cfg;
      return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired,
@@ -1770,6 +2105,14 @@
    {
      approximateIndex.setTrusted(txn, trusted);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.setTrusted(txn, trusted);
      }
    }
  }
  /**
@@ -1798,11 +2141,22 @@
      return false;
    }
    if (approximateIndex != null && approximateIndex.isTrusted())
    if (approximateIndex != null && !approximateIndex.isTrusted())
    {
      return false;
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        if(extensibleIndex !=null && !extensibleIndex.isTrusted())
        {
          return false;
        }
      }
    }
    return true;
  }
@@ -1837,6 +2191,14 @@
    {
      approximateIndex.setRebuildStatus(rebuildRunning);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.setRebuildStatus(rebuildRunning);
      }
    }
  }
  /**
@@ -1900,6 +2262,21 @@
  }
  /**
   * Return the mapping of  extensible index types and indexes.
   *
   * @return The Map of extensible index types and indexes.
   */
  public Map<String,Collection<Index>> getExtensibleIndexes()
  {
    if(extensibleIndexes == null)
    {
      return Collections.emptyMap();
    }
    return extensibleIndexes.getIndexMap();
  }
  /**
   * Retrieves all the indexes used by this attribute index.
   *
   * @return A collection of all indexes in use by this attribute
@@ -1933,6 +2310,332 @@
      indexes.add(approximateIndex);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        indexes.add(extensibleIndex);
      }
    }
    return indexes;
  }
  /**
   * Retrieve the entry IDs that might match an extensible filter.
   *
   * @param extensibleFilter The extensible filter.
   * @param debugBuffer If not null, a diagnostic string will be written
   *                     which will help determine how the indexes contributed
   *                     to this search.
   * @return The candidate entry IDs that might contain the filter
   *         assertion value.
   */
  public EntryIDSet evaluateExtensibleFilter(SearchFilter extensibleFilter,
                                              StringBuilder debugBuffer)
  {
    //Get the Matching Rule OID of the filter.
    String nOID  = extensibleFilter.getMatchingRuleID();
    ExtensibleMatchingRule rule =
            DirectoryServer.getExtensibleMatchingRule(nOID);
    IndexQueryFactory<IndexQuery> factory = null;
    if(extensibleIndexes == null
            || (factory = extensibleIndexes.getQueryFactory(rule))==null)
    {
      // There is no index on this matching rule.
      return IndexQuery.createNullIndexQuery().evaluate();
    }
    try
    {
      if(debugBuffer != null)
      {
        debugBuffer.append("[INDEX:");
        IndexConfig config =
                new JEIndexConfig(indexConfig.getSubstringLength());
        for(ExtensibleIndexer indexer :  rule.getIndexers(config))
        {
          String indexerID = indexer.getExtensibleIndexID();
          String indexName = indexer.getPreferredIndexName();
          String indexID = " "
                         + extensibleFilter.getAttributeType().getNameOrOID()
                         + "." + indexName
                         + "." +indexerID;
          debugBuffer.append(indexID);
        }
        debugBuffer.append("]");
      }
      ByteString assertionValue =
              extensibleFilter.getAssertionValue().getValue();
      IndexQuery expression = rule.createIndexQuery(assertionValue, factory);
      return expression.evaluate();
    }
    catch (DirectoryException e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      return IndexQuery.createNullIndexQuery().evaluate();
    }
  }
  /**
   * This class manages all the configured extensible matching rules and
   * their corresponding indexes.
   */
  private class ExtensibleMatchingRuleIndex
  {
    /**
      * The mapping of index ID and Index database.
      */
    private final Map<String,Index> id2IndexMap;
    /**
     * The mapping of Index ID and Set the matching rules.
     */
    private final Map<String,Set<ExtensibleMatchingRule>> id2RulesMap;
    /**
     * The Map of configured ExtensibleMatchingRule and the corresponding
     * IndexQueryFactory.
     */
    private final Map<ExtensibleMatchingRule,
            IndexQueryFactory<IndexQuery>> rule2FactoryMap;
    /**
     * Creates a new instance of ExtensibleMatchingRuleIndex.
     */
    private ExtensibleMatchingRuleIndex()
    {
      id2IndexMap = new HashMap<String,Index>();
      id2RulesMap = new HashMap<String,Set<ExtensibleMatchingRule>>();
      rule2FactoryMap = new HashMap<ExtensibleMatchingRule,
              IndexQueryFactory<IndexQuery>>();
    }
    /**
     * Returns all configured ExtensibleMatchingRule instances.
     * @return A Set  of extensible matching rules.
     */
    private Set<ExtensibleMatchingRule> getRules()
    {
      return rule2FactoryMap.keySet();
    }
    /**
     * Returns  ExtensibleMatchingRule instances for an index.
     * @param indexID The index ID of an extensible matching rule index.
     * @return A Set of extensible matching rules corresponding to
     *                 an index ID.
     */
    private Set<ExtensibleMatchingRule>
            getRules(String indexID)
    {
      Set<ExtensibleMatchingRule> rules = id2RulesMap.get(indexID);
      if(rules == null)
      {
        return Collections.emptySet();
      }
      else
      {
        return Collections.unmodifiableSet(id2RulesMap.get(indexID));
      }
    }
    /**
     * Returns whether an index is present or not.
     * @param indexID The index ID of an extensible matching rule index.
     * @return True if an index is present. False if there is no matching index.
     */
    private boolean isIndexPresent(String indexID)
    {
      return id2IndexMap.containsKey(indexID);
    }
    /**
     * Returns the index corresponding to an index ID.
     * @param indexID The ID of an index.
     * @return The extensible rule index corresponding to the index ID.
     */
    private Index getIndex(String indexID)
    {
      return id2IndexMap.get(indexID);
    }
    /**
     * Adds a new matching Rule and the name of the associated index.
     * @indexName Name of the index.
     * @rule An ExtensibleMatchingRule instance that needs to be indexed.
     */
    private void addRule(String indexName,ExtensibleMatchingRule rule)
    {
      Set<ExtensibleMatchingRule> rules = id2RulesMap.get(indexName);
      if(rules == null)
      {
        rules = new HashSet<ExtensibleMatchingRule>();
        id2RulesMap.put(indexName, rules);
      }
      rules.add(rule);
    }
    /**
     * Adds a new Index and its name.
     * @param index The extensible matching rule index.
     * @indexName The name of the index.
     */
    private void addIndex(Index index,String indexName)
    {
      id2IndexMap.put(indexName, index);
    }
    /**
     * Returns all the configured extensible indexes.
     * @return All the available extensible matching rule indexes.
     */
    private Collection<Index> getIndexes()
    {
      return Collections.unmodifiableCollection(id2IndexMap.values());
    }
    /**
     * Returns a map of all the configured extensible indexes and their types.
     * @return A map of all the available extensible matching rule indexes
     *             and their types.
     */
    private Map<String,Collection<Index>> getIndexMap()
    {
      if(id2IndexMap.isEmpty())
      {
        return Collections.emptyMap();
      }
      Collection<Index> substring = new ArrayList<Index>();
      Collection<Index> shared = new ArrayList<Index>();
      for(Map.Entry<String,Index> entry :  id2IndexMap.entrySet())
      {
        String indexID = entry.getKey();
        if(indexID.endsWith(EXTENSIBLE_INDEXER_ID_SUBSTRING))
        {
          substring.add(entry.getValue());
        }
        else
        {
          shared.add(entry.getValue());
        }
      }
      Map<String,Collection<Index>> indexMap =
              new HashMap<String,Collection<Index>>();
      indexMap.put(EXTENSIBLE_INDEXER_ID_SUBSTRING, substring);
      indexMap.put(EXTENSIBLE_INDEXER_ID_SHARED, shared);
      return Collections.unmodifiableMap(indexMap);
    }
    /**
     * Deletes an index corresponding to the index ID.
     * @param indexID Name of the index.
     */
    private void deleteIndex(String indexID)
    {
      id2IndexMap.remove(indexID);
    }
    /**
     * Deletes an extensible matching rule from the list of available rules.
     * @param rule The ExtensibleMatchingRule that needs to be removed.
     * @param indexID The name of the index corresponding to the rule.
     */
    private void deleteRule(ExtensibleMatchingRule rule,String indexID)
    {
      Set<ExtensibleMatchingRule> rules = id2RulesMap.get(indexID);
      rules.remove(rule);
      if(rules.size() == 0)
      {
        id2RulesMap.remove(indexID);
      }
      rule2FactoryMap.remove(rule);
    }
    /**
     * Adds an ExtensibleMatchingRule and its corresponding IndexQueryFactory.
     * @param rule An ExtensibleMatchingRule that needs to be added.
     * @param query A query factory matching the rule.
     */
    private void addQueryFactory(ExtensibleMatchingRule rule,
            IndexQueryFactory<IndexQuery> query)
    {
      rule2FactoryMap.put(rule, query);
    }
    /**
     * Returns the query factory associated with the rule.
     * @param rule An ExtensibleMatchingRule that needs to be searched.
     * @return An IndexQueryFactory corresponding to the matching rule.
     */
    private IndexQueryFactory<IndexQuery> getQueryFactory(
            ExtensibleMatchingRule rule)
    {
      return rule2FactoryMap.get(rule);
    }
    /**
     * Deletes  extensible matching rules from the list of available rules.
     * @param indexID The name of the index corresponding to the rules.
     */
    private void deleteRule(String indexID)
    {
      Set<ExtensibleMatchingRule> rules  = id2RulesMap.get(indexID);
      rule2FactoryMap.remove(rules);
      rules.clear();
      id2RulesMap.remove(indexID);
    }
    /**
     * Deletes all references to matching rules and the indexes.
     */
    private void deleteAll()
    {
      id2IndexMap.clear();
      id2RulesMap.clear();
      rule2FactoryMap.clear();
    }
  }
  /**
   * This class extends the IndexConfig for JE Backend.
   */
  private class JEIndexConfig extends IndexConfig
  {
    //The length of the substring index.
    private int substringLength;
    /**
     * Creates a new JEIndexConfig instance.
     * @param substringLength The length of the substring.
     */
    private JEIndexConfig(int substringLength)
    {
      this.substringLength = substringLength;
    }
    /**
     * Returns the length of the substring.
     * @return the length of the substring.
     */
   public int getSubstringLength()
   {
     return substringLength;
   }
  }
}
opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2007-2008 Sun Microsystems, Inc.
 *      Copyright 2007-2009 Sun Microsystems, Inc.
 */
package org.opends.server.backends.jeb;
import org.opends.messages.Message;
@@ -69,9 +69,10 @@
import org.opends.server.admin.std.server.LocalDBIndexCfg;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.types.DN;
import org.opends.server.backends.jeb.importLDIF.Importer;
import org.opends.server.api.ExtensibleMatchingRule;
/**
 * This is an implementation of a Directory Server Backend which stores entries
 * locally in a Berkeley DB JE database.
@@ -1131,13 +1132,30 @@
      envConfig.setConfigParam("je.env.runCheckpointer", "false");
      //Loop through local indexes and see if any are substring.
      boolean hasSubIndex = false;
subIndex:
      for (String idx : cfg.listLocalDBIndexes()) {
        LocalDBIndexCfg indexCfg = cfg.getLocalDBIndex(idx);
        final LocalDBIndexCfg indexCfg = cfg.getLocalDBIndex(idx);
        Set<org.opends.server.admin.std.meta.LocalDBIndexCfgDefn.IndexType>
                                            indexType = indexCfg.getIndexType();
        if(indexType.contains(org.opends.server.admin.std.
                meta.LocalDBIndexCfgDefn.IndexType.SUBSTRING)) {
          hasSubIndex = true;
          break;
        }
        Set<ExtensibleMatchingRule> matchingRules =
                              indexCfg.getIndexExtensibleMatchingRule();
        for(ExtensibleMatchingRule rule: matchingRules)
        {
          for(ExtensibleIndexer indexer: rule.getIndexers(null))
          {
            String indexID = indexer.getExtensibleIndexID();
            if(indexID.equals(EXTENSIBLE_INDEXER_ID_SUBSTRING))
            {
              //The ExtensibelMatchingRule is of substring type.
              hasSubIndex = true;
              break subIndex;
            }
          }
        }
      }
      Importer importer = new Importer(importConfig, hasSubIndex);
opends/src/server/org/opends/server/backends/jeb/IndexFilter.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.backends.jeb;
@@ -180,8 +180,15 @@
        candidates = evaluateApproximateFilter(filter);
        break;
      case NOT:
      case EXTENSIBLE_MATCH:
         if (buffer!= null)
        {
          filter.toString(buffer);
        }
        candidates = evaluateExtensibleFilter(filter);
        break;
      case NOT:
      default:
        if (buffer != null)
        {
@@ -537,4 +544,26 @@
    return candidates;
  }
  /**
   * Evaluate an extensible filter against the indexes.
   *
   * @param extensibleFilter The extensible filter to be evaluated.
   * @return A set of entry IDs representing candidate entries.
   */
  private EntryIDSet evaluateExtensibleFilter(SearchFilter extensibleFilter)
  {
    EntryIDSet candidates;
    AttributeIndex attributeIndex =
         entryContainer.getAttributeIndex(extensibleFilter.getAttributeType());
    if (attributeIndex == null)
    {
      candidates = IndexQuery.createNullIndexQuery().evaluate();
    }
    else
    {
      candidates =
          attributeIndex.evaluateExtensibleFilter(extensibleFilter, buffer);
    }
    return candidates;
  }
}
opends/src/server/org/opends/server/backends/jeb/IndexQuery.java
New file
@@ -0,0 +1,209 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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.opends.server.backends.jeb;
import java.util.Collection;
import static org.opends.server.backends.jeb.IndexFilter.*;
/**
 * This class represents a JE Backend Query.
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.VOLATILE,
     mayInstantiate=false,
     mayExtend=true,
     mayInvoke=false)
public abstract class IndexQuery
{
  /**
  * Evaluates the index query and returns the EntryIDSet.
  * @return The EntryIDSet as a result of evaulation of this query.
  */
  public abstract EntryIDSet evaluate();
   /**
    * Creates an IntersectionIndexQuery object from a collection of IndexQuery
    * objects.
    * @param subIndexQueries A collection of IndexQuery objects.
    * @return An IntersectionIndexQuery object.
    */
   public static IndexQuery createIntersectionIndexQuery(
           Collection<IndexQuery> subIndexQueries)
   {
     return new IntersectionIndexQuery(subIndexQueries);
   }
   /**
    * Creates a union IndexQuery object from a collection of IndexQuery
    * objects.
    * @param subIndexQueries Collection of IndexQuery objects.
    * @return A UnionIndexQuery object.
    */
   public static IndexQuery createUnionIndexQuery(
           Collection<IndexQuery> subIndexQueries)
   {
     return new UnionIndexQuery(subIndexQueries);
   }
   /**
    * Creates an empty IndexQuery object.
    * @return A NullIndexQuery object.
    */
   public static IndexQuery createNullIndexQuery()
   {
     return new NullIndexQuery();
   }
}
/**
* This class creates a Null  IndexQuery. It is used when there
*  is no record in the index. It may also be used when the
 * index contains all the records but an empty EntryIDSet should be
 * returned as part of the optimization.
*/
final class NullIndexQuery extends IndexQuery
{
  /**
   * {@inheritDoc}
   */
  @Override
  public EntryIDSet evaluate()
  {
    return new EntryIDSet();
  }
}
/**
 * This class creates an intersection IndexQuery from a collection of
 * IndexQuery objects.
 */
final class IntersectionIndexQuery extends IndexQuery
{
  /**
   * Collection of IndexQuery objects.
   */
  private final  Collection<IndexQuery> subIndexQueries;
  /**
   * Creates an instance of IntersectionIndexQuery.
   * @param subIndexQueries Collection of IndexQuery objects.
   */
  IntersectionIndexQuery(Collection<IndexQuery> subIndexQueries)
  {
    this.subIndexQueries = subIndexQueries;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public EntryIDSet evaluate()
  {
   EntryIDSet entryIDs = null;
   for (IndexQuery query : subIndexQueries)
   {
     if (entryIDs == null)
     {
       entryIDs = query.evaluate();
     }
     else
     {
       entryIDs.retainAll(query.evaluate());
     }
     if (entryIDs.isDefined() &&
         entryIDs.size() <= FILTER_CANDIDATE_THRESHOLD)
     {
       break;
     }
   }
   return entryIDs;
  }
}
/**
 * This class creates a union of IndexQuery objects.
 */
final class UnionIndexQuery extends IndexQuery
{
  /**
   * Collection containing IndexQuery objects.
   */
  private final Collection<IndexQuery> subIndexQueries;
  /**
   * Creates an instance of UnionIndexQuery.
   * @param subIndexQueries The Collection of IndexQuery objects.
   */
  UnionIndexQuery(Collection<IndexQuery> subIndexQueries)
  {
    this.subIndexQueries = subIndexQueries;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public EntryIDSet evaluate()
  {
    EntryIDSet entryIDs = null;
    for (IndexQuery query : subIndexQueries)
    {
      if (entryIDs == null)
      {
        entryIDs = query.evaluate();
      }
      else
      {
        entryIDs.addAll(query.evaluate());
      }
      if (entryIDs.isDefined() &&
          entryIDs.size() <= FILTER_CANDIDATE_THRESHOLD)
      {
        break;
      }
    }
    return entryIDs;
  }
}
opends/src/server/org/opends/server/backends/jeb/IndexQueryFactoryImpl.java
New file
@@ -0,0 +1,152 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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.opends.server.backends.jeb;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.LockMode;
import java.util.Collection;
import java.util.Map;
import org.opends.server.api.IndexQueryFactory;
/**
 * This class is an implementation of IndexQueryFactory which creates
 * IndexQuery objects as part of the query of the JEB index.
*/
public final class IndexQueryFactoryImpl
        implements IndexQueryFactory<IndexQuery>
{
  /**
   * The Map containing the string type identifier and the corresponding index.
   */
  private Map<String,Index> indexMap;
  /**
   * Creates a new IndexQueryFactoryImpl object.
   * @param indexMap A map containing the index id and the corresponding index.
   */
  public IndexQueryFactoryImpl(Map<String,Index> indexMap)
  {
    this.indexMap = indexMap;
  }
  /**
   *{@inheritDoc}
   */
  public IndexQuery createExactMatchQuery(final String indexID,
          final byte[] value)
  {
    return new IndexQuery()
    {
      @Override
      public EntryIDSet evaluate()
      {
        //Read the database and get Record for the key.
        DatabaseEntry key = new DatabaseEntry(value);
        //Select the right index to be used.
        Index index = indexMap.get(indexID);
        EntryIDSet entrySet = index.readKey(key,null,LockMode.DEFAULT);
        return entrySet;
      }
    };
  }
  /**
   *{@inheritDoc}
   */
  public IndexQuery createRangeMatchQuery(
                                              final String indexID,
                                              final byte[] lowerBound,
                                              final byte[] upperBound,
                                              final boolean includeLowerBound,
                                              final boolean includeUpperBound)
  {
    return new IndexQuery()
    {
      @Override
      public EntryIDSet evaluate()
      {
        //Find the right index.
        Index index = indexMap.get(indexID);
        EntryIDSet entrySet =   index.readRange(lowerBound,upperBound,
                includeLowerBound,
            includeUpperBound);
        return entrySet;
      }
    };
  }
  /**
   *{@inheritDoc}
   */
  public IndexQuery  createIntersectionQuery(Collection<IndexQuery>
                                                                subqueries)
  {
    return IndexQuery.createIntersectionIndexQuery(subqueries);
  }
  /**
   *{@inheritDoc}
   */
  public IndexQuery createUnionQuery(Collection<IndexQuery> subqueries)
  {
    return IndexQuery.createUnionIndexQuery(subqueries);
  }
  /**
   *{@inheritDoc}
   * It returns an empty EntryIDSet object  when either all or no record sets
   * are requested.
   */
  public IndexQuery createMatchAllQuery()
  {
    return new IndexQuery()
    {
      @Override
      public EntryIDSet evaluate()
      {
        return new EntryIDSet();
      }
    };
  }
}
opends/src/server/org/opends/server/backends/jeb/JEExtensibleIndexer.java
New file
@@ -0,0 +1,221 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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.opends.server.backends.jeb;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
import org.opends.server.api.ExtensibleMatchingRule;
import org.opends.server.types.Attribute;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
 *This class implements an Indexer for extensible matching rules in JE Backend.
 */
public final class JEExtensibleIndexer extends Indexer
{
  /**
   * The comparator for index keys generated by this class.
   */
  private static final Comparator<byte[]> comparator =
       new AttributeIndex.KeyComparator();
  /**
   * The attribute type for which this instance will
   * generate index keys.
   */
  private final AttributeType attributeType;
  /**
   * The extensible indexer which will generate the keys
   * for the associated  extensible matching rule.
   */
  private final ExtensibleIndexer extensibleIndexer;
  /**
   * The extensible matching rule which needs to be indexed.
   */
  private final ExtensibleMatchingRule matchingRule;
  /**
   * Creates a new extensible indexer for JE backend.
   *
   * @param attributeType The attribute type for which an indexer is
   *                                            required.
   * @param matchingRule  The extensible matching rule to be indexed.
   * @param extensibleIndexer The extensible indexer to be used.
   */
  public JEExtensibleIndexer(AttributeType attributeType,
          ExtensibleMatchingRule matchingRule,
          ExtensibleIndexer extensibleIndexer)
  {
    this.attributeType = attributeType;
    this.matchingRule = matchingRule;
    this.extensibleIndexer = extensibleIndexer;
  }
   /**
   * Gets a string representation of this object.  The returned value is
   * used to name an index created using this object.
   * @return A string representation of this object.
   */
  @Override
  public String toString()
  {
    return attributeType.getNameOrOID() + "."
            + extensibleIndexer.getExtensibleIndexID();
  }
  /**
   * Gets the comparator that must be used to compare index keys
   * generated by this class.
   *
   * @return A byte array comparator.
   */
  public Comparator<byte[]> getComparator()
  {
    return comparator;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void indexEntry(Entry entry, Set<byte[]> keys)
  {
    List<Attribute> attrList =
         entry.getAttribute(attributeType);
    if (attrList != null)
    {
      indexAttribute(attrList, keys);
    }
  }
 /**
   * {@inheritDoc}
   */
  @Override
  public void replaceEntry(Entry oldEntry, Entry newEntry,
                           Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void modifyEntry(Entry oldEntry, Entry newEntry,
                          List<Modification> mods,
                          Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
  /**
   * Generates the set of extensible  index keys for an attribute.
   * @param attrList The attribute for which substring keys are required.
   * @param keys The set into which the generated keys will be inserted.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Set<byte[]> keys)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      for (AttributeValue value : attr)
      {
        extensibleIndexer.getKeys(value, keys);
      }
    }
  }
  /**
   * Generates the set of index keys for an attribute.
   * @param attrList The attribute to be indexed.
   * @param modifiedKeys The map into which the modified
   * keys will be inserted.
   * @param insert <code>true</code> if generated keys should
   * be inserted or <code>false</code> otherwise.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Map<byte[], Boolean> modifiedKeys,
                              Boolean insert)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      for (AttributeValue value : attr)
      {
        extensibleIndexer.getKeys(value,modifiedKeys,insert);
      }
    }
  }
}
opends/src/server/org/opends/server/backends/jeb/importLDIF/WorkThread.java
@@ -22,12 +22,13 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.backends.jeb.importLDIF;
import static org.opends.server.loggers.debug.DebugLogger.*;
import  static org.opends.server.util.ServerConstants.*;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.*;
import org.opends.server.api.DirectoryThread;
@@ -37,7 +38,6 @@
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.*;
import org.opends.server.util.*;
import com.sleepycat.je.*;
@@ -127,6 +127,7 @@
   * to flush and exit.
   *
   */
  @Override
  public void run()
  {
    try {
@@ -222,6 +223,12 @@
        if((index=attributeIndex.getApproximateIndex()) != null) {
          delete(index, existingEntry, entryID);
        }
        for(Collection<Index> indexes :
                      attributeIndex.getExtensibleIndexes().values()) {
          for(Index extensibleIndex: indexes) {
            delete(extensibleIndex,existingEntry,entryID);
          }
        }
      }
      for(VLVIndex vlvIdx : context.getEntryContainer().getVLVIndexes()) {
          vlvIdx.removeEntry(txn, entryID, existingEntry);
@@ -276,6 +283,26 @@
        for(VLVIndex vlvIdx : context.getEntryContainer().getVLVIndexes()) {
            vlvIdx.addEntry(txn, entryID, entry);
        }
        Map<String,Collection<Index>> extensibleMap =
                attributeIndex.getExtensibleIndexes();
        if(!extensibleMap.isEmpty()) {
          Collection<Index> subIndexes =
                attributeIndex.getExtensibleIndexes().get(
                EXTENSIBLE_INDEXER_ID_SUBSTRING);
          if(subIndexes != null) {
            for(Index subIndex: subIndexes) {
              bufferMgr.insert(subIndex, entry, entryID, insertKeySet);
            }
          }
          Collection<Index> sharedIndexes =
                attributeIndex.getExtensibleIndexes().get(
                EXTENSIBLE_INDEXER_ID_SHARED);
          if(sharedIndexes !=null) {
            for(Index sharedIndex:sharedIndexes) {
              insert(sharedIndex,entry,entryID);
            }
          }
        }
      }
    }
  }
opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.core;
@@ -75,6 +75,7 @@
import org.opends.server.api.AccessControlHandler;
import org.opends.server.api.plugin.PluginType;
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.api.ExtensibleMatchingRule;
import org.opends.server.backends.RootDSEBackend;
import static org.opends.server.config.ConfigConstants.DN_CONFIG_ROOT;
import static org.opends.server.config.ConfigConstants.DN_MONITOR_ROOT;
@@ -3515,6 +3516,41 @@
  /**
   * Retrieves the set of extensible matching rules registered with the
   * Directory Server.  The mapping will be between the lowercase name or OID
   * for each extensible matching rule and the matching rule implementation. The
   * same extensible matching rule instance may be included multiple times with
   * different keys.
   *
   * @return  The set of extensible matching rules registered with the Directory
   *          Server.
   */
  public static Map<String,ExtensibleMatchingRule>
                     getExtensibleMatchingRules()
  {
    return directoryServer.schema.getExtensibleMatchingRules();
  }
  /**
   * Retrieves the extensible matching rule with the specified name or OID.
   *
   * @param  lowerName  The lowercase name or OID for the extensible matching
   *                rule  to retrieve.
   *
   * @return  The requested extensible matching rule, or <CODE>null</CODE> if no
   *          such matching rule has been defined in the server.
   */
  public static ExtensibleMatchingRule
          getExtensibleMatchingRule(String lowerName)
  {
    return directoryServer.schema.getExtensibleMatchingRule(lowerName);
  }
  /**
   * Retrieves the set of objectclasses defined in the Directory Server.
   *
   * @return  The set of objectclasses defined in the Directory Server.
opends/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
@@ -44,21 +44,27 @@
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.opends.messages.Message;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.meta.CollationMatchingRuleCfgDefn.
        MatchingRuleType;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.api.IndexQueryFactory;
import org.opends.server.api.MatchingRuleFactory;
import org.opends.server.admin.std.server.CollationMatchingRuleCfg;
import org.opends.server.api.ExtensibleMatchingRule;
import org.opends.server.api.MatchingRule;
import org.opends.server.backends.jeb.AttributeIndex;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ByteString;
import org.opends.server.types.ConditionResult;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.IndexConfig;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
@@ -68,6 +74,8 @@
import static org.opends.messages.CoreMessages.*;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.api.ExtensibleIndexer.*;
import static org.opends.server.util.ServerConstants.*;
@@ -332,7 +340,6 @@
    for(String collation:configuration.getCollation())
    {
      CollationMapper mapper = new CollationMapper(collation);
      String nOID = mapper.getNumericOID();
      String languageTag = mapper.getLanguageTag();
      Locale locale = getLocale(languageTag);
      createLessThanMatchingRule(mapper,locale);
@@ -510,26 +517,26 @@
    names.add(lTag);
    matchingRule =
          new CollationEqualityMatchingRule(nOID,names,locale);
          new CollationEqualityMatchingRule(nOID,
                  Collections.<String>emptySet(),locale);
    addMatchingRule(nOID, matchingRule);
    // Register OID.3 as the equality matching rule.
    String OID = mapper.getNumericOID() + ".3";
    MatchingRule equalityMatchingRule = getMatchingRule(OID);
    Collection<String> equalityNames = new HashSet<String>();
    if(equalityMatchingRule!=null)
    {
      for(String name: equalityMatchingRule.getAllNames())
      {
        equalityNames.add(name);
        names.add(name);
      }
    }
    equalityNames.add(lTag+".eq");
    equalityNames.add(lTag+".3");
    names.add(lTag+".eq");
    names.add(lTag+".3");
    equalityMatchingRule =
          new CollationEqualityMatchingRule(OID,equalityNames,locale);
          new CollationEqualityMatchingRule(OID,names,locale);
    addMatchingRule(OID, equalityMatchingRule);
  }
@@ -686,34 +693,50 @@
 /**
  *Collation rule for Equality matching rule.
   * Collation Extensible matching rule.
  */
  private final class CollationEqualityMatchingRule
  private abstract class CollationMatchingRule
         extends ExtensibleMatchingRule
  {
    //Names for this class.
    private final Collection<String> names;
    //Collator for performing equality match.
    private final Collator collator;
    protected final Collator collator;
    //Numeric OID of the rule.
    private final String nOID;
    //Locale associated with this rule.
    private final Locale locale;
    //Indexer of this rule.
    protected ExtensibleIndexer indexer;
    /**
     * Constructs a new CollationEqualityMatchingRule.
     * Constructs a new CollationMatchingRule.
     *
     * @param nOID OID of the collation matching rule
     * @param names names of this matching rule
     * @param locale Locale of the collation matching rule
     */
    private CollationEqualityMatchingRule(String nOID,Collection<String> names,
    private CollationMatchingRule(String nOID,Collection<String> names,
            Locale locale)
    {
      super();
      this.names = names;
      this.collator =createCollator(locale);
      this.locale = locale;
      this.nOID = nOID;
    }
@@ -747,6 +770,7 @@
    }
    /**
     * {@inheritDoc}
     */
@@ -782,6 +806,74 @@
    /**
    * Returns the name of the index database for this matching rule.
    * An index name for this rule will be based upon the Locale. This will
    * ensure that multiple collation matching rules corresponding to the same
    * Locale can share the same index database.
    * @return  The name of the index for this matching rule.
    */
    public String getIndexName()
    {
      String language = locale.getLanguage();
      String country = locale.getCountry();
      String variant = locale.getVariant();
      StringBuilder builder = new StringBuilder(language);
      if (country != null && country.length() > 0)
      {
        builder.append("_");
        builder.append(locale.getCountry());
      }
      if (variant != null && variant.length() > 0)
      {
        builder.append("_");
        builder.append(locale.getVariant());
      }
      return builder.toString();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<ExtensibleIndexer> getIndexers(IndexConfig config)
    {
      if(indexer == null)
      {
        //The default implementation contains shared indexer and doesn't use the
        //config.
        indexer = new CollationSharedExtensibleIndexer(this);
      }
      return Collections.singletonList(indexer);
    }
  }
  /**
   *Collation rule for Equality matching rule.
   */
  private final class CollationEqualityMatchingRule
          extends CollationMatchingRule
  {
    /**
     * Constructs a new CollationEqualityMatchingRule.
     *
     * @param nOID OID of the collation matching rule
     * @param names names of this matching rule
     * @param locale Locale of the collation matching rule
     */
    private CollationEqualityMatchingRule(String nOID,Collection<String> names,
            Locale locale)
    {
      super(nOID,names,locale);
    }
    /**
     * {@inheritDoc}
     */
    @Override
@@ -829,23 +921,35 @@
        return ConditionResult.FALSE;
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <T> T createIndexQuery(ByteString assertionValue,
            IndexQueryFactory<T> factory) throws DirectoryException
    {
      //Normalize the assertion value.
      ByteString normalValue = normalizeValue(assertionValue);
      return factory.createExactMatchQuery(
                indexer.getExtensibleIndexID(),
                normalValue.value());
  }
  }
 /**
  * Collation rule for Substring matching rule.
  */
  private final class CollationSubstringMatchingRule
         extends ExtensibleMatchingRule
          extends CollationMatchingRule
  {
    //Names for this class.
    private final Collection<String> names;
    //Substring Indexer associated with this instance.
    private CollationSubstringExtensibleIndexer subIndexer;
    //Collator for performing equality match.
    private final Collator collator;
    //Numeric OID of the rule.
    private final String nOID;
    /**
@@ -858,71 +962,7 @@
    private CollationSubstringMatchingRule(String nOID,
            Collection<String> names,Locale locale)
    {
      super();
      this.names = names;
      this.collator =createCollator(locale);
      this.nOID = nOID;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getName()
    {
      //Concatenate all the names and return.
      StringBuilder builder = new StringBuilder();
      for(String name: getAllNames())
      {
        builder.append(name);
        builder.append("\b");
      }
      return builder.toString();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<String> getAllNames()
    {
      return Collections.unmodifiableCollection(names);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getOID()
    {
      return nOID;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getDescription()
    {
      return null;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getSyntaxOID()
    {
      return SYNTAX_DIRECTORY_STRING_OID;
      super(nOID,names,locale);
    }
@@ -941,10 +981,79 @@
   /**
    * {@inheritDoc}
     * Utility class which abstracts a substring assertion value.
    */
    @Override
    public  ByteString normalizeAssertionValue(ByteString value)
    private final class Assertion
    {
      //Initial part of the substring filter.
      private String subInitial;
      //any parts of the substring filter.
      private List<String> subAny;
      //Final part of the substring filter.
      private String subFinal;
      /**
       * Creates a new instance of Assertion.
       * @param subInitial Initial part of the filter.
       * @param subAny  Any part of the filter.
       * @param subFinal Final part of the filter.
       */
      Assertion(String subInitial, List<String> subAny, String subFinal)
      {
        this.subInitial = subInitial;
        this.subAny = subAny;
        this.subFinal = subFinal;
      }
      /**
       * Returns the Initial part of the assertion.
       * @return Initial part of assertion.
       */
      private String getInitial()
      {
        return subInitial;
      }
      /**
       * Returns the any part of the assertion.
       * @return Any part of the assertion.
       */
      private List<String> getAny()
      {
        return subAny;
      }
      /**
       * Returns the final part of the assertion.
       * @return Final part of the assertion.
       */
      private String getFinal()
      {
        return subFinal;
      }
    }
    /**
     * Parses the assertion from a given value.
     * @param value The value that needs to be parsed.
     * @return The parsed Assertion object containing the
     * @throws org.opends.server.types.DirectoryException
     */
    private Assertion parseAssertion(ByteString value)
           throws DirectoryException
    {
      // Get a string  representation of the value.
@@ -1094,7 +1203,19 @@
          subFinal = filterString.substring(firstPos+1, length + firstPos + 1);
        }
      }
      return new Assertion(subInitial, subAny, subFinal);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public ByteString normalizeAssertionValue(ByteString value)
            throws DirectoryException {
      Assertion assertion = parseAssertion(value);
      String subInitial = assertion.getInitial();
      // Normalize the Values in the following format:
      // initialLength, initial, numberofany, anyLength1, any1, anyLength2,
      // any2, ..., anyLengthn, anyn, finalLength, final
@@ -1117,8 +1238,8 @@
          normalizedList.add((int)initialBytes[i]);
        }
      }
      if(subAny.size()==0)
      {
      List<String> subAny = assertion.getAny();
      if (subAny.size() == 0) {
        normalizedList.add(0);
      }
      else
@@ -1136,6 +1257,7 @@
          }
        }
      }
      String subFinal = assertion.getFinal();
      if(subFinal ==null)
      {
        normalizedList.add(0);
@@ -1268,103 +1390,294 @@
      return ConditionResult.TRUE;
     }
    /**
     * {@inheritDoc}
     */
    @Override
    public final Collection<ExtensibleIndexer> getIndexers(IndexConfig config)
    {
      Collection<ExtensibleIndexer> indexers =
              new ArrayList<ExtensibleIndexer>();
      int substrLength = 6; //Default substring length;
      if(subIndexer == null)
      {
        if(config != null)
        {
          substrLength = config.getSubstringLength();
        }
        subIndexer = new CollationSubstringExtensibleIndexer(this,
                substrLength);
      }
      else
      {
        if(config !=null)
        {
          if(config.getSubstringLength() !=subIndexer.gerSubstringLength())
          {
            subIndexer.setSubstringLength(substrLength);
          }
        }
  }
 /**
  *Collation rule for less-than matching rule.
  */
  private final class CollationLessThanMatchingRule
         extends ExtensibleMatchingRule
      if(indexer == null)
  {
    //Names for this class.
    private final Collection<String> names;
        indexer = new CollationSharedExtensibleIndexer(this);
      }
      indexers.add(subIndexer);
      indexers.add(indexer);
      return indexers;
    }
    //Collator for performing equality match.
    private final Collator collator;
    //Numeric OID of the rule.
    private final String nOID;
    /**
     * Constructs a new CollationLessThanMatchingRule.
     * Decomposes an attribute value into a set of substring index keys.
     *
     * @param attValue Tthe normalized attribute value
     * @param set A set into which the keys will be inserted.
     */
    private void subtringKeys(ByteString attValue,
            Set<byte[]> keys)
    {
      String value = attValue.stringValue();
      int keyLength = subIndexer.gerSubstringLength();
      for (int i = 0, remain = value.length(); remain > 0; i++, remain--)
      {
        int len = Math.min(keyLength, remain);
        byte[] keyBytes = makeSubstringKey(value, i, len);
        keys.add(keyBytes);
      }
    }
    /**
     * Decomposes an attribute value into a set of substring index keys.
     *
     * @param value The normalized attribute value
     * @param modifiedKeys The map into which the modified
     *  keys will be inserted.
     * @param insert <code>true</code> if generated keys should
     * be inserted or <code>false</code> otherwise.
     */
    private void substringKeys(ByteString attValue,
            Map<byte[], Boolean> modifiedKeys,
            Boolean insert)
    {
      String value = attValue.stringValue();
      int keyLength = subIndexer.gerSubstringLength();
      for (int i = 0, remain = value.length(); remain > 0; i++, remain--)
      {
        int len = Math.min(keyLength, remain);
        byte[] keyBytes = makeSubstringKey(value, i, len);
        Boolean cinsert = modifiedKeys.get(keyBytes);
        if (cinsert == null)
        {
          modifiedKeys.put(keyBytes, insert);
        }
        else if (!cinsert.equals(insert))
        {
          modifiedKeys.remove(keyBytes);
        }
      }
    }
    /**
     * Makes a byte array representing a substring index key for
     * one substring of a value.
     *
     * @param value  The String containing the value.
     * @param pos The starting position of the substring.
     * @param len The length of the substring.
     * @return A byte array containing a substring key.
     */
    private byte[] makeSubstringKey(String value, int pos, int len)
    {
      String sub = value.substring(pos, pos + len);
      CollationKey col = collator.getCollationKey(sub);
      byte[] origKey = col.toByteArray();
      byte[] newKey = new byte[origKey.length - 4];
      System.arraycopy(origKey, 0, newKey, 0, newKey.length);
      return newKey;
    }
    /**
     * Uses an equality index to retrieve the entry IDs that might contain a
     * given initial substring.
     * @param bytes A normalized initial substring of an attribute value.
     * @return The candidate entry IDs.
     */
    private <T> T matchInitialSubstring(String value,
            IndexQueryFactory<T> factory)
    {
      byte[] lower = makeSubstringKey(value, 0, value.length());
      byte[] upper = new byte[lower.length];
      System.arraycopy(lower, 0, upper, 0, lower.length);
      for (int i = upper.length - 1; i >= 0; i--)
      {
        if (upper[i] == 0xFF)
        {
          // We have to carry the overflow to the more significant byte.
          upper[i] = 0;
        }
        else
        {
          // No overflow, we can stop.
          upper[i] = (byte) (upper[i] + 1);
          break;
        }
      }
      //Use the shared equality indexer.
      return factory.createRangeMatchQuery(
                              indexer.getExtensibleIndexID(),
                              lower,
                              upper,
                              true,
                              false);
    }
    /**
     * Retrieves the Index Records that might contain a given substring.
     * @param value A String representing  the attribute value.
     * @param factory An IndexQueryFactory which issues calls to the backend.
     * @param substrLength The length of the substring.
     * @return The candidate entry IDs.
     */
    private <T> T matchSubstring(String value,
            IndexQueryFactory<T> factory)
    {
      T intersectionQuery = null;
      int substrLength = subIndexer.gerSubstringLength();
      if (value.length() < substrLength)
      {
        byte[] lower = makeSubstringKey(value, 0, value.length());
        byte[] upper = makeSubstringKey(value, 0, value.length());
        for (int i = upper.length - 1; i >= 0; i--)
        {
          if (upper[i] == 0xFF)
          {
            // We have to carry the overflow to the more significant byte.
            upper[i] = 0;
          } else
          {
            // No overflow, we can stop.
            upper[i] = (byte) (upper[i] + 1);
            break;
          }
        }
        // Read the range: lower <= keys < upper.
        intersectionQuery =
                factory.createRangeMatchQuery(
                subIndexer.getExtensibleIndexID(),
                lower,
                upper,
                true,
                false);
      }
      else
      {
        List<T> queryList = new ArrayList<T>();
        Set<byte[]> set =
                new TreeSet<byte[]>(new AttributeIndex.KeyComparator());
        for (int first = 0, last = substrLength;
                last <= value.length(); first++, last++)
        {
          byte[] keyBytes;
          keyBytes = makeSubstringKey(value, first, substrLength);
          set.add(keyBytes);
        }
        for (byte[] keyBytes : set)
        {
          T single = factory.createExactMatchQuery(
                  subIndexer.getExtensibleIndexID(),
                  keyBytes);
          queryList.add(single);
        }
        intersectionQuery =
                factory.createIntersectionQuery(queryList);
      }
      return intersectionQuery;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <T> T createIndexQuery(ByteString assertionValue,
            IndexQueryFactory<T> factory) throws DirectoryException
    {
      Assertion assertion = parseAssertion(assertionValue);
      String subInitial = assertion.getInitial();
      List<String> subAny = assertion.getAny();
      String subFinal = assertion.getFinal();
      List<T> queries = new ArrayList<T>();
      if (subInitial == null && subAny.size() == 0 && subFinal == null)
      {
        //Can happen with a filter like "cn:en.6:=*".
        //Just return an empty record.
        return factory.createMatchAllQuery();
      }
      List<String> elements = new ArrayList<String>();
      if (subInitial != null)
      {
        //Always use the shared indexer for initial match.
        T query = matchInitialSubstring(subInitial, factory);
        queries.add(query);
      }
      if (subAny != null && subAny.size() > 0)
      {
        elements.addAll(subAny);
      }
      if (subFinal != null)
      {
        elements.add(subFinal);
      }
      for (String element : elements)
      {
        queries.add(matchSubstring(element, factory));
      }
      return factory.createIntersectionQuery(queries);
    }
  }
  /**
   *An abstract Collation rule for Ordering  matching rule.
   */
  private abstract class CollationOrderingMatchingRule
          extends CollationMatchingRule
  {
    /**
     * Constructs a new CollationOrderingMatchingRule.
     *
     * @param nOID OID of the collation matching rule
     * @param names names of this matching rule
     * @param locale Locale of the collation matching rule
     */
    private CollationLessThanMatchingRule(String nOID,
    private CollationOrderingMatchingRule(String nOID,
            Collection<String> names,Locale locale)
    {
      super();
      this.names = names;
      this.collator =createCollator(locale);
      this.nOID = nOID;
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public String getName()
    {
       //Concatenate all the names and return.
      StringBuilder builder = new StringBuilder();
      for(String name: getAllNames())
      {
        builder.append(name);
        builder.append("\b");
      }
      return builder.toString();
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public Collection<String> getAllNames()
    {
      return Collections.unmodifiableCollection(names);
    }
    /**
    * {@inheritDoc}
    */
    @Override
    public String getOID()
    {
      return nOID;
    }
    /**
    * {@inheritDoc}
    */
    @Override
    public String getDescription()
    {
      // There is no standard description for this matching rule.
      return null;
    }
    /**
    * {@inheritDoc}
    */
    @Override
    public String getSyntaxOID()
    {
      return SYNTAX_DIRECTORY_STRING_OID;
      super(nOID,names,locale);
    }
@@ -1397,8 +1710,7 @@
    *          ascending order, or zero if there is no difference
    *          between the values with regard to ordering.
    */
    private int compare(byte[] b1, byte[] b2)
    {
    protected int compare(byte[] b1, byte[] b2) {
      //Compare values using byte arrays.
      int minLength = Math.min(b1.length, b2.length);
@@ -1434,6 +1746,28 @@
        return 1;
      }
    }
  }
  /**
   * Collation matching rule for Less-than matching rule.
   */
  private final class CollationLessThanMatchingRule
          extends CollationOrderingMatchingRule
  {
    /**
     * Constructs a new CollationLessThanMatchingRule.
     *
     * @param nOID OID of the collation matching rule
     * @param names names of this matching rule
     * @param locale Locale of the collation matching rule
     */
    private CollationLessThanMatchingRule(String nOID,
            Collection<String> names, Locale locale)
    {
      super(nOID, names, locale);
    }
@@ -1455,24 +1789,35 @@
        return ConditionResult.FALSE;
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <T> T createIndexQuery(ByteString assertionValue,
            IndexQueryFactory<T> factory) throws DirectoryException
    {
      byte[] lower = new byte[0];
      byte[] upper = normalizeValue(assertionValue).value();
      return factory.createRangeMatchQuery(indexer.getExtensibleIndexID(),
              lower,
              upper,
              false,
              false);
  }
  }
  /**
  * Collation rule for less-than-equal-to matching rule.
  */
  private final class CollationLessThanOrEqualToMatchingRule
         extends ExtensibleMatchingRule
          extends CollationOrderingMatchingRule
  {
    //Names for this class.
    private final Collection<String> names;
    //Collator for performing equality match.
    private final Collator collator;
    //Numeric OID of the rule.
    private final String nOID;
    /**
     * Constructs a new CollationLessThanOrEqualToMatchingRule.
@@ -1485,147 +1830,11 @@
                                    Collection<String> names,
                                    Locale locale)
    {
      super();
      this.names = names;
      this.collator =createCollator(locale);
      this.nOID = nOID;
      super(nOID, names, locale);
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public String getName()
    {
      //Concatenate all the names and return.
      StringBuilder builder = new StringBuilder();
      for(String name: getAllNames())
      {
        builder.append(name);
        builder.append("\b");
      }
      return builder.toString();
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public Collection<String> getAllNames()
    {
      return Collections.unmodifiableCollection(names);
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public String getOID()
    {
      return nOID;
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public String getDescription()
    {
      // There is no standard description for this matching rule.
      return null;
    }
    /**
    * {@inheritDoc}
    */
    @Override
    public String getSyntaxOID()
    {
      return SYNTAX_DIRECTORY_STRING_OID;
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public ByteString normalizeValue(ByteString value)
           throws DirectoryException
    {
      CollationKey key = collator.getCollationKey(value.stringValue());
      return new ASN1OctetString(key.toByteArray());
    }
   /**
    * Compares the first value to the second and returns a value that
    * indicates their relative order.
    *
    * @param  b1  The normalized form of the first value to
    *                 compare.
    * @param  b2  The normalized form of the second value to
    *                 compare.
    *
    * @return  A negative integer if {@code value1} should come before
    *          {@code value2} in ascending order, a positive integer if
    *          {@code value1} should come after {@code value2} in
    *          ascending order, or zero if there is no difference
    *          between the values with regard to ordering.
    */
    public int compare(byte[] b1, byte[] b2)
    {
      //Compare values using byte arrays.
      int minLength = Math.min(b1.length, b2.length);
      for (int i=0; i < minLength; i++)
      {
        int firstByte = 0xFF & ((int)b1[i]);
        int secondByte = 0xFF & ((int)b2[i]);
        if (firstByte == secondByte)
        {
          continue;
        }
        else if (firstByte < secondByte)
        {
          return -1;
        }
        else if (firstByte > secondByte)
        {
          return 1;
        }
      }
      if (b1.length == b2.length)
      {
        return 0;
      }
      else if (b1.length < b2.length)
      {
        return -1;
      }
      else
      {
        return 1;
      }
    }
    /**
     * {@inheritDoc}
@@ -1645,6 +1854,27 @@
        return ConditionResult.FALSE;
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <T> T createIndexQuery(ByteString assertionValue,
            IndexQueryFactory<T> factory)
            throws DirectoryException
    {
      byte[] lower = new byte[0];
      byte[] upper = normalizeValue(assertionValue).value();
      // Read the range: lower < keys <= upper.
      return factory.createRangeMatchQuery(indexer.getExtensibleIndexID(),
              lower,
              upper,
              false,
              true);
    }
  }
@@ -1653,17 +1883,8 @@
  * Collation rule for greater-than matching rule.
  */
  private final class CollationGreaterThanMatchingRule
         extends ExtensibleMatchingRule
          extends CollationOrderingMatchingRule
  {
    //Names for this class.
    private final Collection<String> names;
    //Collator for performing equality match.
    private final Collator collator;
    //Numeric OID of the rule.
    private final String nOID;
    /**
     * Constructs a new CollationGreaterThanMatchingRule.
@@ -1675,145 +1896,7 @@
    private CollationGreaterThanMatchingRule(String nOID,
            Collection<String> names,Locale locale)
    {
      super();
      this.names = names;
      this.collator =createCollator(locale);
      this.nOID = nOID;
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public String getName()
    {
     //Concatenate all the names and return.
      StringBuilder builder = new StringBuilder();
      for(String name: getAllNames())
      {
        builder.append(name);
        builder.append("\b");
      }
      return builder.toString();
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public Collection<String> getAllNames()
    {
      return Collections.unmodifiableCollection(names);
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public String getOID()
    {
      return nOID;
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public String getDescription()
    {
      // There is no standard description for this matching rule.
      return null;
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public String getSyntaxOID()
    {
      return SYNTAX_DIRECTORY_STRING_OID;
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public ByteString normalizeValue(ByteString value)
           throws DirectoryException
    {
      CollationKey key = collator.getCollationKey(value.stringValue());
      return new ASN1OctetString(key.toByteArray());
    }
   /**
    * Compares the first value to the second and returns a value that
    * indicates their relative order.
    *
    * @param  b1  The normalized form of the first value to
    *                 compare.
    * @param  b2  The normalized form of the second value to
    *                 compare.
    *
    * @return  A negative integer if {@code value1} should come before
    *          {@code value2} in ascending order, a positive integer if
    *          {@code value1} should come after {@code value2} in
    *          ascending order, or zero if there is no difference
    *          between the values with regard to ordering.
    */
    public int compare(byte[] b1, byte[] b2)
    {
      //Compare values using byte arrays.
      int minLength = Math.min(b1.length, b2.length);
      for (int i=0; i < minLength; i++)
      {
        int firstByte = 0xFF & ((int)b1[i]);
        int secondByte = 0xFF & ((int)b2[i]);
        if (firstByte == secondByte)
        {
          continue;
        }
        else if (firstByte < secondByte)
        {
          return -1;
        }
        else if (firstByte > secondByte)
        {
          return 1;
        }
      }
      if (b1.length == b2.length)
      {
        return 0;
      }
      else if (b1.length < b2.length)
      {
        return -1;
      }
      else
      {
        return 1;
      }
      super(nOID, names, locale);
    }
@@ -1827,35 +1910,41 @@
    {
      int ret = compare(attributeValue.value(),assertionValue.value());
      if(ret > 0)
      {
      if (ret > 0) {
        return ConditionResult.TRUE;
      }
      else
      {
      } else {
        return ConditionResult.FALSE;
      }
    }
  }
    /**
     * {@inheritDoc}
     */
    @Override
    public <T> T createIndexQuery(ByteString assertionValue,
            IndexQueryFactory<T> factory)
            throws DirectoryException
    {
      byte[] lower = normalizeValue(assertionValue).value();
      byte[] upper = new byte[0];
      return factory.createRangeMatchQuery(indexer.getExtensibleIndexID(),
              lower,
              upper,
              false,
              false);
    }
  }
 /**
  * Collation rule for greater-than-equal-to matching rule.
  */
  private final class CollationGreaterThanOrEqualToMatchingRule
         extends ExtensibleMatchingRule
          extends CollationOrderingMatchingRule
  {
    //Names for this class.
    private final Collection<String> names;
    //Collator for performing equality match.
    private final Collator collator;
    //Numeric OID of the rule.
    private final String nOID;
    /**
     * Constructs a new CollationGreaterThanOrEqualToMatchingRule.
     *
@@ -1867,144 +1956,11 @@
                                    Collection<String> names,
                                    Locale locale)
    {
      super();
      this.names = names;
      this.collator =createCollator(locale);
      this.nOID = nOID;
      super(nOID, names, locale);
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public String getName()
    {
       //Concatenate all the names and return.
      StringBuilder builder = new StringBuilder();
      for(String name: getAllNames())
      {
        builder.append(name);
        builder.append("\b");
      }
      return builder.toString();
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public Collection<String> getAllNames()
    {
      return Collections.unmodifiableCollection(names);
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public String getOID()
    {
      return nOID;
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public String getDescription()
    {
      // There is no standard description for this matching rule.
      return null;
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public String getSyntaxOID()
    {
      return SYNTAX_DIRECTORY_STRING_OID;
    }
   /**
    * {@inheritDoc}
    */
    @Override
    public ByteString normalizeValue(ByteString value)
           throws DirectoryException
    {
      CollationKey key = collator.getCollationKey(value.stringValue());
      return new ASN1OctetString(key.toByteArray());
    }
   /**
    * Compares the first value to the second and returns a value that
    * indicates their relative order.
    *
    * @param  b1  The normalized form of the first value to
    *                 compare.
    * @param  b2  The normalized form of the second value to
    *                 compare.
    *
    * @return  A negative integer if {@code value1} should come before
    *          {@code value2} in ascending order, a positive integer if
    *          {@code value1} should come after {@code value2} in
    *          ascending order, or zero if there is no difference
    *          between the values with regard to ordering.
    */
    public int compare(byte[] b1, byte[] b2)
    {
      //Compare values using byte arrays.
      int minLength = Math.min(b1.length, b2.length);
      for (int i=0; i < minLength; i++)
      {
        int firstByte = 0xFF & ((int)b1[i]);
        int secondByte = 0xFF & ((int)b2[i]);
        if (firstByte == secondByte)
        {
          continue;
        }
        else if (firstByte < secondByte)
        {
          return -1;
        }
        else if (firstByte > secondByte)
        {
          return 1;
        }
      }
      if (b1.length == b2.length)
      {
        return 0;
      }
      else if (b1.length < b2.length)
      {
        return -1;
      }
      else
      {
        return 1;
      }
    }
    /**
     * {@inheritDoc}
@@ -2024,7 +1980,229 @@
        return ConditionResult.FALSE;
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <T> T createIndexQuery(ByteString assertionValue,
            IndexQueryFactory<T> factory)
            throws DirectoryException
    {
      byte[] lower = normalizeValue(assertionValue).value();
      byte[] upper = new byte[0];
      // Read the range: lower <= keys < upper.
      return factory.createRangeMatchQuery(indexer.getExtensibleIndexID(),
              lower,
              upper,
              true,
              false);
  }
  }
  /**
   * Extensible Indexer class for Collation Matching rules which share the
   * same index. This Indexer is shared by Equality and Ordering Collation
   * Matching Rules.
   */
  private final class CollationSharedExtensibleIndexer
          extends ExtensibleIndexer
  {
    /**
     * The Extensible Matching Rule.
     */
    private final CollationMatchingRule matchingRule;
    /**
     * Creates a new instance of CollationSharedExtensibleIndexer.
     *
     * @param matchingRule The Collation Matching Rule.
     */
    private CollationSharedExtensibleIndexer(
            CollationMatchingRule matchingRule)
    {
      this.matchingRule = matchingRule;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getExtensibleIndexID()
    {
      return EXTENSIBLE_INDEXER_ID_SHARED;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public final void getKeys(AttributeValue value,
            Set<byte[]> keys)
    {
      ByteString key;
      try
      {
        key = matchingRule.normalizeValue(value.getValue());
        keys.add(key.value());
      }
      catch (DirectoryException de)
      {
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public final void getKeys(AttributeValue value,
            Map<byte[], Boolean> modifiedKeys,
            Boolean insert)
    {
      Set<byte[]> keys = new HashSet<byte[]>();
      getKeys(value, keys);
      for (byte[] key : keys)
      {
        Boolean cInsert = modifiedKeys.get(key);
        if (cInsert == null)
        {
          modifiedKeys.put(key, insert);
        }
        else if (!cInsert.equals(insert))
        {
          modifiedKeys.remove(key);
        }
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getPreferredIndexName()
    {
      return matchingRule.getIndexName();
    }
  }
  /**
   * Extensible Indexer class for Collation Substring Matching rules.
   * This Indexer is used  by Substring Collation Matching Rules.
   */
  private final class CollationSubstringExtensibleIndexer
          extends ExtensibleIndexer
  {
    //The CollationSubstringMatching Rule.
    private final CollationSubstringMatchingRule matchingRule;
    //The substring length.
    private int substringLen;
    /**
     * Creates a new instance of CollationSubstringExtensibleIndexer.
     *
     * @param matchingRule The CollationSubstringMatching Rule.
     * @param substringLen The substring length.
     */
    private CollationSubstringExtensibleIndexer(
            CollationSubstringMatchingRule matchingRule,
            int substringLen)
    {
      this.matchingRule = matchingRule;
      this.substringLen = substringLen;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void getKeys(AttributeValue value,
                                Set<byte[]> keys)
    {
      matchingRule.subtringKeys(value.getValue(),
                                keys);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void getKeys(AttributeValue attValue,
            Map<byte[], Boolean> modifiedKeys,
            Boolean insert)
    {
      matchingRule.substringKeys(attValue.getValue(),
              modifiedKeys,
              insert);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getPreferredIndexName()
    {
      return matchingRule.getIndexName();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getExtensibleIndexID()
    {
      return EXTENSIBLE_INDEXER_ID_SUBSTRING;
    }
    /**
     * Returns the substring length.
     * @return The length of the substring.
     */
    private int gerSubstringLength()
    {
      return substringLen;
    }
    /**
     * Sets the substring length.
     * @param substringLen The substring length.
     */
    private void setSubstringLength(int substringLen)
    {
      this.substringLen = substringLen;
    }
  }
 /**
opends/src/server/org/opends/server/types/IndexConfig.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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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.opends.server.types;
/**
 * This class represents the configuration of an index.
 */
public abstract class IndexConfig
{
  /**
   * Returns the length of a substring.
   * @return the length of the substring.
   */
  public abstract int getSubstringLength();
}
opends/src/server/org/opends/server/types/Schema.java
@@ -20,9 +20,9 @@
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *©
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.types;
@@ -47,6 +47,7 @@
import org.opends.server.api.ApproximateMatchingRule;
import org.opends.server.api.AttributeSyntax;
import org.opends.server.api.EqualityMatchingRule;
import org.opends.server.api.ExtensibleMatchingRule;
import org.opends.server.api.MatchingRule;
import org.opends.server.api.OrderingMatchingRule;
import org.opends.server.api.SubstringMatchingRule;
@@ -149,6 +150,12 @@
  private ConcurrentHashMap<String,SubstringMatchingRule>
               substringMatchingRules;
  // The set of extensible matching rules for this schema, mapped
  // between the lowercase names and OID for the definition and the
  // matching rule itself.
  private ConcurrentHashMap<String,ExtensibleMatchingRule>
               extensibleMatchingRules;
  // The set of matching rule uses for this schema, mapped between the
  // matching rule for the definition and the matching rule use
  // itself.
@@ -242,6 +249,8 @@
         new ConcurrentHashMap<String,OrderingMatchingRule>();
    substringMatchingRules =
         new ConcurrentHashMap<String,SubstringMatchingRule>();
    extensibleMatchingRules =
        new ConcurrentHashMap<String,ExtensibleMatchingRule>();
    matchingRuleUses =
         new ConcurrentHashMap<MatchingRule,MatchingRuleUse>();
    ditContentRules =
@@ -1001,6 +1010,11 @@
      registerSubstringMatchingRule(
           (SubstringMatchingRule) matchingRule, overwriteExisting);
    }
   else if(matchingRule instanceof ExtensibleMatchingRule)
   {
      registerExtensibleMatchingRule(
           (ExtensibleMatchingRule) matchingRule,overwriteExisting);
   }
    else
    {
      synchronized (matchingRules)
@@ -1834,6 +1848,179 @@
  /**
   * Retrieves the extensible matching rule definitions for this
   * schema, as a mapping between the lowercase names and OIDs for the
   * matching rule and the matching rule itself.  Each matching rule
   * may be associated with multiple keys (once for the OID and again
   * for each name).
   *
   * @return  The extensible matching rule definitions for this
   *          schema.
   */
  public Map<String,ExtensibleMatchingRule>
              getExtensibleMatchingRules()
  {
    return Collections.unmodifiableMap(extensibleMatchingRules);
  }
  /**
   * Retrieves the extensible matching rule definition with the
   * specified name or OID.
   *
   * @param  lowerName  The name or OID of the matching rule to
   *                    retrieve, formatted in all lowercase
   *                    characters.
   *
   * @return  The requested matching rule, or <CODE>null</CODE> if no
   *          extensible matching rule is registered with the
   *          provided name or OID.
   */
  public ExtensibleMatchingRule getExtensibleMatchingRule(
                                      String lowerName)
  {
    return extensibleMatchingRules.get(lowerName);
  }
  /**
   * Registers the provided extensible matching rule with this
   * schema.
   *
   * @param  matchingRule       The extensible matching rule to
   *                            register.
   * @param  overwriteExisting  Indicates whether to overwrite an
   *                            existing mapping if there are any
   *                            conflicts (i.e., another matching rule
   *                            with the same OID or name).
   *
   * @throws  DirectoryException  If a conflict is encountered and the
   *                              <CODE>overwriteExisting</CODE> flag
   *                              is set to <CODE>false</CODE>
   */
  public void registerExtensibleMatchingRule(
                   ExtensibleMatchingRule matchingRule,
                   boolean overwriteExisting)
         throws DirectoryException
  {
    synchronized (matchingRules)
    {
      if (! overwriteExisting)
      {
        String oid = toLowerCase(matchingRule.getOID());
        if (matchingRules.containsKey(oid))
        {
          MatchingRule conflictingRule = matchingRules.get(oid);
          Message message = ERR_SCHEMA_CONFLICTING_MR_OID.
              get(matchingRule.getNameOrOID(), oid,
                  conflictingRule.getNameOrOID());
          throw new DirectoryException(
                         ResultCode.CONSTRAINT_VIOLATION, message);
        }
       for(String name:matchingRule.getAllNames())
       {
        if (name != null)
        {
          name = toLowerCase(name);
          if (matchingRules.containsKey(name))
          {
            MatchingRule conflictingRule = matchingRules.get(name);
            Message message = ERR_SCHEMA_CONFLICTING_MR_NAME.
                get(matchingRule.getOID(), name,
                    conflictingRule.getOID());
            throw new DirectoryException(
                           ResultCode.CONSTRAINT_VIOLATION, message);
          }
        }
       }
      }
      String oid = toLowerCase(matchingRule.getOID());
      extensibleMatchingRules.put(oid, matchingRule);
      matchingRules.put(oid, matchingRule);
      for(String name:matchingRule.getAllNames())
      {
        if (name != null)
        {
          name = toLowerCase(name);
          extensibleMatchingRules.put(name, matchingRule);
          matchingRules.put(name, matchingRule);
        }
      }
      // We'll use an attribute value including the normalized value
      // rather than the attribute type because otherwise it would use
      // a very expensive matching rule (OID first component match)
      // that would kill performance.
      String valueString = matchingRule.toString();
      ASN1OctetString rawValue  = new ASN1OctetString(valueString);
      ByteString normValue = normalizationMatchingRule.normalizeValue(
                                  new ASN1OctetString(valueString));
      matchingRuleSet.add(new AttributeValue(rawValue, normValue));
    }
  }
  /**
   * Deregisters the provided extensible  matching rule definition
   * with this schema.
   *
   * @param  matchingRule  The extensible matching rule to deregister
   *                       with this schema.
   */
    public void deregisterExtensibleMatchingRule(
                   ExtensibleMatchingRule matchingRule)
   {
    synchronized (matchingRules)
    {
      String oid = matchingRule.getOID();
      extensibleMatchingRules.remove(oid, matchingRule);
      matchingRules.remove(oid, matchingRule);
      for(String name:matchingRule.getAllNames())
      {
        if (name != null)
        {
          name = toLowerCase(name);
          extensibleMatchingRules.remove(name, matchingRule);
          matchingRules.remove(name, matchingRule);
        }
      }
      // We'll use an attribute value including the normalized value
      // rather than the attribute type because otherwise it would use
      // a very expensive matching rule (OID first component match)
      // that would kill performance.
      try
      {
        String valueString = matchingRule.toString();
        ASN1OctetString rawValue = new ASN1OctetString(valueString);
        ByteString normValue =
             normalizationMatchingRule.normalizeValue(
                  new ASN1OctetString(valueString));
        matchingRuleSet.remove(new AttributeValue(rawValue,
                                                  normValue));
      }
      catch (Exception e)
      {
        String valueString = matchingRule.toString();
        ASN1OctetString rawValue = new ASN1OctetString(valueString);
        ASN1OctetString normValue =
             new ASN1OctetString(toLowerCase(valueString));
        matchingRuleSet.remove(new AttributeValue(rawValue,
                                                  normValue));
      }
    }
  }
  /**
   * Retrieves the matching rule use definitions for this schema, as a
   * mapping between the matching rule for the matching rule use
   * definition and the matching rule use itself.  Each matching rule
@@ -2942,6 +3129,7 @@
    dupSchema.equalityMatchingRules.putAll(equalityMatchingRules);
    dupSchema.orderingMatchingRules.putAll(orderingMatchingRules);
    dupSchema.substringMatchingRules.putAll(substringMatchingRules);
    dupSchema.extensibleMatchingRules.putAll(extensibleMatchingRules);
    dupSchema.matchingRuleUses.putAll(matchingRuleUses);
    dupSchema.ditContentRules.putAll(ditContentRules);
    dupSchema.ditStructureRulesByID.putAll(ditStructureRulesByID);
@@ -3579,6 +3767,13 @@
      syntaxSet.clear();
      syntaxSet = null;
    }
    if (extensibleMatchingRules != null)
    {
      extensibleMatchingRules.clear();
      extensibleMatchingRules = null;
    }
  }
}
opends/src/server/org/opends/server/util/ServerConstants.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.util;
@@ -2926,6 +2926,22 @@
  /**
   * The extensible indexer identifier string that will be used for a substring
   * type.
   */
  public static final String EXTENSIBLE_INDEXER_ID_SUBSTRING="substring";
  /**
   * The extensible indexer identifier string that will be used for a shared
   * type.
   */
  public static final String EXTENSIBLE_INDEXER_ID_SHARED="shared";
  /**
   * The lines that make up the CDDL header.  They will not have any prefix, so
   * an appropriate prefix may need to be added for some cases (e.g., "# " for
   * shell scripts, "rem " for batch files, etc.).