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

coulbeck
12.20.2006 d6a18c0ea456c2f132c2301b37c80fe7e3e3a276
Fixes for the following issues, reviewed by neil_a_wilson.

Issue 785: attribute options not returned by search of attribute types only
Issue 786: SearchOperation#returnEntry does not always make a copy of the attributes

I have added some new methods so that a common code path can be followed whether the search returns attribute values or not.

Attribute:
public Attribute duplicate(boolean omitValues)

Entry:
public List<Attribute> duplicateUserAttribute(
AttributeType attributeType,
Set<String> options,
boolean omitValues)

public List<Attribute> duplicateOperationalAttribute(
AttributeType attributeType,
Set<String> options,
boolean omitValues)
4 files modified
521 ■■■■ changed files
opends/src/server/org/opends/server/core/SearchOperation.java 164 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/Attribute.java 27 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/Entry.java 141 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/core/SearchOperationTestCase.java 189 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/SearchOperation.java
@@ -932,14 +932,6 @@
            List<Attribute> ocList = new ArrayList<Attribute>(1);
            ocList.add(new Attribute(ocType));
            entryToReturn.putAttribute(ocType, ocList);
            // Next, iterate through all the user attributes and include them.
            for (AttributeType t : entry.getUserAttributes().keySet())
            {
              List<Attribute> attrList = new ArrayList<Attribute>(1);
              attrList.add(new Attribute(t));
              entryToReturn.putAttribute(t, attrList);
            }
          }
          else
          {
@@ -954,13 +946,14 @@
              // We cannot get this exception because the object classes have
              // already been validated in the entry they came from.
            }
          }
            // Next iterate through all the user attributes and include them.
            for (AttributeType t : entry.getUserAttributes().keySet())
            {
              entryToReturn.putAttribute(t, entry.duplicateUserAttribute(t));
            }
            List<Attribute> attrList =
                 entry.duplicateUserAttribute(t, null, typesOnly);
            entryToReturn.putAttribute(t, attrList);
          }
          continue;
@@ -971,18 +964,10 @@
          // attributes should be returned.
          for (AttributeType t : entry.getOperationalAttributes().keySet())
          {
            if (typesOnly)
            {
              List<Attribute> attrList = new ArrayList<Attribute>(1);
              attrList.add(new Attribute(t));
            List<Attribute> attrList =
                 entry.duplicateOperationalAttribute(t, null, typesOnly);
              entryToReturn.putAttribute(t, attrList);
            }
            else
            {
              entryToReturn.putAttribute(t,
                                 entry.duplicateOperationalAttribute(t));
            }
          }
          continue;
        }
@@ -1020,53 +1005,15 @@
          {
            if (t.hasNameOrOID(lowerName))
            {
              if ((options == null) || options.isEmpty())
              List<Attribute> attrList =
                   entry.duplicateUserAttribute(t, options, typesOnly);
              if (attrList != null)
              {
                if (typesOnly)
                {
                  List<Attribute> attrList = new ArrayList<Attribute>(1);
                  attrList.add(new Attribute(t));
                  entryToReturn.putAttribute(t, attrList);
                }
                else
                {
                  entryToReturn.putAttribute(t,
                                             entry.duplicateUserAttribute(t));
                }
                added = true;
                break;
              }
              else
              {
                List<Attribute> attrList = entry.duplicateUserAttribute(t);
                List<Attribute> includeAttrs =
                     new ArrayList<Attribute>(attrList.size());
                for (Attribute a : attrList)
                {
                  if (a.hasOptions(options))
                  {
                    includeAttrs.add(a);
                  }
                }
                if (! includeAttrs.isEmpty())
                {
                  if (typesOnly)
                  {
                    attrList = new ArrayList<Attribute>(1);
                    attrList.add(new Attribute(t));
                    entryToReturn.putAttribute(t, attrList);
                  }
                  else
                  {
                    entryToReturn.putAttribute(t, includeAttrs);
                  }
                  added = true;
                  break;
                }
              }
            }
          }
