KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > slamd > example > ModRateJobClass


1 /*
2  * Sun Public License
3  *
4  * The contents of this file are subject to the Sun Public License Version
5  * 1.0 (the "License"). You may not use this file except in compliance with
6  * the License. A copy of the License is available at http://www.sun.com/
7  *
8  * The Original Code is the SLAMD Distributed Load Generation Engine.
9  * The Initial Developer of the Original Code is Neil A. Wilson.
10  * Portions created by Neil A. Wilson are Copyright (C) 2004.
11  * Some preexisting portions Copyright (C) 2002-2004 Sun Microsystems, Inc.
12  * All Rights Reserved.
13  *
14  * Contributor(s): Neil A. Wilson
15  */

16 package com.sun.slamd.example;
17
18
19
20 import java.io.*;
21 import java.security.*;
22 import java.util.*;
23 import netscape.ldap.*;
24 import netscape.ldap.controls.*;
25 import netscape.ldap.factory.*;
26 import com.sun.slamd.job.*;
27 import com.sun.slamd.parameter.*;
28 import com.sun.slamd.stat.*;
29
30
31
32 /**
33  * This class implements a SLAMD job class for performing repeated modifications
34  * against an LDAP directory server. All of the configuration for this job
35  * thread can be provided through parameters.
36  *
37  *
38  * @author Neil A. Wilson
39  */

