Author: sebb
Date: Sun Apr 22 15:41:25 2007
New Revision: 531295
URL: http://svn.apache.org/viewvc?view=rev&rev=531295
Log:
Bug 40369 - LDAP: Stable search results in sampler
Modified:
jakarta/jmeter/branches/rel-2-2/src/protocol/ldap/org/apache/jmeter/protocol/ldap/sampler/LDAPExtSampler.java
jakarta/jmeter/branches/rel-2-2/xdocs/changes.xml
Modified:
jakarta/jmeter/branches/rel-2-2/src/protocol/ldap/org/apache/jmeter/protocol/ldap/sampler/LDAPExtSampler.java
URL:
http://svn.apache.org/viewvc/jakarta/jmeter/branches/rel-2-2/src/protocol/ldap/org/apache/jmeter/protocol/ldap/sampler/LDAPExtSampler.java?view=diff&rev=531295&r1=531294&r2=531295
==============================================================================
---
jakarta/jmeter/branches/rel-2-2/src/protocol/ldap/org/apache/jmeter/protocol/ldap/sampler/LDAPExtSampler.java
(original)
+++
jakarta/jmeter/branches/rel-2-2/src/protocol/ldap/org/apache/jmeter/protocol/ldap/sampler/LDAPExtSampler.java
Sun Apr 22 15:41:25 2007
@@ -18,6 +18,10 @@
package org.apache.jmeter.protocol.ldap.sampler;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
@@ -33,6 +37,7 @@
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchResult;
+import org.apache.commons.lang.StringEscapeUtils;
import org.apache.jmeter.config.Argument;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.engine.event.LoopIterationEvent;
@@ -45,6 +50,7 @@
import org.apache.jmeter.testelement.property.PropertyIterator;
import org.apache.jmeter.testelement.property.StringProperty;
import org.apache.jmeter.testelement.property.TestElementProperty;
+import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.jorphan.util.XMLBuffer;
import org.apache.log.Logger;
@@ -137,6 +143,9 @@
private static Hashtable ldapContexts = new Hashtable();
+ private static final int MAX_SORTED_RESULTS =
+ JMeterUtils.getPropDefault("ldapsampler.max_sorted_results",
1000); // $NON-NLS-1$
+
/***************************************************************************
* !ToDo (Constructor description)
**************************************************************************/
@@ -813,25 +822,7 @@
if (isParseFlag()) {
try {
xmlBuffer.openTag("searchresults"); // $NON-NLS-1$
- while (srch.hasMore()) {
- try {
-
xmlBuffer.openTag("searchresult"); // $NON-NLS-1$
- SearchResult sr
= (SearchResult) srch.next();
-
xmlBuffer.tag("dn",sr.getName());// $NON-NLS-1$
-
xmlBuffer.tag("returnedattr",String.valueOf(sr.getAttributes().size())); //
$NON-NLS-1$
-
NamingEnumeration attrlist = sr.getAttributes().getIDs();
- while
(attrlist.hasMore()) {
- String
iets = (String) attrlist.next();
-
xmlBuffer.openTag("attribute"); // $NON-NLS-1$
-
xmlBuffer.tag("attributename", iets); // $NON-NLS-1$
-
xmlBuffer.tag("attributevalue", // $NON-NLS-1$
-
sr.getAttributes().get(iets).toString().substring(iets.length() + 2));
-
xmlBuffer.closeTag("attribute"); // $NON-NLS-1$
- }
- } finally {
-
xmlBuffer.closeTag("searchresult"); // $NON-NLS-1$
- }
- }
+ writeSearchResults(xmlBuffer,
srch);
} finally {
xmlBuffer.closeTag("searchresults"); // $NON-NLS-1$
}
@@ -839,6 +830,9 @@
}
} catch (NamingException ex) {
+ //log.warn("DEBUG",ex);
+// e.g. javax.naming.SizeLimitExceededException: [LDAP: error code 4 -
Sizelimit Exceeded]; remaining name ''
+// 123456789012345678901
// TODO: tidy this up
String returnData = ex.toString();
final int indexOfLDAPErrCode =
returnData.indexOf("LDAP: error code");
@@ -862,6 +856,192 @@
return res;
}
+ /*
+ * Write out search results in a stable order (including order of all
subelements which might
+ * be reordered like attributes and their values) so that simple textual
comparison can be done,
+ * unless the number of results exceeds [EMAIL PROTECTED]
#MAX_SORTED_RESULTS} in which case just stream
+ * the results out without sorting.
+ */
+ private void writeSearchResults(final XMLBuffer xmlb, final
NamingEnumeration srch)
+ throws NamingException
+ {
+
+ final ArrayList sortedResults = new ArrayList(MAX_SORTED_RESULTS);
+ final String searchBase = getPropertyAsString(SEARCHBASE);
+ final String rootDn = getRootdn();
+
+ // read all sortedResults into memory so we can guarantee ordering
+ try {
+ while (srch.hasMore() && (sortedResults.size() <
MAX_SORTED_RESULTS)) {
+ final SearchResult sr = (SearchResult)
srch.next();
+
+ // must be done prior to sorting
+ normaliseSearchDN(sr, searchBase, rootDn);
+ sortedResults.add(sr);
+ }
+ } finally { // show what we did manage to retrieve
+
+ sortResults(sortedResults);
+
+ for (Iterator it = sortedResults.iterator(); it.hasNext();)
+ {
+ final SearchResult sr = (SearchResult) it.next();
+ writeSearchResult(sr, xmlb);
+ }
+ }
+
+ while (srch.hasMore()) { // If there's anything left ...
+ final SearchResult sr = (SearchResult) srch.next();
+
+ normaliseSearchDN(sr, searchBase, rootDn);
+ writeSearchResult(sr, xmlb);
+ }
+ }
+
+ private void writeSearchResult(final SearchResult sr, final XMLBuffer xmlb)
+ throws NamingException
+ {
+ final Attributes attrs = sr.getAttributes();
+ final int size = attrs.size();
+ final ArrayList sortedAttrs = new ArrayList(size);
+
+ xmlb.openTag("searchresult"); // $NON-NLS-1$
+ xmlb.tag("dn", sr.getName()); // $NON-NLS-1$
+ xmlb.tag("returnedattr",Integer.toString(size)); // $NON-NLS-1$
+ xmlb.openTag("attributes"); // $NON-NLS-1$
+
+ try {
+ for (NamingEnumeration en = attrs.getAll();
en.hasMore(); )
+ {
+ final Attribute attr = (Attribute) en.next();
+
+ sortedAttrs.add(attr);
+ }
+ sortAttributes(sortedAttrs);
+ for (Iterator ait = sortedAttrs.iterator();
ait.hasNext();)
+ {
+ final Attribute attr = (Attribute) ait.next();
+
+ StringBuffer sb = new StringBuffer();
+ if (attr.size() == 1)
+ sb.append(getWriteValue(attr.get()));
+ else
+ {
+ final ArrayList sortedVals = new
ArrayList(attr.size());
+ boolean first = true;
+
+ for (NamingEnumeration ven = attr.getAll();
ven.hasMore(); )
+ {
+ final Object value =
getWriteValue(ven.next());
+ sortedVals.add(value.toString());
+ }
+
+ Collections.sort(sortedVals);
+
+ for (Iterator vit = sortedVals.iterator();
vit.hasNext();)
+ {
+ final String value = (String) vit.next();
+ if (first) {
+ first = false;
+ } else {
+ sb.append(", "); // $NON-NLS-1$
+ }
+ sb.append(value);
+ }
+ }
+ xmlb.tag(attr.getID(),sb);
+ }
+ } finally {
+ xmlb.closeTag("attributes"); // $NON-NLS-1$
+ xmlb.closeTag("searchresult"); // $NON-NLS-1$
+ }
+ }
+
+ private void sortAttributes(final ArrayList sortedAttrs) {
+ Collections.sort(sortedAttrs, new Comparator()
+ {
+ public int compare(Object o1, Object o2)
+ {
+ String nm1 = ((Attribute) o1).getID();
+ String nm2 = ((Attribute) o2).getID();
+
+ return nm1.compareTo(nm2);
+ }
+ });
+ }
+
+ private void sortResults(final ArrayList sortedResults) {
+ Collections.sort(sortedResults, new Comparator()
+ {
+ private int compareToReverse(final String s1, final String
s2)
+ {
+ int len1 = s1.length();
+ int len2 = s2.length();
+ int s1i = len1 - 1;
+ int s2i = len2 - 1;
+
+ for ( ; (s1i >= 0) && (s2i >= 0); s1i--, s2i--)
+ {
+ char c1 = s1.charAt(s1i);
+ char c2 = s2.charAt(s2i);
+
+ if (c1 != c2)
+ return c1 - c2;
+ }
+ return len1 - len2;
+ }
+
+ public int compare(Object o1, Object o2)
+ {
+ String nm1 = ((SearchResult) o1).getName();
+ String nm2 = ((SearchResult) o2).getName();
+
+ if (nm1 == null)
+ nm1 = "";
+ if (nm2 == null)
+ nm2 = "";
+ return compareToReverse(nm1, nm2);
+ }
+ });
+ }
+
+ private String normaliseSearchDN(final SearchResult sr, final String
searchBase, final String rootDn)
+ {
+ String srName = sr.getName();
+
+ if (!srName.endsWith(searchBase))
+ {
+ if (srName.length() > 0)
+ srName = srName + ',';
+ srName = srName + searchBase;
+ }
+ if ((rootDn.length() > 0) && !srName.endsWith(rootDn))
+ {
+ if (srName.length() > 0)
+ srName = srName + ',';
+ srName = srName + rootDn;
+ }
+ sr.setName(srName);
+ return srName;
+ }
+
+ private String getWriteValue(final Object value)
+ {
+ if (value instanceof String)
+ // assume it's senstive data
+ return StringEscapeUtils.escapeXml((String)value);
+ else if (value instanceof byte[])
+ try
+ {
+ return StringEscapeUtils.escapeXml(new String((byte[])value,
"UTF-8"));
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ log.error("this can't happen: UTF-8 character encoding not
supported", e);
+ }
+ return StringEscapeUtils.escapeXml(value.toString());
+ }
+
public void testStarted() {
testStarted(""); // $NON-NLS-1$
}
@@ -887,8 +1067,7 @@
} catch (NamingException ignored) {
// ignored
}
- it.remove();// Make sure the entry is not left around
for the next
- // run
+ it.remove();// Make sure the entry is not left around
for the next run
}
}
Modified: jakarta/jmeter/branches/rel-2-2/xdocs/changes.xml
URL:
http://svn.apache.org/viewvc/jakarta/jmeter/branches/rel-2-2/xdocs/changes.xml?view=diff&rev=531295&r1=531294&r2=531295
==============================================================================
--- jakarta/jmeter/branches/rel-2-2/xdocs/changes.xml (original)
+++ jakarta/jmeter/branches/rel-2-2/xdocs/changes.xml Sun Apr 22 15:41:25 2007
@@ -133,6 +133,7 @@
<li>Bug 42098 - Use specified encoding for parameter values in HTTP GET</li>
<li>Bug 42088 - Add XPath Assertion for booleans</li>
<li>Bug 42158 - Improve support for multipart/form-data requests in HTTP Proxy
server</li>
+<li>Bug 40369 - LDAP: Stable search results in sampler</li>
</ul>
<h4>Non-functional improvements:</h4>
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]