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

neil_a_wilson
03.52.2007 09dc131d36b33e58f728316c3d445dfc3b865be4
opendj-sdk/opends/resource/config/config.ldif
@@ -1669,6 +1669,34 @@
objectClass: ds-cfg-branch
cn: Virtual Attributes
dn: cn=entryDN,cn=Virtual Attributes,cn=config
objectClass: top
objectClass: ds-cfg-virtual-attribute
cn: entryDN
ds-cfg-virtual-attribute-class: org.opends.server.extensions.EntryDNVirtualAttributeProvider
ds-cfg-virtual-attribute-enabled: true
ds-cfg-virtual-attribute-type: entryDN
ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real
dn: cn=isMemberOf,cn=Virtual Attributes,cn=config
objectClass: top
objectClass: ds-cfg-virtual-attribute
cn: isMemberOf
ds-cfg-virtual-attribute-class: org.opends.server.extensions.IsMemberOfVirtualAttributeProvider
ds-cfg-virtual-attribute-enabled: true
ds-cfg-virtual-attribute-type: isMemberOf
ds-cfg-virtual-attribute-filter: (objectClass=person)
ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real
dn: cn=subschemaSubentry,cn=Virtual Attributes,cn=config
objectClass: top
objectClass: ds-cfg-virtual-attribute
cn: subschemaSubentry
ds-cfg-virtual-attribute-class: org.opends.server.extensions.SubschemaSubentryVirtualAttributeProvider
ds-cfg-virtual-attribute-enabled: true
ds-cfg-virtual-attribute-type: subschemaSubentry
ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real
dn: cn=Work Queue,cn=config
objectClass: top
objectClass: ds-cfg-work-queue
opendj-sdk/opends/resource/schema/00-core.ldif
@@ -420,6 +420,10 @@
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'RFC 1274' )
attributeTypes: ( 0.9.2342.19200300.100.1.31 NAME 'cNAMERecord'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'RFC 1274' )
attributeTypes: ( 2.16.840.1.113730.3.1.602 NAME 'entryDN'
  DESC 'DN of the entry' EQUALITY distinguishedNameMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE NO-USER-MODIFICATION
  USAGE directoryOperation X-ORIGIN 'draft-zeilenga-ldap-entrydn' )
objectClasses: ( 2.5.6.0 NAME 'top' ABSTRACT MUST objectClass
  X-ORIGIN 'RFC 4512' )
objectClasses: ( 2.5.6.1 NAME 'alias' SUP top STRUCTURAL MUST aliasedObjectName
opendj-sdk/opends/resource/schema/02-config.ldif
@@ -1103,6 +1103,28 @@
  NAME 'ds-cfg-case-sensitive-validation' 
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE 
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.325
  NAME 'ds-cfg-virtual-attribute-class' 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.326
  NAME 'ds-cfg-virtual-attribute-enabled' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
  SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.327
  NAME 'ds-cfg-virtual-attribute-type' 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.328
  NAME 'ds-cfg-virtual-attribute-base-dn' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.329
  NAME 'ds-cfg-virtual-attribute-group-dn' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.330
  NAME 'ds-cfg-virtual-attribute-filter' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.331
  NAME 'ds-cfg-virtual-attribute-conflict-behavior'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
  NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL
  MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
@@ -1532,4 +1554,10 @@
  SUP ds-cfg-password-validator STRUCTURAL
  MUST ( ds-cfg-maximum-consecutive-length $ ds-cfg-case-sensitive-validation )
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.91 NAME 'ds-cfg-virtual-attribute'
  SUP top STRUCTURAL MUST ( cn $ ds-cfg-virtual-attribute-class $
  ds-cfg-virtual-attribute-enabled $ ds-cfg-virtual-attribute-type $
  ds-cfg-virtual-attribute-conflict-behavior )
  MAY ( ds-cfg-virtual-attribute-base-dn $ ds-cfg-virtual-attribute-group-dn $
  ds-cfg-virtual-attribute-filter ) X-ORIGIN 'OpenDS Directory Server' )
opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
@@ -61,5 +61,13 @@
      </ldap:rdn-sequence>
    </adm:profile>
  </adm:relation>
  <adm:relation name="virtual-attribute">
    <adm:one-to-many />
    <adm:profile name="ldap">
      <ldap:rdn-sequence>
        cn=Virtual Attributes,cn=config
      </ldap:rdn-sequence>
    </adm:profile>
  </adm:relation>
  <adm:product-name>OpenDS Directory Server</adm:product-name>
</adm:root-managed-object>
opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/VirtualAttributeConfiguration.xml
New file
@@ -0,0 +1,183 @@
<?xml version="1.0" encoding="utf-8"?>
<adm:managed-object name="virtual-attribute"
plural-name="virtual-attributes"
package="org.opends.server.admin.std"
xmlns:adm="http://www.opends.org/admin"
xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    <adm:user-friendly-plural-name />
    are responsible for dynamically generating attribute values that appear in
    entries but are not persistently stored in the backend.
  </adm:synopsis>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.91</ldap:oid>
      <ldap:name>ds-cfg-virtual-attribute</ldap:name>
      <ldap:superior>top</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property name="provider-class" mandatory="true">
    <adm:synopsis>
      The fully-qualified name of the Java class that provides the
      <adm:user-friendly-name />
      implementation.
    </adm:synopsis>
    <adm:syntax>
      <adm:java-class>
        <adm:instance-of>
          org.opends.server.api.VirtualAttributeProvider
        </adm:instance-of>
      </adm:java-class>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.325</ldap:oid>
        <ldap:name>ds-cfg-virtual-attribute-class</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="enabled" mandatory="true">
    <adm:synopsis>
      Indicate whether the
      <adm:user-friendly-name />
      is enabled for use.
    </adm:synopsis>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.326</ldap:oid>
        <ldap:name>ds-cfg-virtual-attribute-enabled</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="attribute-type" mandatory="true">
    <adm:synopsis>
      Specifies the attribute type for the attribute whose values should be
      dynamically assigned by the virtual attribute.
    </adm:synopsis>
    <adm:syntax>
      <adm:attribute-type />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.327</ldap:oid>
        <ldap:name>ds-cfg-virtual-attribute-type</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="base-dn" mandatory="false" multi-valued="true">
    <adm:synopsis>
      Specifies the base DNs for the branches containing entries that may be
      eligible to use this virtual attribute.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          The location of the entry in the server will not be taken into account
          when determining whether an entry is eligible to use this virtual
          attribute.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:dn />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.328</ldap:oid>
        <ldap:name>ds-cfg-virtual-attribute-base-dn</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="group-dn" mandatory="false" multi-valued="true">
    <adm:synopsis>
      Specifies the DNs for the groups whose members may be eligible to use this
      virtual attribute.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          Group membership will not be taken into accountwhen determining
          whether an entry is eligible to use this virtual attribute.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:dn />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.329</ldap:oid>
        <ldap:name>ds-cfg-virtual-attribute-group-dn</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="filter" mandatory="false" multi-valued="true">
    <adm:synopsis>
      Specifies the search filters for entries that may be eligible to use this
      virtual attribute.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>(objectClass=*)</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.330</ldap:oid>
        <ldap:name>ds-cfg-virtual-attribute-filter</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="conflict-behavior" mandatory="false">
    <adm:synopsis>
      Specifies the behavior that the server should exhibit for entries that
      contain one or more real values for the associated attribute.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>real-overrides-virtual</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:enumeration>
        <adm:value name="real-overrides-virtual">
          <adm:synopsis>
            Any real values contained in the entry should be preserved and
            virtual values should not be generated.
          </adm:synopsis>
        </adm:value>
        <adm:value name="virtual-overrides-real">
          <adm:synopsis>
            Any real values contained in the entry should be suppressed and
            virtual values should be generated.
          </adm:synopsis>
        </adm:value>
        <adm:value name="merge-real-and-virtual">
          <adm:synopsis>
            Any real values contained in the entry should be preserved and
            merged with the set of generated virtual values.
          </adm:synopsis>
        </adm:value>
      </adm:enumeration>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.331</ldap:oid>
        <ldap:name>ds-cfg-virtual-attribute-conflict-behavior</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opendj-sdk/opends/src/server/org/opends/server/api/ClientConnection.java
@@ -73,9 +73,6 @@
 */
