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)); } // Next iterate through all the user attributes and include them. for (AttributeType t : entry.getUserAttributes().keySet()) { List<Attribute> attrList = entry.duplicateUserAttribute(t, null, typesOnly); entryToReturn.putAttribute(t, attrList); } continue; @@ -971,17 +964,9 @@ // attributes should be returned. for (AttributeType t : entry.getOperationalAttributes().keySet()) { if (typesOnly) { List<Attribute> attrList = new ArrayList<Attribute>(1); attrList.add(new Attribute(t)); entryToReturn.putAttribute(t, attrList); } else { entryToReturn.putAttribute(t, entry.duplicateOperationalAttribute(t)); } List<Attribute> attrList = entry.duplicateOperationalAttribute(t, null, typesOnly); entryToReturn.putAttribute(t, attrList); } 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)); } entryToReturn.putAttribute(t, attrList); 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,87 +1026,48 @@ { 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); } else { entryToReturn.putAttribute(t, entry.duplicateOperationalAttribute(t)); } entryToReturn.putAttribute(t, attrList); added = true; break; } else { List<Attribute> attrList = entry.duplicateOperationalAttribute(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; } } } } } else { if (attrType.isObjectClassType() && !typesOnly) { Attribute ocAttr = entry.getObjectClassAttribute(); try if (attrType.isObjectClassType()) { if (typesOnly) { entryToReturn.setObjectClasses(ocAttr.getValues()); AttributeType ocType = DirectoryServer.getObjectClassAttributeType(); List<Attribute> ocList = new ArrayList<Attribute>(1); ocList.add(new Attribute(ocType)); entryToReturn.putAttribute(ocType, ocList); } catch (DirectoryException e) else { // We cannot get this exception because the object classes have // already been validated in the entry they came from. List<Attribute> attrList = new ArrayList<Attribute>(1); attrList.add(entry.getObjectClassAttribute()); entryToReturn.putAttribute(attrType, attrList); } } else { List<Attribute> attrList = entry.getAttribute(attrType, options); List<Attribute> attrList = entry.duplicateOperationalAttribute(attrType, options, typesOnly); if (attrList == null) { attrList = entry.duplicateUserAttribute(attrType, options, typesOnly); } if (attrList != null) { if (typesOnly) { attrList = new ArrayList<Attribute>(1); attrList.add(new Attribute(attrType)); entryToReturn.putAttribute(attrType, attrList); } else { entryToReturn.putAttribute(attrType, attrList); } 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,19 +862,25 @@ optionsCopy.add(s); } LinkedHashSet<AttributeValue> valuesCopy = new LinkedHashSet<AttributeValue>(values.size()); for (AttributeValue v : values) if (omitValues) { valuesCopy.add(v); return new Attribute(attributeType, name, optionsCopy, null); } else { LinkedHashSet<AttributeValue> valuesCopy = new LinkedHashSet<AttributeValue>(values.size()); for (AttributeValue v : values) { valuesCopy.add(v); } return new Attribute(attributeType, name, optionsCopy, valuesCopy); return new Attribute(attributeType, name, optionsCopy, valuesCopy); } } /** * Indicates whether the provided object is an attribute that is * equal to this attribute. It will be considered equal if the 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); } }