KickJava   Java API By Example, From Geeks To Geeks.

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


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 add and
34  * delete operations against an LDAP directory server. It works by first
35  * performing the add operations, waits for some specified interval, and then
36  * will delete the entries that it has just added. All of the configuration for
37  * this job thread can be provided through parameters.
38  *
39  *
40  * @author Neil A. Wilson
41  */

42 public class CombinedAddAndDelRateJobClass
43        extends JobClass
44 {
45   /**
46    * The system property used to specify the location of the JSSE key store.
47    */

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

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

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

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

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

89   public static final String JavaDoc STAT_TRACKER_ADD_COUNT = "Adds Performed";
90
91
92
93   /**
94    * The display name for the stat tracker that will be used to track the
95    * result codes of the add operations.
96    */

97   public static final String JavaDoc STAT_TRACKER_ADD_RESULT = "Add Result Codes";
98
99
100
101   /**
102    * The display name for the stat tracker that will be used to accumulate the
103    * total number of adds performed.
104    */

105   public static final String JavaDoc STAT_TRACKER_ADD_TOTAL = "Total Adds";
106
107
108
109   /**
110    * The display name for the stat tracker that will be used to track the time
111    * required to perform each delete.
112    */

113   public static final String JavaDoc STAT_TRACKER_DEL_TIME = "Delete Time (ms)";
114
115
116
117   /**
118    * The display name for the stat tracker that will be used to track the number
119    * of deletes performed.
120    */

121   public static final String JavaDoc STAT_TRACKER_DEL_COUNT = "Deletes Performed";
122
123
124
125   /**
126    * The display name for the stat tracker that will be used to track the
127    * result codes of the delete operations.
128    */

129   public static final String JavaDoc STAT_TRACKER_DEL_RESULT = "Delete Result Codes";
130
131
132
133   /**
134    * The display name for the stat tracker that will be used to accumulate the
135    * total number of deletes performed.
136    */

137   public static final String JavaDoc STAT_TRACKER_DEL_TOTAL = "Total Deletes";
138
139
140
141   /**
142    * The characters that are available for use in the randomly-generated values.
143    */

144   public static final char[] ALPHABET =
145        "abcdefghijklmnopqrstuvwxyz".toCharArray();
146
147
148   /**
149    * The default set of attributes to include in the entries that are generated.
150    */

151   public static final String JavaDoc[] DEFAULT_ATTR_NAMES = new String JavaDoc[]
152   {
153     "givenname",
154     "sn",
155     "cn",
156     "uid",
157     "userpassword",
158     "mail",
159   };
160
161
162
163   /**
164    * The default set of objectclass values to create.
165    */

166   public static final String JavaDoc[] OBJECTCLASS_VALUES = new String JavaDoc[]
167   {
168     "top",
169     "person",
170     "organizationalPerson",
171     "inetOrgPerson",
172     "extensibleObject"
173   };
174
175
176
177   // The parameter that indicates whether the client should trust any SSL cert.
178
BooleanParameter blindTrustParameter =
179     new BooleanParameter("blind_trust", "Blindly Trust Any Certificate",
180                          "Indicates whether the client should blindly trust " +
181                          "any certificate presented by the server, or " +
182                          "whether the key and trust stores should be used.",
183                          true);
184
185   // The parameter that indicates whether the server should clean up any
186
// tombstones that may be left in the directory after performing the deletes.
187
BooleanParameter cleanTombstonesParameter =
188        new BooleanParameter("clean_tombstones", "Clean Up Tombstones",
189                             "Indicates whether the client should clean up "+
190                             "tombstones that may be in the database after " +
191                             "the delete has completed. This should only be " +
192                             "used for improving the reliability of the " +
193                             "testing in a replicated environment and should " +
194                             "never be used in a production server.", false);
195
196   // The parameter that indicates whether to disconnect after each add
197
BooleanParameter disconnectParameter =
198        new BooleanParameter("disconnect", "Always Disconnect",
199                             "Indicates whether to close the connection after " +
200                             "each add", false);
201
202   // The parameter that indicates whether the connection should use SSL
203
BooleanParameter useSSLParameter =
204     new BooleanParameter("usessl", "Use SSL",
205                          "Indicates whether SSL should be used for all " +
206                          "communication with the directory server", false);
207
208   // The parameter that indicates the delay that should be used between each
209
// request sent by a thread.
210
IntegerParameter delayParameter =
211        new IntegerParameter("delay", "Time Between Requests (ms)",
212                             "Specifies the length of time in milliseconds " +
213                             "each thread should wait between add " +
214                             "requests. Note that this delay will be " +
215                             "between consecutive requests and not between " +
216                             "the response of one operation and the request " +
217                             "for the next. If an add takes longer than " +
218                             "this length of time, then there will be no delay.",
219                             true, 0, true, 0, false, 0);
220
221   // The parameter that indicates the delay that should be used between the end
222
// of all the add operations and the beginning of all the deletes.
223
IntegerParameter deleteDelayParameter =
224        new IntegerParameter("delete_delay", "Time Between Adds and Deletes (s)",
225                             "Specifies the length of time in seconds that " +
226                             "the job should wait after completing all the " +
227                             "add operations before it starts deleting those " +
228                             "entries.", true, 0, true, 0, false, 0);
229
230   // The parameter that indicates the length of the generated attribute values.
231
IntegerParameter lengthParameter =
232     new IntegerParameter("value_length", "Generated Value Length",
233                          "Specifies the number of characters that should be " +
234                          "included in the generated values of the attriubtes.",
235                          true, 8, true, 1, false, 0);
236
237   // The parameter that indicates the port number for the directory server
238
IntegerParameter portParameter =
239        new IntegerParameter("ldapport", "Directory Server Port",
240                             "The port number for the LDAP directory server",
241                             true, 389, true, 1, true, 65535);
242
243   // The parameter that specifies the starting number for the RDN values.
244
IntegerParameter rdnStartParameter =
245        new IntegerParameter("rdn_start", "Initial RDN Value Number",
246                             "The number to use as the value of the RDN " +
247                             "attribute for the first entry to create", true, 1,
248                             false, 0, false, 0);
249
250   // The parameter that specifies the ending number for the RDN values.
251
IntegerParameter rdnEndParameter =
252        new IntegerParameter("rdn_end", "Final RDN Value Number",
253                             "The number to use as the value of the RDN " +
254                             "attribute for the last entry to create", false, 0,
255                             false, 0, false, 0);
256
257   // The parameter that indicates the maximum time limit for operations.
258
IntegerParameter timeLimitParameter =
259        new IntegerParameter("time_limit", "Operation Time Limit",
260                             "The maximum length of time in seconds that the " +
261                             "thread should wait for an operation to be " +
262                             "performed before cancelling it and trying " +
263                             "another.", false, 0, true, 0, false, 0);
264
265   // The parameter that indicates the delay to use before cleaning up
266
// tombstones.
267
IntegerParameter tombstoneDelayParameter =
268       new IntegerParameter("tombstone_delay",
269                            "Delay Before Cleaning Tombstones (s)",
270                            "Specifies the length of time in seconds that the " +
271                            "job should wait after completing the deletes " +
272                            "before starting the tombstone cleanup.", true, 30,
273                            true, 0, false, 0);
274
275   // The names of additional attributes to add to the entries that are created.
276
MultiLineTextParameter extraAttrsParameter =
277        new MultiLineTextParameter("extra_attrs", "Additional Attributes",
278                                   "The names of additional attributes to " +
279                                   "include in the entries that will be " +
280                                   "generated. By default, inetOrgPerson " +
281                                   "entries will be created, with attributes " +
282                                   "of givenName, sn, cn, uid, userPassword, " +
283                                   "and mail, but they will also include the " +
284                                   "extensibleObject objectclass so that any " +
285                                   "additional attributes may be used. Each " +
286                                   "attribute specified will be given a " +
287                                   "value of a string of 80 randomly-chosen " +
288                                   "characters.", null, false);
289
290   // The placeholder parameter used as a spacer in the admin interface.
291
PlaceholderParameter placeholder = new PlaceholderParameter();
292
293   // The parameter that indicates the DN to use when binding to the server
294
StringParameter bindDNParameter =
295        new StringParameter("binddn", "Bind DN",
296                            "The DN to use to bind to the server", false, "");
297
298   // The parameter that indicates the base below which entries will be added.
299
StringParameter baseDNParameter =
300        new StringParameter("basedn", "Base DN ",
301                            "The base below which to add the entries",
302                            true, "");
303
304   // The parameter that indicates the address of the directory server
305
StringParameter hostParameter =
306        new StringParameter("ldaphost", "Directory Server Host",
307                            "The DNS hostname or IP address of the LDAP " +
308                            "directory server", true, "");
309
310   // The parameter that indicates the DN to use to proxy the adds.
311
StringParameter proxyAsDNParameter =
312        new StringParameter("proxy_as_dn", "Proxy As DN",
313                            "The DN of the user whose credentials should be " +
314                            "used to perform the adds through the use " +
315                            "of the proxied authorization control.", false, "");
316
317   // The parameter that indicates the RDN attribute for the new entries.
318
StringParameter rdnAttrParameter =
319        new StringParameter("rdn_attr", "RDN Attribute",
320                            "The RDN attribute to use when creating the " +
321                            "entries.", true, "uid");
322
323   // The parameter that specifies the location of the SSL key store
324
StringParameter keyStoreParameter =
325     new StringParameter("sslkeystore", "SSL Key Store",
326                         "The path to the JSSE key store to use for an " +
327                         "SSL-based connection", false, "");
328
329   // The parameter that specifies the location of the SSL trust store
330
StringParameter trustStoreParameter =
331     new StringParameter("ssltruststore", "SSL Trust Store",
332                         "The path to the JSSE trust store to use for an " +
333                         "SSL-based connection", false, "");
334
335   // The parameter that indicates the bind password
336
PasswordParameter bindPWParameter =
337        new PasswordParameter("bindpw", "Bind Password",
338                              "The password for the bind DN", false, "");
339
340   // The parameter that specifies the password for the SSL key store
341
PasswordParameter keyPWParameter =
342     new PasswordParameter("sslkeypw", "SSL Key Store Password",
343                           "The password for the JSSE key store", false, "");
344
345   // The parameter that specifies the password for the SSL key store
346
PasswordParameter trustPWParameter =
347     new PasswordParameter("ssltrustpw", "SSL Trust Store Password",
348                           "The password for the JSSE trust store", false, "");
349
350
351   // Instance variables that correspond to the parameter values
352
static boolean alwaysDisconnect;
353   static boolean blindTrust;
354   static boolean cleanTombstones;
355   static boolean useProxyAuth;
356   static boolean useSSL;
357   static int deleteDelay;
358   static int maxRDNValue;
359   static int minRDNValue;
360   static int ldapPort;
361   static int nextValue;
362   static int timeLimit;
363   static int tombstoneDelay;
364   static int valueLength;
365   static long delay;
366   static String JavaDoc baseDN;
367   static String JavaDoc bindDN;
368   static String JavaDoc bindPassword;
369   static String JavaDoc ldapHost;
370   static String JavaDoc proxyAsDN;
371   static String JavaDoc rdnAttr;
372   static String JavaDoc sslKeyStore;
373   static String JavaDoc sslKeyPassword;
374   static String JavaDoc sslTrustStore;
375   static String JavaDoc sslTrustPassword;
376   static String JavaDoc[] attrsToInclude;
377
378
379   // Variables used for the reference counter that indicate how many threads
380
// are currently processing add operations.
381
static int numActiveAddThreads;
382   static Object JavaDoc addThreadMutex;
383
384
385   // The connection to the directory server over which the adds will be
386
// performed.
387
LDAPConnection conn;
388
389
390   // Variables used for status counters
391
AccumulatingTracker totalAdds;
392   AccumulatingTracker totalDeletes;
393   CategoricalTracker addResultCodes;
394   CategoricalTracker deleteResultCodes;
395   IncrementalTracker addCount;
396   IncrementalTracker deleteCount;
397   TimeTracker addTime;
398   TimeTracker deleteTime;
399
400
401   // One random number generator for use throughout the client and another to
402
// use for only the current thread.
403
static Random parentRandom;
404   Random random;
405
406
407
408
409   /**
410    * The default constructor used to create a new instance of the job thread.
411    * The only thing it should do is to invoke the superclass constructor. All
412    * other initialization should be performed in the <CODE>initialize</CODE>
413    * method.
414    */

415   public CombinedAddAndDelRateJobClass()
416   {
417     super();
418   }
419
420
421
422   /**
423    * Retrieves the name of the job performed by this job thread.
424    *
425    * @return The name of the job performed by this job thread.
426    */

427   public String JavaDoc getJobName()
428   {
429     return "LDAP Add and Delete Rate";
430   }
431
432
433
434   /**
435    * Retrieves a description of the job performed by this job thread.
436    *
437    * @return A description of the job performed by this job thread.
438    */

439   public String JavaDoc getJobDescription()
440   {
441     return "This job can be used to perform repeated add and delete " +
442            "operations against an LDAP directory server to generate load and " +
443            "measure performance";
444   }
445
446
447
448   /**
449    * Retrieves the name of the category in which this job class exists. This is
450    * used to help arrange the job classes in the administrative interface.
451    *
452    * @return The name of the category in which this job class exists.
453    */

454   public String JavaDoc getJobCategoryName()
455   {
456     return "LDAP";
457   }
458
459
460
461   /**
462    * Overrides the number of clients that may be used for this job to ensure
463    * that only a single client will be used.
464    *
465    * @return The number of clients that will always be used for this job.
466    */

467   public int overrideNumClients()
468   {
469     return 1;
470   }
471
472
473
474   /**
475    * Retrieve a parameter list that can be used to determine all of the
476    * customizeable options that are available for this job.
477    *
478    * @return A parameter list that can be used to determine all of the
479    * customizeable options that are available for this job.
480    */

481   public ParameterList getParameterStubs()
482   {
483     Parameter[] parameters = new Parameter[]
484     {
485       placeholder,
486       hostParameter,
487       portParameter,
488       bindDNParameter,
489       bindPWParameter,
490       proxyAsDNParameter,
491       placeholder,
492       baseDNParameter,
493       rdnAttrParameter,
494       rdnStartParameter,
495       rdnEndParameter,
496       lengthParameter,
497       deleteDelayParameter,
498       extraAttrsParameter,
499       placeholder,
500       timeLimitParameter,
501       delayParameter,
502       placeholder,
503       useSSLParameter,
504       blindTrustParameter,
505       keyStoreParameter,
506       keyPWParameter,
507       trustStoreParameter,
508       trustPWParameter,
509       placeholder,
510       disconnectParameter,
511       cleanTombstonesParameter,
512       tombstoneDelayParameter
513     };
514
515     return new ParameterList(parameters);
516   }
517
518
519
520   /**
521    * Retrieves the set of stat trackers that will be maintained by this job
522    * class. The stat trackers returned by this method do not have to actually
523    * contain any statistics -- the display name and stat tracker class should
524    * be the only information that callers of this method should rely upon. Note
525    * that this list can be different from the list of statistics actually
526    * collected by the job in some cases (e.g., if the job may not return all the
527    * stat trackers it advertises in all cases, or if the job may return stat
528    * trackers that it did not advertise), but it is a possibility that only the
529    * stat trackers returned by this method will be accessible for some features
530    * in the SLAMD server.
531    *
532    * @param clientID The client ID that should be used for the
533    * returned stat trackers.
534    * @param threadID The thread ID that should be used for the
535    * returned stat trackers.
536    * @param collectionInterval The collection interval that should be used for
537    * the returned stat trackers.
538    *
539    * @return The set of stat trackers that will be maintained by this job
540    * class.
541    */

542   public StatTracker[] getStatTrackerStubs(String JavaDoc clientID, String JavaDoc threadID,
543                                            int collectionInterval)
544   {
545     return new StatTracker[]
546     {
547       new IncrementalTracker(clientID, threadID, STAT_TRACKER_ADD_COUNT,
548                              collectionInterval),
549       new TimeTracker(clientID, threadID, STAT_TRACKER_ADD_TIME,
550                       collectionInterval),
551       new CategoricalTracker(clientID, threadID, STAT_TRACKER_ADD_RESULT,
552                              collectionInterval),
553       new IncrementalTracker(clientID, threadID, STAT_TRACKER_DEL_COUNT,
554                              collectionInterval),
555       new TimeTracker(clientID, threadID, STAT_TRACKER_DEL_TIME,
556                       collectionInterval),
557       new CategoricalTracker(clientID, threadID, STAT_TRACKER_DEL_RESULT,
558                              collectionInterval)
559     };
560   }
561
562
563
564   /**
565    * Retrieves the stat trackers that are maintained for this job thread.
566    *
567    * @return The stat trackers that are maintained for this job thread.
568    */

569   public StatTracker[] getStatTrackers()
570   {
571     return new StatTracker[]
572     {
573       addCount,
574       addTime,
575       addResultCodes,
576       totalAdds,
577       deleteCount,
578       deleteTime,
579       deleteResultCodes,
580       totalDeletes
581     };
582   }
583
584
585
586   /**
587    * Provides a means of validating the information used to schedule the job,
588    * including the scheduling information and list of parameters.
589    *
590    * @param numClients The number of clients that should be used to
591    * run the job.
592    * @param threadsPerClient The number of threads that should be created on
593    * each client to run the job.
594    * @param threadStartupDelay The delay in milliseconds that should be used
595    * when starting the client threads.
596    * @param startTime The time that the job should start running.
597    * @param stopTime The time that the job should stop running.
598    * @param duration The maximum length of time in seconds that the
599    * job should be allowed to run.
600    * @param collectionInterval The collection interval that should be used
601    * when gathering statistics for the job.
602    * @param parameters The set of parameters provided to this job that
603    * can be used to customize its behavior.
604    *
605    * @throws InvalidValueException If the provided information is not
606    * appropriate for running this job.
607    */

608   public void validateJobInfo(int numClients, int threadsPerClient,
609                               int threadStartupDelay, Date startTime,
610                               Date stopTime, int duration,
611                               int collectionInterval, ParameterList parameters)
612          throws InvalidValueException
613   {
614     if (numClients != 1)
615     {
616       throw new InvalidValueException("The Combined Add and Delete Rate job " +
617                                       "may only run on a single client.");
618     }
619   }
620
621
622
623   /**
624    * Indicates whether this job class implements logic that makes it possible to
625    * test the validity of job parameters before scheduling the job for execution
626    * (e.g., to see if the server is reachable using the information provided).
627    *
628    * @return <CODE>true</CODE> if this job provides a means of testing the job
629    * parameters, or <CODE>false</CODE> if not.
630    */

631   public boolean providesParameterTest()
632   {
633     return true;
634   }
635
636
637
638   /**
639    * Provides a means of testing the provided job parameters to determine
640    * whether they are valid (e.g., to see if the server is reachable) before
641    * scheduling the job for execution. This method will be executed by the
642    * SLAMD server system itself and not by any of the clients.
643    *
644    * @param parameters The job parameters to be tested.
645    * @param outputMessages The lines of output that were generated as part of
646    * the testing process. Each line of output should
647    * be added to this list as a separate string, and
648    * empty strings (but not <CODE>null</CODE> values)
649    * are allowed to provide separation between
650    * different messages. No formatting should be
651    * provided for these messages, however, since they
652    * may be displayed in either an HTML or plain text
653    * interface.
654    *
655    * @return <CODE>true</CODE> if the test completed successfully, or
656    * <CODE>false</CODE> if not. Note that even if the test did not
657    * complete successfully, the user will be presented with a warning
658    * but will still be allowed to schedule the job using the provided
659    * parameters. This is necessary because the parameters may still be
660    * valid even if the server couldn't validate them at the time the
661    * job was scheduled (e.g., if the server wasn't running or could not
662    * be reached by the SLAMD server even though it could be by the
663    * clients).
664    */

665   public boolean testJobParameters(ParameterList parameters,
666                                    ArrayList outputMessages)
667   {
668     // Get all the parameters that we might need to perform the test.
669
StringParameter hostParam =
670          parameters.getStringParameter(hostParameter.getName());
671     if ((hostParam == null) || (! hostParam.hasValue()))
672     {
673       outputMessages.add("ERROR: No directory server address was provided.");
674       return false;
675     }
676     String JavaDoc host = hostParam.getStringValue();
677
678
679     IntegerParameter portParam =
680          parameters.getIntegerParameter(portParameter.getName());
681     if ((portParam == null) || (! hostParam.hasValue()))
682     {
683       outputMessages.add("ERROR: No directory server port was provided.");
684       return false;
685     }
686     int port = portParam.getIntValue();
687
688
689     boolean useSSL = false;
690     BooleanParameter useSSLParam =
691          parameters.getBooleanParameter(useSSLParameter.getName());
692     if (useSSLParam != null)
693     {
694       useSSL = useSSLParam.getBooleanValue();
695     }
696
697
698     boolean blindTrust = true;
699     BooleanParameter blindTrustParam =
700          parameters.getBooleanParameter(blindTrustParameter.getName());
701     if (blindTrustParam != null)
702     {
703       blindTrust = blindTrustParam.getBooleanValue();
704     }
705
706
707     String JavaDoc keyStore = null;
708     StringParameter keyStoreParam =
709          parameters.getStringParameter(keyStoreParameter.getName());
710     if ((keyStoreParam != null) && keyStoreParam.hasValue())
711     {
712       keyStore = keyStoreParam.getStringValue();
713       File keyStoreFile = new File(keyStore);
714       if (useSSL && (! blindTrust) && (! keyStoreFile.exists()))
715       {
716         outputMessages.add("WARNING: Key store file \"" + keyStore +
717                            "\" not found on SLAMD server system. This test " +
718                            "will blindly trust any SSL certificate " +
719                            "presented by the directory server.");
720         outputMessages.add("");
721         blindTrust = true;
722       }
723       else
724       {
725         System.setProperty(SSL_KEY_STORE_PROPERTY, keyStore);
726       }
727     }
728
729
730     String JavaDoc keyStorePassword = "";
731     StringParameter keyPassParam =
732          parameters.getStringParameter(keyPWParameter.getName());
733     if ((keyPassParam != null) && keyPassParam.hasValue())
734     {
735       keyStorePassword = keyPassParam.getStringValue();
736       System.setProperty(SSL_KEY_PASSWORD_PROPERTY, keyStorePassword);
737     }
738
739
740     String JavaDoc trustStore = null;
741     StringParameter trustStoreParam =
742          parameters.getStringParameter(trustStoreParameter.getName());
743     if ((trustStoreParam != null) && trustStoreParam.hasValue())
744     {
745       trustStore = trustStoreParam.getStringValue();
746       File trustStoreFile = new File(trustStore);
747       if (useSSL && (! blindTrust) && (! trustStoreFile.exists()))
748       {
749         outputMessages.add("WARNING: trust store file \"" + trustStore +
750                            "\" not found on SLAMD server system. This test " +
751                            "will blindly trust any SSL certificate " +
752                            "presented by the directory server.");
753         outputMessages.add("");
754         blindTrust = true;
755       }
756       else
757       {
758         System.setProperty(SSL_TRUST_STORE_PROPERTY, trustStore);
759       }
760     }
761
762
763     String JavaDoc trustStorePassword = "";
764     StringParameter trustPassParam =
765          parameters.getStringParameter(trustPWParameter.getName());
766     if ((trustPassParam != null) && trustPassParam.hasValue())
767     {
768       trustStorePassword = trustPassParam.getStringValue();
769       System.setProperty(SSL_TRUST_PASSWORD_PROPERTY, trustStorePassword);
770     }
771
772
773     String JavaDoc bindDN = "";
774     StringParameter bindDNParam =
775          parameters.getStringParameter(bindDNParameter.getName());
776     if ((bindDNParam != null) && bindDNParam.hasValue())
777     {
778       bindDN = bindDNParam.getStringValue();
779     }
780
781
782     String JavaDoc bindPassword = "";
783     PasswordParameter bindPWParam =
784          parameters.getPasswordParameter(bindPWParameter.getName());
785     if ((bindPWParam != null) && bindPWParam.hasValue())
786     {
787       bindPassword = bindPWParam.getStringValue();
788     }
789
790
791     String JavaDoc proxyAsDN = null;
792     StringParameter proxyAsDNParam =
793          parameters.getStringParameter(proxyAsDNParameter.getName());
794     if ((proxyAsDNParam != null) && proxyAsDNParam.hasValue())
795     {
796       proxyAsDN = proxyAsDNParam.getStringValue();
797     }
798
799
800     StringParameter baseDNParam =
801          parameters.getStringParameter(baseDNParameter.getName());
802     if ((baseDNParam == null) || (! baseDNParam.hasValue()))
803     {
804       outputMessages.add("ERROR: No base DN was provided.");
805       return false;
806     }
807     String JavaDoc baseDN = baseDNParam.getStringValue();
808
809
810     // Create the LDAPConnection object that we will use to communicate with the
811
// directory server.
812
LDAPConnection conn;
813     if (useSSL)
814     {
815       if (blindTrust)
816       {
817         try
818         {
819           conn = new LDAPConnection(new JSSEBlindTrustSocketFactory());
820         }
821         catch (Exception JavaDoc e)
822         {
823           outputMessages.add("ERROR: Unable to instantiate the blind trust " +
824                              "socket factory for use in creating the SSL " +
825                              "connection: " + stackTraceToString(e));
826           return false;
827         }
828       }
829       else
830       {
831         conn = new LDAPConnection(new JSSESocketFactory(null));
832       }
833     }
834     else
835     {
836       conn = new LDAPConnection();
837     }
838
839
840     // Attempt to establish a connection to the directory server.
841
try
842     {
843       if (useSSL)
844       {
845         outputMessages.add("Attempting to establish an SSL-based connection " +
846                            "to " + host + ":" + port + "....");
847       }
848       else
849       {
850         outputMessages.add("Attempting to establish a connection to " + host +
851                            ":" + port + "....");
852       }
853       conn.connect(host, port);
854       outputMessages.add("Connected successfully.");
855       outputMessages.add("");
856     }
857     catch (Exception JavaDoc e)
858     {
859       outputMessages.add("ERROR: Unable to connect to the directory " +
860                          "server: " + stackTraceToString(e));
861       return false;
862     }
863
864
865     // Attempt to bind to the directory server using the bind DN and password.
866
try
867     {
868       outputMessages.add("Attempting to perform an LDAPv3 bind to the " +
869                          "directory server with a DN of '" + bindDN + "'....");
870       conn.bind(3, bindDN, bindPassword);
871       outputMessages.add("Bound successfully.");
872       outputMessages.add("");
873     }
874     catch (Exception JavaDoc e)
875     {
876       try
877       {
878         conn.disconnect();
879       } catch (Exception JavaDoc e2) {}
880
881       outputMessages.add("ERROR: Unable to bind to the directory server: " +
882                          stackTraceToString(e));
883       return false;
884     }
885
886
887     // If a proxy user was specified, make sure that it exists.
888
if (proxyAsDN != null)
889     {
890       try
891       {
892         outputMessages.add("Checking to make sure that the proxied user '" +
893                            proxyAsDN + "' exists in the directory....");
894         LDAPEntry proxyUserEntry = conn.read(proxyAsDN, new String JavaDoc[] { "1.1" });
895         if (proxyUserEntry == null)
896         {
897           try
898           {
899             conn.disconnect();
900           } catch (Exception JavaDoc e2) {}
901
902           outputMessages.add("ERROR: Unable to retrieve the proxied user's " +
903                              "entry.");
904           return false;
905         }
906         else
907         {
908           outputMessages.add("Successfully read the proxied user's entry.");
909           outputMessages.add("");
910         }
911       }
912       catch (Exception JavaDoc e)
913       {
914         try
915         {
916           conn.disconnect();
917         } catch (Exception JavaDoc e2) {}
918
919         outputMessages.add("ERROR: Unable to retrieve the proxied user's " +
920                            "entry: " + stackTraceToString(e));
921         return false;
922       }
923     }
924
925
926     // Make sure that the entry specified as the base DN exists.
927
try
928     {
929       outputMessages.add("Checking to make sure that the base DN entry '" +
930                          baseDN + "' exists in the directory....");
931       LDAPEntry baseDNEntry = conn.read(baseDN, new String JavaDoc[] { "1.1" });
932       if (baseDNEntry == null)
933       {
934         try
935         {
936           conn.disconnect();
937         } catch (Exception JavaDoc e2) {}
938
939         outputMessages.add("ERROR: Unable to retrieve the base DN entry.");
940         return false;
941       }
942       else
943       {
944         outputMessages.add("Successfully read the base DN entry.");
945         outputMessages.add("");
946       }
947     }
948     catch (Exception JavaDoc e)
949     {
950       try
951       {
952         conn.disconnect();
953       } catch (Exception JavaDoc e2) {}
954
955       outputMessages.add("ERROR: Unable to retrieve the base DN entry: " +
956                          stackTraceToString(e));
957       return false;
958     }
959
960
961     // At this point, all tests have passed. Close the connection and return
962
// true.
963
try
964     {
965       conn.disconnect();
966     } catch (Exception JavaDoc e) {}
967
968     outputMessages.add("All tests completed successfully.");
969     return true;
970   }
971
972
973
974   /**
975    * Initializes all of the instance variables that correspond to job
976    * parameters.
977    *
978    * @param clientID The client ID for the current client.
979    * @param parameters The set of parameters that have been defined for this
980    * job.
981    *
982    * @throws UnableToRunException If any part of the initialization fails.
983    */

984   public void initializeClient(String JavaDoc clientID, ParameterList parameters)
985          throws UnableToRunException
986   {
987     // Get the address of the target directory server
988
ldapHost = null;
989     hostParameter = parameters.getStringParameter(hostParameter.getName());
990     if (hostParameter != null)
991     {
992       ldapHost = hostParameter.getStringValue();
993     }
994
995     // Get the port for the target directory server
996
ldapPort = 389;
997     portParameter = parameters.getIntegerParameter(portParameter.getName());
998     if (portParameter != null)
999     {
1000      ldapPort = portParameter.getIntValue();
1001    }
1002
1003    // Get the bind DN for the target directory server
1004
bindDN = "";
1005    bindDNParameter = parameters.getStringParameter(bindDNParameter.getName());
1006    if (bindDNParameter != null)
1007    {
1008      bindDN = bindDNParameter.getStringValue();
1009    }
1010
1011    // Get the bind password for the target directory server
1012
bindPassword = "";
1013    bindPWParameter =
1014         parameters.getPasswordParameter(bindPWParameter.getName());
1015    if (bindPWParameter != null)
1016    {
1017      bindPassword = bindPWParameter.getStringValue();
1018    }
1019
1020    // Get the DN of the proxy as user.
1021
useProxyAuth = false;
1022    proxyAsDNParameter =
1023         parameters.getStringParameter(proxyAsDNParameter.getName());
1024    if ((proxyAsDNParameter != null) && (proxyAsDNParameter.hasValue()))
1025    {
1026      useProxyAuth = true;
1027      proxyAsDN = proxyAsDNParameter.getStringValue();
1028    }
1029
1030    // Get the base DN under which to add the entries.
1031
baseDN = null;
1032    baseDNParameter = parameters.getStringParameter(baseDNParameter.getName());
1033    if ((baseDNParameter != null) && (baseDNParameter.hasValue()))
1034    {
1035      baseDN = baseDNParameter.getStringValue();
1036    }
1037
1038    // Get the name of the RDN attribute to use.
1039
rdnAttr = "uid";
1040    rdnAttrParameter =
1041         parameters.getStringParameter(rdnAttrParameter.getName());
1042    if ((rdnAttrParameter != null) && (rdnAttrParameter.hasValue()))
1043    {
1044      rdnAttr = rdnAttrParameter.getStringValue().toLowerCase();
1045    }
1046
1047    // Get the initial RDN value to create.
1048
minRDNValue = 0;
1049    rdnStartParameter =
1050         parameters.getIntegerParameter(rdnStartParameter.getName());
1051    if ((rdnStartParameter != null) && (rdnStartParameter.hasValue()))
1052    {
1053      minRDNValue = rdnStartParameter.getIntValue();
1054    }
1055    nextValue = minRDNValue;
1056
1057    // Get the final RDN value to create.
1058
maxRDNValue = Integer.MAX_VALUE;
1059    rdnEndParameter = parameters.getIntegerParameter(rdnEndParameter.getName());
1060    if ((rdnEndParameter != null) && (rdnEndParameter.hasValue()))
1061    {
1062      maxRDNValue = rdnEndParameter.getIntValue();
1063      if (maxRDNValue <= 0)
1064      {
1065        maxRDNValue = Integer.MAX_VALUE;
1066      }
1067    }
1068
1069    // Get the length to use for the attribute values.
1070
valueLength = 80;
1071    lengthParameter = parameters.getIntegerParameter(lengthParameter.getName());
1072    if ((lengthParameter != null) && (lengthParameter.hasValue()))
1073    {
1074      valueLength = lengthParameter.getIntValue();
1075    }
1076
1077    // Get the extra attributes to include in the entry.
1078
attrsToInclude = DEFAULT_ATTR_NAMES;
1079    extraAttrsParameter =
1080         parameters.getMultiLineTextParameter(extraAttrsParameter.getName());
1081    if ((extraAttrsParameter != null) && (extraAttrsParameter.hasValue()))
1082    {
1083      String JavaDoc[] extraAttrs = extraAttrsParameter.getNonBlankLines();
1084      String JavaDoc[] tmpAttrs = new String JavaDoc[attrsToInclude.length + extraAttrs.length];
1085      System.arraycopy(attrsToInclude, 0, tmpAttrs, 0, attrsToInclude.length);
1086      for (int i=attrsToInclude.length, j=0; i < tmpAttrs.length; i++,j++)
1087      {
1088        tmpAttrs[i] = extraAttrs[j].toLowerCase();
1089      }
1090
1091      attrsToInclude = tmpAttrs;
1092    }
1093
1094    // Get the maximum add time limit.
1095
timeLimit = 0;
1096    timeLimitParameter =
1097         parameters.getIntegerParameter(timeLimitParameter.getName());
1098    if (timeLimitParameter != null)
1099    {
1100      timeLimit = timeLimitParameter.getIntValue();
1101    }
1102
1103    // Get the delay between add and delete operations.
1104
deleteDelay = 0;
1105    deleteDelayParameter =
1106         parameters.getIntegerParameter(deleteDelayParameter.getName());
1107    if (deleteDelayParameter != null)
1108    {
1109      deleteDelay = deleteDelayParameter.getIntValue();
1110    }
1111
1112    // Get the delay between requests.
1113
delay = 0;
1114    delayParameter = parameters.getIntegerParameter(delayParameter.getName());
1115    if (delayParameter != null)
1116    {
1117      delay = delayParameter.getIntValue();
1118    }
1119
1120    // Get the flag indicating whether we should use SSL or not
1121
useSSL = false;
1122    useSSLParameter = parameters.getBooleanParameter(useSSLParameter.getName());
1123    if (useSSLParameter != null)
1124    {
1125      useSSL = useSSLParameter.getBooleanValue();
1126    }
1127
1128    // If we are to use SSL, then get all the other SSL-related info
1129
if (useSSL)
1130    {
1131      // Whether to blindly trust any SSL cert
1132
blindTrustParameter =
1133           parameters.getBooleanParameter(blindTrustParameter.getName());
1134      if (blindTrustParameter != null)
1135      {
1136        blindTrust = blindTrustParameter.getBooleanValue();
1137      }
1138
1139      // The location of the JSSE key store
1140
sslKeyStore = null;
1141      keyStoreParameter =
1142           parameters.getStringParameter(keyStoreParameter.getName());
1143      if ((keyStoreParameter != null) && (keyStoreParameter.hasValue()))
1144      {
1145        sslKeyStore = keyStoreParameter.getStringValue();
1146        System.setProperty(SSL_KEY_STORE_PROPERTY, sslKeyStore);
1147      }
1148
1149      // The JSSE key store password
1150
sslKeyPassword = null;
1151      keyPWParameter =
1152           parameters.getPasswordParameter(keyPWParameter.getName());
1153      if ((keyPWParameter != null) && (keyPWParameter.hasValue()))
1154      {
1155        sslKeyPassword = keyPWParameter.getStringValue();
1156        System.setProperty(SSL_KEY_PASSWORD_PROPERTY, sslKeyPassword);
1157      }
1158
1159      // The location of the JSSE trust store
1160
sslTrustStore = null;
1161      trustStoreParameter =
1162           parameters.getStringParameter(trustStoreParameter.getName());
1163      if ((trustStoreParameter != null) && (trustStoreParameter.hasValue()))
1164      {
1165        sslTrustStore = trustStoreParameter.getStringValue();
1166        System.setProperty(SSL_TRUST_STORE_PROPERTY, sslTrustStore);
1167      }
1168
1169      // The JSSE trust store password
1170
sslTrustPassword = null;
1171      trustPWParameter =
1172           parameters.getPasswordParameter(trustPWParameter.getName());
1173      if ((trustPWParameter != null) && (trustPWParameter.hasValue()))
1174      {
1175        sslTrustPassword = trustPWParameter.getStringValue();
1176        System.setProperty(SSL_TRUST_PASSWORD_PROPERTY, sslTrustPassword);
1177      }
1178    }
1179
1180    // Get the flag indicating whether we should disconnect after each op.
1181
alwaysDisconnect = false;
1182    disconnectParameter =
1183         parameters.getBooleanParameter(disconnectParameter.getName());
1184    if (disconnectParameter != null)
1185    {
1186      alwaysDisconnect = disconnectParameter.getBooleanValue();
1187    }
1188
1189    // Get the flag indicating whether we should clean up tombstones.
1190
cleanTombstones = false;
1191    cleanTombstonesParameter =
1192         parameters.getBooleanParameter(cleanTombstonesParameter.getName());
1193    if (cleanTombstonesParameter != null)
1194    {
1195      cleanTombstones = cleanTombstonesParameter.getBooleanValue();
1196    }
1197
1198    // Get the delay to use before cleaning up tombstones.
1199
tombstoneDelay = 0;
1200    tombstoneDelayParameter =
1201         parameters.getIntegerParameter(tombstoneDelayParameter.getName());
1202    if ((tombstoneDelayParameter != null) &&
1203        tombstoneDelayParameter.hasValue())
1204    {
1205      tombstoneDelay = tombstoneDelayParameter.getIntValue();
1206    }
1207
1208
1209    // Initialize the parent random number generator
1210
parentRandom = new Random();
1211
1212
1213    // Intialize the add count variables.
1214
numActiveAddThreads = 0;
1215    addThreadMutex = new Object JavaDoc();
1216  }
1217
1218
1219
1220  /**
1221   * Initializes this job thread to be used to actually run the job on the
1222   * client. The provided parameter list should be processed to customize the
1223   * behavior of this job thread, and any other initialization that needs to be
1224   * done in order for the job to run should be performed here as well.
1225   *
1226   * @param clientID The client ID for this job thread.
1227   * @param threadID The thread ID for this job thread.
1228   * @param collectionInterval The length of time in seconds to use as the
1229   * statistics collection interval.
1230   * @param parameters The set of parameters provided to this job that
1231   * can be used to customize its behavior.
1232   *
1233   * @throws UnableToRunException If a problem occurs that prevents the thread
1234   * from being able to run properly.
1235   */

1236  public void initializeThread(String JavaDoc clientID, String JavaDoc threadID,
1237                               int collectionInterval, ParameterList parameters)
1238         throws UnableToRunException
1239  {
1240    // Set up the stat trackers
1241
addCount = new IncrementalTracker(clientID, threadID,
1242                                      STAT_TRACKER_ADD_COUNT,
1243                                      collectionInterval);
1244    addTime = new TimeTracker(clientID, threadID, STAT_TRACKER_ADD_TIME,
1245                              collectionInterval);
1246    addResultCodes = new CategoricalTracker(clientID, threadID,
1247                                            STAT_TRACKER_ADD_RESULT,
1248                                            collectionInterval);
1249    totalAdds = new AccumulatingTracker(clientID, threadID,
1250                                        STAT_TRACKER_ADD_TOTAL,
1251                                        collectionInterval);
1252    deleteCount = new IncrementalTracker(clientID, threadID,
1253                                         STAT_TRACKER_DEL_COUNT,
1254                                         collectionInterval);
1255    deleteTime = new TimeTracker(clientID, threadID, STAT_TRACKER_DEL_TIME,
1256                                 collectionInterval);
1257    deleteResultCodes = new CategoricalTracker(clientID, threadID,
1258                                               STAT_TRACKER_DEL_RESULT,
1259                                               collectionInterval);
1260    totalDeletes = new AccumulatingTracker(clientID, threadID,
1261                                           STAT_TRACKER_DEL_TOTAL,
1262                                           collectionInterval);
1263
1264
1265    // Enable real-time reporting of the data for these stat trackers.
1266
RealTimeStatReporter statReporter = getStatReporter();
1267    if (statReporter != null)
1268    {
1269      String JavaDoc jobID = getJobID();
1270      addCount.enableRealTimeStats(statReporter, jobID);
1271      addTime.enableRealTimeStats(statReporter, jobID);
1272      totalAdds.enableRealTimeStats(statReporter, jobID);
1273      deleteCount.enableRealTimeStats(statReporter, jobID);
1274      deleteTime.enableRealTimeStats(statReporter, jobID);
1275      totalDeletes.enableRealTimeStats(statReporter, jobID);
1276    }
1277
1278
1279    // Initialize the random number generator for this thread.
1280
random = new Random(parentRandom.nextLong());
1281
1282
1283    // If the connection is to use SSL, then establish a preliminary connection
1284
// now. The first connection can take a significant amount of time to
1285
// establish, and we want to get it out of the way early before the timer
1286
// starts (if a duration is specified). Don't worry about any exceptions
1287
// that may get thrown here because there's no easy way to report them back
1288
// but they will be repeated and handled when the job starts running anyway,
1289
// so no big deal.
1290
if (useSSL)
1291    {
1292      try
1293      {
1294        LDAPConnection conn;
1295        if (blindTrust)
1296        {
1297          conn = new LDAPConnection(new JSSEBlindTrustSocketFactory());
1298        }
1299        else
1300        {
1301          conn = new LDAPConnection(new JSSESocketFactory(null));
1302        }
1303
1304        conn.connect(3, ldapHost, ldapPort, bindDN, bindPassword);
1305        conn.disconnect();
1306      }
1307      catch (Exception JavaDoc e) {}
1308    }
1309  }
1310
1311
1312
1313  /**
1314   * Perform the work of this job thread by establishing the connection(s) to
1315   * the directory server and issuing all the appropriate queries. The job will
1316   * continue until the specified number of iterations have been performed, the
1317   * stop time has been reached, the maximum duration has been reached, or the
1318   * SLAMD server indicates that a stop has been requested.
1319   */

1320  public void runJob()
1321  {
1322    // Set a variable that we can use to determine if the connection is alive or
1323
// not
1324
boolean connected = false;
1325
1326    // Set a variable that should be used to determine whether all the entries
1327
// have been added yet or not.
1328
boolean allAdded = false;
1329
1330    // Indicates whether an error has occurred that will prevent the job from
1331
// continuing.
1332
boolean errorOccurred = false;
1333
1334    // Set a variable that can be used to determine how long we should sleep
1335
// between adds.
1336
long addStartTime = 0;
1337
1338    // Create a variable that we will use for the LDAP connection
1339
if (useSSL)
1340    {
1341      if (blindTrust)
1342      {
1343        try
1344        {
1345          conn = new LDAPConnection(new JSSEBlindTrustSocketFactory());
1346        }
1347        catch (LDAPException le)
1348        {
1349          logMessage(le.getMessage());
1350          indicateStoppedDueToError();
1351          return;
1352        }
1353      }
1354      else
1355      {
1356        conn = new LDAPConnection(new JSSESocketFactory(null));
1357      }
1358    }
1359    else
1360    {
1361      conn = new LDAPConnection();
1362    }
1363
1364
1365    // Increment the add reference counter.
1366
synchronized (addThreadMutex)
1367    {
1368      numActiveAddThreads++;
1369    }
1370
1371
1372    // Start the stat trackers for the add tests.
1373
addCount.startTracker();
1374    addTime.startTracker();
1375    addResultCodes.startTracker();
1376    totalAdds.startTracker();
1377
1378
1379    // Create a loop that will run until it needs to stop
1380
while ((! shouldStop()) && (! allAdded))
1381    {
1382      // If the connection is currently not connected, then establish it
1383
if (! connected)
1384      {
1385        try
1386        {
1387          conn.connect(3, ldapHost, ldapPort, bindDN, bindPassword);
1388          connected = true;
1389        }
1390        catch (LDAPException le)
1391        {
1392          logMessage("ERROR -- Could not connect to " + ldapHost + ":" +
1393                           ldapPort + " (" + le + ") -- aborting thread");
1394          addResultCodes.increment(String.valueOf(le.getLDAPResultCode()));
1395          indicateStoppedDueToError();
1396          errorOccurred = true;
1397          break;
1398        }
1399      }
1400
1401      LDAPConstraints constraints = conn.getConstraints();
1402      if (useProxyAuth)
1403      {
1404        LDAPProxiedAuthControl proxyAuthControl =
1405             new LDAPProxiedAuthControl(proxyAsDN, true);
1406        constraints.setServerControls(proxyAuthControl);
1407      }
1408      constraints.setTimeLimit(1000 * timeLimit);
1409
1410
1411      LDAPEntry entryToAdd = createEntry();
1412      if (entryToAdd == null)
1413      {
1414        allAdded = true;
1415      }
1416      else
1417      {
1418        // Record the current time as the start of the add.
1419
addTime.startTimer();
1420        if (delay > 0)
1421        {
1422          addStartTime = System.currentTimeMillis();
1423        }
1424
1425        // Perform the add
1426
int resultCode = LDAPException.SUCCESS;
1427        try
1428        {
1429          conn.add(entryToAdd, constraints);
1430        }
1431        catch (LDAPException le)
1432        {
1433          resultCode = le.getLDAPResultCode();
1434        }
1435
1436
1437        // Record the current time as the end of the add.
1438
addCount.increment();
1439        totalAdds.increment();
1440        addTime.stopTimer();
1441        addResultCodes.increment(String.valueOf(resultCode));
1442      }
1443
1444      // If the connection should be broken, then do so
1445
if (alwaysDisconnect)
1446      {
1447        try
1448        {
1449          conn.disconnect();
1450        } catch (LDAPException le) {}
1451        connected = false;
1452      }
1453
1454      // If we need to sleep, then do so
1455
if ((delay > 0) && (! shouldStop()))
1456      {
1457        long now = System.currentTimeMillis();
1458        long sleepTime = delay - (now - addStartTime);
1459        if (sleepTime > 0)
1460        {
1461          try
1462          {
1463            Thread.sleep(sleepTime);
1464          } catch (InterruptedException JavaDoc ie) {}
1465        }
1466      }
1467    }
1468
1469
1470    // Stop all the stat trackers for the add operations.
1471
addCount.stopTracker();
1472    addTime.stopTracker();
1473    addResultCodes.stopTracker();
1474    totalAdds.stopTracker();
1475
1476
1477    // If an error has occurred or we need to stop, then do so now.
1478
if (errorOccurred || shouldStop())
1479    {
1480      // If the connection is still established, then close it
1481
try
1482      {
1483        conn.disconnect();
1484      } catch (LDAPException le) {}
1485
1486
1487      // Decrement the add reference counter
1488
synchronized (addThreadMutex)
1489      {
1490        numActiveAddThreads--;
1491      }
1492
1493      return;
1494    }
1495
1496
1497    // Decrement the add reference counter. If this is the last thread to
1498
// finish with the adds, then also sleep if necessary.
1499
synchronized (addThreadMutex)
1500    {
1501      if (numActiveAddThreads > 1)
1502      {
1503        numActiveAddThreads--;
1504      }
1505      else
1506      {
1507        try
1508        {
1509          Thread.sleep(deleteDelay * 1000);
1510        } catch (InterruptedException JavaDoc ie) {}
1511
1512        // Reset the next value to the minimum so we can start deleting.
1513
nextValue = minRDNValue;
1514
1515        // At this point, signal to all the other threads that the deletes can
1516
// start now.
1517
numActiveAddThreads--;
1518      }
1519    }
1520
1521
1522    // Wait for the other threads to finish their adds.
1523
while ((numActiveAddThreads > 0) && (! shouldStop()))
1524    {
1525      try
1526      {
1527        Thread.sleep(10);
1528      } catch (InterruptedException JavaDoc ie) {}
1529    }
1530
1531
1532    // If we have been requested to stop, then do so.
1533
if (shouldStop())
1534    {
1535      return;
1536    }
1537
1538
1539    // Reset the "allAdded" counter because we'll use it again for deletes.
1540
allAdded = false;
1541
1542
1543    // Start all the delete trackers.
1544
deleteCount.startTracker();
1545    deleteTime.startTracker();
1546    deleteResultCodes.startTracker();
1547    totalDeletes.startTracker();
1548
1549
1550    // Create another loop that will run until it needs to stop
1551
while ((! shouldStop()) && (! allAdded))
1552    {
1553      // If the connection is currently not connected, then establish it
1554
if (! connected)
1555      {
1556        try
1557        {
1558          conn.connect(3, ldapHost, ldapPort, bindDN, bindPassword);
1559          connected = true;
1560        }
1561        catch (LDAPException le)
1562        {
1563          logMessage("ERROR -- Could not connect to " + ldapHost + ":" +
1564                           ldapPort + " (" + le + ") -- aborting thread");
1565          deleteResultCodes.increment(String.valueOf(le.getLDAPResultCode()));
1566          indicateStoppedDueToError();
1567          errorOccurred = true;
1568          break;
1569        }
1570      }
1571
1572      LDAPConstraints constraints = conn.getConstraints();
1573      if (useProxyAuth)
1574      {
1575        LDAPProxiedAuthControl proxyAuthControl =
1576             new LDAPProxiedAuthControl(proxyAsDN, true);
1577        constraints.setServerControls(proxyAuthControl);
1578      }
1579      constraints.setTimeLimit(1000 * timeLimit);
1580
1581
1582      String JavaDoc dnToDelete = getDNToDelete();
1583      if (dnToDelete == null)
1584      {
1585        allAdded = true;
1586      }
1587      else
1588      {
1589        // Record the current time as the start of the delete.
1590
deleteTime.startTimer();
1591        if (delay > 0)
1592        {
1593          addStartTime = System.currentTimeMillis();
1594        }
1595
1596        // Perform the add
1597
int resultCode = LDAPException.SUCCESS;
1598        try
1599        {
1600          conn.delete(dnToDelete, constraints);
1601        }
1602        catch (LDAPException le)
1603        {
1604          resultCode = le.getLDAPResultCode();
1605        }
1606
1607
1608        // Record the current time as the end of the delete.
1609
deleteCount.increment();
1610        totalDeletes.increment();
1611        deleteTime.stopTimer();
1612        deleteResultCodes.increment(String.valueOf(resultCode));
1613      }
1614
1615      // If the connection should be broken, then do so
1616
if (alwaysDisconnect)
1617      {
1618        try
1619        {
1620          conn.disconnect();
1621        } catch (LDAPException le) {}
1622        connected = false;
1623      }
1624
1625      // If we need to sleep, then do so
1626
if ((delay > 0) && (! shouldStop()))
1627      {
1628        long now = System.currentTimeMillis();
1629        long sleepTime = delay - (now - addStartTime);
1630        if (sleepTime > 0)
1631        {
1632          try
1633          {
1634            Thread.sleep(sleepTime);
1635          } catch (InterruptedException JavaDoc ie) {}
1636        }
1637      }
1638    }
1639
1640
1641    // Stop all the delete trackers.
1642
deleteCount.stopTracker();
1643    deleteTime.stopTracker();
1644    deleteResultCodes.stopTracker();
1645    totalDeletes.stopTracker();
1646
1647
1648    // If the connection is still established, then close it
1649
try
1650    {
1651      conn.disconnect();
1652    } catch (LDAPException le) {}
1653  }
1654
1655
1656
1657  /**
1658   * Attempts to force this thread to exit by closing the connection to the
1659   * directory server and setting it to <CODE>null</CODE>.
1660   */

1661  public void destroy()
1662  {
1663    if (conn != null)
1664    {
1665      try
1666      {
1667        conn.disconnect();
1668      } catch (Exception JavaDoc e) {}
1669
1670      conn = null;
1671    }
1672  }
1673
1674
1675
1676  /**
1677   * Performs any per-client finalization that should be done for this job. In
1678   * this case, it checks to see if we need to clean up any tombstones that
1679   * may still exist in the server.
1680   */

1681  public void finalizeClient()
1682  {
1683    if (! cleanTombstones)
1684    {
1685      return;
1686    }
1687
1688
1689    // Sleep for the required length of time.
1690
if (tombstoneDelay > 0)
1691    {
1692      try
1693      {
1694        Thread.sleep(1000 * tombstoneDelay);
1695      } catch (InterruptedException JavaDoc ie) {}
1696    }
1697
1698
1699    // Establish the connection to the server.
1700
if (useSSL)
1701    {
1702      if (blindTrust)
1703      {
1704        try
1705        {
1706          conn = new LDAPConnection(new JSSEBlindTrustSocketFactory());
1707        }
1708        catch (LDAPException le)
1709        {
1710          logMessage("Unable to create LDAP connection to clean up " +
1711                     "tombstones -- " + le.getMessage());
1712          indicateCompletedWithErrors();
1713          return;
1714        }
1715      }
1716      else
1717      {
1718        conn = new LDAPConnection(new JSSESocketFactory(null));
1719      }
1720    }
1721    else
1722    {
1723      conn = new LDAPConnection();
1724    }
1725
1726    try
1727    {
1728      conn.connect(3, ldapHost, ldapPort, bindDN, bindPassword);
1729    }
1730    catch (LDAPException le)
1731    {
1732      logMessage("Unable to connect to server to clean up tombstones -- " +
1733                 le.getMessage());
1734      indicateCompletedWithErrors();
1735      return;
1736    }
1737
1738
1739    // Perform a search to find the tombstone entries.
1740
LDAPSearchResults results = null;
1741    try
1742    {
1743      results = conn.search(baseDN, LDAPConnection.SCOPE_SUB,
1744                            "(&(objectClass=nsTombstone)(!(nsds50RUV=*)))",
1745                            new String JavaDoc[] { "1.1" }, false);
1746    }
1747    catch (LDAPException le)
1748    {
1749      logMessage("Unable to search for tombstone entries -- " +
1750                 le.getMessage());
1751      indicateCompletedWithErrors();
1752
1753      try
1754      {
1755        conn.disconnect();
1756      } catch (Exception JavaDoc e) {}
1757
1758      return;
1759    }
1760
1761
1762    // Iterate through each matching entry and try to delete it.
1763
while (results.hasMoreElements())
1764    {
1765      Object JavaDoc element = results.nextElement();
1766      if (element instanceof LDAPEntry)
1767      {
1768        String JavaDoc entryDN = ((LDAPEntry) element).getDN();
1769        try
1770        {
1771          conn.delete(entryDN);
1772        } catch (LDAPException le) {}
1773      }
1774    }
1775
1776
1777    // Close the connection to the server.
1778
try
1779    {
1780      conn.disconnect();
1781    } catch (Exception JavaDoc e) {}
1782  }
1783
1784
1785
1786  /**
1787   * Creates a randomly-generated LDAP entry to be added to the directory.
1788   *
1789   * @return The randomly-generated entry.
1790   */

1791  public LDAPEntry createEntry()
1792  {
1793    int nextRDNValue = nextValue++;
1794    if (nextRDNValue > maxRDNValue)
1795    {
1796      return null;
1797    }
1798
1799    LDAPAttribute[] attrs = new LDAPAttribute[attrsToInclude.length + 1];
1800    attrs[0] = new LDAPAttribute("objectClass", OBJECTCLASS_VALUES);
1801    for (int i=0; i < attrsToInclude.length; i++)
1802    {
1803      int colonPos;
1804      if (attrsToInclude[i].equals(rdnAttr))
1805      {
1806        attrs[i+1] = new LDAPAttribute(attrsToInclude[i],
1807                                       String.valueOf(nextRDNValue));
1808      }
1809      else if ((colonPos = attrsToInclude[i].indexOf(':')) > 0)
1810      {
1811        attrs[i+1] = new LDAPAttribute(attrsToInclude[i].substring(0, colonPos),
1812                              attrsToInclude[i].substring(colonPos+1).trim());
1813
1814      }
1815      else
1816      {
1817        attrs[i+1] = new LDAPAttribute(attrsToInclude[i],
1818                                       getRandomString(valueLength));
1819      }
1820    }
1821
1822    return new LDAPEntry(rdnAttr + "=" + nextRDNValue + "," + baseDN,
1823                         new LDAPAttributeSet(attrs));
1824  }
1825
1826
1827
1828  /**
1829   * Retrieves the DN of the next entry to be deleted.
1830   *
1831   * @return The DN of the entry to be deleted, or <CODE>null</CODE> if all
1832   * entries have been processed.
1833   */

1834  public String JavaDoc getDNToDelete()
1835  {
1836    int nextRDNValue = nextValue++;
1837    if (nextRDNValue > maxRDNValue)
1838    {
1839      return null;
1840    }
1841
1842    return (rdnAttr + "=" + nextRDNValue + "," + baseDN);
1843  }
1844
1845
1846
1847  /**
1848   * Retrieves a string containing the specified number of randomly-chosen
1849   * characters.
1850   *
1851   * @param length The number of characters to include in the string.
1852   *
1853   * @return A string containing the specified number of randomly-chosen
1854   * characters.
1855   */

1856  public String JavaDoc getRandomString(int length)
1857  {
1858    char[] returnChars = new char[length];
1859
1860    for (int i=0; i < length; i++)
1861    {
1862      returnChars[i] = ALPHABET[Math.abs((random.nextInt()) & 0x7FFFFFFF) %
1863                                ALPHABET.length];
1864    }
1865
1866    return new String JavaDoc(returnChars);
1867  }
1868}
1869
1870
Popular Tags