@@ -1079,88 +1026,49 @@
          {
            if (t.hasNameOrOID(lowerName))
            {
              if ((options == null) || options.isEmpty())
              List<Attribute> attrList =
                   entry.duplicateOperationalAttribute(t, options, typesOnly);
              if (attrList != null)
              {
                if (typesOnly)
                {
                  List<Attribute> attrList = new ArrayList<Attribute>(1);
                  attrList.add(new Attribute(t));
                  entryToReturn.putAttribute(t, attrList);
                break;
              }
            }
          }
                }
                else
                {
                  entryToReturn.putAttribute(t,
                                     entry.duplicateOperationalAttribute(t));
          if (attrType.isObjectClassType()) {
            if (typesOnly)
            {
              AttributeType ocType =
                   DirectoryServer.getObjectClassAttributeType();
              List<Attribute> ocList = new ArrayList<Attribute>(1);
              ocList.add(new Attribute(ocType));
              entryToReturn.putAttribute(ocType, ocList);
                }
                added = true;
                break;
            else
            {
              List<Attribute> attrList = new ArrayList<Attribute>(1);
              attrList.add(entry.getObjectClassAttribute());
              entryToReturn.putAttribute(attrType, attrList);
            }
              }
              else
              {
                List<Attribute> attrList =
                     entry.duplicateOperationalAttribute(t);
                List<Attribute> includeAttrs =
                     new ArrayList<Attribute>(attrList.size());
                for (Attribute a : attrList)
                 entry.duplicateOperationalAttribute(attrType, options,
                                                     typesOnly);
            if (attrList == null)
                {
                  if (a.hasOptions(options))
                  {
                    includeAttrs.add(a);
              attrList = entry.duplicateUserAttribute(attrType, options,
                                                      typesOnly);
                  }
                }
                if (! includeAttrs.isEmpty())
                {
                  if (typesOnly)
                  {
                    attrList = new ArrayList<Attribute>(1);
                    attrList.add(new Attribute(t));
                    entryToReturn.putAttribute(t, attrList);
                  }
                  else
                  {
                    entryToReturn.putAttribute(t, includeAttrs);
                  }
                  added = true;
                  break;
                }
              }
            }
          }
        }
        else
        {
          if (attrType.isObjectClassType() && !typesOnly)
          {
            Attribute ocAttr = entry.getObjectClassAttribute();
            try
            {
              entryToReturn.setObjectClasses(ocAttr.getValues());
            }
            catch (DirectoryException e)
            {
              // We cannot get this exception because the object classes have
              // already been validated in the entry they came from.
            }
          }
          else
          {
            List<Attribute> attrList = entry.getAttribute(attrType, options);
            if (attrList != null)
            {
              if (typesOnly)
              {
                attrList = new ArrayList<Attribute>(1);
                attrList.add(new Attribute(attrType));
                entryToReturn.putAttribute(attrType, attrList);
              }
              else
              {
                entryToReturn.putAttribute(attrType, attrList);
              }
            }
          }
        }
      }
