1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 package org.apache.jmeter.protocol.ldap.sampler;
20
21 import java.io.UnsupportedEncodingException;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.Comparator;
25 import java.util.Hashtable;
26 import java.util.Iterator;
27 import java.util.Map;
28
29 import javax.naming.NamingEnumeration;
30 import javax.naming.NamingException;
31 import javax.naming.directory.Attribute;
32 import javax.naming.directory.Attributes;
33 import javax.naming.directory.BasicAttribute;
34 import javax.naming.directory.BasicAttributes;
35 import javax.naming.directory.DirContext;
36 import javax.naming.directory.InitialDirContext;
37 import javax.naming.directory.ModificationItem;
38 import javax.naming.directory.SearchResult;
39
40 import org.apache.commons.lang.StringEscapeUtils;
41 import org.apache.jmeter.config.Argument;
42 import org.apache.jmeter.config.Arguments;
43 import org.apache.jmeter.engine.event.LoopIterationEvent;
44 import org.apache.jmeter.protocol.ldap.config.gui.LDAPArgument;
45 import org.apache.jmeter.protocol.ldap.config.gui.LDAPArguments;
46 import org.apache.jmeter.samplers.AbstractSampler;
47 import org.apache.jmeter.samplers.Entry;
48 import org.apache.jmeter.samplers.SampleResult;
49 import org.apache.jmeter.testelement.TestListener;
50 import org.apache.jmeter.testelement.property.PropertyIterator;
51 import org.apache.jmeter.testelement.property.StringProperty;
52 import org.apache.jmeter.testelement.property.TestElementProperty;
53 import org.apache.jmeter.util.JMeterUtils;
54 import org.apache.jorphan.logging.LoggingManager;
55 import org.apache.jorphan.util.XMLBuffer;
56 import org.apache.log.Logger;
57
58 /*******************************************************************************
59 * Ldap Sampler class is main class for the LDAP test. This will control all the
60 * test available in the LDAP Test.
61 ******************************************************************************/
62
63 public class LDAPExtSampler extends AbstractSampler implements TestListener {
64
65 private static final Logger log = LoggingManager.getLoggerForClass();
66
67 /*
68 * The following strings are used in the test plan, and the values must not be changed
69 * if test plans are to be upwardly compatible.
70 */
71 public static final String SERVERNAME = "servername"; // $NON-NLS-1$
72
73 public static final String PORT = "port"; // $NON-NLS-1$
74
75 public static final String SECURE = "secure"; // $NON-NLS-1$
76
77 public static final String ROOTDN = "rootdn"; // $NON-NLS-1$
78
79 public static final String TEST = "test"; // $NON-NLS-1$
80
81 // These are values for the TEST attribute above
82 public static final String ADD = "add"; // $NON-NLS-1$
83
84 public static final String MODIFY = "modify"; // $NON-NLS-1$
85
86 public static final String BIND = "bind"; // $NON-NLS-1$
87
88 public static final String UNBIND = "unbind"; // $NON-NLS-1$
89
90 public static final String DELETE = "delete"; // $NON-NLS-1$
91
92 public static final String SEARCH = "search"; // $NON-NLS-1$
93 // end of TEST values
94
95 public static final String SEARCHBASE = "search"; // $NON-NLS-1$
96
97 public static final String SEARCHFILTER = "searchfilter"; // $NON-NLS-1$
98
99 public static final String ARGUMENTS = "arguments"; // $NON-NLS-1$
100
101 public static final String LDAPARGUMENTS = "ldaparguments"; // $NON-NLS-1$
102
103 public static final String BASE_ENTRY_DN = "base_entry_dn"; // $NON-NLS-1$
104
105 public static final String SCOPE = "scope"; // $NON-NLS-1$
106
107 public static final String COUNTLIM = "countlimit"; // $NON-NLS-1$
108
109 public static final String TIMELIM = "timelimit"; // $NON-NLS-1$
110
111 public static final String ATTRIBS = "attributes"; // $NON-NLS-1$
112
113 public static final String RETOBJ = "return_object"; // $NON-NLS-1$
114
115 public static final String DEREF = "deref_aliases"; // $NON-NLS-1$
116
117 public static final String USERDN = "user_dn"; // $NON-NLS-1$
118
119 public static final String USERPW = "user_pw"; // $NON-NLS-1$
120
121 public static final String SBIND = "sbind"; // $NON-NLS-1$
122
123 public static final String COMPARE = "compare"; // $NON-NLS-1$
124
125 public static final String CONNTO = "connection_timeout"; // $NON-NLS-1$
126
127 public static final String COMPAREDN = "comparedn"; // $NON-NLS-1$
128
129 public static final String COMPAREFILT = "comparefilt"; // $NON-NLS-1$
130
131 public static final String PARSEFLAG = "parseflag"; // $NON-NLS-1$
132
133 public static final String RENAME = "rename"; // $NON-NLS-1$
134
135 public static final String MODDDN = "modddn"; // $NON-NLS-1$
136
137 public static final String NEWDN = "newdn"; // $NON-NLS-1$
138
139 private static final String SEMI_COLON = ";"; // $NON-NLS-1$
140
141
142 private static final Hashtable ldapConnections = new Hashtable();
143
144 private static final Hashtable ldapContexts = new Hashtable();
145
146 private static final int MAX_SORTED_RESULTS =
147 JMeterUtils.getPropDefault("ldapsampler.max_sorted_results", 1000); // $NON-NLS-1$
148
149 /***************************************************************************
150 * !ToDo (Constructor description)
151 **************************************************************************/
152 public LDAPExtSampler() {
153 }
154
155 public void setConnTimeOut(String connto) {
156 setProperty(new StringProperty(CONNTO, connto));
157 }
158
159 public String getConnTimeOut() {
160 return getPropertyAsString(CONNTO);
161 }
162
163 public void setSecure(String sec) {
164 setProperty(new StringProperty(SECURE, sec));
165 }
166
167 public boolean isSecure() {
168 return getPropertyAsBoolean(SECURE);
169 }
170
171
172 public boolean isParseFlag() {
173 return getPropertyAsBoolean(PARSEFLAG);
174 }
175
176 public void setParseFlag(String parseFlag) {
177 setProperty(new StringProperty(PARSEFLAG, parseFlag));
178 }
179
180 /***************************************************************************
181 * Gets the username attribute of the LDAP object
182 *
183 * @return The username
184 **************************************************************************/
185
186 public String getUserDN() {
187 return getPropertyAsString(USERDN);
188 }
189
190 /***************************************************************************
191 * Sets the username attribute of the LDAP object
192 *
193 **************************************************************************/
194
195 public void setUserDN(String newUserDN) {
196 setProperty(new StringProperty(USERDN, newUserDN));
197 }
198
199 /***************************************************************************
200 * Gets the password attribute of the LDAP object
201 *
202 * @return The password
203 **************************************************************************/
204
205 public String getUserPw() {
206 return getPropertyAsString(USERPW);
207 }
208
209 /***************************************************************************
210 * Sets the password attribute of the LDAP object
211 *
212 **************************************************************************/
213
214 public void setUserPw(String newUserPw) {
215 setProperty(new StringProperty(USERPW, newUserPw));
216 }
217
218 /***************************************************************************
219 * Sets the Servername attribute of the ServerConfig object
220 *
221 * @param servername
222 * The new servername value
223 **************************************************************************/
224 public void setServername(String servername) {
225 setProperty(new StringProperty(SERVERNAME, servername));
226 }
227
228 /***************************************************************************
229 * Sets the Port attribute of the ServerConfig object
230 *
231 * @param port
232 * The new Port value
233 **************************************************************************/
234 public void setPort(String port) {
235 setProperty(new StringProperty(PORT, port));
236 }
237
238 /***************************************************************************
239 * Gets the servername attribute of the LDAPSampler object
240 *
241 * @return The Servername value
242 **************************************************************************/
243
244 public String getServername() {
245 return getPropertyAsString(SERVERNAME);
246 }
247
248 /***************************************************************************
249 * Gets the Port attribute of the LDAPSampler object
250 *
251 * @return The Port value
252 **************************************************************************/
253
254 public String getPort() {
255 return getPropertyAsString(PORT);
256 }
257
258 /***************************************************************************
259 * Sets the Rootdn attribute of the LDAPSampler object
260 *
261 * @param newRootdn
262 * The new rootdn value
263 **************************************************************************/
264 public void setRootdn(String newRootdn) {
265 this.setProperty(ROOTDN, newRootdn);
266 }
267
268 /***************************************************************************
269 * Gets the Rootdn attribute of the LDAPSampler object
270 *
271 * @return The Rootdn value
272 **************************************************************************/
273 public String getRootdn() {
274 return getPropertyAsString(ROOTDN);
275 }
276
277 /***************************************************************************
278 * Gets the search scope attribute of the LDAPSampler object
279 *
280 * @return The scope value
281 **************************************************************************/
282 public String getScope() {
283 return getPropertyAsString(SCOPE);
284 }
285
286 public int getScopeAsInt() {
287 return getPropertyAsInt(SCOPE);
288 }
289
290 /***************************************************************************
291 * Sets the search scope attribute of the LDAPSampler object
292 *
293 * @param newScope
294 * The new scope value
295 **************************************************************************/
296 public void setScope(String newScope) {
297 this.setProperty(SCOPE, newScope);
298 }
299
300 /***************************************************************************
301 * Gets the size limit attribute of the LDAPSampler object
302 *
303 * @return The size limit
304 **************************************************************************/
305 public String getCountlim() {
306 return getPropertyAsString(COUNTLIM);
307 }
308
309 public long getCountlimAsLong() {
310 return getPropertyAsLong(COUNTLIM);
311 }
312
313 /***************************************************************************
314 * Sets the size limit attribute of the LDAPSampler object
315 *
316 * @param newClim
317 * The new size limit value
318 **************************************************************************/
319 public void setCountlim(String newClim) {
320 this.setProperty(COUNTLIM, newClim);
321 }
322
323 /***************************************************************************
324 * Gets the time limit attribute of the LDAPSampler object
325 *
326 * @return The time limit
327 **************************************************************************/
328 public String getTimelim() {
329 return getPropertyAsString(TIMELIM);
330 }
331
332 public int getTimelimAsInt() {
333 return getPropertyAsInt(TIMELIM);
334 }
335
336 /***************************************************************************
337 * Sets the time limit attribute of the LDAPSampler object
338 *
339 * @param newTlim
340 * The new time limit value
341 **************************************************************************/
342 public void setTimelim(String newTlim) {
343 this.setProperty(TIMELIM, newTlim);
344 }
345
346 /***************************************************************************
347 * Gets the return objects attribute of the LDAPSampler object
348 *
349 * @return if the object(s) are to be returned
350 **************************************************************************/
351 public boolean isRetobj() {
352 return getPropertyAsBoolean(RETOBJ);
353 }
354
355 /***************************************************************************
356 * Sets the return objects attribute of the LDAPSampler object
357 *
358 **************************************************************************/
359 public void setRetobj(String newRobj) {
360 this.setProperty(RETOBJ, newRobj);
361 }
362
363 /***************************************************************************
364 * Gets the deref attribute of the LDAPSampler object
365 *
366 * @return if dereferencing is required
367 **************************************************************************/
368 public boolean isDeref() {
369 return getPropertyAsBoolean(DEREF);
370 }
371
372 /***************************************************************************
373 * Sets the deref attribute of the LDAPSampler object
374 *
375 * @param newDref
376 * The new deref value
377 **************************************************************************/
378 public void setDeref(String newDref) {
379 this.setProperty(DEREF, newDref);
380 }
381
382 /***************************************************************************
383 * Sets the Test attribute of the LdapConfig object
384 *
385 * @param newTest
386 * The new test value(Add,Modify,Delete and search)
387 **************************************************************************/
388 public void setTest(String newTest) {
389 this.setProperty(TEST, newTest);
390 }
391
392 /***************************************************************************
393 * Gets the test attribute of the LDAPSampler object
394 *
395 * @return The test value (Add,Modify,Delete and search)
396 **************************************************************************/
397 public String getTest() {
398 return getPropertyAsString(TEST);
399 }
400
401 /***************************************************************************
402 * Sets the attributes of the LdapConfig object
403 *
404 * @param newAttrs
405 * The new attributes value
406 **************************************************************************/
407 public void setAttrs(String newAttrs) {
408 this.setProperty(ATTRIBS, newAttrs);
409 }
410
411 /***************************************************************************
412 * Gets the attributes of the LDAPSampler object
413 *
414 * @return The attributes
415 **************************************************************************/
416 public String getAttrs() {
417 return getPropertyAsString(ATTRIBS);
418 }
419
420 /***************************************************************************
421 * Sets the Base Entry DN attribute of the LDAPSampler object
422 *
423 * @param newbaseentry
424 * The new Base entry DN value
425 **************************************************************************/
426 public void setBaseEntryDN(String newbaseentry) {
427 setProperty(new StringProperty(BASE_ENTRY_DN, newbaseentry));
428 }
429
430 /***************************************************************************
431 * Gets the BaseEntryDN attribute of the LDAPSampler object
432 *
433 * @return The Base entry DN value
434 **************************************************************************/
435 public String getBaseEntryDN() {
436 return getPropertyAsString(BASE_ENTRY_DN);
437 }
438
439 /***************************************************************************
440 * Sets the Arguments attribute of the LdapConfig object This will collect
441 * values from the table for user defined test case
442 *
443 * @param value
444 * The arguments
445 **************************************************************************/
446 public void setArguments(Arguments value) {
447 setProperty(new TestElementProperty(ARGUMENTS, value));
448 }
449
450 /***************************************************************************
451 * Gets the Arguments attribute of the LdapConfig object
452 *
453 * @return The arguments user defined test case
454 **************************************************************************/
455 public Arguments getArguments() {
456 return (Arguments) getProperty(ARGUMENTS).getObjectValue();
457 }
458
459 /***************************************************************************
460 * Sets the Arguments attribute of the LdapConfig object This will collect
461 * values from the table for user defined test case
462 *
463 * @param value
464 * The arguments
465 **************************************************************************/
466 public void setLDAPArguments(LDAPArguments value) {
467 setProperty(new TestElementProperty(LDAPARGUMENTS, value));
468 }
469
470 /***************************************************************************
471 * Gets the LDAPArguments attribute of the LdapConfig object
472 *
473 * @return The LDAParguments user defined modify test case
474 **************************************************************************/
475 public LDAPArguments getLDAPArguments() {
476 return (LDAPArguments) getProperty(LDAPARGUMENTS).getObjectValue();
477 }
478
479 /***************************************************************************
480 * Collect all the values from the table (Arguments), using this create the
481 * Attributes, this will create the Attributes for the User
482 * defined TestCase for Add Test
483 *
484 * @return The Attributes
485 **************************************************************************/
486 private Attributes getUserAttributes() {
487 Attributes attrs = new BasicAttributes(true);
488 Attribute attr;
489 PropertyIterator iter = getArguments().iterator();
490
491 while (iter.hasNext()) {
492 Argument item = (Argument) iter.next().getObjectValue();
493 attr = attrs.get(item.getName());
494 if (attr == null) {
495 attr = getBasicAttribute(item.getName(), item.getValue());
496 } else {
497 attr.add(item.getValue());
498 }
499 attrs.put(attr);
500 }
501 return attrs;
502 }
503
504 /***************************************************************************
505 * Collect all the value from the table (Arguments), using this create the
506 * basicAttributes This will create the Basic Attributes for the User
507 * defined TestCase for Modify test
508 *
509 * @return The BasicAttributes
510 **************************************************************************/
511 private ModificationItem[] getUserModAttributes() {
512 ModificationItem[] mods = new ModificationItem[getLDAPArguments().getArguments().size()];
513 BasicAttribute attr;
514 PropertyIterator iter = getLDAPArguments().iterator();
515 int count = 0;
516 while (iter.hasNext()) {
517 LDAPArgument item = (LDAPArgument) iter.next().getObjectValue();
518 if ((item.getValue()).length()==0) {
519 attr = new BasicAttribute(item.getName());
520 } else {
521 attr = getBasicAttribute(item.getName(), item.getValue());
522 }
523
524 final String opcode = item.getOpcode();
525 if ("add".equals(opcode)) { // $NON-NLS-1$
526 mods[count++] = new ModificationItem(DirContext.ADD_ATTRIBUTE, attr);
527 } else if ("delete".equals(opcode) // $NON-NLS-1$
528 || "remove".equals(opcode)) { // $NON-NLS-1$
529 mods[count++] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, attr);
530 } else if("replace".equals(opcode)) { // $NON-NLS-1$
531 mods[count++] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr);
532 } else {
533 log.warn("Invalid opCode: "+opcode);
534 }
535 }
536 return mods;
537 }
538
539 /***************************************************************************
540 * Collect all the value from the table (Arguments), using this create the
541 * Attributes This will create the Basic Attributes for the User defined
542 * TestCase for search test
543 *
544 * @return The BasicAttributes
545 **************************************************************************/
546 private String[] getRequestAttributes(String reqAttr) {
547 int index;
548 String[] mods;
549 int count = 0;
550 if (reqAttr.length() == 0) {
551 return null;
552 }
553 if (!reqAttr.endsWith(SEMI_COLON)) {
554 reqAttr = reqAttr + SEMI_COLON;
555 }
556 String attr = reqAttr;
557
558 while (attr.length() > 0) {
559 index = attr.indexOf(SEMI_COLON);
560 count += 1;
561 attr = attr.substring(index + 1);
562 }
563 if (count > 0) {
564 mods = new String[count];
565 attr = reqAttr;
566 count = 0;
567 while (attr.length() > 0) {
568 index = attr.indexOf(SEMI_COLON);
569 mods[count] = attr.substring(0, index);
570 count += 1;
571 attr = attr.substring(index + 1);
572 }
573 } else {
574 mods = null;
575 }
576 return mods;
577 }
578
579 /***************************************************************************
580 * This will create the Basic Attribute for the give name value pair
581 *
582 * @return The BasicAttribute
583 **************************************************************************/
584 private BasicAttribute getBasicAttribute(String name, String value) {
585 BasicAttribute attr = new BasicAttribute(name, value);
586 return attr;
587 }
588
589 /**
590 * Returns a formatted string label describing this sampler Example output:
591 *
592 * @return a formatted string label describing this sampler
593 */
594 public String getLabel() {
595 return ("ldap://" + this.getServername() //$NON-NLS-1$
596 + ":" + getPort() //$NON-NLS-1$
597 + "/" + this.getRootdn()); //$NON-NLS-1$
598 }
599
600 /***************************************************************************
601 * This will do the add test for the User defined TestCase
602 *
603 **************************************************************************/
604 private void addTest(LdapExtClient ldap, DirContext dirContext, SampleResult res) throws NamingException {
605 try {
606 res.sampleStart();
607 ldap.createTest(dirContext, getUserAttributes(), getBaseEntryDN());
608 } finally {
609 res.sampleEnd();
610 }
611 }
612
613 /***************************************************************************
614 * This will do the delete test for the User defined TestCase
615 *
616 **************************************************************************/
617 private void deleteTest(LdapExtClient ldap, DirContext dirContext, SampleResult res) throws NamingException {
618 try {
619 res.sampleStart();
620 ldap.deleteTest(dirContext, getPropertyAsString(DELETE));
621 } finally {
622 res.sampleEnd();
623 }
624 }
625
626 /***************************************************************************
627 * This will do the modify test for the User defined TestCase
628 *
629 **************************************************************************/
630 private void modifyTest(LdapExtClient ldap, DirContext dirContext, SampleResult res) throws NamingException {
631 try {
632 res.sampleStart();
633 ldap.modifyTest(dirContext, getUserModAttributes(), getBaseEntryDN());
634 } finally {
635 res.sampleEnd();
636 }
637 }
638
639 /***************************************************************************
640 * This will do the bind for the User defined Thread, this bind is used for
641 * the whole context
642 *
643 **************************************************************************/
644 private void bindOp(LdapExtClient ldap, DirContext dirContext, SampleResult res) throws NamingException {
645 DirContext ctx = (DirContext) ldapContexts.remove(getThreadName());
646 if (ctx != null) {
647 log.warn("Closing previous context for thread: " + getThreadName());
648 ctx.close();
649 }
650 try {
651 res.sampleStart();
652 ctx = ldap.connect(getServername(), getPort(), getRootdn(), getUserDN(), getUserPw(),getConnTimeOut(),isSecure());
653 } finally {
654 res.sampleEnd();
655 }
656 ldapContexts.put(getThreadName(), ctx);
657 }
658
659 /***************************************************************************
660 * This will do the bind and unbind for the User defined TestCase
661 *
662 **************************************************************************/
663 private void singleBindOp(SampleResult res) throws NamingException {
664 LdapExtClient ldap_temp;
665 ldap_temp = new LdapExtClient();
666 try {
667 res.sampleStart();
668 DirContext ctx = ldap_temp.connect(getServername(), getPort(), getRootdn(), getUserDN(), getUserPw(),getConnTimeOut(),isSecure());
669 ldap_temp.disconnect(ctx);
670 } finally {
671 res.sampleEnd();
672 }
673 }
674
675 /***************************************************************************
676 * This will do a moddn Opp for the User new DN defined
677 *
678 **************************************************************************/
679 private void renameTest(LdapExtClient ldap, DirContext dirContext, SampleResult res) throws NamingException {
680 try {
681 res.sampleStart();
682 ldap.moddnOp(dirContext, getPropertyAsString(MODDDN), getPropertyAsString(NEWDN));
683 } finally {
684 res.sampleEnd();
685 }
686 }
687
688 /***************************************************************************
689 * This will do the unbind for the User defined TestCase as well as inbuilt
690 * test case
691 *
692 **************************************************************************/
693 private void unbindOp(LdapExtClient ldap, DirContext dirContext, SampleResult res) {
694 try {
695 res.sampleStart();
696 ldap.disconnect(dirContext);
697 } finally {
698 res.sampleEnd();
699 }
700 ldapConnections.remove(getThreadName());
701 ldapContexts.remove(getThreadName());
702 log.info("context and LdapExtClients removed");
703 }
704
705 /***************************************************************************
706 * !ToDo (Method description)
707 *
708 * @param e
709 * !ToDo (Parameter description)
710 * @return !ToDo (Return description)
711 **************************************************************************/
712 public SampleResult sample(Entry e) {
713 XMLBuffer xmlBuffer = new XMLBuffer();
714 xmlBuffer.openTag("ldapanswer"); // $NON-NLS-1$
715 SampleResult res = new SampleResult();
716 res.setResponseData("successfull".getBytes());
717 res.setResponseMessage("Success"); // $NON-NLS-1$
718 res.setResponseCode("0"); // $NON-NLS-1$
719 res.setContentType("text/xml");// $NON-NLS-1$
720 boolean isSuccessful = true;
721 res.setSampleLabel(getName());
722 LdapExtClient temp_client = (LdapExtClient) ldapConnections.get(getThreadName());
723 DirContext dirContext = (DirContext) ldapContexts.get(getThreadName());
724 if (temp_client == null) {
725 temp_client = new LdapExtClient();
726 try {
727 dirContext = new InitialDirContext();
728 } catch (NamingException err) {
729 log.error("Ldap client context creation - ", err);
730 }
731 ldapConnections.put(getThreadName(), temp_client);
732 }
733
734 try {
735 xmlBuffer.openTag("operation"); // $NON-NLS-1$
736 final String testType = getTest();
737 xmlBuffer.tag("opertype", testType); // $NON-NLS-1$
738 log.debug("performing test: " + testType);
739 if (testType.equals(UNBIND)) {
740 res.setSamplerData("Unbind");
741 xmlBuffer.tag("baseobj",getRootdn()); // $NON-NLS-1$
742 xmlBuffer.tag("binddn",getUserDN()); // $NON-NLS-1$
743 unbindOp(temp_client, dirContext, res);
744 } else if (testType.equals(BIND)) {
745 res.setSamplerData("Bind as "+getUserDN());
746 xmlBuffer.tag("baseobj",getRootdn()); // $NON-NLS-1$
747 xmlBuffer.tag("binddn",getUserDN()); // $NON-NLS-1$
748 xmlBuffer.tag("connectionTO",getConnTimeOut()); // $NON-NLS-1$
749 bindOp(temp_client, dirContext, res);
750 } else if (testType.equals(SBIND)) {
751 res.setSamplerData("SingleBind as "+getUserDN());
752 xmlBuffer.tag("baseobj",getRootdn()); // $NON-NLS-1$
753 xmlBuffer.tag("binddn",getUserDN()); // $NON-NLS-1$
754 xmlBuffer.tag("connectionTO",getConnTimeOut()); // $NON-NLS-1$
755 singleBindOp(res);
756 } else if (testType.equals(COMPARE)) {
757 res.setSamplerData("Compare "+getPropertyAsString(COMPAREFILT) + " "
758 + getPropertyAsString(COMPAREDN));
759 xmlBuffer.tag("comparedn",getPropertyAsString(COMPAREDN)); // $NON-NLS-1$
760 xmlBuffer.tag("comparefilter",getPropertyAsString(COMPAREFILT)); // $NON-NLS-1$
761 NamingEnumeration cmp=null;
762 try {
763 res.sampleStart();
764 cmp = temp_client.compare(dirContext, getPropertyAsString(COMPAREFILT),
765 getPropertyAsString(COMPAREDN));
766 if (!cmp.hasMore()) {
767 res.setResponseCode("5"); // $NON-NLS-1$
768 res.setResponseMessage("compareFalse");
769 isSuccessful = false;
770 }
771 } finally {
772 res.sampleEnd();
773 if (cmp != null) {
774 cmp.close();
775 }
776 }
777 } else if (testType.equals(ADD)) {
778 res.setSamplerData("Add object " + getBaseEntryDN());
779 xmlBuffer.tag("attributes",getArguments().toString()); // $NON-NLS-1$
780 xmlBuffer.tag("dn",getBaseEntryDN()); // $NON-NLS-1$
781 addTest(temp_client, dirContext, res);
782 } else if (testType.equals(DELETE)) {
783 res.setSamplerData("Delete object " + getBaseEntryDN());
784 xmlBuffer.tag("dn",getBaseEntryDN()); // $NON-NLS-1$
785 deleteTest(temp_client, dirContext, res);
786 } else if (testType.equals(MODIFY)) {
787 res.setSamplerData("Modify object " + getBaseEntryDN());
788 xmlBuffer.tag("dn",getBaseEntryDN()); // $NON-NLS-1$
789 xmlBuffer.tag("attributes",getLDAPArguments().toString()); // $NON-NLS-1$
790 modifyTest(temp_client, dirContext, res);
791 } else if (testType.equals(RENAME)) {
792 res.setSamplerData("ModDN object " + getPropertyAsString(MODDDN) + " to " + getPropertyAsString(NEWDN));
793 xmlBuffer.tag("dn",getPropertyAsString(MODDDN)); // $NON-NLS-1$
794 xmlBuffer.tag("newdn",getPropertyAsString(NEWDN)); // $NON-NLS-1$
795 renameTest(temp_client, dirContext, res);
796 } else if (testType.equals(SEARCH)) {
797 final String scopeStr = getScope();
798 final int scope = getScopeAsInt();
799 final String searchFilter = getPropertyAsString(SEARCHFILTER);
800 final String searchBase = getPropertyAsString(SEARCHBASE);
801 final String timeLimit = getTimelim();
802 final String countLimit = getCountlim();
803
804 res.setSamplerData("Search with filter " + searchFilter);
805 xmlBuffer.tag("searchfilter",searchFilter); // $NON-NLS-1$
806 xmlBuffer.tag("baseobj",getRootdn()); // $NON-NLS-1$
807 xmlBuffer.tag("searchbase",searchBase);// $NON-NLS-1$
808 xmlBuffer.tag("scope" , scopeStr); // $NON-NLS-1$
809 xmlBuffer.tag("countlimit",countLimit); // $NON-NLS-1$
810 xmlBuffer.tag("timelimit",timeLimit); // $NON-NLS-1$
811
812 NamingEnumeration srch=null;
813 try {
814 res.sampleStart();
815 srch = temp_client.searchTest(
816 dirContext, searchBase, searchFilter,
817 scope, getCountlimAsLong(),
818 getTimelimAsInt(),
819 getRequestAttributes(getAttrs()),
820 isRetobj(),
821 isDeref());
822 if (isParseFlag()) {
823 try {
824 xmlBuffer.openTag("searchresults"); // $NON-NLS-1$
825 writeSearchResults(xmlBuffer, srch);
826 } finally {
827 xmlBuffer.closeTag("searchresults"); // $NON-NLS-1$
828 }
829 } else {
830 xmlBuffer.tag("searchresults", // $NON-NLS-1$
831 "hasElements="+srch.hasMoreElements()); // $NON-NLS-1$
832 }
833 } finally {
834 if (srch != null){
835 srch.close();
836 }
837 res.sampleEnd();
838 }
839
840 }
841
842 } catch (NamingException ex) {
843 //log.warn("DEBUG",ex);
844 // e.g. javax.naming.SizeLimitExceededException: [LDAP: error code 4 - Sizelimit Exceeded]; remaining name ''
845 // 123456789012345678901
846 // TODO: tidy this up
847 String returnData = ex.toString();
848 final int indexOfLDAPErrCode = returnData.indexOf("LDAP: error code");
849 if (indexOfLDAPErrCode >= 0) {
850 res.setResponseMessage(returnData.substring(indexOfLDAPErrCode + 21, returnData
851 .indexOf("]"))); // $NON-NLS-1$
852 res.setResponseCode(returnData.substring(indexOfLDAPErrCode + 17, indexOfLDAPErrCode + 19));
853 } else {
854 res.setResponseMessage(returnData);
855 res.setResponseCode("800"); // $NON-NLS-1$
856 }
857 isSuccessful = false;
858 } finally {
859 xmlBuffer.closeTag("operation"); // $NON-NLS-1$
860 xmlBuffer.tag("responsecode",res.getResponseCode()); // $NON-NLS-1$
861 xmlBuffer.tag("responsemessage",res.getResponseMessage()); // $NON-NLS-1$
862 res.setResponseData(xmlBuffer.toString().getBytes());
863 res.setDataType(SampleResult.TEXT);
864 res.setSuccessful(isSuccessful);
865 }
866 return res;
867 }
868
869 /*
870 * Write out search results in a stable order (including order of all subelements which might
871 * be reordered like attributes and their values) so that simple textual comparison can be done,
872 * unless the number of results exceeds {@link #MAX_SORTED_RESULTS} in which case just stream
873 * the results out without sorting.
874 */
875 private void writeSearchResults(final XMLBuffer xmlb, final NamingEnumeration srch)
876 throws NamingException
877 {
878
879 final ArrayList sortedResults = new ArrayList(MAX_SORTED_RESULTS);
880 final String searchBase = getPropertyAsString(SEARCHBASE);
881 final String rootDn = getRootdn();
882
883 // read all sortedResults into memory so we can guarantee ordering
884 try {
885 while (srch.hasMore() && (sortedResults.size() < MAX_SORTED_RESULTS)) {
886 final SearchResult sr = (SearchResult) srch.next();
887
888 // must be done prior to sorting
889 normaliseSearchDN(sr, searchBase, rootDn);
890 sortedResults.add(sr);
891 }
892 } finally { // show what we did manage to retrieve
893
894 sortResults(sortedResults);
895
896 for (Iterator it = sortedResults.iterator(); it.hasNext();)
897 {
898 final SearchResult sr = (SearchResult) it.next();
899 writeSearchResult(sr, xmlb);
900 }
901 }
902
903 while (srch.hasMore()) { // If there's anything left ...
904 final SearchResult sr = (SearchResult) srch.next();
905
906 normaliseSearchDN(sr, searchBase, rootDn);
907 writeSearchResult(sr, xmlb);
908 }
909 }
910
911 private void writeSearchResult(final SearchResult sr, final XMLBuffer xmlb)
912 throws NamingException
913 {
914 final Attributes attrs = sr.getAttributes();
915 final int size = attrs.size();
916 final ArrayList sortedAttrs = new ArrayList(size);
917
918 xmlb.openTag("searchresult"); // $NON-NLS-1$
919 xmlb.tag("dn", sr.getName()); // $NON-NLS-1$
920 xmlb.tag("returnedattr",Integer.toString(size)); // $NON-NLS-1$
921 xmlb.openTag("attributes"); // $NON-NLS-1$
922
923 try {
924 for (NamingEnumeration en = attrs.getAll(); en.hasMore(); )
925 {
926 final Attribute attr = (Attribute) en.next();
927
928 sortedAttrs.add(attr);
929 }
930 sortAttributes(sortedAttrs);
931 for (Iterator ait = sortedAttrs.iterator(); ait.hasNext();)
932 {
933 final Attribute attr = (Attribute) ait.next();
934
935 StringBuffer sb = new StringBuffer();
936 if (attr.size() == 1) {
937 sb.append(getWriteValue(attr.get()));
938 } else {
939 final ArrayList sortedVals = new ArrayList(attr.size());
940 boolean first = true;
941
942 for (NamingEnumeration ven = attr.getAll(); ven.hasMore(); )
943 {
944 final Object value = getWriteValue(ven.next());
945 sortedVals.add(value.toString());
946 }
947
948 Collections.sort(sortedVals);
949
950 for (Iterator vit = sortedVals.iterator(); vit.hasNext();)
951 {
952 final String value = (String) vit.next();
953 if (first) {
954 first = false;
955 } else {
956 sb.append(", "); // $NON-NLS-1$
957 }
958 sb.append(value);
959 }
960 }
961 xmlb.tag(attr.getID(),sb);
962 }
963 } finally {
964 xmlb.closeTag("attributes"); // $NON-NLS-1$
965 xmlb.closeTag("searchresult"); // $NON-NLS-1$
966 }
967 }
968
969 private void sortAttributes(final ArrayList sortedAttrs) {
970 Collections.sort(sortedAttrs, new Comparator()
971 {
972 public int compare(Object o1, Object o2)
973 {
974 String nm1 = ((Attribute) o1).getID();
975 String nm2 = ((Attribute) o2).getID();
976
977 return nm1.compareTo(nm2);
978 }
979 });
980 }
981
982 private void sortResults(final ArrayList sortedResults) {
983 Collections.sort(sortedResults, new Comparator()
984 {
985 private int compareToReverse(final String s1, final String s2)
986 {
987 int len1 = s1.length();
988 int len2 = s2.length();
989 int s1i = len1 - 1;
990 int s2i = len2 - 1;
991
992 for ( ; (s1i >= 0) && (s2i >= 0); s1i--, s2i--)
993 {
994 char c1 = s1.charAt(s1i);
995 char c2 = s2.charAt(s2i);
996
997 if (c1 != c2) {
998 return c1 - c2;
999 }
1000 }
1001 return len1 - len2;
1002 }
1003
1004 public int compare(Object o1, Object o2)
1005 {
1006 String nm1 = ((SearchResult) o1).getName();
1007 String nm2 = ((SearchResult) o2).getName();
1008
1009 if (nm1 == null) {
1010 nm1 = "";
1011 }
1012 if (nm2 == null) {
1013 nm2 = "";
1014 }
1015 return compareToReverse(nm1, nm2);
1016 }
1017 });
1018 }
1019
1020 private String normaliseSearchDN(final SearchResult sr, final String searchBase, final String rootDn)
1021 {
1022 String srName = sr.getName();
1023
1024 if (!srName.endsWith(searchBase))
1025 {
1026 if (srName.length() > 0) {
1027 srName = srName + ',';
1028 }
1029 srName = srName + searchBase;
1030 }
1031 if ((rootDn.length() > 0) && !srName.endsWith(rootDn))
1032 {
1033 if (srName.length() > 0) {
1034 srName = srName + ',';
1035 }
1036 srName = srName + rootDn;
1037 }
1038 sr.setName(srName);
1039 return srName;
1040 }
1041
1042 private String getWriteValue(final Object value)
1043 {
1044 if (value instanceof String) {
1045 // assume it's senstive data
1046 return StringEscapeUtils.escapeXml((String)value);
1047 }
1048 if (value instanceof byte[]) {
1049 try
1050 {
1051 return StringEscapeUtils.escapeXml(new String((byte[])value, "UTF-8")); //$NON-NLS-1$
1052 }
1053 catch (UnsupportedEncodingException e)
1054 {
1055 log.error("this can't happen: UTF-8 character encoding not supported", e);
1056 }
1057 }
1058 return StringEscapeUtils.escapeXml(value.toString());
1059 }
1060
1061 public void testStarted() {
1062 testStarted(""); // $NON-NLS-1$
1063 }
1064
1065 public void testEnded() {
1066 testEnded(""); // $NON-NLS-1$
1067 }
1068
1069 public void testStarted(String host) {
1070 // ignored
1071 }
1072
1073 // Ensure any remaining contexts are closed
1074 public void testEnded(String host) {
1075 Iterator it = ldapContexts.entrySet().iterator();
1076 while (it.hasNext()) {
1077 Map.Entry entry = (Map.Entry) it.next();
1078 String key = (String) entry.getKey();
1079 DirContext dc = (DirContext) entry.getValue();
1080 try {
1081 log.warn("Tidying old Context for thread: " + key);
1082 dc.close();
1083 } catch (NamingException ignored) {
1084 // ignored
1085 }
1086 it.remove();// Make sure the entry is not left around for the next run
1087 }
1088
1089 }
1090
1091 public void testIterationStart(LoopIterationEvent event) {
1092 // ignored
1093 }
1094 }