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" @@ -110,7 +110,7 @@ <adm:requires-admin-action> <adm:other> <adm:synopsis> If any new index types are added for an attribute, and If any new index types are added for an attribute, and values for that attribute already exist in the database, the index must be rebuilt before it will be accurate. @@ -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; @@ -268,6 +268,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 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.*; @@ -118,9 +126,9 @@ } /** * Creates a new instance of CollationMatchingRuleFactory. */ /** * Creates a new instance of CollationMatchingRuleFactory. */ public CollationMatchingRuleFactory() { //Initialize the matchingRules. @@ -130,8 +138,8 @@ /** * {@inheritDoc} */ * {@inheritDoc} */ @Override public final Collection<MatchingRule> getMatchingRules() { @@ -140,16 +148,16 @@ /** * Adds a new mapping of OID and MatchingRule. * * @param oid OID of the matching rule * @param matchingRule instance of a MatchingRule. */ /** * Adds a new mapping of OID and MatchingRule. * * @param oid OID of the matching rule * @param matchingRule instance of a MatchingRule. */ private final void addMatchingRule(String oid, MatchingRule matchingRule) { matchingRules.put(oid, matchingRule); matchingRules.put(oid, matchingRule); } @@ -179,10 +187,10 @@ /** * Reads the configuration and initializes matching rule types. * * @param ruleTypes The Set containing allowed matching rule types. */ * Reads the configuration and initializes matching rule types. * * @param ruleTypes The Set containing allowed matching rule types. */ private void initializeMatchingRuleTypes(SortedSet<MatchingRuleType> ruleTypes) { @@ -209,19 +217,19 @@ substringMatchingRuleType = true; break; default: //No default values allowed. //No default values allowed. } } } /** * Creates a new Collator instance. * * @param locale Locale for the collator * @return Returns a new Collator instance */ /** * Creates a new Collator instance. * * @param locale Locale for the collator * @return Returns a new Collator instance */ private Collator createCollator(Locale locale) { Collator collator = Collator.getInstance(locale); @@ -232,9 +240,9 @@ /** * {@inheritDoc} */ /** * {@inheritDoc} */ @Override public void initializeMatchingRule(CollationMatchingRuleCfg configuration) throws ConfigException, InitializationException @@ -271,7 +279,7 @@ Message msg = WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_LOCALE. get(collation,configuration.dn().toNormalizedString(), languageTag); languageTag); logError(msg); } @@ -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); @@ -352,10 +359,10 @@ } catch (DirectoryException de) { Message message = WARN_CONFIG_SCHEMA_MR_CONFLICTING_MR.get( String.valueOf(configuration.dn()), de.getMessageObject()); adminActionRequired = true; messages.add(message); Message message = WARN_CONFIG_SCHEMA_MR_CONFLICTING_MR.get( String.valueOf(configuration.dn()), de.getMessageObject()); adminActionRequired = true; messages.add(message); } currentConfig = configuration; return new ConfigChangeResult(resultCode, adminActionRequired, messages); @@ -405,7 +412,7 @@ Message msg = WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_LOCALE. get(collation,configuration.dn().toNormalizedString(), languageTag); languageTag); unacceptableReasons.add(msg); configAcceptable = false; continue; @@ -417,11 +424,11 @@ /** * Creates Less-than Matching Rule. * * @param mapper CollationMapper containing OID and the language Tag. * @param locale Locale value */ * Creates Less-than Matching Rule. * * @param mapper CollationMapper containing OID and the language Tag. * @param locale Locale value */ private void createLessThanMatchingRule(CollationMapper mapper,Locale locale) { if(!lessThanMatchingRuleType) @@ -449,12 +456,12 @@ /** * Creates Less-Than-Equal-To Matching Rule. * * @param mapper CollationMapper containing OID and the language Tag. * @param locale Locale value */ /** * Creates Less-Than-Equal-To Matching Rule. * * @param mapper CollationMapper containing OID and the language Tag. * @param locale Locale value */ private void createLessThanOrEqualToMatchingRule(CollationMapper mapper, Locale locale) { @@ -484,12 +491,12 @@ /** * Creates Equality Matching Rule. * * @param mapper CollationMapper containing OID and the language Tag. * @param locale Locale value */ /** * Creates Equality Matching Rule. * * @param mapper CollationMapper containing OID and the language Tag. * @param locale Locale value */ private void createEqualityMatchingRule(CollationMapper mapper,Locale locale) { if(!equalityMatchingRuleType) @@ -510,37 +517,37 @@ 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); } /** * Creates Greater-than-equal-to Matching Rule. * * @param mapper CollationMapper containing OID and the language Tag. * @param locale Locale value */ * Creates Greater-than-equal-to Matching Rule. * * @param mapper CollationMapper containing OID and the language Tag. * @param locale Locale value */ private void createGreaterThanOrEqualToMatchingRule(CollationMapper mapper, Locale locale) { @@ -569,12 +576,12 @@ /** * Creates Greater-than Matching Rule. * * @param mapper CollationMapper containing OID and the language Tag. * @param locale Locale value */ /** * Creates Greater-than Matching Rule. * * @param mapper CollationMapper containing OID and the language Tag. * @param locale Locale value */ private void createGreaterThanMatchingRule(CollationMapper mapper, Locale locale) { @@ -603,12 +610,12 @@ /** * Creates substring Matching Rule. * * @param mapper CollationMapper containing OID and the language Tag. * @param locale Locale value */ /** * Creates substring Matching Rule. * * @param mapper CollationMapper containing OID and the language Tag. * @param locale Locale value */ private void createSubstringMatchingRule(CollationMapper mapper,Locale locale) { if(!substringMatchingRuleType) @@ -636,12 +643,12 @@ /** * Verifies if the locale is supported by the JVM. * * @param lTag The language tag specified in the configuration. * @return Locale The locale correspoding to the languageTag. */ /** * Verifies if the locale is supported by the JVM. * * @param lTag The language tag specified in the configuration. * @return Locale The locale correspoding to the languageTag. */ private Locale getLocale(String lTag) { //Separates the language and the country from the locale. @@ -685,35 +692,51 @@ /** *Collation rule for Equality matching rule. */ private final class CollationEqualityMatchingRule extends ExtensibleMatchingRule /** * Collation Extensible matching rule. */ 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.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 @@ -794,18 +886,18 @@ /** * Indicates whether the two provided normalized values are equal to * each other. * * @param value1 The normalized form of the first value to * compare. * @param value2 The normalized form of the second value to * compare. * * @return {@code true} if the provided values are equal, or * {@code false} if not. */ /** * Indicates whether the two provided normalized values are equal to * each other. * * @param value1 The normalized form of the first value to * compare. * @param value2 The normalized form of the second value to * compare. * * @return {@code true} if the provided values are equal, or * {@code false} if not. */ private boolean areEqual(ByteString value1, ByteString value2) { return Arrays.equals(value1.value(), value2.value()); @@ -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. */ /** * 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,10 +962,7 @@ private CollationSubstringMatchingRule(String nOID, Collection<String> names,Locale locale) { super(); this.names = names; this.collator =createCollator(locale); this.nOID = nOID; super(nOID,names,locale); } @@ -870,67 +971,6 @@ * {@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; } /** * {@inheritDoc} */ @Override public ByteString normalizeValue(ByteString value) throws DirectoryException { @@ -940,12 +980,81 @@ /** * {@inheritDoc} */ @Override public ByteString normalizeAssertionValue(ByteString value) throws DirectoryException /** * Utility class which abstracts a substring assertion 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. String filterString = value.stringValue(); @@ -973,7 +1082,7 @@ if (asteriskPositions.isEmpty()) { Message message = ERR_SEARCH_FILTER_SUBSTRING_NO_ASTERISKS.get( filterString, 0, endPos); filterString, 0, endPos); throw new DirectoryException( ResultCode.PROTOCOL_ERROR, message); } @@ -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,7 +1257,8 @@ } } } if(subFinal ==null) String subFinal = assertion.getFinal(); if (subFinal == null) { normalizedList.add(0); } @@ -1238,7 +1360,7 @@ } assertPos = assertPos + anyLength; } } } int finalLength = 0xFF & assertionBytes[assertPos++]; if(finalLength!=0) @@ -1267,23 +1389,371 @@ } 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); } } } if(indexer == null) { indexer = new CollationSharedExtensibleIndexer(this); } indexers.add(subIndexer); indexers.add(indexer); return indexers; } /** * 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); } } /** *Collation rule for less-than matching rule. */ private final class CollationLessThanMatchingRule extends ExtensibleMatchingRule /** *An abstract Collation rule for Ordering matching rule. */ private abstract class CollationOrderingMatchingRule extends CollationMatchingRule { //Names for this class. private final Collection<String> names; /** * 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 CollationOrderingMatchingRule(String nOID, Collection<String> names, Locale locale) { super(nOID,names,locale); } //Collator for performing equality match. private final Collator collator; //Numeric OID of the rule. private final String nOID; /** * {@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. */ protected 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; } } } /** * Collation matching rule for Less-than matching rule. */ private final class CollationLessThanMatchingRule extends CollationOrderingMatchingRule { /** * Constructs a new CollationLessThanMatchingRule. @@ -1293,160 +1763,24 @@ * @param locale Locale of the collation matching rule */ private CollationLessThanMatchingRule(String nOID, Collection<String> names,Locale locale) 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. */ private 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} */ @Override public ConditionResult valuesMatch(ByteString attributeValue, ByteString assertionValue) ByteString assertionValue) { int ret = compare(attributeValue.value(),assertionValue.value()); int ret = compare(attributeValue.value(), assertionValue.value()); if(ret <0) if (ret < 0) { return ConditionResult.TRUE; } @@ -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. */ * 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. @@ -1482,161 +1827,25 @@ * @param locale Locale of the collation matching rule */ private CollationLessThanOrEqualToMatchingRule(String nOID, Collection<String> names, Locale locale) 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} */ @Override public ConditionResult valuesMatch(ByteString attributeValue, ByteString assertionValue) ByteString assertionValue) { int ret = compare(attributeValue.value(),assertionValue.value()); int ret = compare(attributeValue.value(), assertionValue.value()); if(ret <= 0) if (ret <= 0) { return ConditionResult.TRUE; } @@ -1645,25 +1854,37 @@ 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); } } /** * Collation rule for greater-than matching rule. */ /** * 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. @@ -1673,147 +1894,9 @@ * @param locale Locale of the collation matching rule */ private CollationGreaterThanMatchingRule(String nOID, Collection<String> names,Locale locale) 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); } @@ -1823,39 +1906,45 @@ */ @Override public ConditionResult valuesMatch(ByteString attributeValue, ByteString assertionValue) ByteString assertionValue) { int ret = compare(attributeValue.value(),assertionValue.value()); 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. */ /** * 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. * @@ -1864,143 +1953,31 @@ * @param locale Locale of the collation matching rule */ private CollationGreaterThanOrEqualToMatchingRule(String nOID, Collection<String> names, Locale locale) Collection<String> names, Locale locale) { super(); this.names = names; this.collator =createCollator(locale); this.nOID = nOID; super(nOID, names, locale); } /** * {@inheritDoc} */ /** * {@inheritDoc} */ @Override public String getName() public ConditionResult valuesMatch(ByteString attributeValue, ByteString assertionValue) { //Concatenate all the names and return. StringBuilder builder = new StringBuilder(); for(String name: getAllNames()) int ret = compare(attributeValue.value(),assertionValue.value()); if (ret >= 0) { 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; return ConditionResult.TRUE; } else { return 1; return ConditionResult.FALSE; } } @@ -2010,27 +1987,228 @@ * {@inheritDoc} */ @Override public ConditionResult valuesMatch(ByteString attributeValue, ByteString assertionValue) public <T> T createIndexQuery(ByteString assertionValue, IndexQueryFactory<T> factory) throws DirectoryException { int ret = compare(attributeValue.value(),assertionValue.value()); if(ret >= 0) { return ConditionResult.TRUE; } else { return ConditionResult.FALSE; } 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); } } /** * A utility class for extracting the OID and Language Tag from the * configuration entry. */ /** * 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; } } /** * A utility class for extracting the OID and Language Tag from the * configuration entry. */ private final class CollationMapper { //OID of the collation rule. 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.).