opends/src/server/org/opends/server/types/Attribute.java
@@ -836,6 +836,25 @@
  {
    assert debugEnter(CLASS_NAME, "duplicate");
    return duplicate(false);
  }
  /**
   * Creates a duplicate of this attribute that can be modified
   * without impacting this attribute.
   *
   * @param omitValues <CODE>true</CODE> if the values should be
   *        omitted.
   *
   * @return  A duplicate of this attribute that can be modified
   *          without impacting this attribute.
   */
  public Attribute duplicate(boolean omitValues)
  {
    assert debugEnter(CLASS_NAME, "duplicate",
                      String.valueOf(omitValues));
    LinkedHashSet<String> optionsCopy =
         new LinkedHashSet<String>(options.size());
    for (String s : options)
@@ -843,6 +862,12 @@
      optionsCopy.add(s);
    }
    if (omitValues)
    {
      return new Attribute(attributeType, name, optionsCopy, null);
    }
    else
    {
    LinkedHashSet<AttributeValue> valuesCopy =
         new LinkedHashSet<AttributeValue>(values.size());
    for (AttributeValue v : values)
@@ -853,7 +878,7 @@
    return new Attribute(attributeType, name, optionsCopy,
                         valuesCopy);
  }
  }
  /**
opends/src/server/org/opends/server/types/Entry.java
@@ -1146,6 +1146,122 @@
  /**
   * Makes a copy of attributes matching the specified options.
   *
   * @param  attrList       The attributes to be copied.
   * @param  options        The set of attribute options to include in
   *                        matching elements.
   * @param  omitValues     <CODE>true</CODE> if the values are to be
   *                        omitted.
   *
   * @return  A copy of the attributes matching the specified options,
   *          or <CODE>null</CODE> if there is no such attribute with
   *          the specified set of options.
   */
  private static List<Attribute> duplicateAttribute(
       List<Attribute> attrList,
       Set<String> options,
       boolean omitValues)
  {
    assert debugEnter(CLASS_NAME, "duplicateAttribute",
                      String.valueOf(attrList),
                      String.valueOf(options),
                      String.valueOf(omitValues));
    if (attrList == null)
    {
      return null;
    }
    ArrayList<Attribute> duplicateList =
         new ArrayList<Attribute>(attrList.size());
    for (Attribute a : attrList)
    {
      if (a.hasOptions(options))
      {
        duplicateList.add(a.duplicate(omitValues));
      }
    }
    if (duplicateList.isEmpty())
    {
      return null;
    }
    else
    {
      return duplicateList;
    }
  }
  /**
   * Retrieves a copy of the requested user attribute element(s) for
   * the specified attribute type.  The list returned may include
   * multiple elements if the same attribute exists in the entry
   * multiple times with different sets of options.
   *
   * @param  attributeType  The attribute type to retrieve.
   * @param  options        The set of attribute options to include in
   *                        matching elements.
   * @param  omitValues     <CODE>true</CODE> if the values are to be
   *                        omitted.
   *
   * @return  A copy of the requested attribute element(s) for the
   *          specified attribute type, or <CODE>null</CODE> if there
   *          is no such user attribute with the specified set of
   *          options.
   */
  public List<Attribute> duplicateUserAttribute(
       AttributeType attributeType,
       Set<String> options,
       boolean omitValues)
  {
    assert debugEnter(CLASS_NAME, "duplicateUserAttribute",
                      String.valueOf(attributeType),
                      String.valueOf(options),
                      String.valueOf(omitValues));
    List<Attribute> currentList = userAttributes.get(attributeType);
    return duplicateAttribute(currentList, options, omitValues);
  }
  /**
   * Retrieves a copy of the requested operational attribute
   * element(s) for the specified attribute type.  The list returned
   * may include multiple elements if the same attribute exists in
   * the entry multiple times with different sets of options.
   *
   * @param  attributeType  The attribute type to retrieve.
   * @param  options        The set of attribute options to include in
   *                        matching elements.
   * @param  omitValues     <CODE>true</CODE> if the values are to be
   *                        omitted.
   *
   * @return  A copy of the requested attribute element(s) for the
   *          specified attribute type, or <CODE>null</CODE> if there
   *          is no such user attribute with the specified set of
   *          options.
   */
  public List<Attribute> duplicateOperationalAttribute(
       AttributeType attributeType,
       Set<String> options,
       boolean omitValues)
  {
    assert debugEnter(CLASS_NAME, "duplicateOperationalAttribute",
                      String.valueOf(attributeType),
                      String.valueOf(options),
                      String.valueOf(omitValues));
    List<Attribute> currentList =
         operationalAttributes.get(attributeType);
    return duplicateAttribute(currentList, options, omitValues);
  }
  /**
   * Indicates whether this entry contains the specified operational
   * attribute.
   *
@@ -2660,12 +2776,12 @@
    HashMap<AttributeType,List<Attribute>> userAttrsCopy =
         new HashMap<AttributeType,List<Attribute>>(
              userAttributes.size());
    deepCopy(userAttributes, userAttrsCopy);
    deepCopy(userAttributes, userAttrsCopy, false);
    HashMap<AttributeType,List<Attribute>> operationalAttrsCopy =
         new HashMap<AttributeType,List<Attribute>>(
                  operationalAttributes.size());
    deepCopy(operationalAttributes, operationalAttrsCopy);
    deepCopy(operationalAttributes, operationalAttrsCopy, false);
    return new Entry(dnCopy, objectClassesCopy, userAttrsCopy,
                     operationalAttrsCopy);
@@ -2715,19 +2831,9 @@
      ArrayList<Attribute> ocList = new ArrayList<Attribute>(1);
      ocList.add(new Attribute(ocType));
      userAttrsCopy.put(ocType, ocList);
    }
      for (AttributeType t : userAttributes.keySet())
      {
        ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
        attrList.add(new Attribute(t));
        userAttrsCopy.put(t, attrList);
      }
    }
    else
    {
      deepCopy(userAttributes, userAttrsCopy);
    }
    deepCopy(userAttributes, userAttrsCopy, typesOnly);
    HashMap<AttributeType,List<Attribute>> operationalAttrsCopy =
         new HashMap<AttributeType,List<Attribute>>(0);
@@ -2747,9 +2853,12 @@
   *                 information.
   * @param  target  The target map into which to place the copied
   *                 information.
   * @param  omitValues <CODE>true</CODE> if the values should be
   *                    omitted.
   */
  private void deepCopy(Map<AttributeType,List<Attribute>> source,
                        Map<AttributeType,List<Attribute>> target)
                        Map<AttributeType,List<Attribute>> target,
                        boolean omitValues)
  {
    for (AttributeType t : source.keySet())
    {
@@ -2759,7 +2868,7 @@
      for (Attribute a : sourceList)
      {
        targetList.add(a.duplicate());
        targetList.add(a.duplicate(omitValues));
      }
      target.put(t, targetList);
opends/tests/unit-tests-testng/src/server/org/opends/server/core/SearchOperationTestCase.java
@@ -36,6 +36,8 @@
import org.opends.server.protocols.ldap.*;
import org.opends.server.types.*;
import org.opends.server.TestCaseUtils;
import org.opends.server.controls.MatchedValuesFilter;
import org.opends.server.controls.MatchedValuesControl;
import org.opends.server.plugins.InvocationCounterPlugin;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -181,7 +183,8 @@
  }
  private SearchResultEntryProtocolOp searchExternalForSingleEntry(
       SearchRequestProtocolOp searchRequest)
       SearchRequestProtocolOp searchRequest,
       ArrayList<LDAPControl> controls)
       throws IOException, LDAPException, ASN1Exception, InterruptedException
  {
    // Establish a connection to the server.
@@ -197,7 +200,7 @@
      InvocationCounterPlugin.resetAllCounters();
      LDAPMessage message;
      message = new LDAPMessage(2, searchRequest);
      message = new LDAPMessage(2, searchRequest, controls);
      w.writeElement(message.encode());
      message = LDAPMessage.decode(r.readElement().decodeAsSequence());
@@ -297,7 +300,8 @@
  }
  @Test
  public void testSearchInternalAllUserAttributesTypesOnly() throws Exception
  public void testSearchInternalUnspecifiedAttributesOmitValues()
       throws Exception
  {
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
@@ -467,7 +471,7 @@
              LDAPFilter.decode("(objectclass=inetorgperson)"),
              null);
    SearchResultEntryProtocolOp searchResultEntry =
         searchExternalForSingleEntry(searchRequest);
         searchExternalForSingleEntry(searchRequest, null);
    assertEquals(searchResultEntry.getAttributes().size(), ldapAttrCount);
  }
