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