public abstract class ClientConnection
{
  // The set of authentication information for this client connection.
  private AuthenticationInfo authenticationInfo;
@@ -1197,7 +1194,7 @@
   *                    searches performed using this client
   *                    connection.
   */
  public final void setSizeLimit(int sizeLimit)
  public void setSizeLimit(int sizeLimit)
  {
    this.sizeLimit = sizeLimit;
  }
@@ -1226,7 +1223,7 @@
   *                           entries that should be check for
   *                           matches during a search.
   */
  public final void setLookthroughLimit(int lookthroughLimit)
  public void setLookthroughLimit(int lookthroughLimit)
  {
    this.lookthroughLimit = lookthroughLimit;
  }
@@ -1255,7 +1252,7 @@
   *                    searches performed using this client
   *                    connection.
   */
  public final void setTimeLimit(int timeLimit)
  public void setTimeLimit(int timeLimit)
  {
    this.timeLimit = timeLimit;
  }
opendj-sdk/opends/src/server/org/opends/server/api/Group.java
@@ -63,9 +63,6 @@
 */
public abstract class Group
{
  /**
   * Initializes a "shell" instance of this group implementation that
   * may be used to identify and instantiate instances of this type of
opendj-sdk/opends/src/server/org/opends/server/api/VirtualAttribute.java
File was deleted
opendj-sdk/opends/src/server/org/opends/server/api/VirtualAttributeProvider.java
New file
@@ -0,0 +1,637 @@
/*
 * 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
 *
 *
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.api;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import org.opends.server.admin.std.server.VirtualAttributeCfg;
import org.opends.server.config.ConfigException;
import org.opends.server.core.SearchOperation;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ByteString;
import org.opends.server.types.ConditionResult;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.VirtualAttributeRule;
import static org.opends.server.loggers.debug.DebugLogger.*;
/**
 * This class defines the set of methods and structures that must be
 * implemented by a Directory Server module that implements the
 * functionality required for one or more virtual attributes.
 *
 * @param  <T>  The type of configuration handled by this virtual
 *              attribute provider.
 */
public abstract class VirtualAttributeProvider
       <T extends VirtualAttributeCfg>
{
  /**
   * Initializes this virtual attribute based on the information in
   * the provided configuration entry.
   *
   * @param  configuration  The configuration to use to initialize
   *                        this virtual attribute provider.
   *
   * @throws  ConfigException  If an unrecoverable problem arises in
   *                           the process of performing the
   *                           initialization.
   *
   * @throws  InitializationException  If a problem occurs during
   *                                   initialization that is not
   *                                   related to the server
   *                                   configuration.
   */
  public abstract void initializeVirtualAttributeProvider(
                            T configuration)
         throws ConfigException, InitializationException;
  /**
   * Performs any finalization that may be necessary whenever this
   * virtual attribute provider is taken out of service.
   */
  public void finalizeVirtualAttributeProvider()
  {
    // No implementation required by default.
  }
  /**
   * Indicates whether this virtual attribute provider may generate
   * multiple values.
   *
   * @return  {@code true} if this virtual attribute provider may
   *          generate multiple values, or {@code false} if not.
   */
  public abstract boolean isMultiValued();
  /**
   * Generates a set of values for the provided entry.
   *
   * @param  entry  The entry for which the values are to be
   *                generated.
   * @param  rule   The virtual attribute rule which defines the
   *                constraints for the virtual attribute.
   *
   * @return  The set of values generated for the provided entry.  It
   *          may be empty, but it must not be {@code null}.
   */
  public abstract LinkedHashSet<AttributeValue>
                       getValues(Entry entry,
                                 VirtualAttributeRule rule);
  /**
   * Indicates whether this virtual attribute provider will generate
   * at least one value for the provided entry.
   *
   * @param  entry  The entry for which to make the determination.
   * @param  rule   The virtual attribute rule which defines the
   *                constraints for the virtual attribute.
   *
   * @return  {@code true} if this virtual attribute provider will
   *          generate at least one value for the provided entry, or
   *          {@code false} if not.
   */
  public boolean hasValue(Entry entry, VirtualAttributeRule rule)
  {
    return (! getValues(entry, rule).isEmpty());
  }
  /**
   * Indicates whether this virtual attribute provider will generate
   * the provided value.
   *
   * @param  entry  The entry for which to make the determination.
   * @param  rule   The virtual attribute rule which defines the
   *                constraints for the virtual attribute.
   * @param  value  The value for which to make the determination.
   *
   * @return  {@code true} if this virtual attribute provider will
   *          generate the specified vaule for the provided entry, or
   *          {@code false} if not.
   */
  public boolean hasValue(Entry entry, VirtualAttributeRule rule,
                          AttributeValue value)
  {
    return getValues(entry, rule).contains(value);
  }
  /**
   * Indicates whether this virtual attribute provider will generate
   * all of the values in the provided collection.
   *
   * @param  entry   The entry for which to make the determination.
   * @param  rule    The virtual attribute rule which defines the
   *                 constraints for the virtual attribute.
   * @param  values  The set of values for which to make the
   *                 determination.
   *
   * @return  {@code true} if this attribute provider will generate
   *          all of the values in the provided collection, or
   *          {@code false} if it will not generate at least one of
   *          them.
   */
  public boolean hasAllValues(Entry entry, VirtualAttributeRule rule,
                              Collection<AttributeValue> values)
  {
    for (AttributeValue value : values)
    {
      if (! getValues(entry, rule).contains(value))
      {
        return false;
      }
    }
    return true;
  }
  /**
   * Indicates whether this virutal attribute provider will generate
   * any of the values in the provided collection.
   *
   * @param  entry   The entry for which to make the determination.
   * @param  rule    The virtual attribute rule which defines the
   *                 constraints for the virtual attribute.
   * @param  values  The set of values for which to make the
   *                 determination.
   *
   * @return  {@code true} if this attribute provider will generate
   *          at least one of the values in the provided collection,
   *          or {@code false} if it will not generate any of them.
   */
  public boolean hasAnyValue(Entry entry, VirtualAttributeRule rule,
                             Collection<AttributeValue> values)
  {
    for (AttributeValue value : values)
    {
      if (getValues(entry, rule).contains(value))
      {
        return true;
      }
    }
    return false;
  }
  /**
   * Indicates whether this virtual attribute provider will generate
   * any value which matches the provided substring.
   *
   * @param  entry       The entry for which to make the
   *                     determination.
   * @param  rule        The virtual attribute rule which defines the
   *                     constraints for the virtual attribute.
   * @param  subInitial  The subInitial component to use in the
   *                     determination.
   * @param  subAny      The subAny components to use in the
   *                     determination.
   * @param  subFinal    The subFinal component to use in the
   *                     determination.
   *
   * @return  <CODE>UNDEFINED</CODE> if this attribute does not have a
   *          substring matching rule, <CODE>TRUE</CODE> if at least
   *          one value matches the provided substring, or
   *          <CODE>FALSE</CODE> otherwise.
   */
  public ConditionResult matchesSubstring(Entry entry,
                                          VirtualAttributeRule rule,
                                          ByteString subInitial,
                                          List<ByteString> subAny,
                                          ByteString subFinal)
  {
    SubstringMatchingRule matchingRule =
         rule.getAttributeType().getSubstringMatchingRule();
    if (matchingRule == null)
    {
      return ConditionResult.UNDEFINED;
    }
    ByteString normalizedSubInitial;
    if (subInitial == null)
    {
      normalizedSubInitial = null;
    }
    else
    {
      try
      {
        normalizedSubInitial =
             matchingRule.normalizeSubstring(subInitial);
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        // The substring couldn't be normalized.  We have to return
        // "undefined".
        return ConditionResult.UNDEFINED;
      }
    }
    ArrayList<ByteString> normalizedSubAny;
    if (subAny == null)
    {
      normalizedSubAny = null;
    }
    else
    {
      normalizedSubAny =
           new ArrayList<ByteString>(subAny.size());
      for (ByteString subAnyElement : subAny)
      {
        try
        {
          normalizedSubAny.add(matchingRule.normalizeSubstring(
                                                 subAnyElement));
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
          }
          // The substring couldn't be normalized.  We have to return
          // "undefined".
          return ConditionResult.UNDEFINED;
        }
      }
    }
    ByteString normalizedSubFinal;
    if (subFinal == null)
    {
      normalizedSubFinal = null;
    }
    else
    {
      try
      {
        normalizedSubFinal =
             matchingRule.normalizeSubstring(subFinal);
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        // The substring couldn't be normalized.  We have to return
        // "undefined".
        return ConditionResult.UNDEFINED;
      }
    }
    ConditionResult result = ConditionResult.FALSE;
    for (AttributeValue value : getValues(entry, rule))
    {
      try
      {
        if (matchingRule.valueMatchesSubstring(
                              value.getNormalizedValue(),
                              normalizedSubInitial,
                              normalizedSubAny,
                              normalizedSubFinal))
        {
          return ConditionResult.TRUE;
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        // The value couldn't be normalized.  If we can't find a
        // definite match, then we should return "undefined".
        result = ConditionResult.UNDEFINED;
      }
    }
    return result;
  }
  /**
   * Indicates whether this virtual attribute provider will generate
   * any value for the provided entry that is greater than or equal to
   * the given value.
   *
   * @param  entry  The entry for which to make the determination.
   * @param  rule   The virtual attribute rule which defines the
   *                constraints for the virtual attribute.
   * @param  value  The value for which to make the determination.
   *
   * @return  {@code UNDEFINED} if the associated attribute type does
   *          not have an ordering matching rule, {@code TRUE} if at
   *          least one of the generated values will be greater than
   *          or equal to the specified value, or {@code FALSE} if
   *          none of the generated values will be greater than or
   *          equal to the specified value.
   */
  public ConditionResult greaterThanOrEqualTo(Entry entry,
                              VirtualAttributeRule rule,
                              AttributeValue value)
  {
    OrderingMatchingRule matchingRule =
         rule.getAttributeType().getOrderingMatchingRule();
    if (matchingRule == null)
    {
      return ConditionResult.UNDEFINED;
    }
    ByteString normalizedValue;
    try
    {
      normalizedValue = value.getNormalizedValue();
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      // We couldn't normalize the provided value.  We should return
      // "undefined".
      return ConditionResult.UNDEFINED;
    }
    ConditionResult result = ConditionResult.FALSE;
    for (AttributeValue v : getValues(entry, rule))
    {
      try
      {
        ByteString nv = v.getNormalizedValue();
        int comparisonResult =
                 matchingRule.compareValues(nv, normalizedValue);
        if (comparisonResult >= 0)
        {
          return ConditionResult.TRUE;
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        // We couldn't normalize one of the attribute values.  If we
        // can't find a definite match, then we should return
        // "undefined".
        result = ConditionResult.UNDEFINED;
      }
    }
    return result;
  }
  /**
   * Indicates whether this virtual attribute provider will generate
   * any value for the provided entry that is less than or equal to
   * the given value.
   *
   * @param  entry  The entry for which to make the determination.
   * @param  rule   The virtual attribute rule which defines the
   *                constraints for the virtual attribute.
   * @param  value  The value for which to make the determination.
   *
   * @return  {@code UNDEFINED} if the associated attribute type does
   *          not have an ordering matching rule, {@code TRUE} if at
   *          least one of the generated values will be less than or
   *          equal to the specified value, or {@code FALSE} if none
   *          of the generated values will be greater than or equal to
   *          the specified value.
   */
  public ConditionResult lessThanOrEqualTo(Entry entry,
                              VirtualAttributeRule rule,
                              AttributeValue value)
  {
    OrderingMatchingRule matchingRule =
         rule.getAttributeType().getOrderingMatchingRule();
    if (matchingRule == null)
    {
      return ConditionResult.UNDEFINED;
    }
    ByteString normalizedValue;
    try
    {
      normalizedValue = value.getNormalizedValue();
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      // We couldn't normalize the provided value.  We should return
      // "undefined".
      return ConditionResult.UNDEFINED;
    }
    ConditionResult result = ConditionResult.FALSE;
    for (AttributeValue v : getValues(entry, rule))
    {
      try
      {
        ByteString nv = v.getNormalizedValue();
        int comparisonResult =
                 matchingRule.compareValues(nv, normalizedValue);
        if (comparisonResult <= 0)
        {
          return ConditionResult.TRUE;
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        // We couldn't normalize one of the attribute values.  If we
        // can't find a definite match, then we should return
        // "undefined".
        result = ConditionResult.UNDEFINED;
      }
    }
    return result;
  }
  /**
   * Indicates whether this virtual attribute provider will generate
   * any value for the provided entry that is approximately equal to
   * the given value.
   *
   * @param  entry  The entry for which to make the determination.
   * @param  rule   The virtual attribute rule which defines the
   *                constraints for the virtual attribute.
   * @param  value  The value for which to make the determination.
   *
   * @return  {@code UNDEFINED} if the associated attribute type does
   *          not have an aproximate matching rule, {@code TRUE} if at
   *          least one of the generated values will be approximately
   *          equal to the specified value, or {@code FALSE} if none
   *          of the generated values will be approximately equal to
   *          the specified value.
   */
  public ConditionResult approximatelyEqualTo(Entry entry,
                              VirtualAttributeRule rule,
                              AttributeValue value)
  {
    ApproximateMatchingRule matchingRule =
         rule.getAttributeType().getApproximateMatchingRule();
    if (matchingRule == null)
    {
      return ConditionResult.UNDEFINED;
    }
    ByteString normalizedValue;
    try
    {
      normalizedValue = matchingRule.normalizeValue(value.getValue());
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      // We couldn't normalize the provided value.  We should return
      // "undefined".
      return ConditionResult.UNDEFINED;
    }
    ConditionResult result = ConditionResult.FALSE;
    for (AttributeValue v : getValues(entry, rule))
    {
      try
      {
        ByteString nv = matchingRule.normalizeValue(v.getValue());
        if (matchingRule.approximatelyMatch(nv, normalizedValue))
        {
          return ConditionResult.TRUE;
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        // We couldn't normalize one of the attribute values.  If we
        // can't find a definite match, then we should return
        // "undefined".
        result = ConditionResult.UNDEFINED;
      }
    }
    return result;
  }
  /**
   * Indicates whether this attribute may be included in search
   * filters as part of the criteria for locating entries.
   *
   * @param  rule             The virtual attribute rule which defines
   *                          the constraints for the virtual
   *                          attribute.
   * @param  searchOperation  The search operation for which to make
   *                          the determination.
   *
   * @return  <CODE>true</CODE> if this attribute may be included in
   *          search filters, or <CODE>false</CODE> if not.
   */
  public abstract boolean isSearchable(VirtualAttributeRule rule,
                                       SearchOperation
                                            searchOperation);
  /**
   * Processes the provided search operation in which the search
   * criteria includes an operation targeted at this virtual
   * attribute.  This method should only be called if
   * <CODE>isSearchable</CODE> returns true and it is not possible to
   * construct a manageable candidate list by processing other
   * elements of the search criteria.
   *
   * @param  rule             The virtual attribute rule which defines
   *                          the constraints for the virtual
   *                          attribute.
   * @param  searchOperation  The search operation to be processed.
   */
  public abstract void processSearch(VirtualAttributeRule rule,
                                     SearchOperation searchOperation);
}
opendj-sdk/opends/src/server/org/opends/server/backends/BackupBackend.java
@@ -97,9 +97,6 @@
       extends Backend
       implements ConfigurableComponent
{
  // The DN of the configuration entry for this backend.
  private DN configEntryDN;
@@ -132,8 +129,6 @@
  {
    super();
    // Perform all initialization in initializeBackend.
  }
@@ -413,7 +408,7 @@
    // If the requested entry was the backend base entry, then retrieve it.
    if (entryDN.equals(backupBaseDN))
    {
      return backupBaseEntry;
      return backupBaseEntry.duplicate(true);
    }
@@ -543,7 +538,9 @@
    userAttrs.put(t, attrList);
    return new Entry(entryDN, ocMap, userAttrs, opAttrs);
    Entry e = new Entry(entryDN, ocMap, userAttrs, opAttrs);
    e.processVirtualAttributes();
    return e;
  }
@@ -764,7 +761,9 @@
    }
    return new Entry(entryDN, ocMap, userAttrs, opAttrs);
    Entry e = new Entry(entryDN, ocMap, userAttrs, opAttrs);
    e.processVirtualAttributes();
    return e;
  }
opendj-sdk/opends/src/server/org/opends/server/backends/MemoryBackend.java
@@ -102,9 +102,6 @@
public class MemoryBackend
       extends Backend
{
  // The base DNs for this backend.
  private DN[] baseDNs;
@@ -134,8 +131,6 @@
  {
    super();
    // Perform all initialization in initializeBackend.
  }
@@ -297,8 +292,10 @@
  public synchronized void addEntry(Entry entry, AddOperation addOperation)
         throws DirectoryException
  {
    Entry e = entry.duplicate(true);
    // See if the target entry already exists.  If so, then fail.
    DN entryDN = entry.getDN();
    DN entryDN = e.getDN();
    if (entryMap.containsKey(entryDN))
    {
      int    msgID   = MSGID_MEMORYBACKEND_ENTRY_ALREADY_EXISTS;
@@ -311,7 +308,7 @@
    // If the entry is one of the base DNs, then add it.
    if (baseDNSet.contains(entryDN))
    {
      entryMap.put(entryDN, entry);
      entryMap.put(entryDN, e);
      return;
    }
@@ -332,7 +329,7 @@
      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message, msgID);
    }
    entryMap.put(entryDN, entry);
    entryMap.put(entryDN, e);
    HashSet<DN> children = childDNs.get(parentDN);
    if (children == null)
    {
@@ -441,8 +438,10 @@
                                        ModifyOperation modifyOperation)
         throws DirectoryException
  {
    Entry e = entry.duplicate(true);
    // Make sure the entry exists.  If not, then throw an exception.
    DN entryDN = entry.getDN();
    DN entryDN = e.getDN();
    if (! entryMap.containsKey(entryDN))
    {
      int    msgID   = MSGID_MEMORYBACKEND_ENTRY_DOESNT_EXIST;
@@ -452,7 +451,7 @@
    // Replace the old entry with the new one.
    entryMap.put(entryDN, entry);
    entryMap.put(entryDN, e);
  }
@@ -464,6 +463,8 @@
                                       ModifyDNOperation modifyDNOperation)
         throws DirectoryException
  {
    Entry e = entry.duplicate(true);
    // Make sure that the target entry exists.
    if (! entryMap.containsKey(currentDN))
    {
@@ -492,10 +493,10 @@
    // Make sure that no entry exists with the new DN.
    if (entryMap.containsKey(entry.getDN()))
    if (entryMap.containsKey(e.getDN()))
    {
      int    msgID   = MSGID_MEMORYBACKEND_ENTRY_ALREADY_EXISTS;
      String message = getMessage(msgID, String.valueOf(entry.getDN()));
      String message = getMessage(msgID, String.valueOf(e.getDN()));
      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message, msgID);
    }
@@ -504,7 +505,7 @@
    boolean matchFound = false;
    for (DN dn : baseDNs)
    {
      if (dn.isAncestorOf(entry.getDN()))
      if (dn.isAncestorOf(e.getDN()))
      {
        matchFound = true;
        break;
@@ -521,7 +522,7 @@
    // Make sure that the parent of the new entry exists.
    DN parentDN = entry.getDN().getParentDNInSuffix();
    DN parentDN = e.getDN().getParentDNInSuffix();
    if ((parentDN == null) || (! entryMap.containsKey(parentDN)))
    {
      int    msgID   = MSGID_MEMORYBACKEND_RENAME_PARENT_DOESNT_EXIST;
@@ -534,7 +535,7 @@
    // Delete the current entry and add the new one.
    deleteEntry(currentDN, null);
    addEntry(entry, null);
    addEntry(e, null);
  }
opendj-sdk/opends/src/server/org/opends/server/backends/MonitorBackend.java
@@ -92,9 +92,6 @@
       extends Backend
       implements ConfigurableComponent
{
  // The set of user-defined attributes that will be included in the base
  // monitor entry.
  private ArrayList<Attribute> userDefinedAttributes;
@@ -128,8 +125,6 @@
  {
    super();
    // Perform all initialization in initializeBackend.
  }
@@ -640,8 +635,10 @@
    // Construct and return the entry.
    return new Entry(baseMonitorDN, monitorClasses, monitorUserAttrs,
                     monitorOperationalAttrs);
    Entry e = new Entry(baseMonitorDN, monitorClasses, monitorUserAttrs,
                        monitorOperationalAttrs);
    e.processVirtualAttributes();
    return e;
  }
@@ -706,8 +703,10 @@
      }
    }
    return new Entry(entryDN, monitorClasses, attrMap,
                     new HashMap<AttributeType,List<Attribute>>(0));
    Entry e = new Entry(entryDN, monitorClasses, attrMap,
                        new HashMap<AttributeType,List<Attribute>>(0));
    e.processVirtualAttributes();
    return e;
  }
opendj-sdk/opends/src/server/org/opends/server/backends/RootDSEBackend.java
@@ -105,9 +105,6 @@
       extends Backend
       implements ConfigurableComponent
{
  // The set of standard "static" attributes that we will always include in the
  // root DSE entry and won't change while the server is running.
  private ArrayList<Attribute> staticDSEAttributes;
@@ -153,8 +150,6 @@
  {
    super();
    // Perform all initialization in initializeBackend.
  }
@@ -644,30 +639,6 @@
    }
    // Add the "subschemaSubentry" attribute.
    DN schemaDN = DirectoryServer.getSchemaDN();
    if (schemaDN != null)
    {
      Attribute subschemaSubentryAttr =
           createAttribute(ATTR_SUBSCHEMA_SUBENTRY, ATTR_SUBSCHEMA_SUBENTRY_LC,
                           String.valueOf(schemaDN));
      ArrayList<Attribute> subschemaSubentryAttrs = new ArrayList<Attribute>(1);
      subschemaSubentryAttrs.add(subschemaSubentryAttr);
      if (showAllAttributes ||
          (! subschemaSubentryAttr.getAttributeType().isOperational()))
      {
        dseUserAttrs.put(subschemaSubentryAttr.getAttributeType(),
                         subschemaSubentryAttrs);
      }
      else
      {
        dseOperationalAttrs.put(subschemaSubentryAttr.getAttributeType(),
                                subschemaSubentryAttrs);
      }
    }
    // Add all the standard "static" attributes.
    for (Attribute a : staticDSEAttributes)
    {
@@ -741,8 +712,10 @@
    // Construct and return the entry.
    return new Entry(rootDSEDN, dseObjectClasses, dseUserAttrs,
                     dseOperationalAttrs);
    Entry e = new Entry(rootDSEDN, dseObjectClasses, dseUserAttrs,
                        dseOperationalAttrs);
    e.processVirtualAttributes();
    return e;
  }
opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -952,7 +952,10 @@
    // Construct and return the entry.
    return new Entry(entryDN, schemaObjectClasses, userAttrs, operationalAttrs);
    Entry e = new Entry(entryDN, schemaObjectClasses, userAttrs,
                        operationalAttrs);
    e.processVirtualAttributes();
    return e;
  }
opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ID2Entry.java
@@ -256,12 +256,13 @@
    //Try to decode the entry based on the version number. On later versions,
    //a case could be written to upgrade entries if it is not the current
    //version
    Entry entry = null;
    switch(entryVersion)
    {
      case JebFormat.FORMAT_VERSION :
        try
        {
          return JebFormat.entryFromDatabase(entryBytes);
          entry = JebFormat.entryFromDatabase(entryBytes);
        }
        catch (Exception e)
        {
@@ -269,6 +270,8 @@
          String message = getMessage(msgID, id.toString());
          throw new JebException(msgID, message);
        }
        break;
      //case 0x00                     :
      //  Call upgrade method? Call 0x00 decode method?
      default   :
@@ -276,6 +279,13 @@
        String message = getMessage(msgID, id.toString(), entryVersion);
        throw new JebException(msgID, message);
    }
    if (entry != null)
    {
      entry.processVirtualAttributes();
    }
    return entry;
  }
  /**
opendj-sdk/opends/src/server/org/opends/server/backends/jeb/JebFormat.java
@@ -356,6 +356,10 @@
    {
      for (Attribute a : list)
      {
        if (a.isVirtual())
        {
          continue;
        }
        userAttrElements.add(new LDAPAttribute(a).encode());
      }
    }
@@ -367,6 +371,10 @@
    {
      for (Attribute a : list)
      {
        if (a.isVirtual())
        {
          continue;
        }
        opAttrElements.add(new LDAPAttribute(a).encode());
      }
    }
opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskBackend.java
@@ -84,9 +84,6 @@
       extends Backend
       implements ConfigurableComponent
{
  /**
   * The set of time units that will be used for expressing the task retention
   * time.
@@ -157,8 +154,6 @@
  {
    super();
    // Perform all initialization in initializeBackend.
  }
@@ -586,8 +581,10 @@
  public void addEntry(Entry entry, AddOperation addOperation)
         throws DirectoryException
  {
    Entry e = entry.duplicate(false);
    // Get the DN for the entry and then get its parent.
    DN entryDN = entry.getDN();
    DN entryDN = e.getDN();
    DN parentDN = entryDN.getParentDNInSuffix();
    if (parentDN == null)
@@ -603,7 +600,7 @@
    // treat the provided entry like a scheduled task.
    if (parentDN.equals(scheduledTaskParentDN))
    {
      Task task = taskScheduler.entryToScheduledTask(entry, addOperation);
      Task task = taskScheduler.entryToScheduledTask(e, addOperation);
      taskScheduler.scheduleTask(task, true);
      return;
    }
@@ -612,7 +609,7 @@
    // treat the provided entry like a recurring task.
    if (parentDN.equals(recurringTaskParentDN))
    {
      RecurringTask recurringTask = taskScheduler.entryToRecurringTask(entry);
      RecurringTask recurringTask = taskScheduler.entryToRecurringTask(e);
      taskScheduler.addRecurringTask(recurringTask, true);
      return;
    }
opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
@@ -1360,7 +1360,7 @@
   */
  public Entry getTaskRootEntry()
  {
    return taskRootEntry;
    return taskRootEntry.duplicate(true);
  }
@@ -1374,7 +1374,7 @@
   */
  public Entry getScheduledTaskParentEntry()
  {
    return scheduledTaskParentEntry;
    return scheduledTaskParentEntry.duplicate(true);
  }
@@ -1388,7 +1388,7 @@
   */
  public Entry getRecurringTaskParentEntry()
  {
    return recurringTaskParentEntry;
    return recurringTaskParentEntry.duplicate(true);
  }
@@ -1540,7 +1540,7 @@
        if (scheduledTaskEntryDN.equals(taskEntry.getDN()))
        {
          return taskEntry;
          return taskEntry.duplicate(true);
        }
      }
@@ -1586,7 +1586,7 @@
        try
        {
          Entry e = t.getTaskEntry();
          Entry e = t.getTaskEntry().duplicate(true);
          if (filter.matchesEntry(e))
          {
            if (! searchOperation.returnEntry(e, null))
@@ -1691,7 +1691,7 @@
        if (recurringTaskEntryDN.equals(recurringTaskEntry.getDN()))
        {
          return recurringTaskEntry;
          return recurringTaskEntry.duplicate(true);
        }
      }
@@ -1737,7 +1737,7 @@
        try
        {
          Entry e = rt.getRecurringTaskEntry();
          Entry e = rt.getRecurringTaskEntry().duplicate(true);
          if (filter.matchesEntry(e))
          {
            if (! searchOperation.returnEntry(e, null))
opendj-sdk/opends/src/server/org/opends/server/config/ConfigEntry.java
@@ -459,7 +459,7 @@
   */
  public ConfigEntry duplicate()
  {
    return new ConfigEntry(entry.duplicate(), parent);
    return new ConfigEntry(entry.duplicate(false), parent);
  }
opendj-sdk/opends/src/server/org/opends/server/core/AddOperation.java
@@ -2014,7 +2014,7 @@
            if (postReadRequest != null)
            {
              Entry addedEntry = entry.duplicate();
              Entry addedEntry = entry.duplicate(true);
              if (! postReadRequest.allowsAttribute(
                         DirectoryServer.getObjectClassAttributeType()))
opendj-sdk/opends/src/server/org/opends/server/core/DeleteOperation.java
@@ -1085,7 +1085,7 @@
          if (preReadRequest != null)
          {
            Entry entryCopy = entry.duplicate();
            Entry entryCopy = entry.duplicate(true);
            if (! preReadRequest.allowsAttribute(
                       DirectoryServer.getObjectClassAttributeType()))
opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -296,10 +296,6 @@
  // between the mechanism name and the handler).
  private ConcurrentHashMap<String,SASLMechanismHandler> saslMechanismHandlers;
  // The set of virtual attributes defined in the server (mapped between the
  // lowercase names and the virtual attributes).
  private ConcurrentHashMap<String,VirtualAttribute> virtualAttributes;
  // The connection handler configuration manager for the Directory Server.
  private ConnectionHandlerConfigManager connectionHandlerConfigManager;
@@ -341,6 +337,9 @@
  private CopyOnWriteArrayList<SynchronizationProvider>
               synchronizationProviders;
  // The set of virtual attributes defined in the server.
  private CopyOnWriteArrayList<VirtualAttributeRule> virtualAttributes;
  // The set of backend initialization listeners registered with the Directory
  // Server.
  private CopyOnWriteArraySet<BackendInitializationListener>
@@ -511,6 +510,10 @@
  // The trust manager provider configuration manager for the Directory Server.
  private TrustManagerProviderConfigManager trustManagerProviderConfigManager;
  // The virtual attribute provider configuration manager for the Directory
  // Server.
  private VirtualAttributeConfigManager virtualAttributeConfigManager;
  // The work queue that will be used to service client requests.
  private WorkQueue workQueue;
@@ -631,7 +634,7 @@
    directoryServer.supportedControls = new TreeSet<String>();
    directoryServer.supportedFeatures = new TreeSet<String>();
    directoryServer.virtualAttributes =
         new ConcurrentHashMap<String,VirtualAttribute>();
         new CopyOnWriteArrayList<VirtualAttributeRule>();
    directoryServer.connectionHandlers =
         new CopyOnWriteArrayList<ConnectionHandler>();
    directoryServer.identityMappers =
@@ -2218,6 +2221,8 @@
    supportedControls.add(OID_MATCHED_VALUES);
    supportedControls.add(OID_LDAP_SUBENTRIES);
    supportedControls.add(OID_PASSWORD_POLICY_CONTROL);
    supportedControls.add(OID_REAL_ATTRS_ONLY);
    supportedControls.add(OID_VIRTUAL_ATTRS_ONLY);
  }
@@ -2314,7 +2319,8 @@
  private void initializeVirtualAttributes()
          throws ConfigException, InitializationException
  {
    // NYI
    virtualAttributeConfigManager = new VirtualAttributeConfigManager();
    virtualAttributeConfigManager.initializeVirtualAttributes();
  }
@@ -3926,6 +3932,113 @@
  /**
   * Retrieves the set of virtual attribute rules registered with the Directory
   * Server.
   *
   * @return  The set of virtual attribute rules registered with the Directory
   *          Server.
   */
  public static List<VirtualAttributeRule> getVirtualAttributes()
  {
    return directoryServer.virtualAttributes;
  }
  /**
   * Retrieves the set of virtual attribute rules registered with the Directory
   * Server that are applicable to the provided entry.
   *
   * @param  entry  The entry for which to retrieve the applicable virtual
   *                attribute rules.
   *
   * @return  The set of virtual attribute rules registered with the Directory
   *          Server that apply to the given entry.  It may be an empty list if
   *          there are no applicable virtual attribute rules.
   */
  public static List<VirtualAttributeRule> getVirtualAttributes(Entry entry)
  {
    LinkedList<VirtualAttributeRule> ruleList =
         new LinkedList<VirtualAttributeRule>();
    for (VirtualAttributeRule rule : directoryServer.virtualAttributes)
    {
      if (rule.appliesToEntry(entry))
      {
        ruleList.add(rule);
      }
    }
    return ruleList;
  }
  /**
   * Registers the provided virtual attribute rule with the Directory Server.
   *
   * @param  rule  The virtual attribute rule to be registered.
   */
  public static void registerVirtualAttribute(VirtualAttributeRule rule)
  {
    synchronized (directoryServer.virtualAttributes)
    {
      directoryServer.virtualAttributes.add(rule);
    }
  }
  /**
   * Deregisters the provided virtual attribute rule with the Directory Server.
   *
   * @param  rule  The virutal attribute rule to be deregistered.
   */
  public static void deregisterVirtualAttribute(VirtualAttributeRule rule)
  {
    synchronized (directoryServer.virtualAttributes)
    {
      directoryServer.virtualAttributes.remove(rule);
    }
  }
  /**
   * Replaces the specified virtual attribute rule in the set of virtual
   * attributes registered with the Directory Server.  If the old rule cannot
   * be found in the list, then the set of registered virtual attributes is not
   * updated.
   *
   * @param  oldRule  The existing rule that should be replaced with the new
   *                  rule.
   * @param  newRule  The new rule that should be used in place of the existing
   *                  rule.
   *
   * @return  {@code true} if the old rule was found and replaced with the new
   *          version, or {@code false} if it was not.
   */
  public static boolean replaceVirtualAttribute(VirtualAttributeRule oldRule,
                                                VirtualAttributeRule newRule)
  {
    synchronized (directoryServer.virtualAttributes)
    {
      int pos = directoryServer.virtualAttributes.indexOf(oldRule);
      if (pos >= 0)
      {
        directoryServer.virtualAttributes.set(pos, newRule);
        return true;
      }
      else
      {
        return false;
      }
    }
  }
  /**
   * Retrieves a reference to the JMX MBean server that is associated with the
   * Directory Server.
   *
opendj-sdk/opends/src/server/org/opends/server/core/ModifyDNOperation.java
@@ -1383,7 +1383,7 @@
        // Duplicate the entry and set its new DN.  Also, create an empty list
        // to hold the attribute-level modifications.
        newEntry = currentEntry.duplicate();
        newEntry = currentEntry.duplicate(false);
        newEntry.setDN(newDN);
        modifications = new ArrayList<Modification>();
@@ -1818,7 +1818,7 @@
          if (preReadRequest != null)
          {
            Entry entry = currentEntry.duplicate();
            Entry entry = currentEntry.duplicate(true);
            if (! preReadRequest.allowsAttribute(
                       DirectoryServer.getObjectClassAttributeType()))
@@ -1869,7 +1869,7 @@
          if (postReadRequest != null)
          {
            Entry entry = newEntry.duplicate();
            Entry entry = newEntry.duplicate(true);
            if (! postReadRequest.allowsAttribute(
                       DirectoryServer.getObjectClassAttributeType()))
opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java
@@ -1189,7 +1189,7 @@
        // Create a duplicate of the entry and apply the changes to it.
        modifiedEntry = currentEntry.duplicate();
        modifiedEntry = currentEntry.duplicate(false);
        if (! noOp)
        {
@@ -2605,7 +2605,7 @@
          if (preReadRequest != null)
          {
            Entry entry = currentEntry.duplicate();
            Entry entry = currentEntry.duplicate(true);
            if (! preReadRequest.allowsAttribute(
                       DirectoryServer.getObjectClassAttributeType()))
@@ -2656,7 +2656,7 @@
          if (postReadRequest != null)
          {
            Entry entry = modifiedEntry.duplicate();
            Entry entry = modifiedEntry.duplicate(true);
            if (! postReadRequest.allowsAttribute(
                       DirectoryServer.getObjectClassAttributeType()))
opendj-sdk/opends/src/server/org/opends/server/core/SearchOperation.java
@@ -102,9 +102,6 @@
                  PostOperationSearchOperation, PostResponseSearchOperation,
                  SearchEntrySearchOperation, SearchReferenceSearchOperation
{
  // Indicates whether a search result done response has been sent to the
  // client.
  private AtomicBoolean responseSent;
@@ -116,12 +113,18 @@
  // entries.
  private boolean includeUsableControl;
  // Indicates whether to only real attributes should be returned.
  private boolean realAttributesOnly;
  // Indicates whether LDAP subentries should be returned.
  private boolean returnLDAPSubentries;
  // Indicates whether to include attribute types only or both types and values.
  private boolean typesOnly;
  // Indicates whether to only virtual attributes should be returned.
  private boolean virtualAttributesOnly;
  // The raw, unprocessed base DN as included in the request from the client.
  private ByteString rawBaseDN;
@@ -274,6 +277,8 @@
    persistentSearch       = null;
    returnLDAPSubentries   = false;
    matchedValuesControl   = null;
    realAttributesOnly     = false;
    virtualAttributesOnly  = false;
  }
@@ -865,7 +870,8 @@
    Entry entryToReturn;
    if ((attributes == null) || attributes.isEmpty())
    {
      entryToReturn = entry.duplicateWithoutOperationalAttributes(typesOnly);
      entryToReturn = entry.duplicateWithoutOperationalAttributes(typesOnly,
                                                                  true);
    }
    else
    {
@@ -1028,6 +1034,16 @@
    }
    if (realAttributesOnly)
    {
      entryToReturn.stripVirtualAttributes();
    }
    else if (virtualAttributesOnly)
    {
      entryToReturn.stripRealAttributes();
    }
    // If there is a matched values control, then further pare down the entry
    // based on the filters that it contains.
    if ((matchedValuesControl != null) && (! typesOnly))
@@ -1877,6 +1893,14 @@
          {
            includeUsableControl = true;
          }
          else if (oid.equals(OID_REAL_ATTRS_ONLY))
          {
            realAttributesOnly = true;
          }
          else if (oid.equals(OID_VIRTUAL_ATTRS_ONLY))
          {
            virtualAttributesOnly = true;
          }
          // NYI -- Add support for additional controls.
          else if (c.isCritical())
opendj-sdk/opends/src/server/org/opends/server/core/VirtualAttributeConfigManager.java
New file
@@ -0,0 +1,578 @@
/*
 * 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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.core;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.LinkedHashSet;
import java.util.concurrent.ConcurrentHashMap;
import org.opends.server.admin.ClassPropertyDefinition;
import org.opends.server.admin.server.ConfigurationAddListener;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.std.meta.VirtualAttributeCfgDefn;
import org.opends.server.admin.std.server.VirtualAttributeCfg;
import org.opends.server.admin.std.server.RootCfg;
import org.opends.server.admin.server.ServerManagementContext;
import org.opends.server.api.VirtualAttributeProvider;
import org.opends.server.config.ConfigException;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.VirtualAttributeRule;
import static org.opends.server.loggers.Error.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.messages.ConfigMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.StaticUtils.*;
/**
 * This class defines a utility that will be used to manage the set of
 * virtual attribute providers defined in the Directory Server.  It will
 * initialize the providers when the server starts, and then will manage any
 * additions, removals, or modifications to any virtual attribute providers
 * while the server is running.
 */
public class VirtualAttributeConfigManager
       implements ConfigurationChangeListener<VirtualAttributeCfg>,
                  ConfigurationAddListener<VirtualAttributeCfg>,
                  ConfigurationDeleteListener<VirtualAttributeCfg>
{
  // A mapping between the DNs of the config entries and the associated
  // virtual attribute rules.
  private ConcurrentHashMap<DN,VirtualAttributeRule> rules;
  /**
   * Creates a new instance of this virtual attribute config manager.
   */
  public VirtualAttributeConfigManager()
  {
    rules = new ConcurrentHashMap<DN,VirtualAttributeRule>();
  }
  /**
   * Initializes all virtual attribute providers currently defined in the
   * Directory Server configuration.  This should only be called at Directory
   * Server startup.
   *
   * @throws  ConfigException  If a configuration problem causes the virtual
   *                           attribute provider initialization process to
   *                           fail.
   *
   * @throws  InitializationException  If a problem occurs while initializing
   *                                   the virtual attribute providers that is
   *                                   not related to the server configuration.
   */
  public void initializeVirtualAttributes()
         throws ConfigException, InitializationException
  {
    // Get the root configuration object.
    ServerManagementContext managementContext =
         ServerManagementContext.getInstance();
    RootCfg rootConfiguration =
         managementContext.getRootConfiguration();
    // Register as an add and delete listener with the root configuration so we
    // can be notified if any virtual attribute provider entries are added or
    // removed.
    rootConfiguration.addVirtualAttributeAddListener(this);
    rootConfiguration.addVirtualAttributeDeleteListener(this);
    //Initialize the existing virtual attribute providers.
    for (String providerName : rootConfiguration.listVirtualAttributes())
    {
      VirtualAttributeCfg cfg =
           rootConfiguration.getVirtualAttribute(providerName);
      cfg.addChangeListener(this);
      if (cfg.isEnabled())
      {
        String className = cfg.getProviderClass();
        try
        {
          VirtualAttributeProvider<? extends VirtualAttributeCfg> provider =
               loadProvider(className, cfg);
          LinkedHashSet<SearchFilter> filters =
               new LinkedHashSet<SearchFilter>();
          for (String filterString : cfg.getFilter())
          {
            try
            {
              filters.add(SearchFilter.createFilterFromString(filterString));
            }
            catch (DirectoryException de)
            {
              if (debugEnabled())
              {
                debugCaught(DebugLogLevel.ERROR, de);
              }
              int    msgID   = MSGID_CONFIG_VATTR_INVALID_SEARCH_FILTER;
              String message = getMessage(msgID, filterString,
                                          String.valueOf(cfg.dn()),
                                          de.getErrorMessage());
              throw new ConfigException(msgID, message, de);
            }
          }
          if (cfg.getAttributeType().isSingleValue())
          {
            if (provider.isMultiValued())
            {
              int    msgID   = MSGID_CONFIG_VATTR_SV_TYPE_WITH_MV_PROVIDER;
              String message = getMessage(msgID, String.valueOf(cfg.dn()),
                                          cfg.getAttributeType().getNameOrOID(),
                                          className);
              throw new ConfigException(msgID, message);
            }
            else if (cfg.getConflictBehavior() ==
                     VirtualAttributeCfgDefn.ConflictBehavior.
                          MERGE_REAL_AND_VIRTUAL)
            {
              int    msgID   = MSGID_CONFIG_VATTR_SV_TYPE_WITH_MERGE_VALUES;
              String message = getMessage(msgID, String.valueOf(cfg.dn()),
                                    cfg.getAttributeType().getNameOrOID());
              throw new ConfigException(msgID, message);
            }
          }
          VirtualAttributeRule rule =
               new VirtualAttributeRule(cfg.getAttributeType(), provider,
                                        cfg.getBaseDN(), cfg.getGroupDN(),
                                        filters, cfg.getConflictBehavior());
          rules.put(cfg.dn(), rule);
          DirectoryServer.registerVirtualAttribute(rule);
        }
        catch (InitializationException ie)
        {
          logError(ErrorLogCategory.CONFIGURATION,
                   ErrorLogSeverity.SEVERE_ERROR,
                   ie.getMessage(), ie.getMessageID());
          continue;
        }
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationAddAcceptable(
                      VirtualAttributeCfg configuration,
                      List<String> unacceptableReasons)
  {
    if (configuration.isEnabled())
    {
      // Get the name of the class and make sure we can instantiate it as a
      // virtual attribute provider.
      String className = configuration.getProviderClass();
      try
      {
        loadProvider(className, null);
      }
      catch (InitializationException ie)
      {
        unacceptableReasons.add(ie.getMessage());
        return false;
      }
    }
    // If there were any search filters provided, then make sure they are all
    // valid.
    for (String filterString : configuration.getFilter())
    {
      try
      {
        SearchFilter.createFilterFromString(filterString);
      }
      catch (DirectoryException de)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, de);
        }
        int    msgID   = MSGID_CONFIG_VATTR_INVALID_SEARCH_FILTER;
        String message = getMessage(msgID, filterString,
                                    String.valueOf(configuration.dn()),
                                    de.getErrorMessage());
        unacceptableReasons.add(message);
        return false;
      }
    }
    // If we've gotten here, then it's fine.
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationAdd(
                                 VirtualAttributeCfg configuration)
  {
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<String> messages            = new ArrayList<String>();
    configuration.addChangeListener(this);
    if (! configuration.isEnabled())
    {
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    // Make sure that we can parse all of the search filters.
    LinkedHashSet<SearchFilter> filters =
         new LinkedHashSet<SearchFilter>();
    for (String filterString : configuration.getFilter())
    {
      try
      {
        filters.add(SearchFilter.createFilterFromString(filterString));
      }
      catch (DirectoryException de)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, de);
        }
        if (resultCode == ResultCode.SUCCESS)
        {
          resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
        }
        int    msgID   = MSGID_CONFIG_VATTR_INVALID_SEARCH_FILTER;
        String message = getMessage(msgID, filterString,
                                    String.valueOf(configuration.dn()),
                                    de.getErrorMessage());
        messages.add(message);
      }
    }
    // Get the name of the class and make sure we can instantiate it as a
    // certificate mapper.
    VirtualAttributeProvider<? extends VirtualAttributeCfg> provider = null;
    if (resultCode == ResultCode.SUCCESS)
    {
      String className = configuration.getProviderClass();
      try
      {
        provider = loadProvider(className, configuration);
      }
      catch (InitializationException ie)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
        messages.add(ie.getMessage());
      }
    }
    if (resultCode == ResultCode.SUCCESS)
    {
      VirtualAttributeRule rule =
           new VirtualAttributeRule(configuration.getAttributeType(), provider,
                                    configuration.getBaseDN(),
                                    configuration.getGroupDN(),
                                    filters,
                                    configuration.getConflictBehavior());
      rules.put(configuration.dn(), rule);
      DirectoryServer.registerVirtualAttribute(rule);
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationDeleteAcceptable(
                      VirtualAttributeCfg configuration,
                      List<String> unacceptableReasons)
  {
    // We will always allow getting rid of a virtual attribute rule.
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationDelete(
                                 VirtualAttributeCfg configuration)
  {
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<String> messages            = new ArrayList<String>();
    VirtualAttributeRule rule = rules.remove(configuration.dn());
    if (rule != null)
    {
      DirectoryServer.deregisterVirtualAttribute(rule);
      rule.getProvider().finalizeVirtualAttributeProvider();
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
                      VirtualAttributeCfg configuration,
                      List<String> unacceptableReasons)
  {
    if (configuration.isEnabled())
    {
      // Get the name of the class and make sure we can instantiate it as a
      // virtual attribute provider.
      String className = configuration.getProviderClass();
      try
      {
        loadProvider(className, null);
      }
      catch (InitializationException ie)
      {
        unacceptableReasons.add(ie.getMessage());
        return false;
      }
    }
    // If there were any search filters provided, then make sure they are all
    // valid.
    for (String filterString : configuration.getFilter())
    {
      try
      {
        SearchFilter.createFilterFromString(filterString);
      }
      catch (DirectoryException de)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, de);
        }
        int    msgID   = MSGID_CONFIG_VATTR_INVALID_SEARCH_FILTER;
        String message = getMessage(msgID, filterString,
                                    String.valueOf(configuration.dn()),
                                    de.getErrorMessage());
        unacceptableReasons.add(message);
        return false;
      }
    }
    // If we've gotten here, then it's fine.
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(
                                 VirtualAttributeCfg configuration)
  {
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<String> messages            = new ArrayList<String>();
    // Get the existing rule if it's already enabled.
    VirtualAttributeRule existingRule = rules.get(configuration.dn());
    // If the new configuration has the rule disabled, then disable it if it
    // is enabled, or do nothing if it's already disabled.
    if (! configuration.isEnabled())
    {
      if (existingRule != null)
      {
        DirectoryServer.deregisterVirtualAttribute(existingRule);
        existingRule.getProvider().finalizeVirtualAttributeProvider();
      }
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
    // Make sure that we can parse all of the search filters.
    LinkedHashSet<SearchFilter> filters =
         new LinkedHashSet<SearchFilter>();
    for (String filterString : configuration.getFilter())
    {
      try
      {
        filters.add(SearchFilter.createFilterFromString(filterString));
      }
      catch (DirectoryException de)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, de);
        }
        if (resultCode == ResultCode.SUCCESS)
        {
          resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
        }
        int    msgID   = MSGID_CONFIG_VATTR_INVALID_SEARCH_FILTER;
        String message = getMessage(msgID, filterString,
                                    String.valueOf(configuration.dn()),
                                    de.getErrorMessage());
        messages.add(message);
      }
    }
    // Get the name of the class and make sure we can instantiate it as a
    // certificate mapper.
    VirtualAttributeProvider<? extends VirtualAttributeCfg> provider = null;
    if (resultCode == ResultCode.SUCCESS)
    {
      String className = configuration.getProviderClass();
      try
      {
        provider = loadProvider(className, configuration);
      }
      catch (InitializationException ie)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
        messages.add(ie.getMessage());
      }
    }
    if (resultCode == ResultCode.SUCCESS)
    {
      VirtualAttributeRule rule =
           new VirtualAttributeRule(configuration.getAttributeType(), provider,
                                    configuration.getBaseDN(),
                                    configuration.getGroupDN(),
                                    filters,
                                    configuration.getConflictBehavior());
      rules.put(configuration.dn(), rule);
      if (existingRule == null)
      {
        DirectoryServer.registerVirtualAttribute(rule);
      }
      else
      {
        DirectoryServer.replaceVirtualAttribute(existingRule, rule);
        existingRule.getProvider().finalizeVirtualAttributeProvider();
      }
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * Loads the specified class, instantiates it as a certificate mapper, and
   * optionally initializes that instance.
   *
   * @param  className      The fully-qualified name of the certificate mapper
   *                        class to load, instantiate, and initialize.
   * @param  configuration  The configuration to use to initialize the
   *                        certificate mapper, or {@code null} if the
   *                        certificate mapper should not be initialized.
   *
   * @return  The possibly initialized certificate mapper.
   *
   * @throws  InitializationException  If a problem occurred while attempting to
   *                                   initialize the certificate mapper.
   */
  private VirtualAttributeProvider<? extends VirtualAttributeCfg>
               loadProvider(String className, VirtualAttributeCfg configuration)
          throws InitializationException
  {
    try
    {
      VirtualAttributeCfgDefn definition =
           VirtualAttributeCfgDefn.getInstance();
      ClassPropertyDefinition propertyDefinition =
           definition.getProviderClassPropertyDefinition();
      Class<? extends VirtualAttributeProvider> providerClass =
           propertyDefinition.loadClass(className,
                                        VirtualAttributeProvider.class);
      VirtualAttributeProvider<? extends VirtualAttributeCfg> provider =
           (VirtualAttributeProvider<? extends VirtualAttributeCfg>)
           providerClass.newInstance();
      if (configuration != null)
      {
        Method method =
             provider.getClass().getMethod("initializeVirtualAttributeProvider",
                  configuration.definition().getServerConfigurationClass());
        method.invoke(provider, configuration);
      }
      return provider;
    }
    catch (Exception e)
    {
      int msgID = MSGID_CONFIG_VATTR_INITIALIZATION_FAILED;
      String message = getMessage(msgID, className,
                                  String.valueOf(configuration.dn()),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
  }
}
opendj-sdk/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
@@ -983,7 +983,7 @@
      return null;
    }
    return configEntry.getEntry();
    return configEntry.getEntry().duplicate(true);
  }
@@ -1027,6 +1027,8 @@
  public void addEntry(Entry entry, AddOperation addOperation)
         throws DirectoryException
  {
    Entry e = entry.duplicate(false);
    // If there is an add operation, then make sure that the associated user has
    // both the CONFIG_READ and CONFIG_WRITE privileges.
    if (addOperation != null)
@@ -1051,7 +1053,7 @@
    {
      // Make sure that the target DN does not already exist.  If it does, then
      // fail.
      DN entryDN = entry.getDN();
      DN entryDN = e.getDN();
      if (configEntries.containsKey(entryDN))
      {
        int    msgID   = MSGID_CONFIG_FILE_ADD_ALREADY_EXISTS;
@@ -1099,7 +1101,7 @@
      // Encapsulate the provided entry in a config entry.
      ConfigEntry newEntry = new ConfigEntry(entry, parentEntry);
      ConfigEntry newEntry = new ConfigEntry(e, parentEntry);
      // See if the parent entry has any add listeners.  If so, then iterate
@@ -1327,6 +1329,8 @@
  public void replaceEntry(Entry entry, ModifyOperation modifyOperation)
         throws DirectoryException
  {
    Entry e = entry.duplicate(false);
    // If there is a modify operation, then make sure that the associated user
    // has both the CONFIG_READ and CONFIG_WRITE privileges.  Also, if the
    // operation targets the set of root privileges then make sure the user has
@@ -1373,7 +1377,7 @@
    try
    {
      // Get the DN of the target entry for future reference.
      DN entryDN = entry.getDN();
      DN entryDN = e.getDN();
      // Get the target entry.  If it does not exist, then fail.
@@ -1405,7 +1409,7 @@
      // Create a new config entry to use for the validation testing.
      ConfigEntry newEntry = new ConfigEntry(entry, currentEntry.getParent());
      ConfigEntry newEntry = new ConfigEntry(e, currentEntry.getParent());
      // See if there are any config change listeners registered for this entry.
@@ -1465,7 +1469,7 @@
      // We'll just overwrite the core entry in the current config entry so that
      // we keep all the registered listeners, references to the parent and
      // children, and other metadata.
      currentEntry.setEntry(entry);
      currentEntry.setEntry(e);
      writeUpdatedConfig();
@@ -1606,7 +1610,7 @@
      case BASE_OBJECT:
        // We are only interested in the base entry itself.  See if it matches
        // and if so then return the entry.
        Entry e = baseEntry.getEntry();
        Entry e = baseEntry.getEntry().duplicate(true);
        if (filter.matchesEntry(e))
        {
          searchOperation.returnEntry(e, null);
@@ -1619,7 +1623,7 @@
        // Iterate through them and return the ones that match the filter.
        for (ConfigEntry child : baseEntry.getChildren().values())
        {
          e = child.getEntry();
          e = child.getEntry().duplicate(true);
          if (filter.matchesEntry(e))
          {
            if (! searchOperation.returnEntry(e, null))
@@ -1680,7 +1684,7 @@
                                SearchOperation searchOperation)
          throws DirectoryException
  {
    Entry e = baseEntry.getEntry();
    Entry e = baseEntry.getEntry().duplicate(true);
    if (filter.matchesEntry(e))
    {
      if (! searchOperation.returnEntry(e, null))
opendj-sdk/opends/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProvider.java
New file
@@ -0,0 +1,399 @@
/*
 * 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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.extensions;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import org.opends.server.admin.std.server.VirtualAttributeCfg;
import org.opends.server.api.VirtualAttributeProvider;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.SearchOperation;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ByteString;
import org.opends.server.types.ByteStringFactory;
import org.opends.server.types.ConditionResult;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchScope;
import org.opends.server.types.VirtualAttributeRule;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.ServerConstants.*;
/**
 * This class implements a virtual attribute provider that is meant to serve the
 * entryDN operational attribute as described in draft-zeilenga-ldap-entrydn.
 */
public class EntryDNVirtualAttributeProvider
       extends VirtualAttributeProvider<VirtualAttributeCfg>
{
  /**
   * Creates a new instance of this entryDN virtual attribute provider.
   */
  public EntryDNVirtualAttributeProvider()
  {
    super();
    // All initialization should be performed in the
    // initializeVirtualAttributeProvider method.
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void initializeVirtualAttributeProvider(
                            VirtualAttributeCfg configuration)
         throws ConfigException, InitializationException
  {
    // No initialization is required.
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean isMultiValued()
  {
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public LinkedHashSet<AttributeValue> getValues(Entry entry,
                                                 VirtualAttributeRule rule)
  {
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
    String normDNString = entry.getDN().toNormalizedString();
    values.add(new AttributeValue(ByteStringFactory.create(normDNString),
                                  ByteStringFactory.create(normDNString)));
    return values;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean hasValue(Entry entry, VirtualAttributeRule rule)
  {
    // This virtual attribute provider will always generate a value.
    return true;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean hasValue(Entry entry, VirtualAttributeRule rule,
                          AttributeValue value)
  {
    try
    {
      String normalizedDN    = entry.getDN().toNormalizedString();
      String normalizedValue = value.getNormalizedStringValue();
      return normalizedDN.equals(normalizedValue);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      return false;
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean hasAnyValue(Entry entry, VirtualAttributeRule rule,
                             Collection<AttributeValue> values)
  {
    String ndnString = entry.getDN().toNormalizedString();
    AttributeValue v = new AttributeValue(ByteStringFactory.create(ndnString),
                                          ByteStringFactory.create(ndnString));
    return values.contains(v);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult matchesSubstring(Entry entry,
                                          VirtualAttributeRule rule,
                                          ByteString subInitial,
                                          List<ByteString> subAny,
                                          ByteString subFinal)
  {
    // DNs cannot be used in substring matching.
    return ConditionResult.UNDEFINED;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult greaterThanOrEqualTo(Entry entry,
                              VirtualAttributeRule rule,
                              AttributeValue value)
  {
    // DNs cannot be used in ordering matching.
    return ConditionResult.UNDEFINED;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult lessThanOrEqualTo(Entry entry,
                              VirtualAttributeRule rule,
                              AttributeValue value)
  {
    // DNs cannot be used in ordering matching.
    return ConditionResult.UNDEFINED;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult approximatelyEqualTo(Entry entry,
                              VirtualAttributeRule rule,
                              AttributeValue value)
  {
    // DNs cannot be used in approximate matching.
    return ConditionResult.UNDEFINED;
  }
  /**
   * {@inheritDoc}.  This virtual attribute will support search operations only
   * if one of the following is true about the search filter:
   * <UL>
   *   <LI>It is an equality filter targeting the associated attribute
   *       type.</LI>
   *   <LI>It is an AND filter in which at least one of the components is an
   *       equality filter targeting the associated attribute type.</LI>
   *   <LI>It is an OR filter in which all of the components are equality
   *       filters targeting the associated attribute type.</LI>
   * </UL>
   */
  @Override()
  public boolean isSearchable(VirtualAttributeRule rule,
                              SearchOperation searchOperation)
  {
    return isSearchable(rule.getAttributeType(), searchOperation.getFilter(),
                        0);
  }
  /**
   * Indicates whether the provided search filter is one that may be used with
   * this virtual attribute provider, optionally operating in a recursive manner
   * to make the determination.
   *
   * @param  attributeType  The attribute type used to hold the entryDN value.
   * @param  searchFilter   The search filter for which to make the
   *                        determination.
   * @param  depth          The current recursion depth for this processing.
   *
   * @return  {@code true} if the provided filter may be used with this virtual
   *          attribute provider, or {@code false} if not.
   */
  private boolean isSearchable(AttributeType attributeType, SearchFilter filter,
                               int depth)
  {
    switch (filter.getFilterType())
    {
      case AND:
        if (depth >= MAX_NESTED_FILTER_DEPTH)
        {
          return false;
        }
        for (SearchFilter f : filter.getFilterComponents())
        {
          if (isSearchable(attributeType, f, depth+1))
          {
            return true;
          }
        }
        return false;
      case OR:
        if (depth >= MAX_NESTED_FILTER_DEPTH)
        {
          return false;
        }
        for (SearchFilter f : filter.getFilterComponents())
        {
          if (! isSearchable(attributeType, f, depth+1))
          {
            return false;
          }
        }
        return true;
      case EQUALITY:
        return filter.getAttributeType().equals(attributeType);
      default:
        return false;
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void processSearch(VirtualAttributeRule rule,
                            SearchOperation searchOperation)
  {
    SearchFilter      filter = searchOperation.getFilter();
    LinkedHashSet<DN> dnSet  = new LinkedHashSet<DN>();
    extractDNs(rule.getAttributeType(), filter, dnSet);
    if (dnSet.isEmpty())
    {
      return;
    }
    DN          baseDN = searchOperation.getBaseDN();
    SearchScope scope  = searchOperation.getScope();
    for (DN dn : dnSet)
    {
      if (! dn.matchesBaseAndScope(baseDN, scope))
      {
        continue;
      }
      try
      {
        Entry entry = DirectoryServer.getEntry(dn);
        if ((entry != null) && filter.matchesEntry(entry))
        {
          searchOperation.returnEntry(entry, null);
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
      }
    }
  }
  /**
   * Extracts the user DNs from the provided filter, operating recursively as
   * necessary, and adds them to the provided set.
   *
   * @param  attributeType  The attribute type holding the entryDN value.
   * @param  filter         The search filter to be processed.
   * @param  dnSet          The set into which the identified DNs should be
   *                        placed.
   */
  private void extractDNs(AttributeType attributeType, SearchFilter filter,
                          LinkedHashSet<DN> dnSet)
  {
    switch (filter.getFilterType())
    {
      case AND:
      case OR:
        for (SearchFilter f : filter.getFilterComponents())
        {
          extractDNs(attributeType, f, dnSet);
        }
        break;
      case EQUALITY:
        if (filter.getAttributeType().equals(attributeType))
        {
          try
          {
            dnSet.add(DN.decode(filter.getAssertionValue().getValue()));
          }
          catch (Exception e)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, e);
            }
          }
        }
        break;
    }
  }
}
opendj-sdk/opends/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java
New file
@@ -0,0 +1,437 @@
/*
 * 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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.extensions;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import org.opends.server.admin.std.server.VirtualAttributeCfg;
import org.opends.server.api.Group;
import org.opends.server.api.VirtualAttributeProvider;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.SearchOperation;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ByteString;
import org.opends.server.types.ConditionResult;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.MemberList;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchScope;
import org.opends.server.types.VirtualAttributeRule;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.ServerConstants.*;
/**
 * This class implements a virtual attribute provider that is meant to serve the
 * isMemberOf operational attribute.  This attribute will be used to provide a
 * list of all groups in which the specified user is a member.
 */
public class IsMemberOfVirtualAttributeProvider
       extends VirtualAttributeProvider<VirtualAttributeCfg>
{
  /**
   * Creates a new instance of this entryDN virtual attribute provider.
   */
  public IsMemberOfVirtualAttributeProvider()
  {
    super();
    // All initialization should be performed in the
    // initializeVirtualAttributeProvider method.
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void initializeVirtualAttributeProvider(
                            VirtualAttributeCfg configuration)
         throws ConfigException, InitializationException
  {
    // No initialization is required.
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean isMultiValued()
  {
    return true;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public LinkedHashSet<AttributeValue> getValues(Entry entry,
                                                 VirtualAttributeRule rule)
  {
    // FIXME -- This probably isn't the most efficient implementation.
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
    for (Group g : DirectoryServer.getGroupManager().getGroupInstances())
    {
      try
      {
        if (g.isMember(entry))
        {
          values.add(new AttributeValue(rule.getAttributeType(),
                                        g.getGroupDN().toString()));
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
      }
    }
    return values;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean hasValue(Entry entry, VirtualAttributeRule rule)
  {
    // FIXME -- This probably isn't the most efficient implementation.
    for (Group g : DirectoryServer.getGroupManager().getGroupInstances())
    {
      try
      {
        if (g.isMember(entry))
        {
          return true;
        }
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
      }
    }
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean hasValue(Entry entry, VirtualAttributeRule rule,
                          AttributeValue value)
  {
    try
    {
      DN groupDN = DN.decode(value.getValue());
      Group g = DirectoryServer.getGroupManager().getGroupInstance(groupDN);
      if (g == null)
      {
        return false;
      }
      else
      {
        return g.isMember(entry);
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      return false;
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean hasAnyValue(Entry entry, VirtualAttributeRule rule,
                             Collection<AttributeValue> values)
  {
    for (AttributeValue value : values)
    {
      if (hasValue(entry, rule, value))
      {
        return true;
      }
    }
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult matchesSubstring(Entry entry,
                                          VirtualAttributeRule rule,
                                          ByteString subInitial,
                                          List<ByteString> subAny,
                                          ByteString subFinal)
  {
    // DNs cannot be used in substring matching.
    return ConditionResult.UNDEFINED;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult greaterThanOrEqualTo(Entry entry,
                              VirtualAttributeRule rule,
                              AttributeValue value)
  {
    // DNs cannot be used in ordering matching.
    return ConditionResult.UNDEFINED;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult lessThanOrEqualTo(Entry entry,
                              VirtualAttributeRule rule,
                              AttributeValue value)
  {
    // DNs cannot be used in ordering matching.
    return ConditionResult.UNDEFINED;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult approximatelyEqualTo(Entry entry,
                              VirtualAttributeRule rule,
                              AttributeValue value)
  {
    // DNs cannot be used in approximate matching.
    return ConditionResult.UNDEFINED;
  }
  /**
   * {@inheritDoc}.  This virtual attribute will support search operations only
   * if one of the following is true about the search filter:
   * <UL>
   *   <LI>It is an equality filter targeting the associated attribute
   *       type.</LI>
   *   <LI>It is an AND filter in which at least one of the components is an
   *       equality filter targeting the associated attribute type.</LI>
   * </UL>
   */
  @Override()
  public boolean isSearchable(VirtualAttributeRule rule,
                              SearchOperation searchOperation)
  {
    return isSearchable(rule.getAttributeType(), searchOperation.getFilter(),
                        0);
  }
  /**
   * Indicates whether the provided search filter is one that may be used with
   * this virtual attribute provider, optionally operating in a recursive manner
   * to make the determination.
   *
   * @param  attributeType  The attribute type used to hold the entryDN value.
   * @param  searchFilter   The search filter for which to make the
   *                        determination.
   * @param  depth          The current recursion depth for this processing.
   *
   * @return  {@code true} if the provided filter may be used with this virtual
   *          attribute provider, or {@code false} if not.
   */
  private boolean isSearchable(AttributeType attributeType, SearchFilter filter,
                               int depth)
  {
    switch (filter.getFilterType())
    {
      case AND:
        if (depth >= MAX_NESTED_FILTER_DEPTH)
        {
          return false;
        }
        for (SearchFilter f : filter.getFilterComponents())
        {
          if (isSearchable(attributeType, f, depth+1))
          {
            return true;
          }
        }
        return false;
      case EQUALITY:
        return filter.getAttributeType().equals(attributeType);
      default:
        return false;
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void processSearch(VirtualAttributeRule rule,
                            SearchOperation searchOperation)
  {
    SearchFilter filter = searchOperation.getFilter();
    Group group = extractGroup(rule.getAttributeType(), filter);
    if (group == null)
    {
      return;
    }
    DN          baseDN = searchOperation.getBaseDN();
    SearchScope scope  = searchOperation.getScope();
    try
    {
      MemberList  memberList = group.getMembers();
      while (memberList.hasMoreMembers())
      {
        try
        {
          Entry e = memberList.nextMemberEntry();
          if (e.matchesBaseAndScope(baseDN, scope) &&
              filter.matchesEntry(e))
          {
            searchOperation.returnEntry(e, null);
          }
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
          }
        }
      }
    }
    catch (DirectoryException de)
    {
      searchOperation.setResponseData(de);
    }
  }
  /**
   * Extracts the first group DN encountered in the provided filter, operating
   * recursively as necessary.
   *
   * @param  attributeType  The attribute type holding the entryDN value.
   * @param  filter         The search filter to be processed.
   *
   * @return  The first group encountered in the provided filter, or
   *          {@code null} if there is no match.
   */
  private Group extractGroup(AttributeType attributeType, SearchFilter filter)
  {
    switch (filter.getFilterType())
    {
      case AND:
        for (SearchFilter f : filter.getFilterComponents())
        {
          Group g = extractGroup(attributeType, f);
          if (g != null)
          {
            return g;
          }
        }
        break;
      case EQUALITY:
        if (filter.getAttributeType().equals(attributeType))
        {
          try
          {
            DN dn = DN.decode(filter.getAssertionValue().getValue());
            return DirectoryServer.getGroupManager().getGroupInstance(dn);
          }
          catch (Exception e)
          {
            if (debugEnabled())
            {
              debugCaught(DebugLogLevel.ERROR, e);
            }
          }
        }
        break;
    }
    return null;
  }
}
opendj-sdk/opends/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProvider.java
New file
@@ -0,0 +1,210 @@
/*
 * 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
 *
 *
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.extensions;
import java.util.LinkedHashSet;
import java.util.List;
import org.opends.server.admin.std.server.VirtualAttributeCfg;
import org.opends.server.api.VirtualAttributeProvider;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.SearchOperation;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ByteString;
import org.opends.server.types.ConditionResult;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import org.opends.server.types.VirtualAttributeRule;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.messages.ExtensionsMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.ServerConstants.*;
/**
 * This class implements a virtual attribute provider that is meant to serve the
 * subschemaSubentry operational attribute as described in RFC 4512.
 */
public class SubschemaSubentryVirtualAttributeProvider
       extends VirtualAttributeProvider<VirtualAttributeCfg>
{
  /**
   * Creates a new instance of this subschemaSubentry virtual attribute
   * provider.
   */
  public SubschemaSubentryVirtualAttributeProvider()
  {
    super();
    // All initialization should be performed in the
    // initializeVirtualAttributeProvider method.
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void initializeVirtualAttributeProvider(
                            VirtualAttributeCfg configuration)
         throws ConfigException, InitializationException
  {
    // No initialization is required.
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean isMultiValued()
  {
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public LinkedHashSet<AttributeValue> getValues(Entry entry,
                                                 VirtualAttributeRule rule)
  {
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
    values.add(new AttributeValue(rule.getAttributeType(),
                                  DirectoryServer.getSchemaDN().toString()));
    return values;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult matchesSubstring(Entry entry,
                                          VirtualAttributeRule rule,
                                          ByteString subInitial,
                                          List<ByteString> subAny,
                                          ByteString subFinal)
  {
    // DNs cannot be used in substring matching.
    return ConditionResult.UNDEFINED;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult greaterThanOrEqualTo(Entry entry,
                              VirtualAttributeRule rule,
                              AttributeValue value)
  {
    // DNs cannot be used in ordering matching.
    return ConditionResult.UNDEFINED;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult lessThanOrEqualTo(Entry entry,
                              VirtualAttributeRule rule,
                              AttributeValue value)
  {
    // DNs cannot be used in ordering matching.
    return ConditionResult.UNDEFINED;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult approximatelyEqualTo(Entry entry,
                              VirtualAttributeRule rule,
                              AttributeValue value)
  {
    // DNs cannot be used in approximate matching.
    return ConditionResult.UNDEFINED;
  }
  /**
   * {@inheritDoc}.  This virtual attribute will support search operations only
   * if one of the following is true about the search filter:
   * <UL>
   *   <LI>It is an equality filter targeting the associated attribute
   *       type.</LI>
   *   <LI>It is an AND filter in which at least one of the components is an
   *       equality filter targeting the associated attribute type.</LI>
   *   <LI>It is an OR filter in which all of the components are equality
   *       filters targeting the associated attribute type.</LI>
   * </UL>
   */
  @Override()
  public boolean isSearchable(VirtualAttributeRule rule,
                              SearchOperation searchOperation)
  {
    // This attribute is not searchable, since it will have the same value in
    // tons of entries.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void processSearch(VirtualAttributeRule rule,
                            SearchOperation searchOperation)
  {
    searchOperation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
    int    msgID   = MSGID_SUBSCHEMASUBENTRY_VATTR_NOT_SEARCHABLE;
    String message = getMessage(msgID, rule.getAttributeType().getNameOrOID());
  }
}
opendj-sdk/opends/src/server/org/opends/server/interop/LazyDN.java
@@ -29,11 +29,11 @@
import org.opends.server.types.DN;
import org.opends.server.types.RDN;
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.RDN;
import org.opends.server.types.SearchScope;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.StaticUtils.*;
@@ -57,9 +57,6 @@
public class LazyDN
       extends DN
{
  /**
   * The serial version identifier required to satisfy the compiler because this
   * class implements the {@code java.io.Serializable} interface.  This value
@@ -229,6 +226,18 @@
   * {@inheritDoc}
   */
  @Override()
  public boolean matchesBaseAndScope(DN baseDN, SearchScope scope)
         throws RuntimeException
  {
    return getDecodedDN().matchesBaseAndScope(baseDN, scope);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean equals(Object o)
         throws RuntimeException
  {
opendj-sdk/opends/src/server/org/opends/server/messages/ConfigMessages.java
@@ -6528,6 +6528,51 @@
  /**
   * The message ID for the message that will be used if a virtual attribute
   * definition has an invalid search filter.  This takes three arguments, which
   * are the filter string, the configuration entry DN, and a message explaining
   * the problem that occurred.
   */
  public static final int MSGID_CONFIG_VATTR_INVALID_SEARCH_FILTER =
       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 649;
  /**
   * The message ID for the message that will be used if an error occurs while
   * trying to load and/or initialize a class as a virtual attribute provider.
   * This takes three arguments, which are the class name, the configuration
   * entry DN, and string representation of the exception that was caught.
   */
  public static final int MSGID_CONFIG_VATTR_INITIALIZATION_FAILED =
       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 650;
  /**
   * The message ID for the message that will be used if the configured
   * attribute type is single-valued, but the virtual attribute provider may
   * generate multiple values.  This takes three arguments, which are the DN of
   * the configuration entry, the name or OID of the attribute type, and the
   * name of the virtual attribute provider class.
   */
  public static final int MSGID_CONFIG_VATTR_SV_TYPE_WITH_MV_PROVIDER =
       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 651;
  /**
   * The message ID for the message that will be used if the configured
   * attribute type is single-valued, but the conflict behavior is to merge the
   * real and virtual values.  This takes two arguments, which are the DN of
   * the configuration entry and the name or OID of the attribute type.
   */
  public static final int MSGID_CONFIG_VATTR_SV_TYPE_WITH_MERGE_VALUES =
       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 652;
  /**
   * Associates a set of generic messages with the message IDs defined in this
   * class.
   */
@@ -9383,6 +9428,24 @@
    registerMessage(MSGID_CONFIG_CHANGE_RESULT_MESSAGES,
                    "%s.%s succeeded but generated the following messages " +
                    "for entry %s:  %s.");
    registerMessage(MSGID_CONFIG_VATTR_INVALID_SEARCH_FILTER,
                    "Unable to parse value \"%s\" from config entry \"%s\" " +
                    "as a valid search filter:  %s.");
    registerMessage(MSGID_CONFIG_VATTR_SV_TYPE_WITH_MV_PROVIDER,
                    "The virtual attribute configuration in entry \"%s\" is " +
                    "not valid because attribute type %s is single-valued " +
                    "but provider %s may generate multiple values.");
    registerMessage(MSGID_CONFIG_VATTR_SV_TYPE_WITH_MERGE_VALUES,
                    "The virtual attribute configuration in entry \"%s\" is " +
                    "not valid because attribute type %s is single-valued " +
                    "but the conflict behavior is configured to merge real " +
                    "and virtual values.");
    registerMessage(MSGID_CONFIG_VATTR_INITIALIZATION_FAILED,
                    "An error occurred while trying to load an instance " +
                    "of class %s referenced in configuration entry %s as a " +
                    "virtual attribute provider:  %s.");
  }
}
opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
@@ -4826,6 +4826,16 @@
  /**
   * The message ID for the message that will be used if a search operation has
   * a filter targeting the subschemaSubentry virtual attribute, which is not
   * searchable.  This takes a single argument, which is the name of the
   * subschemaSubentry attribute type.
   */
  public static final int MSGID_SUBSCHEMASUBENTRY_VATTR_NOT_SEARCHABLE =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 459;
  /**
   * Associates a set of generic messages with the message IDs defined in this
   * class.
   */
@@ -6962,6 +6972,11 @@
                    "The provided password does not contain enough unique " +
                    "characters.  The minimum number of unique characters " +
                    "that may appear in a user password is %d.");
    registerMessage(MSGID_SUBSCHEMASUBENTRY_VATTR_NOT_SEARCHABLE,
                    "The %s attribute is not searchable and should not be " +
                    "included in otherwise unindexed search filters.");
  }
}
opendj-sdk/opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java
@@ -102,8 +102,6 @@
public class InternalClientConnection
       extends ClientConnection
{
  // The message ID counter to use for internal connections.
  private static AtomicInteger nextMessageID;
@@ -207,9 +205,9 @@
      this.authenticationInfo =
           new AuthenticationInfo(internalUserEntry, true);
      super.setAuthenticationInfo(authenticationInfo);
      setSizeLimit(0);
      setTimeLimit(0);
      setLookthroughLimit(0);
      super.setSizeLimit(0);
      super.setTimeLimit(0);
      super.setLookthroughLimit(0);
    }
    catch (DirectoryException de)
    {
@@ -257,9 +255,9 @@
    this.authenticationInfo = authInfo;
    super.setAuthenticationInfo(authInfo);
    setSizeLimit(0);
    setTimeLimit(0);
    setLookthroughLimit(0);
    super.setSizeLimit(0);
    super.setTimeLimit(0);
    super.setLookthroughLimit(0);
    connectionID  = nextConnectionID.getAndDecrement();
    operationList = new LinkedList<Operation>();
@@ -457,6 +455,42 @@
  /**
   * {@inheritDoc}
   */
  @Override()
  public void setSizeLimit(int sizeLimit)
  {
    // No implementation required.  We never want to set a nonzero
    // size limit for internal client connections.
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void setLookthroughLimit(int lookthroughLimit)
  {
    // No implementation required.  We never want to set a nonzero
    // lookthrough limit for internal client connections.
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void setTimeLimit(int timeLimit)
  {
    // No implementation required.  We never want to set a nonzero
    // time limit for internal client connections.
  }
  /**
   * Indicates whether this client connection is currently using a
   * secure mechanism to communicate with the server.  Note that this
   * may change over time based on operations performed by the client
opendj-sdk/opends/src/server/org/opends/server/tools/LDAPToolUtils.java
@@ -111,6 +111,16 @@
    {
      controlOID = OID_SUBTREE_DELETE_CONTROL;
    }
    else if (lowerOID.equals("realattrsonly") ||
             lowerOID.equals("realattributesonly"))
    {
      controlOID = OID_REAL_ATTRS_ONLY;
    }
    else if (lowerOID.equals("virtualattrsonly") ||
             lowerOID.equals("virtualattributesonly"))
    {
      controlOID = OID_VIRTUAL_ATTRS_ONLY;
    }
    if (idx < 0)
    {
opendj-sdk/opends/src/server/org/opends/server/types/Attribute.java
@@ -40,10 +40,7 @@
import org.opends.server.core.DirectoryServer;
import org.opends.server.util.Base64;
import static
    org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static
    org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
@@ -55,9 +52,6 @@
 */
public class Attribute
{
  // The attribute type for this attribute.
  private final AttributeType attributeType;
@@ -385,7 +379,7 @@
   */
  public boolean hasValue()
  {
    return (! values.isEmpty());
    return (! getValues().isEmpty());
  }
@@ -400,7 +394,7 @@
   */
  public boolean hasValue(AttributeValue value)
  {
    return values.contains(value);
    return getValues().contains(value);
  }
@@ -420,7 +414,7 @@
  {
    for (AttributeValue value : values)
    {
      if (! this.values.contains(value))
      if (! getValues().contains(value))
      {
        return false;
      }
@@ -447,7 +441,7 @@
  {
    for (AttributeValue value : values)
    {
      if (this.values.contains(value))
      if (getValues().contains(value))
      {
        return true;
      }
@@ -570,7 +564,7 @@
    ConditionResult result = ConditionResult.FALSE;
    for (AttributeValue value : values)
    for (AttributeValue value : getValues())
    {
      try
      {
@@ -639,7 +633,7 @@
    }
    ConditionResult result = ConditionResult.FALSE;
    for (AttributeValue v : values)
    for (AttributeValue v : getValues())
    {
      try
      {
@@ -708,7 +702,7 @@
    }
    ConditionResult result = ConditionResult.FALSE;
    for (AttributeValue v : values)
    for (AttributeValue v : getValues())
    {
      try
      {
@@ -777,7 +771,7 @@
    }
    ConditionResult result = ConditionResult.FALSE;
    for (AttributeValue v : values)
    for (AttributeValue v : getValues())
    {
      try
      {
@@ -807,6 +801,20 @@
  /**
   * Indicates whether this is a virtual attribute rather than a real
   * attribute.
   *
   * @return  {@code true} if this is a virtual attribute, or
   *          {@code false} if it is a real attribute.
   */
  public boolean isVirtual()
  {
    return false;
  }
  /**
   * Creates a duplicate of this attribute that can be modified
   * without impacting this attribute.
   *
@@ -845,11 +853,7 @@
    else
    {
      LinkedHashSet<AttributeValue> valuesCopy =
           new LinkedHashSet<AttributeValue>(values.size());
      for (AttributeValue v : values)
      {
        valuesCopy.add(v);
      }
           new LinkedHashSet<AttributeValue>(getValues());
      return new Attribute(attributeType, name, optionsCopy,
                           valuesCopy);
@@ -886,12 +890,12 @@
      return false;
    }
    if (values.size() != a.values.size())
    if (getValues().size() != a.getValues().size())
    {
      return false;
    }
    if (! hasAllValues(a.values))
    if (! hasAllValues(a.getValues()))
    {
      return false;
    }
@@ -911,7 +915,7 @@
  public int hashCode()
  {
    int hashCode = attributeType.hashCode();
    for (AttributeValue value : values)
    for (AttributeValue value : getValues())
    {
      hashCode += value.hashCode();
    }
@@ -949,7 +953,7 @@
    buffer.append(", {");
    boolean firstValue = true;
    for (AttributeValue value : values)
    for (AttributeValue value : getValues())
    {
      if (! firstValue)
      {
@@ -988,7 +992,7 @@
   */
  public void toLDIF(StringBuilder buffer)
  {
    for (AttributeValue value : values)
    for (AttributeValue value : getValues())
    {
      buffer.append(name);
opendj-sdk/opends/src/server/org/opends/server/types/DN.java
@@ -29,11 +29,11 @@
/********************
 * NOTE:  Any changes to the set of public methods defined in this
 *        class or the arguments that they contain must also be made
 *        in the org.opends.server.interop.LazyDN package to ensure
 *        continued interoperability with third-party applications
 *        that rely on that functionality.
 * NOTE:  Any changes to the set of non-static public methods defined
 *        in this class or the arguments that they contain must also
 *        be made in the org.opends.server.interop.LazyDN package to
 *        ensure continued interoperability with third-party
 *        applications that rely on that functionality.
 ********************/
@@ -45,10 +45,7 @@
import org.opends.server.protocols.asn1.ASN1OctetString;
import static org.opends.server.config.ConfigConstants.*;
import static
    org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static
    org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.SchemaMessages.*;
import static org.opends.server.util.StaticUtils.*;
@@ -63,9 +60,6 @@
public class DN
       implements Comparable<DN>, Serializable
{
  /**
   * A singleton instance of the null DN (a DN with no components).
   */
@@ -429,6 +423,46 @@
  /**
   * Indicates whether this entry falls within the range of the
   * provided search base DN and scope.
   *
   * @param  baseDN  The base DN for which to make the determination.
   * @param  scope   The search scope for which to make the
   *                 determination.
   *
   * @return  <CODE>true</CODE> if this entry is within the given
   *          base and scope, or <CODE>false</CODE> if it is not.
   */
  public boolean matchesBaseAndScope(DN baseDN, SearchScope scope)
  {
    switch (scope)
    {
      case BASE_OBJECT:
        // The base DN must equal this DN.
        return equals(baseDN);
      case SINGLE_LEVEL:
        // The parent DN must equal the base DN.
        return baseDN.equals(getParent());
      case WHOLE_SUBTREE:
        // This DN must be a descendant of the provided base DN.
        return isDescendantOf(baseDN);
      case SUBORDINATE_SUBTREE:
        // This DN must be a descendant of the provided base DN, but
        // not equal to it.
        return ((! equals(baseDN)) && isDescendantOf(baseDN));
      default:
        // This is a scope that we don't recognize.
        return false;
    }
  }
  /**
   * Decodes the provided ASN.1 octet string as a DN.
   *
   * @param  dnString  The ASN.1 octet string to decode as a DN.
opendj-sdk/opends/src/server/org/opends/server/types/Entry.java
@@ -51,14 +51,7 @@
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.util.LDIFException;
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static
    org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static
    org.opends.server.loggers.debug.DebugLogger.debugVerbose;
import static org.opends.server.loggers.debug.DebugLogger.debugInfo;
import static
    org.opends.server.loggers.debug.DebugLogger.debugWarning;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.loggers.Error.*;
import static org.opends.server.messages.CoreMessages.*;
import static org.opends.server.messages.MessageHandler.*;
@@ -90,8 +83,9 @@
public class Entry
       implements ProtocolElement
{
  // Indicates whether virtual attribute processing has been performed
  // for this entry.
  private boolean virtualAttributeProcessingPerformed;
  // The set of operational attributes for this entry.
  private Map<AttributeType,List<Attribute>> operationalAttributes;
@@ -99,6 +93,9 @@
  // The set of user attributes for this entry.
  private Map<AttributeType,List<Attribute>> userAttributes;
  // The set of suppressed real attributes for this entry.
  private Map<AttributeType,List<Attribute>> suppressedAttributes;
  // The set of objectclasses for this entry.
  private Map<ObjectClass,String> objectClasses;
@@ -137,8 +134,13 @@
               Map<AttributeType,List<Attribute>>
                    operationalAttributes)
  {
    attachment = null;
    schema     = DirectoryServer.getSchema();
    attachment                          = null;
    schema                              = DirectoryServer.getSchema();
    virtualAttributeProcessingPerformed = false;
    suppressedAttributes =
         new LinkedHashMap<AttributeType,List<Attribute>>();
    if (dn == null)
@@ -3094,10 +3096,14 @@
   * Creates a duplicate of this entry that may be altered without
   * impacting the information in this entry.
   *
   * @param  processVirtual  Indicates whether virtual attribute
   *                         processing should be performed for the
   *                         entry.
   *
   * @return  A duplicate of this entry that may be altered without
   *          impacting the information in this entry.
   */
  public Entry duplicate()
  public Entry duplicate(boolean processVirtual)
  {
    HashMap<ObjectClass,String> objectClassesCopy =
         new HashMap<ObjectClass,String>(objectClasses);
@@ -3112,8 +3118,26 @@
                  operationalAttributes.size());
    deepCopy(operationalAttributes, operationalAttrsCopy, false);
    return new Entry(dn, objectClassesCopy, userAttrsCopy,
                     operationalAttrsCopy);
    for (AttributeType t : suppressedAttributes.keySet())
    {
      List<Attribute> attrList = suppressedAttributes.get(t);
      if (t.isOperational())
      {
        operationalAttributes.put(t, attrList);
      }
      else
      {
        userAttributes.put(t, attrList);
      }
    }
    Entry e = new Entry(dn, objectClassesCopy, userAttrsCopy,
                        operationalAttrsCopy);
    if (processVirtual)
    {
      e.processVirtualAttributes();
    }
    return e;
  }
@@ -3123,15 +3147,18 @@
   * attributes that may be altered without impacting the information
   * in this entry.
   *
   * @param  typesOnly  Indicates whether to include attribute types
   *                    only without values.
   * @param  typesOnly       Indicates whether to include attribute
   *                         types only without values.
   * @param  processVirtual  Indicates whether virtual attribute
   *                         processing should be performed for the
   *                         entry.
   *
   * @return  A duplicate of this entry that may be altered without
   *          impacting the information in this entry and that does
   *          not contain any operational attributes.
   */
  public Entry duplicateWithoutOperationalAttributes(
                    boolean typesOnly)
                    boolean typesOnly, boolean processVirtual)
  {
    HashMap<ObjectClass,String> objectClassesCopy;
    if (typesOnly)
@@ -3163,8 +3190,24 @@
    HashMap<AttributeType,List<Attribute>> operationalAttrsCopy =
         new HashMap<AttributeType,List<Attribute>>(0);
    return new Entry(dn, objectClassesCopy, userAttrsCopy,
                     operationalAttrsCopy);
    for (AttributeType t : suppressedAttributes.keySet())
    {
      List<Attribute> attrList = suppressedAttributes.get(t);
      if (! t.isOperational())
      {
        userAttributes.put(t, attrList);
      }
    }
    Entry e = new Entry(dn, objectClassesCopy, userAttrsCopy,
                        operationalAttrsCopy);
    if (processVirtual)
    {
      e.processVirtualAttributes(false);
    }
    return e;
  }
@@ -3172,14 +3215,15 @@
  /**
   * Performs a deep copy from the source map to the target map.  In
   * this case, the attributes in the list will be duplicates rather
   * than re-using the same reference.
   * than re-using the same reference.  Virtual attributes will not be
   * included when making the copy.
   *
   * @param  source  The source map from which to obtain the
   *                 information.
   * @param  target  The target map into which to place the copied
   *                 information.
   * @param  omitValues <CODE>true</CODE> if the values should be
   *                    omitted.
   * @param  source      The source map from which to obtain the
   *                     information.
   * @param  target      The target map into which to place the
   *                     copied information.
   * @param  omitValues  Indicates whether to omit attribute values
   *                     when processing.
   */
  private void deepCopy(Map<AttributeType,List<Attribute>> source,
                        Map<AttributeType,List<Attribute>> target,
@@ -3193,10 +3237,18 @@
      for (Attribute a : sourceList)
      {
        if (a.isVirtual())
        {
          continue;
        }
        targetList.add(a.duplicate(omitValues));
      }
      target.put(t, targetList);
      if (! targetList.isEmpty())
      {
        target.put(t, targetList);
      }
    }
  }
@@ -3522,28 +3574,267 @@
   */
  public boolean matchesBaseAndScope(DN baseDN, SearchScope scope)
  {
    switch (scope)
    return dn.matchesBaseAndScope(baseDN, scope);
  }
  /**
   * Performs any necessary virtual attribute processing for this
   * entry.  This should only be called at the time the entry is
   * decoded or created within the backend.
   */
  public void processVirtualAttributes()
  {
    processVirtualAttributes(true);
  }
  /**
   * Performs any necessary virtual attribute processing for this
   * entry.  This should only be called at the time the entry is
   * decoded or created within the backend.
   *
   * @param  includeOperational  Indicates whether to include
   *                             operational attributes.
   */
  public void processVirtualAttributes(boolean includeOperational)
  {
    for (VirtualAttributeRule rule :
         DirectoryServer.getVirtualAttributes(this))
    {
      case BASE_OBJECT:
        // The entry DN must equal the base DN.
        return baseDN.equals(dn);
      AttributeType attributeType = rule.getAttributeType();
      if (attributeType.isOperational() && (! includeOperational))
      {
        continue;
      }
      case SINGLE_LEVEL:
        // The parent DN for this entry must equal the base DN.
        return baseDN.equals(dn.getParentDNInSuffix());
      List<Attribute> attrList = userAttributes.get(attributeType);
      if ((attrList == null) || attrList.isEmpty())
      {
        attrList = operationalAttributes.get(attributeType);
        if ((attrList == null) || attrList.isEmpty())
        {
          // There aren't any conflicts, so we can just add the
          // attribute to the entry.
          attrList = new LinkedList<Attribute>();
          attrList.add(new VirtualAttribute(attributeType, this,
                                            rule));
          if (attributeType.isOperational())
          {
            operationalAttributes.put(attributeType, attrList);
          }
          else
          {
            userAttributes.put(attributeType, attrList);
          }
        }
        else
        {
          // There is a conflict with an existing operational
          // attribute.
          if (attrList.get(0).isVirtual())
          {
            // The existing attribute is already virtual, so we've got
            // a different conflict, but we'll let the first win.
            // FIXME -- Should we handle this differently?
            continue;
          }
      case WHOLE_SUBTREE:
        // The base DN must be an ancestor of the entry DN.
        return baseDN.isAncestorOf(dn);
          // The conflict is with a real attribute.  See what the
          // conflict behavior is and figure out how to handle it.
          switch (rule.getConflictBehavior())
          {
            case REAL_OVERRIDES_VIRTUAL:
              // We don't need to update the entry because the real
              // attribute will take precedence.
              break;
      case SUBORDINATE_SUBTREE:
        // The base DN must be an ancstor of the entry DN, but it
        // must not equal the entry DN.
        return ((! baseDN.equals(dn)) && baseDN.isAncestorOf(dn));
            case VIRTUAL_OVERRIDES_REAL:
              // We need to move the real attribute to the suppressed
              // list and replace it with the virtual attribute.
              suppressedAttributes.put(attributeType, attrList);
              attrList = new LinkedList<Attribute>();
              attrList.add(new VirtualAttribute(attributeType, this,
                                                rule));
              operationalAttributes.put(attributeType, attrList);
              break;
      default:
        // This is a scope that we don't recognize.
        return false;
            case MERGE_REAL_AND_VIRTUAL:
              // We need to add the virtual attribute to the list and
              // keep the existing real attribute(s).
              attrList.add(new VirtualAttribute(attributeType, this,
                                                rule));
              break;
          }
        }
      }
      else
      {
        // There is a conflict with an existing user attribute.
        if (attrList.get(0).isVirtual())
        {
          // The existing attribute is already virtual, so we've got
          // a different conflict, but we'll let the first win.
          // FIXME -- Should we handle this differently?
          continue;
        }
        // The conflict is with a real attribute.  See what the
        // conflict behavior is and figure out how to handle it.
        switch (rule.getConflictBehavior())
        {
          case REAL_OVERRIDES_VIRTUAL:
            // We don't need to update the entry because the real
            // attribute will take precedence.
            break;
          case VIRTUAL_OVERRIDES_REAL:
            // We need to move the real attribute to the suppressed
            // list and replace it with the virtual attribute.
            suppressedAttributes.put(attributeType, attrList);
            attrList = new LinkedList<Attribute>();
            attrList.add(new VirtualAttribute(attributeType, this,
                                              rule));
            userAttributes.put(attributeType, attrList);
            break;
          case MERGE_REAL_AND_VIRTUAL:
            // We need to add the virtual attribute to the list and
            // keep the existing real attribute(s).
            attrList.add(new VirtualAttribute(attributeType, this,
                                              rule));
            break;
        }
      }
    }
    virtualAttributeProcessingPerformed = true;
  }
  /**
   * Indicates whether virtual attribute processing has been performed
   * for this entry.
   *
   * @return  {@code true} if virtual attribute processing has been
   *          performed for this entry, or {@code false} if not.
   */
  public boolean virtualAttributeProcessingPerformed()
  {
    return virtualAttributeProcessingPerformed;
  }
  /**
   * Strips out all real attributes from this entry so that it only
   * contains virtual attributes.
   */
  public void stripRealAttributes()
  {
    // The objectClass attribute will always be a real attribute.
    objectClasses.clear();
    Iterator<Map.Entry<AttributeType,List<Attribute>>>
         attrListIterator = userAttributes.entrySet().iterator();
    while (attrListIterator.hasNext())
    {
      Map.Entry<AttributeType,List<Attribute>> mapEntry =
           attrListIterator.next();
      Iterator<Attribute> attrIterator =
           mapEntry.getValue().iterator();
      while (attrIterator.hasNext())
      {
        Attribute a = attrIterator.next();
        if (! a.isVirtual())
        {
          attrIterator.remove();
        }
      }
      if (mapEntry.getValue().isEmpty())
      {
        attrListIterator.remove();
      }
    }
    attrListIterator = operationalAttributes.entrySet().iterator();
    while (attrListIterator.hasNext())
    {
      Map.Entry<AttributeType,List<Attribute>> mapEntry =
           attrListIterator.next();
      Iterator<Attribute> attrIterator =
           mapEntry.getValue().iterator();
      while (attrIterator.hasNext())
      {
        Attribute a = attrIterator.next();
        if (! a.isVirtual())
        {
          attrIterator.remove();
        }
      }
      if (mapEntry.getValue().isEmpty())
      {
        attrListIterator.remove();
      }
    }
  }
  /**
   * Strips out all virtual attributes from this entry so that it only
   * contains real attributes.
   */
  public void stripVirtualAttributes()
  {
    Iterator<Map.Entry<AttributeType,List<Attribute>>>
         attrListIterator = userAttributes.entrySet().iterator();
    while (attrListIterator.hasNext())
    {
      Map.Entry<AttributeType,List<Attribute>> mapEntry =
           attrListIterator.next();
      Iterator<Attribute> attrIterator =
           mapEntry.getValue().iterator();
      while (attrIterator.hasNext())
      {
        Attribute a = attrIterator.next();
        if (a.isVirtual())
        {
          attrIterator.remove();
        }
      }
      if (mapEntry.getValue().isEmpty())
      {
        attrListIterator.remove();
      }
    }
    attrListIterator = operationalAttributes.entrySet().iterator();
    while (attrListIterator.hasNext())
    {
      Map.Entry<AttributeType,List<Attribute>> mapEntry =
           attrListIterator.next();
      Iterator<Attribute> attrIterator =
           mapEntry.getValue().iterator();
      while (attrIterator.hasNext())
      {
        Attribute a = attrIterator.next();
        if (a.isVirtual())
        {
          attrIterator.remove();
        }
      }
      if (mapEntry.getValue().isEmpty())
      {
        attrListIterator.remove();
      }
    }
  }
@@ -3742,6 +4033,12 @@
        List<Attribute> attrList = userAttributes.get(attrType);
        for (Attribute a : attrList)
        {
          if (a.isVirtual() &&
              (! exportConfig.includeVirtualAttributes()))
          {
            continue;
          }
          if (exportConfig.typesOnly())
          {
            StringBuilder attrName = new StringBuilder(a.getName());
@@ -3787,7 +4084,7 @@
    }
    // Finally, the set of operational attributes.
    // Next, the set of operational attributes.
    if (exportConfig.includeOperationalAttributes())
    {
      for (AttributeType attrType : operationalAttributes.keySet())
@@ -3798,6 +4095,12 @@
               operationalAttributes.get(attrType);
          for (Attribute a : attrList)
          {
            if (a.isVirtual() &&
                (! exportConfig.includeVirtualAttributes()))
            {
              continue;
            }
            if (exportConfig.typesOnly())
            {
              StringBuilder attrName = new StringBuilder(a.getName());
@@ -3854,6 +4157,55 @@
      }
    }
    // If we are not supposed to include virtual attributes, then
    // write any attributes that may normally be suppressed by a
    // virtual attribute.
    if (! exportConfig.includeVirtualAttributes())
    {
      for (AttributeType t : suppressedAttributes.keySet())
      {
        if (exportConfig.includeAttribute(t))
        {
          for (Attribute a : suppressedAttributes.get(t))
          {
            if (exportConfig.typesOnly())
            {
              StringBuilder attrName = new StringBuilder(a.getName());
              for (String o : a.getOptions())
              {
                attrName.append(";");
                attrName.append(o);
              }
              attrName.append(":");
              writeLDIFLine(attrName, writer, wrapLines, wrapColumn);
            }
            else
            {
              StringBuilder attrName = new StringBuilder(a.getName());
              for (String o : a.getOptions())
              {
                attrName.append(";");
                attrName.append(o);
              }
              for (AttributeValue v : a.getValues())
              {
                StringBuilder attrLine = new StringBuilder();
                attrLine.append(attrName);
                appendLDIFSeparatorAndValue(attrLine,
                                            v.getValueBytes());
                writeLDIFLine(attrLine, writer, wrapLines,
                              wrapColumn);
              }
            }
          }
        }
      }
    }
    // Make sure there is a blank line after the entry.
    writer.newLine();
opendj-sdk/opends/src/server/org/opends/server/types/LDIFExportConfig.java
@@ -54,9 +54,6 @@
 */
public class LDIFExportConfig
{
  // Indicates whether the data should be compressed as it is written.
  private boolean compressData;
@@ -75,6 +72,9 @@
  // export.
  private boolean includeOperationalAttributes;
  // Indicates whether to include virutal attributes in the export.
  private boolean includeVirtualAttributes;
  // Indicates whether to invoke LDIF export plugins on entries being
  // exported.
  private boolean invokeExportPlugins;
@@ -149,6 +149,7 @@
    hashData                     = false;
    includeObjectClasses         = true;
    includeOperationalAttributes = true;
    includeVirtualAttributes     = false;
    invokeExportPlugins          = false;
    signHash                     = false;
    typesOnly                    = false;
@@ -182,6 +183,7 @@
    hashData                     = false;
    includeObjectClasses         = true;
    includeOperationalAttributes = true;
    includeVirtualAttributes     = false;
    invokeExportPlugins          = false;
    signHash                     = false;
    typesOnly                    = false;
@@ -593,6 +595,36 @@
  /**
   * Indicates whether virtual attributes should be included in the
   * export.
   *
   * @return  {@code true} if virtual attributes should be included in
   *          the export, or {@code false} if not.
   */
  public boolean includeVirtualAttributes()
  {
    return includeVirtualAttributes;
  }
  /**
   * Specifies whether virtual attributes should be included in the
   * export.
   *
   * @param  includeVirtualAttributes  Specifies whether virtual
   *                                   attributes should be included
   *                                   in the export.
   */
  public void setIncludeVirtualAttributes(
                   boolean includeVirtualAttributes)
  {
    this.includeVirtualAttributes = includeVirtualAttributes;
  }
  /**
   * Retrieves the set of attributes that should be excluded from the
   * entries written to LDIF.  The set that is returned may be altered
   * by the caller.
opendj-sdk/opends/src/server/org/opends/server/types/VirtualAttribute.java
New file
@@ -0,0 +1,258 @@
/*
 * 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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.types;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import org.opends.server.admin.std.server.VirtualAttributeCfg;
import org.opends.server.api.VirtualAttributeProvider;
/**
 * This class defines a virtual attribute, which is a special kind of
 * attribute whose values do not actually exist in persistent storage
 * but rather are computed or otherwise obtained dynamically.
 */
public class VirtualAttribute
       extends Attribute
{
  // The entry with which this virtual attribute is associated.
  private final Entry entry;
  // The virtual attribute provider for this virtual attribute.
  private final VirtualAttributeProvider<
                     ? extends VirtualAttributeCfg> provider;
  // The virtual attribute rule for this virtual attribute.
  private final VirtualAttributeRule rule;
  /**
   * Creates a new virtual attribute with the provided information.
   *
   * @param  attributeType  The attribute type for this virtual
   *                        attribute.
   * @param  entry          The entry in which this virtual attribute
   *                        exists.
* @param  rule           The virutal attribute rule that governs
   *                        the behavior of this virtual attribute.
   */
  public VirtualAttribute(AttributeType attributeType, Entry entry,
                          VirtualAttributeRule rule)
  {
    super(attributeType);
    this.entry = entry;
    this.rule  = rule;
    provider = rule.getProvider();
  }
  /**
   * Retrieves the entry in which this virtual attribute exists.
   *
   * @return  The entry in which this virtual attribute exists.
   */
  public Entry getEntry()
  {
    return entry;
  }
  /**
   * Retrieves the virtual attribute rule that governs the behavior of
   * this virtual attribute.
   *
   * @return  The virtual attribute rule that governs the behavior of
   *          this virtual attribute.
   */
  public VirtualAttributeRule getVirtualAttributeRule()
  {
    return rule;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public LinkedHashSet<AttributeValue> getValues()
  {
    return provider.getValues(entry, rule);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean hasValue()
  {
    return provider.hasValue(entry, rule);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean hasValue(AttributeValue value)
  {
    return provider.hasValue(entry, rule, value);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean hasAllValues(Collection<AttributeValue> values)
  {
    return provider.hasAllValues(entry, rule, values);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean hasAnyValue(Collection<AttributeValue> values)
  {
    return provider.hasAnyValue(entry, rule, values);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult matchesSubstring(ByteString subInitial,
                                          List<ByteString> subAny,
                                          ByteString subFinal)
  {
    return provider.matchesSubstring(entry, rule, subInitial, subAny,
                                     subFinal);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult greaterThanOrEqualTo(AttributeValue value)
  {
    return provider.greaterThanOrEqualTo(entry, rule, value);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult lessThanOrEqualTo(AttributeValue value)
  {
    return provider.lessThanOrEqualTo(entry, rule, value);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult approximatelyEqualTo(AttributeValue value)
  {
    return provider.approximatelyEqualTo(entry, rule, value);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean isVirtual()
  {
    return true;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public Attribute duplicate(boolean omitValues)
  {
    return new VirtualAttribute(getAttributeType(), entry, rule);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void toString(StringBuilder buffer)
  {
    buffer.append("VirtualAttribute(");
    buffer.append(getAttributeType().getNameOrOID());
    buffer.append(", {");
    boolean firstValue = true;
    for (AttributeValue value : getValues())
    {
      if (! firstValue)
      {
        buffer.append(", ");
      }
      value.toString(buffer);
      firstValue = false;
    }
    buffer.append("})");
  }
}
opendj-sdk/opends/src/server/org/opends/server/types/VirtualAttributeRule.java
New file
@@ -0,0 +1,408 @@
/*
 * 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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.types;
import java.util.Iterator;
import java.util.Set;
import org.opends.server.admin.std.meta.VirtualAttributeCfgDefn;
import org.opends.server.admin.std.server.VirtualAttributeCfg;
import org.opends.server.api.Group;
import org.opends.server.api.VirtualAttributeProvider;
import org.opends.server.core.DirectoryServer;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.Validator.*;
/**
 * This class defines a virtual attribute rule, which associates a
 * virtual attribute provider with its associated configuration,
 * including the attribute type for which the values should be
 * generated; the base DN(s), group DN(s), and search filter(s) that
 * should be used to identify which entries should have the virtual
 * attribute, and how conflicts between real and virtual values should
 * be handled.
 */
public class VirtualAttributeRule
{
  // The attribute type for which the values should be generated.
  private final AttributeType attributeType;
  // The set of base DNs for branches that are eligible to have this
  // virtual attribute.
  private final Set<DN> baseDNs;
  // The set of DNs for groups whose members are eligible to have this
  // virtual attribute.
  private final Set<DN> groupDNs;
  // The set of search filters for entries that are eligible to have
  // this virtual attribute.
  private final Set<SearchFilter> filters;
  // The virtual attribute provider used to generate the values.
  private final VirtualAttributeProvider<
                     ? extends VirtualAttributeCfg> provider;
  // The behavior that should be exhibited for entries that already
  // have real values for the target attribute.
  private final VirtualAttributeCfgDefn.ConflictBehavior
                     conflictBehavior;
  /**
   * Creates a new virtual attribute rule with the provided
   * information.
   *
   * @param  attributeType     The attribute type for which the values
   *                           should be generated.
   * @param  provider          The virtual attribute provider to use
   *                           to generate the values.
   * @param  baseDNs           The set of base DNs for branches that
   *                           are eligible to have this virtual
   *                           attribute.
   * @param  groupDNs          The set of DNs for groups whose members
   *                           are eligible to have this virtual
   *                           attribute.
   * @param  filters           The set of search filters for entries
   *                           that are eligible to have this virtual
   *                           attribute.
   * @param  conflictBehavior  The behavior that the server should
   *                           exhibit for entries that already have
   *                           one or more real values for the target
   *                           attribute.
   */
  public VirtualAttributeRule(AttributeType attributeType,
              VirtualAttributeProvider<? extends VirtualAttributeCfg>
                   provider,
              Set<DN> baseDNs, Set<DN> groupDNs,
              Set<SearchFilter> filters,
              VirtualAttributeCfgDefn.ConflictBehavior
                   conflictBehavior)
  {
    ensureNotNull(attributeType, provider, baseDNs, groupDNs);
    ensureNotNull(filters, conflictBehavior);
    this.attributeType    = attributeType;
    this.provider         = provider;
    this.baseDNs          = baseDNs;
    this.groupDNs         = groupDNs;
    this.filters          = filters;
    this.conflictBehavior = conflictBehavior;
  }
  /**
   * Retrieves the attribute type for which the values should be
   * generated.
   *
   * @return  The attribute type for which the values should be
   *          generated.
   */
  public AttributeType getAttributeType()
  {
    return attributeType;
  }
  /**
   *
   * Retrieves the virtual attribute provider used to generate the
   * values.
   *
   * @return  The virtual attribute provider to use to generate the
   *          values.
   */
  public VirtualAttributeProvider<? extends VirtualAttributeCfg>
              getProvider()
  {
    return provider;
  }
  /**
   * Retrieves the set of base DNs for branches that are eligible to
   * have this virtual attribute.
   *
   * @return  The set of base DNs for branches that are eligible to
   *          have this virtual attribute.
   */
  public Set<DN> getBaseDNs()
  {
    return baseDNs;
  }
  /**
   * Retrieves the set of DNs for groups whose members are eligible to
   * have this virtual attribute.
   *
   * @return  The set of DNs for groups whose members are eligible to
   *          have this virtual attribute.
   */
  public Set<DN> getGroupDNs()
  {
    return groupDNs;
  }
  /**
   * Retrieves the set of search filters for entries that are eligible
   * to have this virtual attribute.
   *
   * @return  The set of search filters for entries that are eligible
   *          to have this virtual attribute.
   */
  public Set<SearchFilter> getFilters()
  {
    return filters;
  }
  /**
   * Retrieves the behavior that the server should exhibit for entries
   * that already have one or more real values for the target
   * attribute.
   *
   * @return  The behavior that the server should exhibit for entries
   *          that already have one or more real values for the target
   *          attribute.
   */
  public VirtualAttributeCfgDefn.ConflictBehavior
              getConflictBehavior()
  {
    return conflictBehavior;
  }
  /**
   * Indicates whether this virtual attribute rule applies to the
   * provided entry, taking into account the eligibility requirements
   * defined in the rule.
   *
   * @param  entry  The entry for which to make the determination.
   *
   * @return  {@code true} if this virtual attribute rule may be used
   *          to generate values for the entry, or {@code false} if
   *          not.
   */
  public boolean appliesToEntry(Entry entry)
  {
    // We'll do this in order of expense so that the checks which are
    // potentially most expensive are done last.  First, check to see
    // if real values should override virtual ones and if so whether
    // the entry already has virtual values.
    if ((conflictBehavior == VirtualAttributeCfgDefn.ConflictBehavior.
                                  REAL_OVERRIDES_VIRTUAL) &&
        entry.hasAttribute(attributeType))
    {
      return false;
    }
    // If there are any base DNs defined, then the entry must be below
    // one of them.
    DN entryDN = entry.getDN();
    if (! baseDNs.isEmpty())
    {
      boolean found = false;
      for (DN dn : baseDNs)
      {
        if (entryDN.isDescendantOf(dn))
        {
          found = true;
          break;
        }
      }
      if (! found)
      {
        return false;
      }
    }
    // If there are any search filters defined, then the entry must
    // match one of them.
    if (! filters.isEmpty())
    {
      boolean found = false;
      for (SearchFilter filter : filters)
      {
        try
        {
          if (filter.matchesEntry(entry))
          {
            found = true;
            break;
          }
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
          }
        }
      }
      if (! found)
      {
        return false;
      }
    }
    // If there are any group memberships defined, then the entry must
    // be a member of one of them.
    if (! groupDNs.isEmpty())
    {
      boolean found = false;
      for (DN dn : groupDNs)
      {
        try
        {
          Group group =
               DirectoryServer.getGroupManager().getGroupInstance(dn);
          if ((group != null) && group.isMember(entry))
          {
            found = true;
            break;
          }
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
          }
        }
      }
      if (! found)
      {
        return false;
      }
    }
    // If we've gotten here, then the rule is applicable.
    return true;
  }
  /**
   * Retrieves a string representation of this virtual attribute rule.
   *
   * @return  A string representation of this virutal attribute rule.
   */
  public String toString()
  {
    StringBuilder buffer = new StringBuilder();
    toString(buffer);
    return buffer.toString();
  }
  /**
   * Appends a string representation of this virtual attribute rule to
   * the provided buffer.
   *
   * @param  buffer  The buffer to which the information should be
   *                 written.
   */
  public void toString(StringBuilder buffer)
  {
    buffer.append("VirtualAttributeRule(attrType=");
    buffer.append(attributeType.getNameOrOID());
    buffer.append(", providerDN=\"");
    buffer.append(provider.getClass().getName());
    buffer.append("\", baseDNs={");
    if (! baseDNs.isEmpty())
    {
      buffer.append("\"");
      Iterator<DN> iterator = baseDNs.iterator();
      buffer.append(iterator.next());
      while (iterator.hasNext())
      {
        buffer.append("\", \"");
        buffer.append(iterator.next());
      }
      buffer.append("\"");
    }
    buffer.append("}, groupDNs={");
    if (! groupDNs.isEmpty())
    {
      buffer.append("\"");
      Iterator<DN> iterator = groupDNs.iterator();
      buffer.append(iterator.next());
      while (iterator.hasNext())
      {
        buffer.append("\", \"");
        buffer.append(iterator.next());
      }
      buffer.append("\"");
    }
    buffer.append("}, filters={");
    if (! filters.isEmpty())
    {
      buffer.append("\"");
      Iterator<SearchFilter> iterator = filters.iterator();
      buffer.append(iterator.next());
      while (iterator.hasNext())
      {
        buffer.append("\", \"");
        buffer.append(iterator.next());
      }
      buffer.append("\"");
    }
    buffer.append("}, conflictBehavior=");
    buffer.append(conflictBehavior);
    buffer.append(")");
  }
}
opendj-sdk/opends/src/server/org/opends/server/util/LDIFWriter.java
@@ -58,8 +58,6 @@
 */
public final class LDIFWriter
{
  // FIXME -- Add support for generating a hash when writing the data.
  // FIXME -- Add support for signing the hash that is generated.
opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -1771,6 +1771,13 @@
  /**
   * The OID for the real attributes only control.
   */
  public static final String OID_REAL_ATTRS_ONLY = "2.16.840.1.113730.3.4.17";
  /**
   * The OID for the subtree delete control.
   */
  public static final String OID_SUBTREE_DELETE_CONTROL =
@@ -1803,6 +1810,14 @@
  /**
   * The OID for the virtual attributes only control.
   */
  public static final String OID_VIRTUAL_ATTRS_ONLY =
       "2.16.840.1.113730.3.4.19";
  /**
   * The block length in bytes used when generating an HMAC-MD5 digest.
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SearchOperationTestCase.java
@@ -136,7 +136,7 @@
    }
    // The add operation changes the attributes, so let's duplicate the entry.
    Entry duplicateEntry = testEntry.duplicate();
    Entry duplicateEntry = testEntry.duplicate(false);
    AddOperation addOperation =
         connection.processAdd(duplicateEntry.getDN(),
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProviderTestCase.java
New file
@@ -0,0 +1,1116 @@
/*
 * 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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.extensions;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.std.meta.VirtualAttributeCfgDefn;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ByteString;
import org.opends.server.types.ByteStringFactory;
import org.opends.server.types.ConditionResult;
import org.opends.server.types.Control;
import org.opends.server.types.DereferencePolicy;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchScope;
import org.opends.server.types.VirtualAttributeRule;
import static org.testng.Assert.*;
import static org.opends.server.util.ServerConstants.*;
/**
 * A set of test cases for the entryDN virtual attribute provider.
 */
public class EntryDNVirtualAttributeProviderTestCase
       extends ExtensionsTestCase
{
  // The attribute type for the entryDN attribute.
  private AttributeType entryDNType;
  /**
   * Ensures that the Directory Server is running.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @BeforeClass()
  public void startServer()
         throws Exception
  {
    TestCaseUtils.startServer();
    entryDNType = DirectoryServer.getAttributeType("entrydn", false);
    assertNotNull(entryDNType);
  }
  /**
   * Retrieves a set of entry DNs for use in testing the entryDN virtual
   * attribute.
   *
   * @return  A set of entry DNs for use in testing the entryDN virtual
   *          attribute.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @DataProvider(name = "testEntryDNs")
  public Object[][] getTestEntryDNs()
         throws Exception
  {
    return new Object[][]
    {
      new Object[] { DN.decode("") },
      new Object[] { DN.decode("o=test") },
      new Object[] { DN.decode("dc=example,dc=com") },
      new Object[] { DN.decode("cn=config") },
      new Object[] { DN.decode("cn=schema") },
      new Object[] { DN.decode("cn=tasks") },
      new Object[] { DN.decode("cn=monitor") },
      new Object[] { DN.decode("cn=backups") }
    };
  }
  /**
   * Tests the {@code getEntry} method for the specified entry to ensure that
   * the entry returned includes the entryDN operational attribute with the
   * correct value.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testGetEntry(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    Entry e = DirectoryServer.getEntry(entryDN);
    assertNotNull(e);
    assertTrue(e.hasAttribute(entryDNType));
    List<Attribute> attrList = e.getAttribute(entryDNType);
    assertNotNull(attrList);
    assertFalse(attrList.isEmpty());
    for (Attribute a : attrList)
    {
      assertTrue(a.hasValue());
      assertEquals(a.getValues().size(), 1);
      assertTrue(a.hasValue(new AttributeValue(entryDNType,
                                               entryDN.toNormalizedString())));
    }
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the entryDN attribute is not included when the list of attributes requested
   * is empty (defaulting to all user attributes).
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchEmptyAttrs(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(objectClass=*)");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(entryDN, SearchScope.BASE_OBJECT, filter);
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertFalse(e.hasAttribute(entryDNType));
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the entryDN attribute is not included when the list of requested attributes
   * is "1.1", meaning no attributes.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchNoAttrs(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(objectClass=*)");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("1.1");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
                            DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                            filter, attrList);
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertFalse(e.hasAttribute(entryDNType));
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the entryDN attribute is not included when all user attributes are
   * requested.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchAllUserAttrs(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(objectClass=*)");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("*");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
                            DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                            filter, attrList);
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertFalse(e.hasAttribute(entryDNType));
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the entryDN attribute is included when all operational attributes are
   * requested.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchAllOperationalAttrs(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(objectClass=*)");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("+");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
                            DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                            filter, attrList);
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertTrue(e.hasAttribute(entryDNType));
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the entryDN attribute is included when the entryDN attribute is
   * specifically requested.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchEntryDNAttr(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(objectClass=*)");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("entrydn");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
                            DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                            filter, attrList);
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertTrue(e.hasAttribute(entryDNType));
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the entryDN attribute is not included when it is not in the list of
   * attributes that is explicitly requested.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchExcludeEntryDNAttr(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(objectClass=*)");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("objectClass");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
                            DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                            filter, attrList);
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertFalse(e.hasAttribute(entryDNType));
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the entryDN attribute is included when the entryDN attribute is
   * specifically requested and the entryDN attribute is used in the search
   * filter with a matching value.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchEntryDNAttrInMatchingFilter(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(entryDN=" + entryDN.toString() +
                                             ")");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("entrydn");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
                            DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                            filter, attrList);
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertTrue(e.hasAttribute(entryDNType));
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * no entries are returned when the entryDN attribute is used in the search
   * filter with a non-matching value.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchEntryDNAttrInNonMatchingFilter(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(entryDN=cn=Not A Match)");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("entrydn");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
                            DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                            filter, attrList);
    assertEquals(searchOperation.getSearchEntries().size(), 0);
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the entryDN attribute is not included when the entryDN attribute is
   * specifically requested and the real attributes only control is included in
   * the request.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchEntryDNAttrRealAttrsOnly(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(objectClass=*)");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("entrydn");
    LinkedList<Control> requestControls = new LinkedList<Control>();
    requestControls.add(new Control(OID_REAL_ATTRS_ONLY, true));
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         new InternalSearchOperation(conn, conn.nextOperationID(),
                                     conn.nextMessageID(), requestControls,
                                     entryDN, SearchScope.BASE_OBJECT,
                                     DereferencePolicy.NEVER_DEREF_ALIASES, 0,
                                     0, false, filter, attrList, null);
    searchOperation.run();
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertFalse(e.hasAttribute(entryDNType));
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the entryDN attribute is included when the entryDN attribute is
   * specifically requested and the virtual attributes only control is included
   * in the request.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchEntryDNAttrVirtualAttrsOnly(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(objectClass=*)");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("entrydn");
    LinkedList<Control> requestControls = new LinkedList<Control>();
    requestControls.add(new Control(OID_VIRTUAL_ATTRS_ONLY, true));
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         new InternalSearchOperation(conn, conn.nextOperationID(),
                                     conn.nextMessageID(), requestControls,
                                     entryDN, SearchScope.BASE_OBJECT,
                                     DereferencePolicy.NEVER_DEREF_ALIASES, 0,
                                     0, false, filter, attrList, null);
    searchOperation.run();
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertTrue(e.hasAttribute(entryDNType));
  }
  /**
   * Tests the {@code isMultiValued} method.
   */
  @Test()
  public void testIsMultiValued()
  {
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    assertFalse(provider.isMultiValued());
  }
  /**
   * Tests the {@code getValues} method for an entry.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testGetValues()
         throws Exception
  {
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(entryDNType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    LinkedHashSet<AttributeValue> values = provider.getValues(entry, rule);
    assertNotNull(values);
    assertEquals(values.size(), 1);
    assertTrue(values.contains(new AttributeValue(entryDNType, "o=test")));
  }
  /**
   * Tests the {@code hasValue} method variant that doesn't take a specific
   * value.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasAnyValue()
         throws Exception
  {
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(entryDNType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    assertTrue(provider.hasValue(entry, rule));
  }
  /**
   * Tests the {@code hasValue} method variant that takes a specific value when
   * the provided value is a match.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasMatchingValue()
         throws Exception
  {
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(entryDNType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    assertTrue(provider.hasValue(entry, rule,
                                 new AttributeValue(entryDNType, "o=test")));
  }
  /**
   * Tests the {@code hasValue} method variant that takes a specific value when
   * the provided value is not a match.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasNonMatchingValue()
         throws Exception
  {
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(entryDNType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    assertFalse(provider.hasValue(entry, rule,
                     new AttributeValue(entryDNType, "o=not test")));
  }
  /**
   * Tests the {@code hasAnyValue} method with an empty set of values.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasAnyValueEmptySet()
         throws Exception
  {
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(entryDNType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    assertFalse(provider.hasAnyValue(entry, rule,
                                     Collections.<AttributeValue>emptySet()));
  }
  /**
   * Tests the {@code hasAnyValue} method with a set of values containing only
   * the correct value.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasAnyValueOnlyCorrect()
         throws Exception
  {
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(entryDNType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
    values.add(new AttributeValue(entryDNType, "o=test"));
    assertTrue(provider.hasAnyValue(entry, rule, values));
  }
  /**
   * Tests the {@code hasAnyValue} method with a set of values containing only
   * an incorrect value.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasAnyValueOnlyIncorrect()
         throws Exception
  {
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(entryDNType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
    values.add(new AttributeValue(entryDNType, "o=not test"));
    assertFalse(provider.hasAnyValue(entry, rule, values));
  }
  /**
   * Tests the {@code hasAnyValue} method with a set of values containing the
   * correct value as well as multiple incorrect values.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasAnyValueIncludesCorrect()
         throws Exception
  {
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(entryDNType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(3);
    values.add(new AttributeValue(entryDNType, "o=test"));
    values.add(new AttributeValue(entryDNType, "o=not test"));
    values.add(new AttributeValue(entryDNType, "o=not test either"));
    assertTrue(provider.hasAnyValue(entry, rule, values));
  }
  /**
   * Tests the {@code hasAnyValue} method with a set of multiple values, none of
   * which are correct.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasAnyValueMissingCorrect()
         throws Exception
  {
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(entryDNType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(3);
    values.add(new AttributeValue(entryDNType, "o=not test"));
    values.add(new AttributeValue(entryDNType, "o=not test either"));
    values.add(new AttributeValue(entryDNType, "o=still not test"));
    assertFalse(provider.hasAnyValue(entry, rule, values));
  }
  /**
   * Tests the {@code matchesSubstring} method to ensure that it returns a
   * result of "undefined".
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testMatchesSubstring()
         throws Exception
  {
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(entryDNType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    LinkedList<ByteString> subAny = new LinkedList<ByteString>();
    subAny.add(ByteStringFactory.create("="));
    assertEquals(provider.matchesSubstring(entry, rule, null, subAny, null),
                 ConditionResult.UNDEFINED);
  }
  /**
   * Tests the {@code greaterThanOrEqualTo} method to ensure that it returns a
   * result of "undefined".
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testGreaterThanOrEqualTo()
         throws Exception
  {
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(entryDNType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    AttributeValue value = new AttributeValue(entryDNType, "o=test2");
    assertEquals(provider.greaterThanOrEqualTo(entry, rule, value),
                 ConditionResult.UNDEFINED);
  }
  /**
   * Tests the {@code lessThanOrEqualTo} method to ensure that it returns a
   * result of "undefined".
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testLessThanOrEqualTo()
         throws Exception
  {
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(entryDNType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    AttributeValue value = new AttributeValue(entryDNType, "o=test2");
    assertEquals(provider.lessThanOrEqualTo(entry, rule, value),
                 ConditionResult.UNDEFINED);
  }
  /**
   * Tests the {@code approximatelyEqualTo} method to ensure that it returns a
   * result of "undefined".
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testApproximatelyEqualTo()
         throws Exception
  {
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(entryDNType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    AttributeValue value = new AttributeValue(entryDNType, "o=test2");
    assertEquals(provider.approximatelyEqualTo(entry, rule, value),
                 ConditionResult.UNDEFINED);
  }
  /**
   * Retrieves a set of filters for use in testing searchability.  The returned
   * data will actually include three elements:
   * <OL>
   *   <LI>The string representation of the search filter to use</LI>
   *   <LI>An indication of whether it should be searchable</LI>
   *   <LI>An indication of whether a minimal o=test entry should match</LI>
   * </OL>
   *
   * @return  A set of filters for use in testing searchability.
   */
  @DataProvider(name = "testFilters")
  public Object[][] getTestFilters()
  {
    return new Object[][]
    {
      new Object[] { "(entryDN=o=test)", true, true },
      new Object[] { "(entryDN=o=not test)", true, false },
      new Object[] { "(o=test)", false, false },
      new Object[] { "(entryDN=*)", false, false },
      new Object[] { "(&(objectClass=*)(entryDN=o=test))", true, true },
      new Object[] { "(&(entryDN=o=test)(entryDN=o=not test))", true, false },
      new Object[] { "(|(objectClass=*)(entryDN=o=test))", false, false },
      new Object[] { "(|(entryDN=o=test)(entryDN=o=not test))", true, true },
      new Object[] { "(&(|(entryDN=o=test)(entryDN=o=not test))" +
                       "(&(objectClass=top)(|(objectClass=organization)" +
                                            "(objectClass=domain)))" +
                       "(|(o=test)(o=not test)))", true, true }
    };
  }
  /**
   * Tests the {@code isSearchable} method with the provided information.
   *
   * @param  filterString  The string representation of the search filter to use
   *                       for the test.
   * @param  isSearchable  Indicates whether a search with the given filter
   *                       should be considered searchable.
   * @param  shouldMatch   Indicates whether the provided filter should match
   *                       a minimal o=test entry.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testFilters")
  public void testIsSearchable(String filterString, boolean isSearchable,
                               boolean shouldMatch)
         throws Exception
  {
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(entryDNType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    SearchFilter filter = SearchFilter.createFilterFromString(filterString);
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         new InternalSearchOperation(conn, conn.nextOperationID(),
                                     conn.nextMessageID(), null,
                                     DN.decode("o=test"),
                                     SearchScope.WHOLE_SUBTREE,
                                     DereferencePolicy.NEVER_DEREF_ALIASES, 0,
                                     0, false, filter, null, null);
    assertEquals(provider.isSearchable(rule, searchOperation), isSearchable);
  }
  /**
   * Tests the {@code processSearch} method with the provided information.
   *
   * @param  filterString  The string representation of the search filter to use
   *                       for the test.
   * @param  isSearchable  Indicates whether a search with the given filter
   *                       should be considered searchable.
   * @param  shouldMatch   Indicates whether the provided filter should match
   *                       a minimal o=test entry.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testFilters")
  public void testProcessSearch(String filterString, boolean isSearchable,
                                boolean shouldMatch)
         throws Exception
  {
    if (! isSearchable)
    {
      return;
    }
    TestCaseUtils.initializeTestBackend(true);
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(entryDNType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    SearchFilter filter = SearchFilter.createFilterFromString(filterString);
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         new InternalSearchOperation(conn, conn.nextOperationID(),
                                     conn.nextMessageID(), null,
                                     DN.decode("o=test"),
                                     SearchScope.WHOLE_SUBTREE,
                                     DereferencePolicy.NEVER_DEREF_ALIASES, 0,
                                     0, false, filter, null, null);
    provider.processSearch(rule, searchOperation);
    if (shouldMatch)
    {
      assertEquals(searchOperation.getSearchEntries().size(), 1);
    }
    else
    {
      assertEquals(searchOperation.getSearchEntries().size(), 0);
    }
  }
}
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProviderTestCase.java
New file
@@ -0,0 +1,1277 @@
/*
 * 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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.extensions;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.std.meta.VirtualAttributeCfgDefn;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ByteString;
import org.opends.server.types.ByteStringFactory;
import org.opends.server.types.ConditionResult;
import org.opends.server.types.Control;
import org.opends.server.types.DereferencePolicy;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchScope;
import org.opends.server.types.VirtualAttributeRule;
import static org.testng.Assert.*;
import static org.opends.server.util.ServerConstants.*;
/**
 * A set of test cases for the isMemberOf virtual attribute provider.
 */
public class IsMemberOfVirtualAttributeProviderTestCase
       extends ExtensionsTestCase
{
  // The attribute type for the isMemberOf attribute.
  private AttributeType isMemberOfType;
  /**
   * Ensures that the Directory Server is running.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @BeforeClass()
  public void startServer()
         throws Exception
  {
    TestCaseUtils.startServer();
    isMemberOfType = DirectoryServer.getAttributeType("ismemberof", false);
    assertNotNull(isMemberOfType);
  }
  /**
   * Tests that the isMemberOf virtual attribute is properly generated for an
   * entry that is a member of a static group based on the member attribute.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testStaticGroupMembershipMember()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntries(
      "dn: ou=People,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: People",
      "",
      "dn: uid=test.user,ou=People,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password",
      "",
      "dn: ou=Groups,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: Groups",
      "",
      "dn: cn=Test Static Group,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfNames",
      "cn: Test Static Group",
      "member: uid=test.user,ou=People,o=test");
    Entry e =
         DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
    assertNotNull(e);
    assertTrue(e.hasAttribute(isMemberOfType));
    for (Attribute a : e.getAttribute(isMemberOfType))
    {
      assertEquals(a.getValues().size(), 1);
      assertTrue(a.hasValue());
      assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
                                     "cn=test static group,ou=groups,o=test")));
      assertFalse(a.hasValue(new AttributeValue(isMemberOfType,
                                      "cn=not a group,ou=groups,o=test")));
      assertFalse(a.hasValue(new AttributeValue(isMemberOfType, "invalid")));
    }
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    DeleteOperation deleteOperation =
         conn.processDelete(DN.decode("cn=test static group,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests that the isMemberOf virtual attribute is properly generated for an
   * entry that is a member of a static group based on the uniqueMember
   * attribute.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testStaticGroupMembershipUniqueMember()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntries(
      "dn: ou=People,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: People",
      "",
      "dn: uid=test.user,ou=People,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password",
      "",
      "dn: ou=Groups,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: Groups",
      "",
      "dn: cn=Test Static Group,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfUniqueNames",
      "cn: Test Static Group",
      "uniqueMember: uid=test.user,ou=People,o=test");
    Entry e =
         DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
    assertNotNull(e);
    assertTrue(e.hasAttribute(isMemberOfType));
    for (Attribute a : e.getAttribute(isMemberOfType))
    {
      assertEquals(a.getValues().size(), 1);
      assertTrue(a.hasValue());
      assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
                                     "cn=test static group,ou=groups,o=test")));
      assertFalse(a.hasValue(new AttributeValue(isMemberOfType,
                                      "cn=not a group,ou=groups,o=test")));
      assertFalse(a.hasValue(new AttributeValue(isMemberOfType, "invalid")));
    }
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    DeleteOperation deleteOperation =
         conn.processDelete(DN.decode("cn=test static group,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests that the isMemberOf virtual attribute is properly generated for an
   * entry that is a member of a dynamic group.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testDynamicGroupMembership()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntries(
      "dn: ou=People,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: People",
      "",
      "dn: uid=test.user,ou=People,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password",
      "",
      "dn: ou=Groups,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: Groups",
      "",
      "dn: cn=Test Dynamic Group,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfURLs",
      "cn: Test Dynamic Group",
      "memberURL: ldap:///ou=People,o=test??sub?(sn=user)");
    Entry e =
         DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
    assertNotNull(e);
    assertTrue(e.hasAttribute(isMemberOfType));
    for (Attribute a : e.getAttribute(isMemberOfType))
    {
      assertEquals(a.getValues().size(), 1);
      assertTrue(a.hasValue());
      assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
                      "cn=test dynamic group,ou=groups,o=test")));
      assertFalse(a.hasValue(new AttributeValue(isMemberOfType,
                                      "cn=not a group,ou=groups,o=test")));
      assertFalse(a.hasValue(new AttributeValue(isMemberOfType, "invalid")));
    }
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    DeleteOperation deleteOperation =
         conn.processDelete(
              DN.decode("cn=test dynamic group,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests that the isMemberOf virtual attribute is properly generated for an
   * entry that is a member of multiple static groups.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testMultipleStaticGroups()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntries(
      "dn: ou=People,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: People",
      "",
      "dn: uid=test.user,ou=People,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password",
      "",
      "dn: uid=test.user2,ou=People,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user2",
      "givenName: Test",
      "sn: User2",
      "cn: Test User2",
      "userPassword: password",
      "",
      "dn: ou=Groups,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: Groups",
      "",
      "dn: cn=Test Group 1,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfNames",
      "cn: Test Group 1",
      "member: uid=test.user,ou=People,o=test",
      "",
      "dn: cn=Test Group 2,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfNames",
      "cn: Test Group 2",
      "member: uid=test.user2,ou=People,o=test",
      "",
      "dn: cn=Test Group 3,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfNames",
      "cn: Test Group 3",
      "member: uid=test.user,ou=People,o=test",
      "member: uid=test.user2,ou=People,o=test");
    Entry e =
         DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
    assertNotNull(e);
    assertTrue(e.hasAttribute(isMemberOfType));
    for (Attribute a : e.getAttribute(isMemberOfType))
    {
      assertEquals(a.getValues().size(), 2);
      assertTrue(a.hasValue());
      assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
                                     "cn=test group 1,ou=groups,o=test")));
      assertFalse(a.hasValue(new AttributeValue(isMemberOfType,
                                      "cn=test group 2,ou=groups,o=test")));
      assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
                                     "cn=test group 3,ou=groups,o=test")));
      assertFalse(a.hasValue(new AttributeValue(isMemberOfType,
                                      "cn=not a group,ou=groups,o=test")));
      assertFalse(a.hasValue(new AttributeValue(isMemberOfType, "invalid")));
    }
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    DeleteOperation deleteOperation =
         conn.processDelete(DN.decode("cn=test group 1,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
    deleteOperation =
         conn.processDelete(DN.decode("cn=test group 2,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
    deleteOperation =
         conn.processDelete(DN.decode("cn=test group 3,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests that the isMemberOf virtual attribute is properly generated for an
   * entry that is a member of multiple static and dynamic groups.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testMultipleGroups()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntries(
      "dn: ou=People,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: People",
      "",
      "dn: uid=test.user,ou=People,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password",
      "",
      "dn: uid=test.user2,ou=People,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user2",
      "givenName: Test",
      "sn: User2",
      "cn: Test User2",
      "userPassword: password",
      "",
      "dn: ou=Groups,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: Groups",
      "",
      "dn: cn=Test Group 1,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfNames",
      "cn: Test Group 1",
      "member: uid=test.user,ou=People,o=test",
      "",
      "dn: cn=Test Group 2,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfNames",
      "cn: Test Group 2",
      "member: uid=test.user2,ou=People,o=test",
      "",
      "dn: cn=Test Group 3,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfNames",
      "cn: Test Group 3",
      "member: uid=test.user,ou=People,o=test",
      "member: uid=test.user2,ou=People,o=test",
      "",
      "dn: cn=Test Group 4,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfURLs",
      "cn: Test Group 4",
      "memberURL: ldap:///o=test??sub?(uid=test.user)",
      "",
      "dn: cn=Test Group 5,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfURLs",
      "cn: Test Group 5",
      "memberURL: ldap:///o=test??sub?(uid=test.user1)",
      "",
      "dn: cn=Test Group 6,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfURLs",
      "cn: Test Group 6",
      "memberURL: ldap:///o=test??sub?(givenName=test)");
    Entry e =
         DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
    assertNotNull(e);
    assertTrue(e.hasAttribute(isMemberOfType));
    for (Attribute a : e.getAttribute(isMemberOfType))
    {
      assertEquals(a.getValues().size(), 4);
      assertTrue(a.hasValue());
      assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
                                     "cn=test group 1,ou=groups,o=test")));
      assertFalse(a.hasValue(new AttributeValue(isMemberOfType,
                                      "cn=test group 2,ou=groups,o=test")));
      assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
                                     "cn=test group 3,ou=groups,o=test")));
      assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
                                     "cn=test group 4,ou=groups,o=test")));
      assertFalse(a.hasValue(new AttributeValue(isMemberOfType,
                                      "cn=test group 5,ou=groups,o=test")));
      assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
                                     "cn=test group 6,ou=groups,o=test")));
      assertFalse(a.hasValue(new AttributeValue(isMemberOfType,
                                      "cn=not a group,ou=groups,o=test")));
      assertFalse(a.hasValue(new AttributeValue(isMemberOfType, "invalid")));
    }
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    DeleteOperation deleteOperation =
         conn.processDelete(DN.decode("cn=test group 1,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
    deleteOperation =
         conn.processDelete(DN.decode("cn=test group 2,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
    deleteOperation =
         conn.processDelete(DN.decode("cn=test group 3,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
    deleteOperation =
         conn.processDelete(DN.decode("cn=test group 4,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
    deleteOperation =
         conn.processDelete(DN.decode("cn=test group 5,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
    deleteOperation =
         conn.processDelete(DN.decode("cn=test group 6,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests the {@code isMultiValued} method.
   */
  @Test()
  public void testIsMultiValued()
  {
    IsMemberOfVirtualAttributeProvider provider =
         new IsMemberOfVirtualAttributeProvider();
    assertTrue(provider.isMultiValued());
  }
  /**
   * Tests the {@code hasAnyValue} method with an empty set of values.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasAnyValueEmptySet()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntries(
      "dn: ou=People,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: People",
      "",
      "dn: uid=test.user,ou=People,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password",
      "",
      "dn: ou=Groups,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: Groups",
      "",
      "dn: cn=Test Static Group,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfNames",
      "cn: Test Static Group",
      "member: uid=test.user,ou=People,o=test");
    Entry e =
         DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
    IsMemberOfVirtualAttributeProvider provider =
         new IsMemberOfVirtualAttributeProvider();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(isMemberOfType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    assertFalse(provider.hasAnyValue(e, rule,
                                     Collections.<AttributeValue>emptySet()));
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    DeleteOperation deleteOperation =
         conn.processDelete(DN.decode("cn=test static group,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests the {@code hasAnyValue} method with a set of values containing only
   * the correct value.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasAnyValueOnlyCorrect()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntries(
      "dn: ou=People,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: People",
      "",
      "dn: uid=test.user,ou=People,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password",
      "",
      "dn: ou=Groups,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: Groups",
      "",
      "dn: cn=Test Static Group,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfNames",
      "cn: Test Static Group",
      "member: uid=test.user,ou=People,o=test");
    Entry e =
         DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
    IsMemberOfVirtualAttributeProvider provider =
         new IsMemberOfVirtualAttributeProvider();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(isMemberOfType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
    values.add(new AttributeValue(isMemberOfType,
                                  "cn=test static group,ou=groups,o=test"));
    assertTrue(provider.hasAnyValue(e, rule, values));
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    DeleteOperation deleteOperation =
         conn.processDelete(DN.decode("cn=test static group,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests the {@code hasAnyValue} method with a set of values containing only
   * an incorrect value.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasAnyValueOnlyIncorrect()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntries(
      "dn: ou=People,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: People",
      "",
      "dn: uid=test.user,ou=People,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password",
      "",
      "dn: ou=Groups,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: Groups",
      "",
      "dn: cn=Test Static Group,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfNames",
      "cn: Test Static Group",
      "member: uid=test.user,ou=People,o=test");
    Entry e =
         DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
    IsMemberOfVirtualAttributeProvider provider =
         new IsMemberOfVirtualAttributeProvider();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(isMemberOfType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
    values.add(new AttributeValue(isMemberOfType,
                                  "cn=test dynamic group,ou=groups,o=test"));
    assertFalse(provider.hasAnyValue(e, rule, values));
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    DeleteOperation deleteOperation =
         conn.processDelete(DN.decode("cn=test static group,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests the {@code hasAnyValue} method with a set of values containing the
   * correct value as well as multiple incorrect values.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasAnyValueIncludesCorrect()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntries(
      "dn: ou=People,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: People",
      "",
      "dn: uid=test.user,ou=People,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password",
      "",
      "dn: ou=Groups,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: Groups",
      "",
      "dn: cn=Test Static Group,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfNames",
      "cn: Test Static Group",
      "member: uid=test.user,ou=People,o=test");
    Entry e =
         DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
    IsMemberOfVirtualAttributeProvider provider =
         new IsMemberOfVirtualAttributeProvider();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(isMemberOfType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
    values.add(new AttributeValue(isMemberOfType,
                                  "cn=test static group,ou=groups,o=test"));
    values.add(new AttributeValue(isMemberOfType,
                                  "cn=test dynamic group,ou=groups,o=test"));
    assertTrue(provider.hasAnyValue(e, rule, values));
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    DeleteOperation deleteOperation =
         conn.processDelete(DN.decode("cn=test static group,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests the {@code hasAnyValue} method with a set of multiple values, none of
   * which are correct.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasAnyValueMissingCorrect()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntries(
      "dn: ou=People,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: People",
      "",
      "dn: uid=test.user,ou=People,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password",
      "",
      "dn: ou=Groups,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: Groups",
      "",
      "dn: cn=Test Static Group,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfNames",
      "cn: Test Static Group",
      "member: uid=test.user,ou=People,o=test");
    Entry e =
         DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
    IsMemberOfVirtualAttributeProvider provider =
         new IsMemberOfVirtualAttributeProvider();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(isMemberOfType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
    values.add(new AttributeValue(isMemberOfType,
                                  "cn=test nonstatic group,ou=groups,o=test"));
    values.add(new AttributeValue(isMemberOfType,
                                  "cn=test dynamic group,ou=groups,o=test"));
    assertFalse(provider.hasAnyValue(e, rule, values));
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    DeleteOperation deleteOperation =
         conn.processDelete(DN.decode("cn=test static group,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests the {@code matchesSubstring} method to ensure that it returns a
   * result of "undefined".
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testMatchesSubstring()
         throws Exception
  {
    IsMemberOfVirtualAttributeProvider provider =
         new IsMemberOfVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(isMemberOfType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    LinkedList<ByteString> subAny = new LinkedList<ByteString>();
    subAny.add(ByteStringFactory.create("="));
    assertEquals(provider.matchesSubstring(entry, rule, null, subAny, null),
                 ConditionResult.UNDEFINED);
  }
  /**
   * Tests the {@code greaterThanOrEqualTo} method to ensure that it returns a
   * result of "undefined".
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testGreaterThanOrEqualTo()
         throws Exception
  {
    IsMemberOfVirtualAttributeProvider provider =
         new IsMemberOfVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(isMemberOfType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    AttributeValue value = new AttributeValue(isMemberOfType, "o=test2");
    assertEquals(provider.greaterThanOrEqualTo(entry, rule, value),
                 ConditionResult.UNDEFINED);
  }
  /**
   * Tests the {@code lessThanOrEqualTo} method to ensure that it returns a
   * result of "undefined".
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testLessThanOrEqualTo()
         throws Exception
  {
    IsMemberOfVirtualAttributeProvider provider =
         new IsMemberOfVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(isMemberOfType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    AttributeValue value = new AttributeValue(isMemberOfType, "o=test2");
    assertEquals(provider.lessThanOrEqualTo(entry, rule, value),
                 ConditionResult.UNDEFINED);
  }
  /**
   * Tests the {@code approximatelyEqualTo} method to ensure that it returns a
   * result of "undefined".
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testApproximatelyEqualTo()
         throws Exception
  {
    IsMemberOfVirtualAttributeProvider provider =
         new IsMemberOfVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(isMemberOfType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    AttributeValue value = new AttributeValue(isMemberOfType, "o=test2");
    assertEquals(provider.approximatelyEqualTo(entry, rule, value),
                 ConditionResult.UNDEFINED);
  }
  /**
   * Retrieves a set of filters for use in testing searchability.  The returned
   * data will actually include three elements:
   * <OL>
   *   <LI>The string representation of the search filter to use</LI>
   *   <LI>An indication of whether it should be searchable</LI>
   *   <LI>An indication of whether the uid=test.user,ou=People,o=test entry
   *       should match</LI>
   * </OL>
   *
   * @return  A set of filters for use in testing searchability.
   */
  @DataProvider(name = "testFilters")
  public Object[][] getTestFilters()
  {
    return new Object[][]
    {
      new Object[] { "(isMemberOf=*)", false, false },
      new Object[] { "(isMemberOf=cn*)", false, false },
      new Object[] { "(isMemberOf=invalid)", true, false },
      new Object[] { "(&(isMemberOf=invalid1)(isMemberOf=invalid2))",
                     true, false },
      new Object[] { "(isMemberOf>=cn=Test Group 1,ou=Groups,o=test)",
                     false, false },
      new Object[] { "(isMemberOf<=cn=Test Group 1,ou=Groups,o=test)",
                     false, false },
      new Object[] { "(isMemberOf~=cn=Test Group 1,ou=Groups,o=test)",
                     false, false },
      new Object[] { "(isMemberOf=cn=Test Group 1,ou=Groups,o=test)",
                     true, true },
      new Object[] { "(isMemberOf=cn=Test Group 2,ou=Groups,o=test)",
                     true, false },
      new Object[] { "(&(isMemberOf=cn=Test Group 1,ou=Groups,o=test)" +
                       "(givenName=test))",
                     true, true },
      new Object[] { "(&(isMemberOf=cn=Test Group 1,ou=Groups,o=test)" +
                       "(isMemberOf=invalid))",
                     true, false },
      new Object[] { "(&(isMemberOf=invalid)" +
                       "(isMemberOf=cn=Test Group 1,ou=Groups,o=test))",
                     true, false },
      new Object[] { "(&(isMemberOf=cn=Test Group 1,ou=Groups,o=test)" +
                       "(givenName=not test))",
                     true, false },
      new Object[] { "(&(isMemberOf=cn=Test Group 1,ou=Groups,o=test)" +
                       "(isMemberOf=cn=Test Group 2,ou=Groups,o=test))",
                     true, false },
      new Object[] { "(&(isMemberOf=cn=Test Group 1,ou=Groups,o=test)" +
                       "(isMemberOf=cn=Test Group 3,ou=Groups,o=test))",
                     true, true },
      new Object[] { "(&(isMemberOf=cn=Test Group 2,ou=Groups,o=test)" +
                       "(isMemberOf=cn=Test Group 4,ou=Groups,o=test))",
                     true, false },
      new Object[] { "(|(isMemberOf=cn=Test Group 1,ou=Groups,o=test)" +
                       "(isMemberOf=cn=Test Group 3,ou=Groups,o=test))",
                     false, false },
    };
  }
  /**
   * Tests the {@code isSearchable} method with the provided information.
   *
   * @param  filterString  The string representation of the search filter to use
   *                       for the test.
   * @param  isSearchable  Indicates whether a search with the given filter
   *                       should be considered searchable.
   * @param  shouldMatch   Indicates whether the provided filter should match
   *                       a minimal o=test entry.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testFilters")
  public void testIsSearchable(String filterString, boolean isSearchable,
                               boolean shouldMatch)
         throws Exception
  {
    IsMemberOfVirtualAttributeProvider provider =
         new IsMemberOfVirtualAttributeProvider();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(isMemberOfType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    SearchFilter filter = SearchFilter.createFilterFromString(filterString);
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         new InternalSearchOperation(conn, conn.nextOperationID(),
                                     conn.nextMessageID(), null,
                                     DN.decode("o=test"),
                                     SearchScope.WHOLE_SUBTREE,
                                     DereferencePolicy.NEVER_DEREF_ALIASES, 0,
                                     0, false, filter, null, null);
    assertEquals(provider.isSearchable(rule, searchOperation), isSearchable);
  }
  /**
   * Tests the {@code processSearch} method with the provided information.
   *
   * @param  filterString  The string representation of the search filter to use
   *                       for the test.
   * @param  isSearchable  Indicates whether a search with the given filter
   *                       should be considered searchable.
   * @param  shouldMatch   Indicates whether the provided filter should match
   *                       a minimal o=test entry.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testFilters")
  public void testProcessSearch(String filterString, boolean isSearchable,
                                boolean shouldMatch)
         throws Exception
  {
    if (! isSearchable)
    {
      return;
    }
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntries(
      "dn: ou=People,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: People",
      "",
      "dn: uid=test.user,ou=People,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password",
      "",
      "dn: uid=test.user2,ou=People,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user2",
      "givenName: Test",
      "sn: User2",
      "cn: Test User2",
      "userPassword: password",
      "",
      "dn: uid=test.user3,ou=People,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user3",
      "givenName: Test",
      "sn: User3",
      "cn: Test User3",
      "userPassword: password",
      "",
      "dn: ou=Groups,o=test",
      "objectClass: top",
      "objectClass: organizationalUnit",
      "ou: Groups",
      "",
      "dn: cn=Test Group 1,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfNames",
      "cn: Test Group 1",
      "member: uid=test.user,ou=People,o=test",
      "",
      "dn: cn=Test Group 2,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfNames",
      "cn: Test Group 2",
      "member: uid=test.user2,ou=People,o=test",
      "",
      "dn: cn=Test Group 3,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfNames",
      "cn: Test Group 3",
      "member: uid=test.user,ou=People,o=test",
      "member: uid=test.user2,ou=People,o=test",
      "",
      "dn: cn=Test Group 4,ou=Groups,o=test",
      "objectClass: top",
      "objectClass: groupOfNames",
      "cn: Test Group 4",
      "member: uid=test.user2,ou=People,o=test",
      "member: uid=test.user3,ou=People,o=test");
    Entry userEntry =
         DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
    assertNotNull(userEntry);
    IsMemberOfVirtualAttributeProvider provider =
         new IsMemberOfVirtualAttributeProvider();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(isMemberOfType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    SearchFilter filter = SearchFilter.createFilterFromString(filterString);
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         new InternalSearchOperation(conn, conn.nextOperationID(),
                                     conn.nextMessageID(), null,
                                     DN.decode("o=test"),
                                     SearchScope.WHOLE_SUBTREE,
                                     DereferencePolicy.NEVER_DEREF_ALIASES, 0,
                                     0, false, filter, null, null);
    provider.processSearch(rule, searchOperation);
    boolean matchFound = false;
    for (Entry e : searchOperation.getSearchEntries())
    {
      if (e.getDN().equals(userEntry.getDN()))
      {
        if (matchFound)
        {
          fail("Multiple matches found for the same user.");
        }
        else
        {
          matchFound = true;
        }
      }
    }
    assertEquals(matchFound, shouldMatch);
    DeleteOperation deleteOperation =
         conn.processDelete(DN.decode("cn=test group 1,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
    deleteOperation =
         conn.processDelete(DN.decode("cn=test group 2,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
    deleteOperation =
         conn.processDelete(DN.decode("cn=test group 3,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
    deleteOperation =
         conn.processDelete(DN.decode("cn=test group 4,ou=groups,o=test"));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
}
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProviderTestCase.java
New file
@@ -0,0 +1,848 @@
/*
 * 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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.extensions;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.std.meta.VirtualAttributeCfgDefn;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ByteString;
import org.opends.server.types.ByteStringFactory;
import org.opends.server.types.ConditionResult;
import org.opends.server.types.Control;
import org.opends.server.types.DereferencePolicy;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchScope;
import org.opends.server.types.VirtualAttributeRule;
import static org.testng.Assert.*;
import static org.opends.server.util.ServerConstants.*;
/**
 * A set of test cases for the subschemaSubentry virtual attribute provider.
 */
public class SubschemaSubentryVirtualAttributeProviderTestCase
       extends ExtensionsTestCase
{
  // The attribute type for the subschemaSubentry attribute.
  private AttributeType subschemaSubentryType;
  /**
   * Ensures that the Directory Server is running.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @BeforeClass()
  public void startServer()
         throws Exception
  {
    TestCaseUtils.startServer();
    subschemaSubentryType =
         DirectoryServer.getAttributeType("subschemasubentry", false);
    assertNotNull(subschemaSubentryType);
  }
  /**
   * Retrieves a set of entry DNs for use in testing the subschemaSubentry
   * virtual attribute.
   *
   * @return  A set of entry DNs for use in testing the subschemaSubentry
   *          virtual attribute.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @DataProvider(name = "testEntryDNs")
  public Object[][] getTestEntryDNs()
         throws Exception
  {
    return new Object[][]
    {
      new Object[] { DN.decode("") },
      new Object[] { DN.decode("o=test") },
      new Object[] { DN.decode("dc=example,dc=com") },
      new Object[] { DN.decode("cn=config") },
      new Object[] { DN.decode("cn=schema") },
      new Object[] { DN.decode("cn=tasks") },
      new Object[] { DN.decode("cn=monitor") },
      new Object[] { DN.decode("cn=backups") }
    };
  }
  /**
   * Tests the {@code getEntry} method for the specified entry to ensure that
   * the entry returned includes the subschemaSubentry operational attribute
   * with the correct value.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testGetEntry(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    Entry e = DirectoryServer.getEntry(entryDN);
    assertNotNull(e);
    assertTrue(e.hasAttribute(subschemaSubentryType));
    List<Attribute> attrList = e.getAttribute(subschemaSubentryType);
    assertNotNull(attrList);
    assertFalse(attrList.isEmpty());
    for (Attribute a : attrList)
    {
      assertTrue(a.hasValue());
      assertEquals(a.getValues().size(), 1);
      assertTrue(a.hasValue(new AttributeValue(subschemaSubentryType,
                                               "cn=schema")));
    }
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the subschemaSubentry attribute is not included when the list of attributes
   * requested is empty (defaulting to all user attributes).
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchEmptyAttrs(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(objectClass=*)");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(entryDN, SearchScope.BASE_OBJECT, filter);
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertFalse(e.hasAttribute(subschemaSubentryType));
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the subschemaSubentry attribute is not included when the list of requested
   * attributes is "1.1", meaning no attributes.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchNoAttrs(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(objectClass=*)");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("1.1");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
                            DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                            filter, attrList);
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertFalse(e.hasAttribute(subschemaSubentryType));
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the subschemaSubentry attribute is not included when all user attributes
   * are requested.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchAllUserAttrs(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(objectClass=*)");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("*");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
                            DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                            filter, attrList);
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertFalse(e.hasAttribute(subschemaSubentryType));
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the subschemaSubentry attribute is included when all operational attributes
   * are requested.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchAllOperationalAttrs(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(objectClass=*)");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("+");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
                            DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                            filter, attrList);
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertTrue(e.hasAttribute(subschemaSubentryType));
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the subschemaSubentry attribute is included when that attribute is
   * specifically requested.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchSubschemaSubentryAttr(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(objectClass=*)");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("subschemasubentry");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
                            DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                            filter, attrList);
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertTrue(e.hasAttribute(subschemaSubentryType));
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the subschemaSubentry attribute is not included when it is not in the list
   * of attributes that is explicitly requested.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchExcludeSubschemaSubentryAttr(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(objectClass=*)");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("objectClass");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
                            DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                            filter, attrList);
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertFalse(e.hasAttribute(subschemaSubentryType));
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the subschemaSubentry attribute is included when that attribute is
   * specifically requested and the subschemaSubentry attribute is used in the
   * search filter with a matching value.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchSubschemaSubentryAttrInMatchingFilter(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(subschemaSubentry=cn=schema)");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("subschemaSubentry");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
                            DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                            filter, attrList);
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertTrue(e.hasAttribute(subschemaSubentryType));
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * no entries are returned when the subschemaSubentry attribute is used in the
   * search filter with a non-matching value.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchSubschemaSubentryAttrInNonMatchingFilter(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(subschemaSubentry=cn=foo)");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("subschemaSubentry");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
                            DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                            filter, attrList);
    assertEquals(searchOperation.getSearchEntries().size(), 0);
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the subschemaSubentry attribute is not included when that attribute is
   * specifically requested and the real attributes only control is included in
   * the request.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchSubschemaSubentryAttrRealAttrsOnly(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(objectClass=*)");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("subschemaSubentry");
    LinkedList<Control> requestControls = new LinkedList<Control>();
    requestControls.add(new Control(OID_REAL_ATTRS_ONLY, true));
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         new InternalSearchOperation(conn, conn.nextOperationID(),
                                     conn.nextMessageID(), requestControls,
                                     entryDN, SearchScope.BASE_OBJECT,
                                     DereferencePolicy.NEVER_DEREF_ALIASES, 0,
                                     0, false, filter, attrList, null);
    searchOperation.run();
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertFalse(e.hasAttribute(subschemaSubentryType));
  }
  /**
   * Performs an internal search to retrieve the specified entry, ensuring that
   * the subschemaSubentry attribute is included when that attribute is
   * specifically requested and the virtual attributes only control is included
   * in the request.
   *
   * @param  entryDN  The DN of the entry to retrieve and verify.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testEntryDNs")
  public void testSearchSubschemaSubentryAttrVirtualAttrsOnly(DN entryDN)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
    SearchFilter filter =
         SearchFilter.createFilterFromString("(objectClass=*)");
    LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
    attrList.add("subschemaSubentry");
    LinkedList<Control> requestControls = new LinkedList<Control>();
    requestControls.add(new Control(OID_VIRTUAL_ATTRS_ONLY, true));
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         new InternalSearchOperation(conn, conn.nextOperationID(),
                                     conn.nextMessageID(), requestControls,
                                     entryDN, SearchScope.BASE_OBJECT,
                                     DereferencePolicy.NEVER_DEREF_ALIASES, 0,
                                     0, false, filter, attrList, null);
    searchOperation.run();
    assertEquals(searchOperation.getSearchEntries().size(), 1);
    Entry e = searchOperation.getSearchEntries().get(0);
    assertNotNull(e);
    assertTrue(e.hasAttribute(subschemaSubentryType));
  }
  /**
   * Tests the {@code isMultiValued} method.
   */
  @Test()
  public void testIsMultiValued()
  {
    SubschemaSubentryVirtualAttributeProvider provider =
         new SubschemaSubentryVirtualAttributeProvider();
    assertFalse(provider.isMultiValued());
  }
  /**
   * Tests the {@code getValues} method for an entry.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testGetValues()
         throws Exception
  {
    SubschemaSubentryVirtualAttributeProvider provider =
         new SubschemaSubentryVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(subschemaSubentryType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    LinkedHashSet<AttributeValue> values = provider.getValues(entry, rule);
    assertNotNull(values);
    assertEquals(values.size(), 1);
    assertTrue(values.contains(new AttributeValue(subschemaSubentryType,
                                                  "cn=schema")));
  }
  /**
   * Tests the {@code hasValue} method variant that doesn't take a specific
   * value.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasAnyValue()
         throws Exception
  {
    SubschemaSubentryVirtualAttributeProvider provider =
         new SubschemaSubentryVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(subschemaSubentryType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    assertTrue(provider.hasValue(entry, rule));
  }
  /**
   * Tests the {@code hasValue} method variant that takes a specific value when
   * the provided value is a match.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasMatchingValue()
         throws Exception
  {
    SubschemaSubentryVirtualAttributeProvider provider =
         new SubschemaSubentryVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(subschemaSubentryType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    assertTrue(provider.hasValue(entry, rule,
                                 new AttributeValue(subschemaSubentryType,
                                                    "cn=schema")));
  }
  /**
   * Tests the {@code hasValue} method variant that takes a specific value when
   * the provided value is not a match.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasNonMatchingValue()
         throws Exception
  {
    SubschemaSubentryVirtualAttributeProvider provider =
         new SubschemaSubentryVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(subschemaSubentryType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    assertFalse(provider.hasValue(entry, rule,
                     new AttributeValue(subschemaSubentryType,
                                        "cn=not schema")));
  }
  /**
   * Tests the {@code hasAnyValue} method with an empty set of values.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasAnyValueEmptySet()
         throws Exception
  {
    SubschemaSubentryVirtualAttributeProvider provider =
         new SubschemaSubentryVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(subschemaSubentryType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    assertFalse(provider.hasAnyValue(entry, rule,
                                     Collections.<AttributeValue>emptySet()));
  }
  /**
   * Tests the {@code hasAnyValue} method with a set of values containing only
   * the correct value.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasAnyValueOnlyCorrect()
         throws Exception
  {
    SubschemaSubentryVirtualAttributeProvider provider =
         new SubschemaSubentryVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(subschemaSubentryType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
    values.add(new AttributeValue(subschemaSubentryType, "cn=schema"));
    assertTrue(provider.hasAnyValue(entry, rule, values));
  }
  /**
   * Tests the {@code hasAnyValue} method with a set of values containing only
   * an incorrect value.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasAnyValueOnlyIncorrect()
         throws Exception
  {
    SubschemaSubentryVirtualAttributeProvider provider =
         new SubschemaSubentryVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(subschemaSubentryType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
    values.add(new AttributeValue(subschemaSubentryType, "cn=not schema"));
    assertFalse(provider.hasAnyValue(entry, rule, values));
  }
  /**
   * Tests the {@code hasAnyValue} method with a set of values containing the
   * correct value as well as multiple incorrect values.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasAnyValueIncludesCorrect()
         throws Exception
  {
    SubschemaSubentryVirtualAttributeProvider provider =
         new SubschemaSubentryVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(subschemaSubentryType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(3);
    values.add(new AttributeValue(subschemaSubentryType, "cn=schema"));
    values.add(new AttributeValue(subschemaSubentryType, "cn=not schema"));
    values.add(new AttributeValue(subschemaSubentryType,
                                  "cn=not schema either"));
    assertTrue(provider.hasAnyValue(entry, rule, values));
  }
  /**
   * Tests the {@code hasAnyValue} method with a set of multiple values, none of
   * which are correct.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testHasAnyValueMissingCorrect()
         throws Exception
  {
    SubschemaSubentryVirtualAttributeProvider provider =
         new SubschemaSubentryVirtualAttributeProvider();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    entry.processVirtualAttributes();
    VirtualAttributeRule rule =
         new VirtualAttributeRule(subschemaSubentryType, provider,
                  Collections.<DN>emptySet(), Collections.<DN>emptySet(),
                  Collections.<SearchFilter>emptySet(),
                  VirtualAttributeCfgDefn.ConflictBehavior.
                       VIRTUAL_OVERRIDES_REAL);
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(3);
    values.add(new AttributeValue(subschemaSubentryType, "cn=not schema"));
    values.add(new AttributeValue(subschemaSubentryType,
                                  "cn=not schema either"));
    values.add(new AttributeValue(subschemaSubentryType,
                                  "cn=still not schema"));
    assertFalse(provider.hasAnyValue(entry, rule, values));
  }
}
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/interop/LazyDNTestCase.java
@@ -40,6 +40,7 @@
import org.opends.server.TestCaseUtils;
import org.opends.server.types.DN;
import org.opends.server.types.RDN;
import org.opends.server.types.SearchScope;
import static org.testng.Assert.*;
@@ -110,6 +111,10 @@
    sigs.add(new String[] { "isAncestorOf",
                            "boolean",
                            "org.opends.server.types.DN" });
    sigs.add(new String[] { "matchesBaseAndScope",
                            "boolean",
                            "org.opends.server.types.DN",
                            "org.opends.server.types.SearchScope" });
    sigs.add(new String[] { "equals",
                            "boolean",
                            "java.lang.Object" });
@@ -578,6 +583,40 @@
  /**
   * Tests the {@code matchesBaseAndScope} method with valid DN strings.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testMatchesBaseAndScope()
         throws Exception
  {
    assertTrue(new LazyDN("").matchesBaseAndScope(DN.nullDN(),
                                                  SearchScope.BASE_OBJECT));
    assertTrue(new LazyDN("dc=example,dc=com").matchesBaseAndScope(
                    DN.decode("dc=example,dc=com"), SearchScope.BASE_OBJECT));
    assertTrue(new LazyDN("ou=People,dc=example,dc=com").matchesBaseAndScope(
                    DN.decode("dc=example,dc=com"), SearchScope.WHOLE_SUBTREE));
  }
  /**
   * Tests the {@code matchesBaseAndScope} method with an invalid DN string.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(expectedExceptions = { RuntimeException.class })
  public void testMatchesBaseandScopeInvalid()
         throws Exception
  {
    new LazyDN("invalid").matchesBaseAndScope(DN.decode("dc=example,dc=com"),
                                              SearchScope.WHOLE_SUBTREE);
  }
  /**
   * Tests the {@code equals} method with valid DN strings.
   *
   * @throws  Exception  If an unexpected problem occurs.
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/ProtocolWindowTest.java
@@ -113,7 +113,7 @@
      assertTrue(checkChangelogQueueSize(CHANGELOG_QUEUE_SIZE));
      // Create an Entry (add operation) that will be later used in the test.
      Entry tmp = personEntry.duplicate();
      Entry tmp = personEntry.duplicate(false);
      AddOperation addOp = new AddOperation(connection,
          InternalClientConnection.nextOperationID(), InternalClientConnection
          .nextMessageID(), null, tmp.getDN(),
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/StressTest.java
@@ -121,7 +121,7 @@
       */
      // Create an Entry (add operation) that will be later used in the test.
      Entry tmp = personEntry.duplicate();
      Entry tmp = personEntry.duplicate(false);
      AddOperation addOp = new AddOperation(connection,
          InternalClientConnection.nextOperationID(), InternalClientConnection
          .nextMessageID(), null, tmp.getDN(),
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/SynchronizationTestCase.java
@@ -410,11 +410,11 @@
    while ((count> 0) && (found != exist))
    {
      Thread.sleep(200);
      found = DirectoryServer.entryExists(dn);
      count--;
    }
    Lock lock = null;
    for (int i=0; i < 3; i++)
    {
@@ -424,19 +424,19 @@
        break;
      }
    }
    if (lock == null)
    {
      throw new Exception("could not lock entry " + dn);
    }
    try
    {
      Entry entry = DirectoryServer.getEntry(dn);
      if (entry == null)
        return null;
      else
        return entry.duplicate();
        return entry.duplicate(true);
    }
    finally
    {
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java
@@ -844,7 +844,7 @@
       */
      // Create an Entry (add operation)
      Entry tmp = personEntry.duplicate();
      Entry tmp = personEntry.duplicate(false);
      AddOperation addOp = new AddOperation(connection,
          InternalClientConnection.nextOperationID(), InternalClientConnection
          .nextMessageID(), null, tmp.getDN(),
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/VirtualAttributeRuleTestCase.java
New file
@@ -0,0 +1,337 @@
/*
 * 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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.types;
import java.util.Collections;
import java.util.LinkedHashSet;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.std.meta.
            VirtualAttributeCfgDefn.ConflictBehavior;
import org.opends.server.extensions.EntryDNVirtualAttributeProvider;
import org.opends.server.protocols.internal.InternalClientConnection;
import static org.testng.Assert.*;
/**
 * This class provides a set of test cases for virtual attribute rules, which
 * link a virtual attribute provider implementation with an attribute type and a
 * set of criteria for identifying the entries with which that provider should
 * be used.
 */
public class VirtualAttributeRuleTestCase
       extends TypesTestCase
{
  // The attribute type for the entryDN attribute.
  private AttributeType entryDNType;
  /**
   * Ensures that the Directory Server is running.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @BeforeClass()
  public void startServer()
         throws Exception
  {
    TestCaseUtils.startServer();
    entryDNType = DirectoryConfig.getAttributeType("entrydn", false);
    assertNotNull(entryDNType);
  }
  /**
   * Retrieves a set of virtual attribute rules that may be used for testing
   * purposes.  The return data will also include a Boolean value indicating
   * whether the rule would apply to a minimal "o=test" entry.
   *
   * @return  A set of virtual attribute rules that may be used for testing
   *          purposes.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @DataProvider(name = "testRules")
  public Object[][] getVirtualAttributeRules()
         throws Exception
  {
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    LinkedHashSet<DN> dnSet1 = new LinkedHashSet<DN>(1);
    dnSet1.add(DN.decode("o=test"));
    LinkedHashSet<DN> dnSet2 = new LinkedHashSet<DN>(1);
    dnSet2.add(DN.decode("dc=example,dc=com"));
    LinkedHashSet<DN> dnSet3 = new LinkedHashSet<DN>(2);
    dnSet3.add(DN.decode("o=test"));
    dnSet3.add(DN.decode("dc=example,dc=com"));
    LinkedHashSet<DN> groupSet1 = new LinkedHashSet<DN>(1);
    groupSet1.add(DN.decode("cn=Test Group,o=test"));
    LinkedHashSet<DN> groupSet2 = new LinkedHashSet<DN>(1);
    groupSet2.add(DN.decode("cn=Example Group,o=test"));
    LinkedHashSet<DN> groupSet3= new LinkedHashSet<DN>(2);
    groupSet3.add(DN.decode("cn=Test Group,o=test"));
    groupSet3.add(DN.decode("cn=Example Group,o=test"));
    LinkedHashSet<SearchFilter> filterSet1 = new LinkedHashSet<SearchFilter>(1);
    filterSet1.add(SearchFilter.createFilterFromString("(objectClass=*)"));
    LinkedHashSet<SearchFilter> filterSet2 = new LinkedHashSet<SearchFilter>(1);
    filterSet2.add(SearchFilter.createFilterFromString("(o=test)"));
    LinkedHashSet<SearchFilter> filterSet3 = new LinkedHashSet<SearchFilter>(1);
    filterSet3.add(SearchFilter.createFilterFromString("(foo=bar)"));
    LinkedHashSet<SearchFilter> filterSet4 = new LinkedHashSet<SearchFilter>(2);
    filterSet4.add(SearchFilter.createFilterFromString("(o=test)"));
    filterSet4.add(SearchFilter.createFilterFromString("(foo=bar)"));
    return new Object[][]
    {
      new Object[]
      {
        new VirtualAttributeRule(entryDNType, provider,
                                 Collections.<DN>emptySet(),
                                 Collections.<DN>emptySet(),
                                 Collections.<SearchFilter>emptySet(),
                                 ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
        true
      },
      new Object[]
      {
        new VirtualAttributeRule(entryDNType, provider, dnSet1,
                                 Collections.<DN>emptySet(),
                                 Collections.<SearchFilter>emptySet(),
                                 ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
        true
      },
      new Object[]
      {
        new VirtualAttributeRule(entryDNType, provider, dnSet2,
                                 Collections.<DN>emptySet(),
                                 Collections.<SearchFilter>emptySet(),
                                 ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
        false
      },
      new Object[]
      {
        new VirtualAttributeRule(entryDNType, provider, dnSet3,
                                 Collections.<DN>emptySet(),
                                 Collections.<SearchFilter>emptySet(),
                                 ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
        true
      },
      new Object[]
      {
        new VirtualAttributeRule(entryDNType, provider,
                                 Collections.<DN>emptySet(), groupSet1,
                                 Collections.<SearchFilter>emptySet(),
                                 ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
        true
      },
      new Object[]
      {
        new VirtualAttributeRule(entryDNType, provider,
                                 Collections.<DN>emptySet(), groupSet2,
                                 Collections.<SearchFilter>emptySet(),
                                 ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
        false
      },
      new Object[]
      {
        new VirtualAttributeRule(entryDNType, provider,
                                 Collections.<DN>emptySet(), groupSet3,
                                 Collections.<SearchFilter>emptySet(),
                                 ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
        true
      },
      new Object[]
      {
        new VirtualAttributeRule(entryDNType, provider,
                                 Collections.<DN>emptySet(),
                                 Collections.<DN>emptySet(), filterSet1,
                                 ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
        true
      },
      new Object[]
      {
        new VirtualAttributeRule(entryDNType, provider,
                                 Collections.<DN>emptySet(),
                                 Collections.<DN>emptySet(), filterSet2,
                                 ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
        true
      },
      new Object[]
      {
        new VirtualAttributeRule(entryDNType, provider,
                                 Collections.<DN>emptySet(),
                                 Collections.<DN>emptySet(), filterSet3,
                                 ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
        false
      },
      new Object[]
      {
        new VirtualAttributeRule(entryDNType, provider,
                                 Collections.<DN>emptySet(),
                                 Collections.<DN>emptySet(), filterSet4,
                                 ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
        true
      },
    };
  }
  /**
   * Tests the various getter methods in the virtual attribute rule class.
   *
   * @param  rule            The rule for which to perform the test.
   * @param  appliesToEntry  Indicates whether the provided rule applies to a
   *                         minimal "o=test" entry.
   */
  @Test(dataProvider = "testRules")
  public void testGetters(VirtualAttributeRule rule, boolean appliesToEntry)
  {
    assertEquals(rule.getAttributeType(), entryDNType);
    assertEquals(rule.getProvider().getClass().getName(),
                 EntryDNVirtualAttributeProvider.class.getName());
    assertNotNull(rule.getBaseDNs());
    assertNotNull(rule.getGroupDNs());
    assertNotNull(rule.getFilters());
    assertNotNull(rule.getConflictBehavior());
  }
  /**
   * Tests the {@code appliesToEntry} method.
   *
   * @param  rule            The rule for which to perform the test.
   * @param  appliesToEntry  Indicates whether the provided rule applies to a
   *                         minimal "o=test" entry.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testRules")
  public void testAppliesToEntry(VirtualAttributeRule rule,
                                 boolean appliesToEntry)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    addGroups();
    assertEquals(rule.appliesToEntry(
                      DirectoryConfig.getEntry(DN.decode("o=test"))),
                 appliesToEntry);
    removeGroups();
  }
  /**
   * Tests the {@code toString} method.
   *
   * @param  rule            The rule for which to perform the test.
   * @param  appliesToEntry  Indicates whether the provided rule applies to a
   *                         minimal "o=test" entry.
   */
  @Test(dataProvider = "testRules")
  public void testToString(VirtualAttributeRule rule, boolean appliesToEntry)
  {
    String ruleString = rule.toString();
    assertNotNull(ruleString);
    assertTrue(ruleString.length() > 0);
  }
  /**
   * Adds a group to the server in which the "o=test" entry is a member.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  private void addGroups()
          throws Exception
  {
    TestCaseUtils.addEntries(
      "dn: cn=Test Group,o=test",
      "objectClass: top",
      "objectClass: groupOfUniqueNames",
      "cn: Test Group",
      "uniqueMember: o=test",
      "",
      "dn: cn=Example Group,o=test",
      "objectClass: top",
      "objectClass: groupOfUniqueNames",
      "cn: Example Group",
      "uniqueMember: dc=example,dc=com");
  }
  /**
   * Removes the test group from the server.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  private void removeGroups()
          throws Exception
  {
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    conn.processDelete(DN.decode("cn=Test Group,o=Test"));
    conn.processDelete(DN.decode("cn=Example Group,o=Test"));
  }
}
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/VirtualAttributeTestCase.java
New file
@@ -0,0 +1,192 @@
/*
 * 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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.types;
import java.util.Collections;
import java.util.LinkedHashSet;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.std.meta.
            VirtualAttributeCfgDefn.ConflictBehavior;
import org.opends.server.extensions.EntryDNVirtualAttributeProvider;
import org.opends.server.protocols.internal.InternalClientConnection;
import static org.testng.Assert.*;
/**
 * This class provides a set of test cases for virtual attributes.
 */
public class VirtualAttributeTestCase
       extends TypesTestCase
{
  // The attribute type for the entryDN attribute.
  private AttributeType entryDNType;
  // The virtual attribute instance that will be used for all the testing.
  private VirtualAttribute virtualAttribute;
  // The virutal attribute rule that will be used for the testing.
  private VirtualAttributeRule virtualAttributeRule;
  /**
   * Ensures that the Directory Server is running.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @BeforeClass()
  public void startServer()
         throws Exception
  {
    TestCaseUtils.startServer();
    entryDNType = DirectoryConfig.getAttributeType("entrydn", false);
    assertNotNull(entryDNType);
    EntryDNVirtualAttributeProvider provider =
         new EntryDNVirtualAttributeProvider();
    virtualAttributeRule = new VirtualAttributeRule(entryDNType, provider,
                                    Collections.<DN>emptySet(),
                                    Collections.<DN>emptySet(),
                                    Collections.<SearchFilter>emptySet(),
                                    ConflictBehavior.VIRTUAL_OVERRIDES_REAL);
    Entry entry = TestCaseUtils.makeEntry(
      "dn: o=test",
      "objectClass: top",
      "objectClass: organization",
      "o: test");
    virtualAttribute = new VirtualAttribute(entryDNType, entry,
                                            virtualAttributeRule);
  }
  /**
   * Tests the various getter methods for virtual attributes.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testGetters()
         throws Exception
  {
    assertNotNull(virtualAttribute.getEntry());
    assertEquals(virtualAttribute.getEntry().getDN(),
                 DN.decode("o=test"));
    assertEquals(virtualAttribute.getVirtualAttributeRule(),
                 virtualAttributeRule);
    assertTrue(virtualAttribute.isVirtual());
    assertTrue(virtualAttribute.duplicate(true).isVirtual());
    assertTrue(virtualAttribute.duplicate(false).isVirtual());
  }
  /**
   * Tests the various methods that interact with the virtual values.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testValues()
         throws Exception
  {
    LinkedHashSet<AttributeValue> values = virtualAttribute.getValues();
    assertEquals(values.size(), 1);
    assertTrue(values.contains(new AttributeValue(entryDNType, "o=test")));
    assertTrue(virtualAttribute.hasValue());
    assertTrue(virtualAttribute.hasValue(new AttributeValue(entryDNType,
                                                            "o=test")));
    assertFalse(virtualAttribute.hasValue(new AttributeValue(entryDNType,
                                                             "o=not test")));
    LinkedHashSet<AttributeValue> testValues =
         new LinkedHashSet<AttributeValue>();
    testValues.add(new AttributeValue(entryDNType, "o=test"));
    assertTrue(virtualAttribute.hasAllValues(testValues));
    assertTrue(virtualAttribute.hasAnyValue(testValues));
    testValues.add(new AttributeValue(entryDNType, "o=not test"));
    assertFalse(virtualAttribute.hasAllValues(testValues));
    assertTrue(virtualAttribute.hasAnyValue(testValues));
  }
  /**
   * Tests the various methods that apply to different kinds of matching.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testMatching()
         throws Exception
  {
    assertEquals(virtualAttribute.matchesSubstring(
                      ByteStringFactory.create("o="), null,
                      ByteStringFactory.create("test")),
                 ConditionResult.UNDEFINED);
    AttributeValue assertionValue = new AttributeValue(entryDNType, "o=test");
    assertEquals(virtualAttribute.greaterThanOrEqualTo(assertionValue),
                 ConditionResult.UNDEFINED);
    assertEquals(virtualAttribute.lessThanOrEqualTo(assertionValue),
                 ConditionResult.UNDEFINED);
    assertEquals(virtualAttribute.approximatelyEqualTo(assertionValue),
                 ConditionResult.UNDEFINED);
  }
  /**
   * Tests the {@code toString} method.
   */
  @Test()
  public void testToString()
  {
    String vattrString = virtualAttribute.toString();
    assertNotNull(vattrString);
    assertTrue(vattrString.length() > 0);
  }
}