@@ -487,12 +491,36 @@
              LDAPFilter.decode("(objectclass=inetorgperson)"),
              attributes);
    SearchResultEntryProtocolOp searchResultEntry =
         searchExternalForSingleEntry(searchRequest);
         searchExternalForSingleEntry(searchRequest, null);
    assertEquals(searchResultEntry.getAttributes().size(), ldapAttrCount);
  }
  @Test
  public void testSearchExternalAllUserAttributesTypesOnly() throws Exception
  public void testSearchExternalUnspecifiedAttributesOmitValues()
       throws Exception
  {
    SearchRequestProtocolOp searchRequest =
         new SearchRequestProtocolOp(
              new ASN1OctetString("o=test"),
              SearchScope.WHOLE_SUBTREE,
              DereferencePolicy.NEVER_DEREF_ALIASES,
              Integer.MAX_VALUE,
              Integer.MAX_VALUE,
              true,
              LDAPFilter.decode("(objectclass=inetorgperson)"),
              null);
    SearchResultEntryProtocolOp searchResultEntry =
         searchExternalForSingleEntry(searchRequest, null);
    // The attributes will include the objectclass type.
    assertEquals(searchResultEntry.getAttributes().size(), ldapAttrCount);
    for (LDAPAttribute a : searchResultEntry.getAttributes())
    {
      assertEquals(a.getValues().size(), 0);
    }
  }
  @Test
  public void testSearchExternalAllUserAttributesOmitValues() throws Exception
  {
    LinkedHashSet<String> attributes = new LinkedHashSet<String>();
    attributes.add("*");
@@ -507,10 +535,13 @@
              LDAPFilter.decode("(objectclass=inetorgperson)"),
              attributes);
    SearchResultEntryProtocolOp searchResultEntry =
         searchExternalForSingleEntry(searchRequest);
         searchExternalForSingleEntry(searchRequest, null);
    // The attributes will include the objectclass type.
    assertEquals(searchResultEntry.getAttributes().size(),
                 entry.getUserAttributes().size() + 1);
    assertEquals(searchResultEntry.getAttributes().size(), ldapAttrCount);
    for (LDAPAttribute a : searchResultEntry.getAttributes())
    {
      assertEquals(a.getValues().size(), 0);
    }
  }
  @Test