40 public class ModRateJobClass
41        extends JobClass
42        implements LDAPRebind
43 {
44   /**
45    * The system property used to specify the location of the JSSE key store.
46    */

47   public static final String JavaDoc SSL_KEY_STORE_PROPERTY =
48        "javax.net.ssl.keyStore";
49
50
51
52   /**
53    * The system property used to specify the password for the JSSE key store.
54    */

55   public static final String JavaDoc SSL_KEY_PASSWORD_PROPERTY =
56        "javax.net.ssl.keyStorePassword";
57
58
59
60   /**
61    * The system property used to specify the location of the JSSE trust store.
62    */

63   public static final String JavaDoc SSL_TRUST_STORE_PROPERTY =
64        "javax.net.ssl.trustStore";
65
66
67
68   /**
69    * The system property used to specify the password for the JSSE trust store.
70    */

71   public static final String JavaDoc SSL_TRUST_PASSWORD_PROPERTY =
72        "javax.net.ssl.trustStorePassword";
73
74
75
76   /**
77    * The display name for the stat tracker that will be used to track the time
78    * required to perform each modification.
79    */

80   public static final String JavaDoc STAT_TRACKER_MOD_TIME = "Modify Time (ms)";
81
82
83
84   /**
85    * The display name for the stat tracker that will be used to track the number
86    * of modifications performed.
87    */

88   public static final String JavaDoc STAT_TRACKER_MOD_COUNT = "Modifications Performed";
89
90
91
92   /**
93    * The display name for the stat tracker that will be used to accumulate the
94    * number of modifications performed.
95    */

96   public static final String JavaDoc STAT_TRACKER_MOD_TOTAL = "Total Modifications";
97
98
99
100   /**
101    * The display name for the stat tracker that will be used to track the number
102    * of exceptions caught.
103    */

104   public static final String JavaDoc STAT_TRACKER_EXCEPTIONS_CAUGHT =
105        "Exceptions Caught";
106
107
108
109   /**
110    * The characters that are available for use in the randomly-generated values.
111    */

112   public static final char[] ALPHABET =
113        "abcdefghijklmnopqrstuvwxyz".toCharArray();
114
115
116
117
118
119   // The parameter that indicates whether the client should trust any SSL cert.
120
BooleanParameter blindTrustParameter =
121     new BooleanParameter("blind_trust", "Blindly Trust Any Certificate",
122                          "Indicates whether the client should blindly trust " +
123                          "any certificate presented by the server, or " +
124                          "whether the key and trust stores should be used.",
125                          true);
126
127   // The parameter that indicates whether to disconnect after each modify
128
BooleanParameter disconnectParameter =
129        new BooleanParameter("disconnect", "Always Disconnect",
130                             "Indicates whether to close the connection after " +
131                             "each modification", false);
132
133   // The parameter that indicates whether referrals are to be followed or not
134
BooleanParameter followReferralsParameter =
135        new BooleanParameter("followreferrals", "Follow Referrals",
136                             "Indicates whether to follow referrals received " +
137                             "while making modifications", false);
138
139   // The parameter that indicates whether the connection should use SSL
140
BooleanParameter useSSLParameter =
141     new BooleanParameter("usessl", "Use SSL",
142                          "Indicates whether SSL should be used for all " +
143                          "communication with the directory server", false);
144
145   // The parameter that specifies a file containing DNs to modify.
146
FileURLParameter dnURLParameter =
147        new FileURLParameter("dn_url", "DN File URL",
148                             "The URL (FILE or HTTP) that specifies the " +
149                             "DNs of the entries that may be modified.", null,
150                             false);
151
152   // The parmeter that specifies the cool-down time in seconds.
153
IntegerParameter coolDownParameter =
154        new IntegerParameter("cool_down", "Cool Down Time",
155                             "The time in seconds that the job should " +
156                             "continue making modifications after ending " +
157                             "statistics collection.", true, 0, true, 0, false,
158                             0);
159
160   // The parameter that indicates the delay that should be used between each
161
// request sent by a thread.
162
IntegerParameter delayParameter =
163        new IntegerParameter("delay", "Time Between Requests (ms)",
164                             "Specifies the length of time in milliseconds " +
165                             "each thread should wait between modify " +
166                             "requests. Note that this delay will be " +
167                             "between consecutive requests and not between " +
168                             "the response of one operation and the request " +
169                             "for the next. If a modify takes longer than " +
170                             "this length of time, then there will be no delay.",
171                             true, 0, true, 0, false, 0);
172
173   // The parameter that indicates the number of times the modify should be
174
// performed
175
IntegerParameter iterationsParameter =
176        new IntegerParameter("iterations", "Number of Iterations",
177                             "The number of modifications that should be " +
178                             "performed by each thread", false, -1);
179
180   // The parameter that indicates the number of characters that should be
181
// included in the replacement value used in the modify.
182
IntegerParameter lengthParameter =
183        new IntegerParameter("length", "Value Length",
184                             "The length that should be used for the new " +
185                             "value of the modified attribute.", true, 80,
186                             true, 1, false, 0);
187
188   // The parameter that indicates the port number for the directory server
189
IntegerParameter portParameter =
190        new IntegerParameter("ldapport", "Directory Server Port",
191                             "The port number for the LDAP directory server",
192                             true, 389, true, 1, true, 65535);
193
194   // The parameter that indicates the maximum time limit for modify operations.
195
IntegerParameter timeLimitParameter =
196        new IntegerParameter("time_limit", "Modify Time Limit",
197                             "The maximum length of time in seconds that the " +
198                             "thread should wait for a modify operation to be " +
199                             "performed before cancelling it and trying " +
200                             "another.", false, 0, true, 0, false, 0);
201
202   // The parmeter that specifies the cool-down time in seconds.
203
IntegerParameter warmUpParameter =
204        new IntegerParameter("warm_up", "Warm Up Time",
205                             "The time in seconds that the job should " +
206                             "modify before beginning statistics collection.",
207                             true, 0, true, 0, false, 0);
208
209   // The parameter that specifies the attribute to modify
210
MultiLineTextParameter attributeParameter =
211        new MultiLineTextParameter("attribute", "Attribute(s) to Modify",
212                                   "The set of attribute to modify in the " +
213                                   "entry. If multiple attributes are to be " +
214                                   "modified, then a separate attribute " +
215                                   "should be listed per line. Each will be " +
216                                   "given the same value.",
217                                   new String JavaDoc[] { "description" }, true);
218
219   // The placeholder parameter used as a spacer in the admin interface.
220
PlaceholderParameter placeholder = new PlaceholderParameter();
221
222   // The parameter that indicates the DN to use when binding to the server
223
StringParameter bindDNParameter =
224        new StringParameter("binddn", "Bind DN",
225                            "The DN to use to bind to the server", false, "");
226
227   // The parameter that indicates the DN of the entry to modify
228
StringParameter entryDNParameter =
229        new StringParameter("entrydn", "Entry DN",
230                            "The DN of the entry to modify", false, "");
231
232   // The parameter that indicates the address of the directory server
233
StringParameter hostParameter =
234        new StringParameter("ldaphost", "Directory Server Host",
235                            "The DNS hostname or IP address of the LDAP " +
236                            "directory server", true, "");
237
238   // The parameter that indicates the DN to use to proxy the modifications.
239
StringParameter proxyAsDNParameter =
240        new StringParameter("proxy_as_dn", "Proxy As DN",
241                            "The DN of the user whose credentials should be " +
242                            "used to perform the modification through the use " +
243                            "of the proxied authorization control.", false, "");
244
245   // The parameter that specifies the location of the SSL key store
246
StringParameter keyStoreParameter =
247     new StringParameter("sslkeystore", "SSL Key Store",
248                         "The path to the JSSE key store to use for an " +
249                         "SSL-based connection", false, "");
250
251   // The parameter that specifies the location of the SSL trust store
252
StringParameter trustStoreParameter =
253     new StringParameter("ssltruststore", "SSL Trust Store",
254                         "The path to the JSSE trust store to use for an " +
255                         "SSL-based connection", false, "");
256
257   // The parameter that indicates the bind password
258
PasswordParameter bindPWParameter =
259        new PasswordParameter("bindpw", "Bind Password",
260                              "The password for the bind DN", false, "");
261
262   // The parameter that specifies the password for the SSL key store
263
PasswordParameter keyPWParameter =
264     new PasswordParameter("sslkeypw", "SSL Key Store Password",
265                           "The password for the JSSE key store", false, "");
266
267   // The parameter that specifies the password for the SSL key store
268
PasswordParameter trustPWParameter =
269     new PasswordParameter("ssltrustpw", "SSL Trust Store Password",
270                           "The password for the JSSE trust store", false, "");
271
272
273   // Instance variables that correspond to the parameter values
274
static boolean alwaysDisconnect;
275   static boolean blindTrust;
276   static boolean followReferrals;
277   static boolean useDNFile;
278   static boolean useDNRange;
279   static boolean useProxyAuth;
280   static boolean useSequential;
281   static boolean useSSL;
282   static int coolDownTime;
283   static int dnRangeMax;
284   static int dnRangeMin;
285   static int dnRangeSpan;
286   static int iterations;
287   static int ldapPort;
288   static int length;
289   static int sequentialCounter;
290   static int timeLimit;
291   static int warmUpTime;
292   static long delay;
293   static String JavaDoc bindDN;
294   static String JavaDoc bindPassword;
295   static String JavaDoc dnInitial;
296   static String JavaDoc dnFinal;
297   static String JavaDoc ldapHost;
298   static String JavaDoc proxyAsDN;
299   static String JavaDoc sslKeyStore;
300   static String JavaDoc sslKeyPassword;
301   static String JavaDoc sslTrustStore;
302   static String JavaDoc sslTrustPassword;
303   static String JavaDoc[] modifyAttrs;
304   static String JavaDoc[] modifyDNs;
305
306
307   // The connection to the directory to use to perform the modifies.
308
LDAPConnection conn;
309
310
311   // Variables used for status counters
312
AccumulatingTracker totalMods;
313   IncrementalTracker exceptionsCaught;
314   IncrementalTracker modCount;
315   TimeTracker modTime;
316
317
318   // One random number generator for use throughout the client and another to
319
// use for only the current thread.
320
static Random parentRandom;
321   Random random;
322
323
324
325
326   /**
327    * The default constructor used to create a new instance of the modify thread.
328    * The only thing it should do is to invoke the superclass constructor. All
329    * other initialization should be performed in the <CODE>initialize</CODE>
330    * method.
331    */

332   public ModRateJobClass()
333   {
334     super();
335   }
336
337
338
339   /**
340    * Retrieves the name of the job performed by this job thread.
341    *
342    * @return The name of the job performed by this job thread.
343    */

344   public String JavaDoc getJobName()
345   {
346     return "LDAP ModRate";
347   }
348
349
350
351   /**
352    * Retrieves a description of the job performed by this job thread.
353    *
354    * @return A description of the job performed by this job thread.
355    */

356   public String JavaDoc getJobDescription()
357   {
358     return "This job can be used to perform repeated modifications against " +
359            "an LDAP directory server to generate load and measure performance";
360   }
361
362
363
364   /**
365    * Retrieves the name of the category in which this job class exists. This is
366    * used to help arrange the job classes in the administrative interface.
367    *
368    * @return The name of the category in which this job class exists.
369    */

370   public String JavaDoc getJobCategoryName()
371   {
372     return "LDAP";
373   }
374
375
376
377   /**
378    * Retrieve a parameter list that can be used to determine all of the
379    * customizeable options that are available for this job.
380    *
381    * @return A parameter list that can be used to determine all of the
382    * customizeable options that are available for this job.
383    */

384   public ParameterList getParameterStubs()
385   {
386     Parameter[] parameters = new Parameter[]
387     {
388       placeholder,
389       hostParameter,
390       portParameter,
391       bindDNParameter,
392       bindPWParameter,
393       proxyAsDNParameter,
394       placeholder,
395       entryDNParameter,
396       dnURLParameter,
397       attributeParameter,
398       lengthParameter,
399       placeholder,
400       warmUpParameter,
401       coolDownParameter,
402       timeLimitParameter,
403       delayParameter,
404       placeholder,
405       useSSLParameter,
406       blindTrustParameter,
407       keyStoreParameter,
408       keyPWParameter,
409       trustStoreParameter,
410       trustPWParameter,
411       placeholder,
412       iterationsParameter,
413       disconnectParameter,
414       followReferralsParameter
415     };
416
417     return new ParameterList(parameters);
418   }
419
420
421
422   /**
423    * Retrieves the set of stat trackers that will be maintained by this job
424    * class. The stat trackers returned by this method do not have to actually
425    * contain any statistics -- the display name and stat tracker class should
426    * be the only information that callers of this method should rely upon. Note
427    * that this list can be different from the list of statistics actually
428    * collected by the job in some cases (e.g., if the job may not return all the
429    * stat trackers it advertises in all cases, or if the job may return stat
430    * trackers that it did not advertise), but it is a possibility that only the
431    * stat trackers returned by this method will be accessible for some features
432    * in the SLAMD server.
433    *
434    * @param clientID The client ID that should be used for the
435    * returned stat trackers.
436    * @param threadID The thread ID that should be used for the
437    * returned stat trackers.
438    * @param collectionInterval The collection interval that should be used for
439    * the returned stat trackers.
440    *
441    * @return The set of stat trackers that will be maintained by this job
442    * class.
443    */

444   public StatTracker[] getStatTrackerStubs(String JavaDoc clientID, String JavaDoc threadID,
445                                            int collectionInterval)
446   {
447     return new StatTracker[]
448     {
449       new IncrementalTracker(clientID, threadID, STAT_TRACKER_MOD_COUNT,
450                              collectionInterval),
451       new TimeTracker(clientID, threadID, STAT_TRACKER_MOD_TIME,
452                       collectionInterval),
453       new IncrementalTracker(clientID, threadID, STAT_TRACKER_EXCEPTIONS_CAUGHT,
454                              collectionInterval)
455     };
456   }
457
458
459
460   /**
461    * Retrieves the stat trackers that are maintained for this job thread.
462    *
463    * @return The stat trackers that are maintained for this job thread.
464    */

465   public StatTracker[] getStatTrackers()
466   {
467     return new StatTracker[]
468     {
469       modCount,
470       modTime,
471       exceptionsCaught,
472       totalMods
473     };
474   }
475
476
477
478   /**
479    * Provides a means of validating the information used to schedule the job,
480    * including the scheduling information and list of parameters.
481    *
482    * @param numClients The number of clients that should be used to
483    * run the job.
484    * @param threadsPerClient The number of threads that should be created on
485    * each client to run the job.
486    * @param threadStartupDelay The delay in milliseconds that should be used
487    * when starting the client threads.
488    * @param startTime The time that the job should start running.
489    * @param stopTime The time that the job should stop running.
490    * @param duration The maximum length of time in seconds that the
491    * job should be allowed to run.
492    * @param collectionInterval The collection interval that should be used
493    * when gathering statistics for the job.
494    * @param parameters The set of parameters provided to this job that
495    * can be used to customize its behavior.
496    *
497    * @throws InvalidValueException If the provided information is not
498    * appropriate for running this job.
499    */

500   public void validateJobInfo(int numClients, int threadsPerClient,
501                               int threadStartupDelay, Date startTime,
502                               Date stopTime, int duration,
503                               int collectionInterval, ParameterList parameters)
504          throws InvalidValueException
505   {
506     StringParameter dnParameter =
507          parameters.getStringParameter(entryDNParameter.getName());
508     FileURLParameter dnFileParameter =
509          parameters.getFileURLParameter(dnURLParameter.getName());
510     if (((dnParameter == null) || (! dnParameter.hasValue())) &&
511         ((dnFileParameter == null) || (! dnFileParameter.hasValue())))
512     {
513       throw new InvalidValueException("Either a single entry DN or a DN file " +
514                                       "URL must be specified.");
515     }
516   }
517
518
519
520   /**
521    * Indicates whether this job class implements logic that makes it possible to
522    * test the validity of job parameters before scheduling the job for execution
523    * (e.g., to see if the server is reachable using the information provided).
524    *
525    * @return <CODE>true</CODE> if this job provides a means of testing the job
526    * parameters, or <CODE>false</CODE> if not.
527    */

528   public boolean providesParameterTest()
529   {
530     return true;
531   }
532
533
534
535   /**
536    * Provides a means of testing the provided job parameters to determine
537    * whether they are valid (e.g., to see if the server is reachable) before
538    * scheduling the job for execution. This method will be executed by the
539    * SLAMD server system itself and not by any of the clients.
540    *
541    * @param parameters The job parameters to be tested.
542    * @param outputMessages The lines of output that were generated as part of
543    * the testing process. Each line of output should
544    * be added to this list as a separate string, and
545    * empty strings (but not <CODE>null</CODE> values)
546    * are allowed to provide separation between
547    * different messages. No formatting should be
548    * provided for these messages, however, since they
549    * may be displayed in either an HTML or plain text
550    * interface.
551    *
552    * @return <CODE>true</CODE> if the test completed successfully, or
553    * <CODE>false</CODE> if not. Note that even if the test did not
554    * complete successfully, the user will be presented with a warning
555    * but will still be allowed to schedule the job using the provided
556    * parameters. This is necessary because the parameters may still be
557    * valid even if the server couldn't validate them at the time the
558    * job was scheduled (e.g., if the server wasn't running or could not
559    * be reached by the SLAMD server even though it could be by the
560    * clients).
561    */

562   public boolean testJobParameters(ParameterList parameters,
563                                    ArrayList outputMessages)
564   {
565     // Get all the parameters that we might need to perform the test.
566
StringParameter hostParam =
567          parameters.getStringParameter(hostParameter.getName());
568     if ((hostParam == null) || (! hostParam.hasValue()))
569     {
570       outputMessages.add("ERROR: No directory server address was provided.");
571       return false;
572     }
573     String JavaDoc host = hostParam.getStringValue();
574
575
576     IntegerParameter portParam =
577          parameters.getIntegerParameter(portParameter.getName());
578     if ((portParam == null) || (! hostParam.hasValue()))
579     {
580       outputMessages.add("ERROR: No directory server port was provided.");
581       return false;
582     }
583     int port = portParam.getIntValue();
584
585
586     boolean useSSL = false;
587     BooleanParameter useSSLParam =
588          parameters.getBooleanParameter(useSSLParameter.getName());
589     if (useSSLParam != null)
590     {
591       useSSL = useSSLParam.getBooleanValue();
592     }
593
594
595     boolean blindTrust = true;
596     BooleanParameter blindTrustParam =
597          parameters.getBooleanParameter(blindTrustParameter.getName());
598     if (blindTrustParam != null)
599     {
600       blindTrust = blindTrustParam.getBooleanValue();
601     }
602
603
604     String JavaDoc keyStore = null;
605     StringParameter keyStoreParam =
606          parameters.getStringParameter(keyStoreParameter.getName());
607     if ((keyStoreParam != null) && keyStoreParam.hasValue())
608     {
609       keyStore = keyStoreParam.getStringValue();
610       File keyStoreFile = new File(keyStore);
611       if (useSSL && (! blindTrust) && (! keyStoreFile.exists()))
612       {
613         outputMessages.add("WARNING: Key store file \"" + keyStore +
614                            "\" not found on SLAMD server system. This test " +
615                            "will blindly trust any SSL certificate " +
616                            "presented by the directory server.");
617         outputMessages.add("");
618         blindTrust = true;
619       }
620       else
621       {
622         System.setProperty(SSL_KEY_STORE_PROPERTY, keyStore);
623       }
624     }
625
626
627     String JavaDoc keyStorePassword = "";
628     StringParameter keyPassParam =
629          parameters.getStringParameter(keyPWParameter.getName());
630     if ((keyPassParam != null) && keyPassParam.hasValue())
631     {
632       keyStorePassword = keyPassParam.getStringValue();
633       System.setProperty(SSL_KEY_PASSWORD_PROPERTY, keyStorePassword);
634     }
635
636
637     String JavaDoc trustStore = null;
638     StringParameter trustStoreParam =
639          parameters.getStringParameter(trustStoreParameter.getName());
640     if ((trustStoreParam != null) && trustStoreParam.hasValue())
641     {
642       trustStore = trustStoreParam.getStringValue();
643       File trustStoreFile = new File(trustStore);
644       if (useSSL && (! blindTrust) && (! trustStoreFile.exists()))
645       {
646         outputMessages.add("WARNING: trust store file \"" + trustStore +
647                            "\" not found on SLAMD server system. This test " +
648                            "will blindly trust any SSL certificate " +
649                            "presented by the directory server.");
650         outputMessages.add("");
651         blindTrust = true;
652       }
653       else
654       {
655         System.setProperty(SSL_TRUST_STORE_PROPERTY, trustStore);
656       }
657     }
658
659
660     String JavaDoc trustStorePassword = "";
661     StringParameter trustPassParam =
662          parameters.getStringParameter(trustPWParameter.getName());
663     if ((trustPassParam != null) && trustPassParam.hasValue())
664     {
665       trustStorePassword = trustPassParam.getStringValue();
666       System.setProperty(SSL_TRUST_PASSWORD_PROPERTY, trustStorePassword);
667     }
668
669
670     String JavaDoc bindDN = "";
671     StringParameter bindDNParam =
672          parameters.getStringParameter(bindDNParameter.getName());
673     if ((bindDNParam != null) && bindDNParam.hasValue())
674     {
675       bindDN = bindDNParam.getStringValue();
676     }
677
678
679     String JavaDoc bindPassword = "";
680     PasswordParameter bindPWParam =
681          parameters.getPasswordParameter(bindPWParameter.getName());
682     if ((bindPWParam != null) && bindPWParam.hasValue())
683     {
684       bindPassword = bindPWParam.getStringValue();
685     }
686
687
688     String JavaDoc proxyAsDN = null;
689     StringParameter proxyAsDNParam =
690          parameters.getStringParameter(proxyAsDNParameter.getName());
691     if ((proxyAsDNParam != null) && proxyAsDNParam.hasValue())
692     {
693       proxyAsDN = proxyAsDNParam.getStringValue();
694     }
695
696
697     // Create the LDAPConnection object that we will use to communicate with the
698
// directory server.
699
LDAPConnection conn;
700     if (useSSL)
701     {
702       if (blindTrust)
703       {
704         try
705         {
706           conn = new LDAPConnection(new JSSEBlindTrustSocketFactory());
707         }
708         catch (Exception JavaDoc e)
709         {
710           outputMessages.add("ERROR: Unable to instantiate the blind trust " +
711                              "socket factory for use in creating the SSL " +
712                              "connection: " + stackTraceToString(e));
713           return false;
714         }
715       }
716       else
717       {
718         conn = new LDAPConnection(new JSSESocketFactory(null));
719       }
720     }
721     else
722     {
723       conn = new LDAPConnection();
724     }
725
726
727     // Attempt to establish a connection to the directory server.
728
try
729     {
730       if (useSSL)
731       {
732         outputMessages.add("Attempting to establish an SSL-based connection " +
733                            "to " + host + ":" + port + "....");
734       }
735       else
736       {
737         outputMessages.add("Attempting to establish a connection to " + host +
738                            ":" + port + "....");
739       }
740       conn.connect(host, port);
741       outputMessages.add("Connected successfully.");
742       outputMessages.add("");
743     }
744     catch (Exception JavaDoc e)
745     {
746       outputMessages.add("ERROR: Unable to connect to the directory " +
747                          "server: " + stackTraceToString(e));
748       return false;
749     }
750
751
752     // Attempt to bind to the directory server using the bind DN and password.
753
try
754     {
755       outputMessages.add("Attempting to perform an LDAPv3 bind to the " +
756                          "directory server with a DN of '" + bindDN + "'....");
757       conn.bind(3, bindDN, bindPassword);
758       outputMessages.add("Bound successfully.");
759       outputMessages.add("");
760     }
761     catch (Exception JavaDoc e)
762     {
763       try
764       {
765         conn.disconnect();
766       } catch (Exception JavaDoc e2) {}
767
768       outputMessages.add("ERROR: Unable to bind to the directory server: " +
769                          stackTraceToString(e));
770       return false;
771     }
772
773
774     // If a proxy user was specified, make sure that it exists.
775
if (proxyAsDN != null)
776     {
777       try
778       {
779         outputMessages.add("Checking to make sure that the proxied user '" +
780                            proxyAsDN + "' exists in the directory....");
781         LDAPEntry proxyUserEntry = conn.read(proxyAsDN, new String JavaDoc[] { "1.1" });
782         if (proxyUserEntry == null)
783         {
784           try
785           {
786             conn.disconnect();
787           } catch (Exception JavaDoc e2) {}
788
789           outputMessages.add("ERROR: Unable to retrieve the proxied user's " +
790                              "entry.");
791           return false;
792         }
793         else
794         {
795           outputMessages.add("Successfully read the proxied user's entry.");
796           outputMessages.add("");
797         }
798       }
799       catch (Exception JavaDoc e)
800       {
801         try
802         {
803           conn.disconnect();
804         } catch (Exception JavaDoc e2) {}
805
806         outputMessages.add("ERROR: Unable to retrieve the proxied user's " +
807                            "entry: " + stackTraceToString(e));
808         return false;
809       }
810     }
811
812
813     // At this point, all tests have passed. Close the connection and return
814
// true.
815
try
816     {
817       conn.disconnect();
818     } catch (Exception JavaDoc e) {}
819
820     outputMessages.add("All tests completed successfully.");
821     return true;
822   }
823
824
825
826   /**
827    * Initializes all of the instance variables that correspond to job
828    * parameters.
829    *
830    * @param clientID The client ID for the current client.
831    * @param parameters The set of parameters that have been defined for this
832    * job.
833    *
834    * @throws UnableToRunException If any part of the initialization fails.
835    */

836   public void initializeClient(String JavaDoc clientID, ParameterList parameters)
837          throws UnableToRunException
838   {
839     // Get the address of the target directory server
840
ldapHost = null;
841     hostParameter = parameters.getStringParameter(hostParameter.getName());
842     if (hostParameter != null)
843     {
844       ldapHost = hostParameter.getStringValue();
845     }
846
847     // Get the port for the target directory server
848
ldapPort = 389;
849     portParameter = parameters.getIntegerParameter(portParameter.getName());
850     if (portParameter != null)
851     {
852       ldapPort = portParameter.getIntValue();
853     }
854
855     // Get the bind DN for the target directory server
856
bindDN = "";
857     bindDNParameter = parameters.getStringParameter(bindDNParameter.getName());
858     if (bindDNParameter != null)
859     {
860       bindDN = bindDNParameter.getStringValue();
861     }
862
863     // Get the bind password for the target directory server
864
bindPassword = "";
865     bindPWParameter =
866          parameters.getPasswordParameter(bindPWParameter.getName());
867     if (bindPWParameter != null)
868     {
869       bindPassword = bindPWParameter.getStringValue();
870     }
871
872     // Get the DN of the proxy as user.
873
useProxyAuth = false;
874     proxyAsDNParameter =
875          parameters.getStringParameter(proxyAsDNParameter.getName());
876     if ((proxyAsDNParameter != null) && (proxyAsDNParameter.hasValue()))
877     {
878       useProxyAuth = true;
879       proxyAsDN = proxyAsDNParameter.getStringValue();
880     }
881
882     // Get the DN of the entry to modify
883
useDNFile = false;
884     entryDNParameter =
885          parameters.getStringParameter(entryDNParameter.getName());
886     if (entryDNParameter != null)
887     {
888       String JavaDoc entryDN = entryDNParameter.getStringValue();
889       useDNRange = true;
890       useSequential = false;
891
892       try
893       {
894         int openPos = entryDN.indexOf('[');
895         int dashPos = entryDN.indexOf('-', openPos);
896         if (dashPos < 0)
897         {
898           dashPos = entryDN.indexOf(':', openPos);
899           useSequential = true;
900         }
901         int closePos = entryDN.indexOf(']', dashPos);
902
903         dnInitial = entryDN.substring(0, openPos);
904         dnFinal = entryDN.substring(closePos+1);
905
906         dnRangeMin = Integer.parseInt(entryDN.substring(openPos+1, dashPos));
907         dnRangeMax = Integer.parseInt(entryDN.substring(dashPos+1, closePos));
908         dnRangeSpan = dnRangeMax - dnRangeMin + 1;
909         sequentialCounter = dnRangeMin;
910       }
911       catch (Exception JavaDoc e)
912       {
913         useDNRange = false;
914         dnInitial = entryDN;
915       }
916     }
917
918     dnURLParameter = parameters.getFileURLParameter(dnURLParameter.getName());
919     if ((dnURLParameter != null) && dnURLParameter.hasValue())
920     {
921       try
922       {
923         useDNFile = true;
924         modifyDNs = dnURLParameter.getNonBlankFileLines();
925       }
926       catch (Exception JavaDoc e)
927       {
928         throw new UnableToRunException("Could not retrieve the DNs of the " +
929                                        "entries to be modified.", e);
930       }
931     }
932
933     // Get the attribute to modify.
934
attributeParameter =
935          parameters.getMultiLineTextParameter(attributeParameter.getName());
936     if (attributeParameter != null)
937     {
938       modifyAttrs = attributeParameter.getNonBlankLines();
939     }
940
941     // Get the length to use in the modification.
942
length = 80;
943     lengthParameter = parameters.getIntegerParameter(lengthParameter.getName());
944     if (lengthParameter != null)
945     {
946       length = lengthParameter.getIntValue();
947     }
948
949     // Get the warm up time.
950
warmUpTime = 0;
951     warmUpParameter = parameters.getIntegerParameter(warmUpParameter.getName());
952     if (warmUpParameter != null)
953     {
954       warmUpTime = warmUpParameter.getIntValue();
955     }
956
957     // Get the cool down time.
958
coolDownTime = 0;
959     coolDownParameter =
960          parameters.getIntegerParameter(coolDownParameter.getName());
961     if (coolDownParameter != null)
962     {
963       coolDownTime = coolDownParameter.getIntValue();
964     }
965
966     // Get the maximum modify time limit.
967
timeLimit = 0;
968     timeLimitParameter =
969          parameters.getIntegerParameter(timeLimitParameter.getName());
970     if (timeLimitParameter != null)
971     {
972       timeLimit = timeLimitParameter.getIntValue();
973     }
974
975     // Get the delay between requests.
976
delay = 0;
977     delayParameter = parameters.getIntegerParameter(delayParameter.getName());
978     if (delayParameter != null)
979     {
980       delay = delayParameter.getIntValue();
981     }
982
983     // Get the number of iterations to perform
984
iterations = -1;
985     iterationsParameter =
986          parameters.getIntegerParameter(iterationsParameter.getName());
987     if (iterationsParameter != null)
988     {
989       iterations = iterationsParameter.getIntValue();
990     }
991
992     // Get the flag indicating whether we should use SSL or not
993
useSSL = false;
994     useSSLParameter = parameters.getBooleanParameter(useSSLParameter.getName());
995     if (useSSLParameter != null)
996     {
997       useSSL = useSSLParameter.getBooleanValue();
998     }
999
1000    // If we are to use SSL, then get all the other SSL-related info
1001
if (useSSL)
1002    {
1003      // Whether to blindly trust SSL certificates.
1004
blindTrustParameter =
1005           parameters.getBooleanParameter(blindTrustParameter.getName());
1006      if (blindTrustParameter != null)
1007      {
1008        blindTrust = blindTrustParameter.getBooleanValue();
1009      }
1010
1011      // The location of the JSSE key store
1012
sslKeyStore = null;
1013      keyStoreParameter =
1014           parameters.getStringParameter(keyStoreParameter.getName());
1015      if ((keyStoreParameter != null) && (keyStoreParameter.hasValue()))
1016      {
1017        sslKeyStore = keyStoreParameter.getStringValue();
1018        System.setProperty(SSL_KEY_STORE_PROPERTY, sslKeyStore);
1019      }
1020
1021      // The JSSE key store password
1022
sslKeyPassword = null;
1023      keyPWParameter =
1024           parameters.getPasswordParameter(keyPWParameter.getName());
1025      if ((keyPWParameter != null) && (keyPWParameter.hasValue()))
1026      {
1027        sslKeyPassword = keyPWParameter.getStringValue();
1028        System.setProperty(SSL_KEY_PASSWORD_PROPERTY, sslKeyPassword);
1029      }
1030
1031      // The location of the JSSE trust store
1032
sslTrustStore = null;
1033      trustStoreParameter =
1034           parameters.getStringParameter(trustStoreParameter.getName());
1035      if ((trustStoreParameter != null) && (trustStoreParameter.hasValue()))
1036      {
1037        sslTrustStore = trustStoreParameter.getStringValue();
1038        System.setProperty(SSL_TRUST_STORE_PROPERTY, sslTrustStore);
1039      }
1040
1041      // The JSSE trust store password
1042
sslTrustPassword = null;
1043      trustPWParameter =
1044           parameters.getPasswordParameter(trustPWParameter.getName());
1045      if ((trustPWParameter != null) && (trustPWParameter.hasValue()))
1046      {
1047        sslTrustPassword = trustPWParameter.getStringValue();
1048        System.setProperty(SSL_TRUST_PASSWORD_PROPERTY, sslTrustPassword);
1049      }
1050    }
1051
1052    // Get the flag indicating whether we should disconnect after each modify
1053
alwaysDisconnect = false;
1054    disconnectParameter =
1055         parameters.getBooleanParameter(disconnectParameter.getName());
1056    if (disconnectParameter != null)
1057    {
1058      alwaysDisconnect = disconnectParameter.getBooleanValue();
1059    }
1060
1061    // Get the flag indicating whether we should follow referrals
1062
followReferrals = false;
1063    followReferralsParameter =
1064         parameters.getBooleanParameter(followReferralsParameter.getName());
1065    if (followReferralsParameter != null)
1066    {
1067      followReferrals = followReferralsParameter.getBooleanValue();
1068    }
1069
1070
1071    // Initialize the parent random number generator
1072
parentRandom = new Random();
1073  }
1074
1075
1076
1077  /**
1078   * Initializes this job thread to be used to actually run the job on the
1079   * client. The provided parameter list should be processed to customize the
1080   * behavior of this job thread, and any other initialization that needs to be
1081   * done in order for the job to run should be performed here as well.
1082   *
1083   * @param clientID The client ID for this job thread.
1084   * @param threadID The thread ID for this job thread.
1085   * @param collectionInterval The length of time in seconds to use as the
1086   * statistics collection interval.
1087   * @param parameters The set of parameters provided to this job that
1088   * can be used to customize its behavior.
1089   *
1090   * @throws UnableToRunException If a problem occurs that prevents the thread
1091   * from being able to run properly.
1092   */

1093  public void initializeThread(String JavaDoc clientID, String JavaDoc threadID,
1094                               int collectionInterval, ParameterList parameters)
1095         throws UnableToRunException
1096  {
1097    // Set up the stat trackers
1098
modCount = new IncrementalTracker(clientID, threadID,
1099                                      STAT_TRACKER_MOD_COUNT,
1100                                      collectionInterval);
1101    exceptionsCaught = new IncrementalTracker(clientID, threadID,
1102                                              STAT_TRACKER_EXCEPTIONS_CAUGHT,
1103                                              collectionInterval);
1104    modTime = new TimeTracker(clientID, threadID, STAT_TRACKER_MOD_TIME,
1105                              collectionInterval);
1106    totalMods = new AccumulatingTracker(clientID, threadID,
1107                                        STAT_TRACKER_MOD_TOTAL,
1108                                        collectionInterval);
1109
1110
1111    // Enable real-time reporting of the data for these stat trackers.
1112
RealTimeStatReporter statReporter = getStatReporter();
1113    if (statReporter != null)
1114    {
1115      String JavaDoc jobID = getJobID();
1116      modCount.enableRealTimeStats(statReporter, jobID);
1117      exceptionsCaught.enableRealTimeStats(statReporter, jobID);
1118      modTime.enableRealTimeStats(statReporter, jobID);
1119      totalMods.enableRealTimeStats(statReporter, jobID);
1120    }
1121
1122
1123    // Initialize the random number generator for this thread.
1124
random = new Random(parentRandom.nextLong());
1125
1126
1127    // If the connection is to use SSL, then establish a preliminary connection
1128
// now. The first connection can take a significant amount of time to
1129
// establish, and we want to get it out of the way early before the timer
1130
// starts (if a duration is specified). Don't worry about any exceptions
1131
// that may get thrown here because there's no easy way to report them back
1132
// but they will be repeated and handled when the job starts running anyway,
1133
// so no big deal.
1134
if (useSSL)
1135    {
1136      try
1137      {
1138        LDAPConnection conn;
1139        if (blindTrust)
1140        {
1141          conn = new LDAPConnection(new JSSEBlindTrustSocketFactory());
1142        }
1143        else
1144        {
1145          conn = new LDAPConnection(new JSSESocketFactory(null));
1146        }
1147        conn.connect(3, ldapHost, ldapPort, bindDN, bindPassword);
1148        conn.disconnect();
1149      }
1150      catch (Exception JavaDoc e) {}
1151    }
1152  }
1153
1154
1155
1156  /**
1157   * Perform the work of this job thread by establishing the connection(s) to
1158   * the directory server and issuing all the appropriate queries. The job will
1159   * continue until the specified number of iterations have been performed, the
1160   * stop time has been reached, the maximum duration has been reached, or the
1161   * SLAMD server indicates that a stop has been requested.
1162   */

1163  public void runJob()
1164  {
1165    // Determine the range of time for which we should collect statistics.
1166
long currentTime = System.currentTimeMillis();
1167    boolean collectingStats = false;
1168    long startCollectingTime = currentTime + (1000 * warmUpTime);
1169    long stopCollectingTime = Long.MAX_VALUE;
1170    if ((coolDownTime > 0) && (getShouldStopTime() > 0))
1171    {
1172      stopCollectingTime = getShouldStopTime() - (1000 * coolDownTime);
1173    }
1174
1175    // Set a variable that we can use to determine if the connection is alive or
1176
// not
1177
boolean connected = false;
1178
1179    // Set a variable that can be used to determine how long we should sleep
1180
// between modifications.
1181
long modStartTime = 0;
1182
1183    // First, see if this should operate "infinitely" (i.e., not a fixed number
1184
// of iterations
1185
boolean infinite = (iterations <= 0);
1186
1187    // Create a variable that we will use for the LDAP connection
1188
if (useSSL)
1189    {
1190      if (blindTrust)
1191      {
1192        try
1193        {
1194          conn = new LDAPConnection(new JSSEBlindTrustSocketFactory());
1195        }
1196        catch (LDAPException le)
1197        {
1198          logMessage(le.getMessage());
1199          indicateStoppedDueToError();
1200          return;
1201        }
1202      }
1203      else
1204      {
1205        conn = new LDAPConnection(new JSSESocketFactory(null));
1206      }
1207    }
1208    else
1209    {
1210      conn = new LDAPConnection();
1211    }
1212
1213
1214    // Create a loop that will run until it needs to stop
1215
for (int i=0; ((! shouldStop()) && ((infinite || (i < iterations)))); i++)
1216    {
1217      currentTime = System.currentTimeMillis();
1218      if ((! collectingStats) && (currentTime >= startCollectingTime) &&
1219          (currentTime < stopCollectingTime))
1220      {
1221        // Tell the stat trackers that they should start tracking now
1222
modCount.startTracker();
1223        exceptionsCaught.startTracker();
1224        modTime.startTracker();
1225        totalMods.startTracker();
1226        collectingStats = true;
1227      }
1228      else if ((collectingStats) && (currentTime >= stopCollectingTime))
1229      {
1230        modCount.stopTracker();
1231        exceptionsCaught.stopTracker();
1232        modTime.stopTracker();
1233        totalMods.stopTracker();
1234        collectingStats = false;
1235      }
1236
1237      // If the connection is currently not connected, then establish it
1238
if (! connected)
1239      {
1240        try
1241        {
1242          conn.connect(3, ldapHost, ldapPort, bindDN, bindPassword);
1243          connected = true;
1244        }
1245        catch (LDAPException le)
1246        {
1247          logMessage("ERROR -- Could not connect to " + ldapHost + ":" +
1248                           ldapPort + " (" + le + ") -- aborting thread");
1249          if (collectingStats)
1250          {
1251            exceptionsCaught.increment();
1252          }
1253          indicateStoppedDueToError();
1254          break;
1255        }
1256      }
1257
1258      LDAPConstraints constraints = conn.getConstraints();
1259      if (useProxyAuth)
1260      {
1261        LDAPProxiedAuthControl proxyAuthControl =
1262             new LDAPProxiedAuthControl(proxyAsDN, true);
1263        constraints.setServerControls(proxyAuthControl);
1264      }
1265      constraints.setTimeLimit(1000 * timeLimit);
1266      constraints.setReferrals(followReferrals);
1267      constraints.setRebindProc(this);
1268
1269
1270      // Create a flag used to determine if the modification was successful.
1271
boolean successfulMod = false;
1272
1273
1274      // Record the current time as the start of the modification
1275
if (collectingStats)
1276      {
1277        modTime.startTimer();
1278      }
1279      if (delay > 0)
1280      {
1281        modStartTime = System.currentTimeMillis();
1282      }
1283
1284
1285      // Perform the modification and iterate through all matching entries
1286
try
1287      {
1288        String JavaDoc attrValue = getRandomString(length);
1289        LDAPModification[] mods = new LDAPModification[modifyAttrs.length];
1290        for (int j=0; j < modifyAttrs.length; j++)
1291        {
1292          LDAPAttribute attr = new LDAPAttribute(modifyAttrs[j], attrValue);
1293          mods[j] = new LDAPModification(LDAPModification.REPLACE, attr);
1294        }
1295        conn.modify(getRandomEntryDN(), mods, constraints);
1296        successfulMod = true;
1297      }
1298      catch (LDAPException le)
1299      {
1300        writeVerbose("ERROR while performing modification -- " + le);
1301        if (collectingStats)
1302        {
1303          exceptionsCaught.increment();
1304        }
1305        indicateCompletedWithErrors();
1306      }
1307
1308
1309      // Record the current time as the end of the modification
1310
if (collectingStats)
1311      {
1312        modTime.stopTimer();
1313      }
1314
1315
1316      // Update the appropriate status counters
1317
if (successfulMod && collectingStats)
1318      {
1319        modCount.increment();
1320        totalMods.increment();
1321      }
1322
1323      // If the connection should be broken, then do so
1324
if (alwaysDisconnect)
1325      {
1326        try
1327        {
1328          conn.disconnect();
1329        } catch (LDAPException le) {}
1330        connected = false;
1331      }
1332
1333      // If we need to sleep, then do so
1334
if ((delay > 0) && (! shouldStop()))
1335      {
1336        long now = System.currentTimeMillis();
1337        long sleepTime = delay - (now - modStartTime);
1338        if (sleepTime > 0)
1339        {
1340          try
1341          {
1342            Thread.sleep(sleepTime);
1343          } catch (InterruptedException JavaDoc ie) {}
1344        }
1345      }
1346    }
1347
1348
1349    // If the connection is still established, then close it
1350
try
1351    {
1352      conn.disconnect();
1353    } catch (LDAPException le) {}
1354
1355
1356    // Tell the stat trackers that they should stop tracking
1357
if (collectingStats)
1358    {
1359      modCount.stopTracker();
1360      exceptionsCaught.stopTracker();
1361      modTime.stopTracker();
1362      totalMods.stopTracker();
1363    }
1364  }
1365
1366
1367
1368  /**
1369   * Attempts to force this thread to exit by closing the connection to the
1370   * directory server and setting it to <CODE>null</CODE>.
1371   */

1372  public void destroy()
1373  {
1374    if (conn != null)
1375    {
1376      try
1377      {
1378        conn.disconnect();
1379      } catch (Exception JavaDoc e) {}
1380
1381      conn = null;
1382    }
1383  }
1384
1385
1386
1387  /**
1388   * Specifies the credentials that will be used to bind to the target server if
1389   * a referral is encountered. In this case, we will always attempt the bind
1390   * using the same credentials used to bind to the original target.
1391   *
1392   * @param host The address of the directory server targeted by the referral.
1393   * @param port The port of the directory server targeted by the referral.
1394   *
1395   * @return The credentials that will be used to bind to the server targeted
1396   * by the referral.
1397   */

1398  public LDAPRebindAuth getRebindAuthentication(String JavaDoc host, int port)
1399  {
1400    return new LDAPRebindAuth(bindDN, bindPassword);
1401  }
1402
1403
1404
1405  /**
1406   * Retrieves a random DN from the list of DNs that can be modified.
1407   *
1408   * @return A randomly-chosen DN of an entry to modify.
1409   */

1410  public String JavaDoc getRandomEntryDN()
1411  {
1412    if (useDNFile)
1413    {
1414      return modifyDNs[(random.nextInt() & 0x7FFFFFFF) % modifyDNs.length];
1415    }
1416    else
1417    {
1418      if (useDNRange)
1419      {
1420        int value;
1421        if (useSequential)
1422        {
1423          value = sequentialCounter++;
1424          if (sequentialCounter > dnRangeMax)
1425          {
1426            sequentialCounter = dnRangeMin;
1427          }
1428        }
1429        else
1430        {
1431          value = ((random.nextInt() & 0x7FFFFFFF) % dnRangeSpan) + dnRangeMin;
1432        }
1433        return dnInitial + value + dnFinal;
1434      }
1435      else
1436      {
1437        return dnInitial;
1438      }
1439    }
1440  }
1441
1442
1443
1444  /**
1445   * Retrieves a string containing the specified number of randomly-chosen
1446   * characters.
1447   *
1448   * @param length The number of characters to include in the string.
1449   *
1450   * @return A string containing the specified number of randomly-chosen
1451   * characters.
1452   */

1453  public String JavaDoc getRandomString(int length)
1454  {
1455    char[] returnChars = new char[length];
1456
1457    for (int i=0; i < length; i++)
1458    {
1459      returnChars[i] = ALPHABET[Math.abs((random.nextInt()) & 0x7FFFFFFF) %
1460                                ALPHABET.length];
1461    }
1462
1463    return new String JavaDoc(returnChars);
1464  }
1465}
1466
1467
Popular Tags