@@ -530,7 +561,7 @@
              attributes);
    SearchResultEntryProtocolOp searchResultEntry =
         searchExternalForSingleEntry(searchRequest);
         searchExternalForSingleEntry(searchRequest, null);
    assertEquals(searchResultEntry.getAttributes().size(), 1);
    assertEquals(searchResultEntry.getAttributes().
@@ -538,6 +569,31 @@
  }
  @Test
  public void testSearchExternalObjectClassAttributeOmitValues()
       throws Exception
  {
    LinkedHashSet<String> attributes = new LinkedHashSet<String>();
    attributes.add("objectclass");
    SearchRequestProtocolOp searchRequest =
         new SearchRequestProtocolOp(
              new ASN1OctetString("o=test"),
              SearchScope.WHOLE_SUBTREE,
              DereferencePolicy.NEVER_DEREF_ALIASES,
              Integer.MAX_VALUE,
              Integer.MAX_VALUE,
              true,
              LDAPFilter.decode("(objectclass=inetorgperson)"),
              attributes);
    SearchResultEntryProtocolOp searchResultEntry =
         searchExternalForSingleEntry(searchRequest, null);
    assertEquals(searchResultEntry.getAttributes().size(), 1);
    assertEquals(searchResultEntry.getAttributes().
         getFirst().getValues().size(), 0);
  }
  @Test
  public void testSearchExternalSelectedAttributes() throws Exception
  {
    LinkedHashSet<String> attributes = new LinkedHashSet<String>();
@@ -555,8 +611,119 @@
              attributes);
    SearchResultEntryProtocolOp searchResultEntry =
         searchExternalForSingleEntry(searchRequest);
         searchExternalForSingleEntry(searchRequest, null);
    assertEquals(searchResultEntry.getAttributes().size(), 2);
  }
  @Test
  public void testSearchExternalAttributeWithSubtypes() throws Exception
  {
    LinkedHashSet<String> attributes = new LinkedHashSet<String>();
    attributes.add("title");
    SearchRequestProtocolOp searchRequest =
         new SearchRequestProtocolOp(
              new ASN1OctetString("o=test"),
              SearchScope.WHOLE_SUBTREE,
              DereferencePolicy.NEVER_DEREF_ALIASES,
              Integer.MAX_VALUE,
              Integer.MAX_VALUE,
              false,
              LDAPFilter.decode("(objectclass=inetorgperson)"),
              attributes);
    SearchResultEntryProtocolOp searchResultEntry =
         searchExternalForSingleEntry(searchRequest, null);
    assertEquals(searchResultEntry.getAttributes().size(), 4);
  }
  @Test
  public void testSearchExternalAttributeWithSubtypesOmitValues()
       throws Exception
  {
    LinkedHashSet<String> attributes = new LinkedHashSet<String>();
    attributes.add("title");
    SearchRequestProtocolOp searchRequest =
         new SearchRequestProtocolOp(
              new ASN1OctetString("o=test"),
              SearchScope.WHOLE_SUBTREE,
              DereferencePolicy.NEVER_DEREF_ALIASES,
              Integer.MAX_VALUE,
              Integer.MAX_VALUE,
              true,
              LDAPFilter.decode("(objectclass=inetorgperson)"),
              attributes);
    SearchResultEntryProtocolOp searchResultEntry =
         searchExternalForSingleEntry(searchRequest, null);
    assertEquals(searchResultEntry.getAttributes().size(), 4);
    for (LDAPAttribute a : searchResultEntry.getAttributes())
    {
      assertEquals(a.getValues().size(), 0);
    }
  }
  @Test
  public void testSearchExternalAttributeWithOptions() throws Exception
  {
    LinkedHashSet<String> attributes = new LinkedHashSet<String>();
    attributes.add("title;lang-ja;phonetic");
    SearchRequestProtocolOp searchRequest =
         new SearchRequestProtocolOp(
              new ASN1OctetString("o=test"),
              SearchScope.WHOLE_SUBTREE,
              DereferencePolicy.NEVER_DEREF_ALIASES,
              Integer.MAX_VALUE,
              Integer.MAX_VALUE,
              false,
              LDAPFilter.decode("(objectclass=inetorgperson)"),
              attributes);
    SearchResultEntryProtocolOp searchResultEntry =
         searchExternalForSingleEntry(searchRequest, null);
    assertEquals(searchResultEntry.getAttributes().size(), 1);
  }
  @Test
  public void testSearchExternalMatchedValues() throws Exception
  {
    // Add a matched values control.
    LDAPFilter ldapFilter = LDAPFilter.decode("(title=*director*)");
    MatchedValuesFilter matchedValuesFilter =
         MatchedValuesFilter.createFromLDAPFilter(ldapFilter);
    ArrayList<MatchedValuesFilter> filters =
         new ArrayList<MatchedValuesFilter>();
    filters.add(matchedValuesFilter);
    MatchedValuesControl mvc = new MatchedValuesControl(true, filters);
    ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
    controls.add(new LDAPControl(mvc));
    SearchRequestProtocolOp searchRequest =
         new SearchRequestProtocolOp(
              new ASN1OctetString("o=test"),
              SearchScope.WHOLE_SUBTREE,
              DereferencePolicy.NEVER_DEREF_ALIASES,
              Integer.MAX_VALUE,
              Integer.MAX_VALUE,
              false,
              LDAPFilter.decode("(objectclass=inetorgperson)"),
              null);
    SearchResultEntryProtocolOp searchResultEntry =
         searchExternalForSingleEntry(searchRequest, controls);
    // Per RFC 3876, an attribute that has no values selected is returned
    // with an empty set of values.  We should therefore expect all the
    // attributes but only one value.
    int valueCount = 0;
    for (LDAPAttribute a : searchResultEntry.getAttributes())
    {
      valueCount += a.getValues().size();
    }
    assertEquals(valueCount, 1);
  }
}