KickJava   Java API By Example, From Geeks To Geeks.

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


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.util.*;
21 import netscape.ldap.*;
22 import netscape.ldap.controls.*;
23 import com.sun.slamd.job.*;
24 import com.sun.slamd.parameter.*;
25 import com.sun.slamd.stat.*;
26
27
28
29 /**
30  * This class implements a SLAMD job class for performing repeated add
31  * operations against an LDAP directory server. All of the configuration for
32  * this job thread can be provided through parameters.
33  *
34  *
35  * @author Neil A. Wilson
36  */

37 public class CombinedAddAndDelRateWithReplicaLatencyJobClass
38        extends ReplicaLatencyCheckJobClass
39 {
40   /**
41    * The display name for the stat tracker that will be used to track the time
42    * required to perform each add.
43    */

44   public static final String JavaDoc STAT_TRACKER_ADD_TIME = "Add Time (ms)";
45
46
47
48   /**
49    * The display name for the stat tracker that will be used to track the number
50    * of adds performed.
51    */

52   public static final String JavaDoc STAT_TRACKER_ADD_COUNT = "Adds Performed";
53
54
55
56   /**
57    * The display name for the stat tracker that will be used to track the number
58    * of exceptions caught.
59    */

60   public static final String JavaDoc STAT_TRACKER_ADD_RESULT_CODES = "Add Result Codes";
61
62
63
64   /**
65    * The display name for the stat tracker that will be used to track the time
66    * required to perform each delete.
67    */

68   public static final String JavaDoc STAT_TRACKER_DEL_TIME = "Delete Time (ms)";
69
70
71
72   /**
73    * The display name for the stat tracker that will be used to track the number
74    * of adds performed.
75    */

76   public static final String JavaDoc STAT_TRACKER_DEL_COUNT = "Deletes Performed";
77
78
79
80   /**
81    * The display name for the stat tracker that will be used to track the number
82    * of exceptions caught.
83    */

84   public static final String JavaDoc STAT_TRACKER_DEL_RESULT_CODES =
85        "Delete Result Codes";
86
87
88
89   /**
90    * The display name for the stat tracker that will be used to track the
91    * replication latency.
92    */

93   public static final String JavaDoc STAT_TRACKER_REPLICA_LATENCY =
94        "Replication Latency (ms)";
95
96
97
98   /**
99    * The display name for the stat tracker that will be used to categorize the
100    * replication latencies.
101    */

102   public static final String JavaDoc STAT_TRACKER_CATEGORIZED_LATENCY =
103        "Categorized Latency";
104
105
106
107   /**
108    * The characters that are available for use in the randomly-generated values.
109    */

110   public static final char[] ALPHABET =
111        "abcdefghijklmnopqrstuvwxyz".toCharArray();
112
113
114   /**
115    * The default set of attributes to include in the entries that are generated.
116    */

117   public static final String JavaDoc[] DEFAULT_ATTR_NAMES = new String JavaDoc[]
118   {
119     "givenname",
120     "sn",
121     "cn",
122     "uid",
123     "userpassword",
124     "mail",
125   };
126
127
128
129   /**
130    * The default set of objectclass values to create.
131    */

132   public static final String JavaDoc[] OBJECTCLASS_VALUES = new String JavaDoc[]
133   {
134     "top",
135     "person",
136     "organizationalPerson",
137     "inetOrgPerson",
138     "extensibleObject"
139   };
140
141
142
143   // The parameter that indicates whether the server should clean up any
144
// tombstones that may be left in the directory after performing the deletes.
145
BooleanParameter cleanTombstonesParameter =
146        new BooleanParameter("clean_tombstones", "Clean Up Tombstones",
147                             "Indicates whether the client should clean up "+
148                             "tombstones that may be in the database after " +
149                             "the delete has completed. This should only be " +
150                             "used for improving the reliability of the " +
151                             "testing in a replicated environment and should " +
152                             "never be used in a production server.", false);
153
154   // The parameter that indicates whether to disconnect after each add
155
BooleanParameter disconnectParameter =
156        new BooleanParameter("disconnect", "Always Disconnect",
157                             "Indicates whether to close the connection after " +
158                             "each add", false);
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 add " +
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 an add 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 delay that should be used between the end
174
// of all the add operations and the beginning of all the deletes.
175
IntegerParameter deleteDelayParameter =
176        new IntegerParameter("delete_delay", "Time Between Adds and Deletes (s)",
177                             "Specifies the length of time in seconds that " +
178                             "the job should wait after completing all the " +
179                             "add operations before it starts deleting those " +
180                             "entries.", true, 0, true, 0, false, 0);
181
182   // The parameter that indicates the minimum length of time to sleep between
183
// modifications of the latency check entry.
184
IntegerParameter replicaDelayParameter =
185        new IntegerParameter("latency_delay", "Time Between Latency Checks (ms)",
186                             "Specifies the minimum length of time in " +
187                             "milliseconds that should pass between latency " +
188                             "checks. If a replicated operation takes longer " +
189                             "than this length of time, then there will be no " +
190                             "delay.", true, 0, true, 0, false, 0);
191
192   // The parameter that indicates the length of the generated attribute values.
193
IntegerParameter lengthParameter =
194     new IntegerParameter("value_length", "Generated Value Length",
195                          "Specifies the number of characters that should be " +
196                          "included in the generated values of the attriubtes.",
197                          true, 80, true, 1, false, 0);
198
199   // The parameter that indicates the port number for the master directory
200
IntegerParameter masterPortParameter =
201        new IntegerParameter("masterport", "Master Directory Port",
202                             "The port number for the master directory server",
203                             true, 389, true, 1, true, 65535);
204
205   // The parameter that indicates the port number for the replica directory
206
IntegerParameter replicaPortParameter =
207        new IntegerParameter("replicaport", "Replica Directory Port",
208                             "The port number for the replica directory server",
209                             true, 389, true, 1, true, 65535);
210
211   // The parameter that specifies the starting number for the RDN values.
212
IntegerParameter rdnStartParameter =
213        new IntegerParameter("rdn_start", "Initial RDN Value Number",
214                             "The number to use as the value of the RDN " +
215                             "attribute for the first entry to create", true, 1,
216                             false, 0, false, 0);
217
218   // The parameter that specifies the ending number for the RDN values.
219
IntegerParameter rdnEndParameter =
220        new IntegerParameter("rdn_end", "Final RDN Value Number",
221                             "The number to use as the value of the RDN " +
222                             "attribute for the last entry to create", false, 0,
223                             false, 0, false, 0);
224
225   // The parameter that indicates the maximum time limit for add operations.
226
IntegerParameter timeLimitParameter =
227        new IntegerParameter("time_limit", "Operation Time Limit",
228                             "The maximum length of time in seconds that the " +
229                             "thread should wait for an operation to be " +
230                             "performed before cancelling it and trying " +
231                             "another.", false, 0, true, 0, false, 0);
232
233   // The parameter that indicates the delay to use before cleaning up
234
// tombstones.
235
IntegerParameter tombstoneDelayParameter =
236       new IntegerParameter("tombstone_delay",
237                            "Delay Before Cleaning Tombstones (s)",
238                            "Specifies the length of time in seconds that the " +
239                            "job should wait after completing the deletes " +
240                            "before starting the tombstone cleanup.", true, 30,
241                            true, 0, false, 0);
242
243   // The names of additional attributes to add to the entries that are created.
244
MultiLineTextParameter extraAttrsParameter =
245        new MultiLineTextParameter("extra_attrs", "Additional Attributes",
246                                   "The names of additional attributes to " +
247                                   "include in the entries that will be " +
248                                   "generated. By default, inetOrgPerson " +
249                                   "entries will be created, with attributes " +
250                                   "of givenName, sn, cn, uid, userPassword, " +
251                                   "and mail, but they will also include the " +
252                                   "extensibleObject objectclass so that any " +
253                                   "additional attributes may be used. Each " +
254                                   "attribute specified will be given a " +
255                                   "value of a string of 80 randomly-chosen " +
256                                   "characters.", null, false);
257
258   // The placeholder parameter used as a spacer in the admin interface.
259
PlaceholderParameter placeholder = new PlaceholderParameter();
260
261   // The parameter that specifies the attribute to modify
262
StringParameter attributeParameter =
263        new StringParameter("attribute", "Attribute to Modify",
264                            "The attribute to modify for the latency checks",
265                            true, "description");
266
267   // The parameter that indicates the DN to use when binding to the server
268
StringParameter bindDNParameter =
269        new StringParameter("binddn", "Bind DN",
270                            "The DN to use to bind to the server", false, "");
271
272   // The parameter that indicates the base below which entries will be added.
273
StringParameter baseDNParameter =
274        new StringParameter("basedn", "Base DN ",
275                            "The base below which to add the entries",
276                            true, "");
277
278   // The parameter that indicates the address of the master directory server
279
StringParameter masterHostParameter =
280        new StringParameter("masterhost", "Master Directory Host",
281                            "The DNS hostname or IP address of the master " +
282                            "directory server", true, "");
283
284   // The parameter that indicates the address of the replica directory server
285
StringParameter replicaHostParameter =
286        new StringParameter("replicahost", "Replica Directory Host",
287                            "The DNS hostname or IP address of the replica " +
288                            "directory server", true, "");
289
290   // The parameter that specifies the DN of the entry to watch for replication
291
// latency
292
StringParameter replicaEntryDNParameter =
293        new StringParameter("replica_entry_dn", "Latency Check Entry DN",
294                            "The DN of the entry that should be periodically " +
295                            "modified to measure the latency of replication. " +
296                            "Note that this DN must not be the same as " +
297                            "the DN of any of the entries to modify, nor " +
298                            "should this entry be modified by any external " +
299                            "process during the test.", true, "");
300
301   // The parameter that indicates the DN to use to proxy the adds.
302
StringParameter proxyAsDNParameter =
303        new StringParameter("proxy_as_dn", "Proxy As DN",
304                            "The DN of the user whose credentials should be " +
305                            "used to perform the adds through the use " +
306                            "of the proxied authorization control.", false, "");
307
308   // The parameter that indicates the RDN attribute for the new entries.
309
StringParameter rdnAttrParameter =
310        new StringParameter("rdn_attr", "RDN Attribute",
311                            "The RDN attribute to use when creating the " +
312                            "entries.", true, "uid");
313
314   // The parameter that indicates the bind password
315
PasswordParameter bindPWParameter =
316        new PasswordParameter("bindpw", "Bind Password",
317                              "The password for the bind DN", false, "");
318
319
320   // Instance variables that correspond to the parameter values
321
static boolean alwaysDisconnect;
322   static boolean cleanTombstones;
323   static boolean useProxyAuth;
324   static int deleteDelay;
325   static int latencyDelay;
326   static int maxRDNValue;
327   static int minRDNValue;
328   static int masterPort;
329   static int replicaPort;
330   static int nextValue;
331   static int timeLimit;
332   static int tombstoneDelay;
333   static int valueLength;
334   static long delay;
335   static String JavaDoc attribute;
336   static String JavaDoc baseDN;
337   static String JavaDoc bindDN;
338   static String JavaDoc bindPassword;
339   static String JavaDoc masterHost;
340   static String JavaDoc replicaHost;
341   static String JavaDoc replicaEntryDN;
342   static String JavaDoc proxyAsDN;
343   static String JavaDoc rdnAttr;
344   static String JavaDoc[] attrsToInclude;
345
346
347   // Variables used for the reference counter that indicate how many threads
348
// are currently processing add operations.
349
static int numActiveAddThreads;
350   static Object JavaDoc addThreadMutex;
351
352
353   // The connection to the directory server over which the adds will be
354
// performed.
355
LDAPConnection conn;
356
357
358   // Variables used for status counters
359
CategoricalTracker addResultCodes;
360   CategoricalTracker deleteResultCodes;
361   IncrementalTracker addCount;
362   IncrementalTracker deleteCount;
363   TimeTracker addTime;
364   TimeTracker deleteTime;
365
366
367   // Variables used for tracking replication latency.
368
static boolean latencyTrackerChosen;
369   boolean reportLatencyTracker;
370   LatencyCheckMasterThread masterThread;
371   LatencyCheckReplicaThread replicaThread;
372
373
374   // One random number generator for use throughout the client and another to
375
// use for only the current thread.
376
static Random parentRandom;
377   Random random;
378
379
380
381
382   /**
383    * The default constructor used to create a new instance of the job thread.
384    * The only thing it should do is to invoke the superclass constructor. All
385    * other initialization should be performed in the <CODE>initialize</CODE>
386    * method.
387    */

388   public CombinedAddAndDelRateWithReplicaLatencyJobClass()
389   {
390     super();
391   }
392
393
394
395   /**
396    * Retrieves the name of the job performed by this job thread.
397    *
398    * @return The name of the job performed by this job thread.
399    */

400   public String JavaDoc getJobName()
401   {
402     return "LDAP Add and Delete with Replica Latency";
403   }
404
405
406
407   /**
408    * Retrieves a description of the job performed by this job thread.
409    *
410    * @return A description of the job performed by this job thread.
411    */

412   public String JavaDoc getJobDescription()
413   {
414     return "This job can be used to perform repeated add and delete " +
415            "operations against an LDAP directory server to generate load and " +
416            "measure performance. It also provides the capability to measure " +
417            "the latency associated with replication while the adds are in " +
418            "progress.";
419   }
420
421
422
423   /**
424    * Retrieves the name of the category in which this job class exists. This is
425    * used to help arrange the job classes in the administrative interface.
426    *
427    * @return The name of the category in which this job class exists.
428    */

429   public String JavaDoc getJobCategoryName()
430   {
431     return "LDAP";
432   }
433
434
435
436   /**
437    * Overrides the number of clients that may be used for this job to ensure
438    * that only a single client will be used.
439    *
440    * @return The number of clients that will always be used for this job.
441    */

442   public int overrideNumClients()
443   {
444     return 1;
445   }
446
447
448
449   /**
450    * Retrieve a parameter list that can be used to determine all of the
451    * customizeable options that are available for this job.
452    *
453    * @return A parameter list that can be used to determine all of the
454    * customizeable options that are available for this job.
455    */

456   public ParameterList getParameterStubs()
457   {
458     Parameter[] parameters = new Parameter[]
459     {
460       placeholder,
461       masterHostParameter,
462       masterPortParameter,
463       bindDNParameter,
464       bindPWParameter,
465       proxyAsDNParameter,
466       placeholder,
467       baseDNParameter,
468       rdnAttrParameter,
469       rdnStartParameter,
470       rdnEndParameter,
471       lengthParameter,
472       deleteDelayParameter,
473       extraAttrsParameter,
474       placeholder,
475       replicaHostParameter,
476       replicaPortParameter,
477       replicaEntryDNParameter,
478       replicaDelayParameter,
479       attributeParameter,
480       placeholder,
481       timeLimitParameter,
482       delayParameter,
483       placeholder,
484       disconnectParameter,
485       cleanTombstonesParameter,
486       tombstoneDelayParameter
487     };
488
489     return new ParameterList(parameters);
490   }
491
492
493
494   /**
495    * Retrieves the set of stat trackers that will be maintained by this job
496    * class. The stat trackers returned by this method do not have to actually
497    * contain any statistics -- the display name and stat tracker class should
498    * be the only information that callers of this method should rely upon. Note
499    * that this list can be different from the list of statistics actually
500    * collected by the job in some cases (e.g., if the job may not return all the
501    * stat trackers it advertises in all cases, or if the job may return stat
502    * trackers that it did not advertise), but it is a possibility that only the
503    * stat trackers returned by this method will be accessible for some features
504    * in the SLAMD server.
505    *
506    * @param clientID The client ID that should be used for the
507    * returned stat trackers.
508    * @param threadID The thread ID that should be used for the
509    * returned stat trackers.
510    * @param collectionInterval The collection interval that should be used for
511    * the returned stat trackers.
512    *
513    * @return The set of stat trackers that will be maintained by this job
514    * class.
515    */

516   public StatTracker[] getStatTrackerStubs(String JavaDoc clientID, String JavaDoc threadID,
517                                            int collectionInterval)
518   {
519     return new StatTracker[]
520     {
521       new IncrementalTracker(clientID, threadID, STAT_TRACKER_ADD_COUNT,
522                              collectionInterval),
523       new TimeTracker(clientID, threadID, STAT_TRACKER_ADD_TIME,
524                       collectionInterval),
525       new CategoricalTracker(clientID, threadID, STAT_TRACKER_ADD_RESULT_CODES,
526                              collectionInterval),
527       new IncrementalTracker(clientID, threadID, STAT_TRACKER_DEL_COUNT,
528                              collectionInterval),
529       new TimeTracker(clientID, threadID, STAT_TRACKER_DEL_TIME,
530                       collectionInterval),
531       new CategoricalTracker(clientID, threadID, STAT_TRACKER_DEL_RESULT_CODES,
532                              collectionInterval),
533       new TimeTracker(clientID, threadID, STAT_TRACKER_REPLICA_LATENCY,
534                       collectionInterval),
535       new CategoricalTracker(clientID, threadID,
536                              STAT_TRACKER_CATEGORIZED_LATENCY,
537                              collectionInterval)
538     };
539   }
540
541
542
543   /**
544    * Retrieves the stat trackers that are maintained for this job thread.
545    *
546    * @return The stat trackers that are maintained for this job thread.
547    */

548   public StatTracker[] getStatTrackers()
549   {
550     if (reportLatencyTracker)
551     {
552       return new StatTracker[]
553       {
554         addCount,
555         addTime,
556         addResultCodes,
557         deleteCount,
558         deleteTime,
559         deleteResultCodes,
560         latencyTime,
561         latencyCategories
562       };
563     }
564     else
565     {
566       return new StatTracker[]
567       {
568         addCount,
569         addTime,
570         addResultCodes,
571         deleteCount,
572         deleteTime,
573         deleteResultCodes
574       };
575     }
576   }
577
578
579
580   /**
581    * Provides a means of validating the information used to schedule the job,
582    * including the scheduling information and list of parameters.
583    *
584    * @param numClients The number of clients that should be used to
585    * run the job.
586    * @param threadsPerClient The number of threads that should be created on
587    * each client to run the job.
588    * @param threadStartupDelay The delay in milliseconds that should be used
589    * when starting the client threads.
590    * @param startTime The time that the job should start running.
591    * @param stopTime The time that the job should stop running.
592    * @param duration The maximum length of time in seconds that the
593    * job should be allowed to run.
594    * @param collectionInterval The collection interval that should be used
595    * when gathering statistics for the job.
596    * @param parameters The set of parameters provided to this job that
597    * can be used to customize its behavior.
598    *
599    * @throws InvalidValueException If the provided information is not
600    * appropriate for running this job.
601    */

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

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

659   public boolean testJobParameters(ParameterList parameters,
660                                    ArrayList outputMessages)
661   {
662     // Get all the parameters that we might need to perform the test.
663
StringParameter masterHostParam =
664          parameters.getStringParameter(masterHostParameter.getName());
665     if ((masterHostParam == null) || (! masterHostParam.hasValue()))
666     {
667       outputMessages.add("ERROR: No master directory server address was " +
668                          "provided.");
669       return false;
670     }
671     String JavaDoc masterHost = masterHostParam.getStringValue();
672
673
674     IntegerParameter masterPortParam =
675          parameters.getIntegerParameter(masterPortParameter.getName());
676     if ((masterPortParam == null) || (! masterPortParam.hasValue()))
677     {
678       outputMessages.add("ERROR: No master directory server port was " +
679                          "provided.");
680       return false;
681     }
682     int masterPort = masterPortParam.getIntValue();
683
684
685     StringParameter replicaHostParam =
686          parameters.getStringParameter(replicaHostParameter.getName());
687     if ((replicaHostParam == null) || (! replicaHostParam.hasValue()))
688     {
689       outputMessages.add("ERROR: No replica directory server address was " +
690                          "provided.");
691       return false;
692     }
693     String JavaDoc replicaHost = replicaHostParam.getStringValue();
694
695
696     IntegerParameter replicaPortParam =
697          parameters.getIntegerParameter(replicaPortParameter.getName());
698     if ((replicaPortParam == null) || (! replicaPortParam.hasValue()))
699     {
700       outputMessages.add("ERROR: No replica directory server port was " +
701                          "provided.");
702       return false;
703     }
704     int replicaPort = replicaPortParam.getIntValue();
705
706
707     String JavaDoc bindDN = "";
708     StringParameter bindDNParam =
709          parameters.getStringParameter(bindDNParameter.getName());
710     if ((bindDNParam != null) && bindDNParam.hasValue())
711     {
712       bindDN = bindDNParam.getStringValue();
713     }
714
715
716     String JavaDoc bindPassword = "";
717     PasswordParameter bindPWParam =
718          parameters.getPasswordParameter(bindPWParameter.getName());
719     if ((bindPWParam != null) && bindPWParam.hasValue())
720     {
721       bindPassword = bindPWParam.getStringValue();
722     }
723
724
725     String JavaDoc proxyAsDN = null;
726     StringParameter proxyAsDNParam =
727          parameters.getStringParameter(proxyAsDNParameter.getName());
728     if ((proxyAsDNParam != null) && proxyAsDNParam.hasValue())
729     {
730       proxyAsDN = proxyAsDNParam.getStringValue();
731     }
732
733
734     StringParameter baseDNParam =
735          parameters.getStringParameter(baseDNParameter.getName());
736     if ((baseDNParam == null) || (! baseDNParam.hasValue()))
737     {
738       outputMessages.add("ERROR: No base DN was provided.");
739       return false;
740     }
741     String JavaDoc baseDN = baseDNParam.getStringValue();
742
743
744     StringParameter replicaEntryDNParam =
745          parameters.getStringParameter(replicaEntryDNParameter.getName());
746     if ((replicaEntryDNParam == null) || (! replicaEntryDNParam.hasValue()))
747     {
748       outputMessages.add("ERROR: No replica check entry DN was provided.");
749       return false;
750     }
751     String JavaDoc replicaEntryDN = replicaEntryDNParam.getStringValue();
752
753
754     // Create the LDAPConnection object that we will use to communicate with the
755
// directory server.
756
LDAPConnection conn = new LDAPConnection();
757
758
759     // Attempt to establish a connection to the master server.
760
try
761     {
762       outputMessages.add("Attempting to establish a connection to master " +
763                          masterHost + ":" + masterPort + "....");
764       conn.connect(masterHost, masterPort);
765       outputMessages.add("Connected successfully.");
766       outputMessages.add("");
767     }
768     catch (Exception JavaDoc e)
769     {
770       outputMessages.add("ERROR: Unable to connect to the master server: " +
771                          stackTraceToString(e));
772       return false;
773     }
774
775
776     // Attempt to bind to the master server using the bind DN and password.
777
try
778     {
779       outputMessages.add("Attempting to perform an LDAPv3 bind to the " +
780                          "master server with a DN of '" + bindDN + "'....");
781       conn.bind(3, bindDN, bindPassword);
782       outputMessages.add("Bound successfully.");
783       outputMessages.add("");
784     }
785     catch (Exception JavaDoc e)
786     {
787       try
788       {
789         conn.disconnect();
790       } catch (Exception JavaDoc e2) {}
791
792       outputMessages.add("ERROR: Unable to bind to the master server: " +
793                          stackTraceToString(e));
794       return false;
795     }
796
797
798     // If a proxy user was specified, make sure that it exists.
799
if (proxyAsDN != null)
800     {
801       try
802       {
803         outputMessages.add("Checking to make sure that the proxied user '" +
804                            proxyAsDN + "' exists in the master....");
805         LDAPEntry proxyUserEntry = conn.read(proxyAsDN, new String JavaDoc[] { "1.1" });
806         if (proxyUserEntry == null)
807         {
808           try
809           {
810             conn.disconnect();
811           } catch (Exception JavaDoc e2) {}
812
813           outputMessages.add("ERROR: Unable to retrieve the proxied user's " +
814                              "entry.");
815           return false;
816         }
817         else
818         {
819           outputMessages.add("Successfully read the proxied user's entry.");
820           outputMessages.add("");
821         }
822       }
823       catch (Exception JavaDoc e)
824       {
825         try
826         {
827           conn.disconnect();
828         } catch (Exception JavaDoc e2) {}
829
830         outputMessages.add("ERROR: Unable to retrieve the proxied user's " +
831                            "entry: " + stackTraceToString(e));
832         return false;
833       }
834     }
835
836
837     // Make sure that the entry specified as the base DN exists.
838
try
839     {
840       outputMessages.add("Checking to make sure that the base DN entry '" +
841                          baseDN + "' exists in the master....");
842       LDAPEntry baseDNEntry = conn.read(baseDN, new String JavaDoc[] { "1.1" });
843       if (baseDNEntry == null)
844       {
845         try
846         {
847           conn.disconnect();
848         } catch (Exception JavaDoc e2) {}
849
850         outputMessages.add("ERROR: Unable to retrieve the base DN entry.");
851         return false;
852       }
853       else
854       {
855         outputMessages.add("Successfully read the base DN entry.");
856         outputMessages.add("");
857       }
858     }
859     catch (Exception JavaDoc e)
860     {
861       try
862       {
863         conn.disconnect();
864       } catch (Exception JavaDoc e2) {}
865
866       outputMessages.add("ERROR: Unable to retrieve the base DN entry: " +
867                          stackTraceToString(e));
868       return false;
869     }
870
871
872     // Make sure that the entry specified as the replica check entry exists.
873
try
874     {
875       outputMessages.add("Checking to make sure that the replica check " +
876                          "entry '" + replicaEntryDN +
877                          "' exists in the master....");
878       LDAPEntry baseDNEntry = conn.read(replicaEntryDN, new String JavaDoc[] { "1.1" });
879       if (baseDNEntry == null)
880       {
881         try
882         {
883           conn.disconnect();
884         } catch (Exception JavaDoc e2) {}
885
886         outputMessages.add("ERROR: Unable to retrieve the replica check " +
887                            "entry.");
888         return false;
889       }
890       else
891       {
892         outputMessages.add("Successfully read the replica check entry.");
893         outputMessages.add("");
894       }
895     }
896     catch (Exception JavaDoc e)
897     {
898       try
899       {
900         conn.disconnect();
901       } catch (Exception JavaDoc e2) {}
902
903       outputMessages.add("ERROR: Unable to retrieve the replica check " +
904                          "entry: " + stackTraceToString(e));
905       return false;
906     }
907
908
909     // At this point, all tests on the master have passed. Close the connection
910
// and prepare to start on the replica.
911
try
912     {
913       conn.disconnect();
914     } catch (Exception JavaDoc e) {}
915
916
917     // Attempt to establish a connection to the replica server.
918
try
919     {
920       outputMessages.add("Attempting to establish a connection to replica " +
921                          replicaHost + ":" + replicaPort + "....");
922       conn.connect(replicaHost, replicaPort);
923       outputMessages.add("Connected successfully.");
924       outputMessages.add("");
925     }
926     catch (Exception JavaDoc e)
927     {
928       outputMessages.add("ERROR: Unable to connect to the replica server: " +
929                          stackTraceToString(e));
930       return false;
931     }
932
933
934     // Attempt to bind to the replica server using the bind DN and password.
935
try
936     {
937       outputMessages.add("Attempting to perform an LDAPv3 bind to the " +
938                          "replica server with a DN of '" + bindDN + "'....");
939       conn.bind(3, bindDN, bindPassword);
940       outputMessages.add("Bound successfully.");
941       outputMessages.add("");
942     }
943     catch (Exception JavaDoc e)
944     {
945       try
946       {
947         conn.disconnect();
948       } catch (Exception JavaDoc e2) {}
949
950       outputMessages.add("ERROR: Unable to bind to the replica server: " +
951                          stackTraceToString(e));
952       return false;
953     }
954
955
956     // Make sure that the entry specified as the base DN exists.
957
try
958     {
959       outputMessages.add("Checking to make sure that the base DN entry '" +
960                          baseDN + "' exists in the replica....");
961       LDAPEntry baseDNEntry = conn.read(baseDN, new String JavaDoc[] { "1.1" });
962       if (baseDNEntry == null)
963       {
964         try
965         {
966           conn.disconnect();
967         } catch (Exception JavaDoc e2) {}
968
969         outputMessages.add("ERROR: Unable to retrieve the base DN entry.");
970         return false;
971       }
972       else
973       {
974         outputMessages.add("Successfully read the base DN entry.");
975         outputMessages.add("");
976       }
977     }
978     catch (Exception JavaDoc e)
979     {
980       try
981       {
982         conn.disconnect();
983       } catch (Exception JavaDoc e2) {}
984
985       outputMessages.add("ERROR: Unable to retrieve the base DN entry: " +
986                          stackTraceToString(e));
987       return false;
988     }
989
990
991     // Make sure that the entry specified as the replica check entry exists.
992
try
993     {
994       outputMessages.add("Checking to make sure that the replica check " +
995                          "entry '" + replicaEntryDN +
996                          "' exists in the replica....");
997       LDAPEntry baseDNEntry = conn.read(replicaEntryDN, new String JavaDoc[] { "1.1" });
998       if (baseDNEntry == null)
999       {
1000        try
1001        {
1002          conn.disconnect();
1003        } catch (Exception JavaDoc e2) {}
1004
1005        outputMessages.add("ERROR: Unable to retrieve the replica check " +
1006                           "entry.");
1007        return false;
1008      }
1009      else
1010      {
1011        outputMessages.add("Successfully read the replica check entry.");
1012        outputMessages.add("");
1013      }
1014    }
1015    catch (Exception JavaDoc e)
1016    {
1017      try
1018      {
1019        conn.disconnect();
1020      } catch (Exception JavaDoc e2) {}
1021
1022      outputMessages.add("ERROR: Unable to retrieve the replica check " +
1023                         "entry: " + stackTraceToString(e));
1024      return false;
1025    }
1026
1027
1028    // At this point, all tests on the replica have passed. Close the
1029
// connection and return true.
1030
try
1031    {
1032      conn.disconnect();
1033    } catch (Exception JavaDoc e) {}
1034
1035
1036    outputMessages.add("All tests completed successfully.");
1037    return true;
1038  }
1039
1040
1041
1042  /**
1043   * Initializes all of the instance variables that correspond to job
1044   * parameters.
1045   *
1046   * @param clientID The client ID for the current client.
1047   * @param parameters The set of parameters that have been defined for this
1048   * job.
1049   *
1050   * @throws UnableToRunException If any part of the initialization fails.
1051   */

1052  public void initializeClient(String JavaDoc clientID, ParameterList parameters)
1053         throws UnableToRunException
1054  {
1055    // Initialize the latency check mutex and indicate that no latency tracking
1056
// thread has yet been chosen.
1057
latencyTrackerChosen = false;
1058
1059
1060    // Get the address of the master directory server
1061
masterHost = null;
1062    masterHostParameter =
1063         parameters.getStringParameter(masterHostParameter.getName());
1064    if (masterHostParameter != null)
1065    {
1066      masterHost = masterHostParameter.getStringValue();
1067    }
1068
1069    // Get the port for the master directory server
1070
masterPort = 389;
1071    masterPortParameter =
1072         parameters.getIntegerParameter(masterPortParameter.getName());
1073    if (masterPortParameter != null)
1074    {
1075      masterPort = masterPortParameter.getIntValue();
1076    }
1077
1078    // Get the address of the replica directory server
1079
replicaHost = null;
1080    replicaHostParameter =
1081         parameters.getStringParameter(replicaHostParameter.getName());
1082    if (replicaHostParameter != null)
1083    {
1084      replicaHost = replicaHostParameter.getStringValue();
1085    }
1086
1087    // Get the port for the replica directory server
1088
replicaPort = 389;
1089    replicaPortParameter =
1090         parameters.getIntegerParameter(replicaPortParameter.getName());
1091    if (replicaPortParameter != null)
1092    {
1093      replicaPort = replicaPortParameter.getIntValue();
1094    }
1095
1096    // Get the bind DN for the target directory server
1097
bindDN = "";
1098    bindDNParameter = parameters.getStringParameter(bindDNParameter.getName());
1099    if (bindDNParameter != null)
1100    {
1101      bindDN = bindDNParameter.getStringValue();
1102    }
1103
1104    // Get the bind password for the target directory server
1105
bindPassword = "";
1106    bindPWParameter =
1107         parameters.getPasswordParameter(bindPWParameter.getName());
1108    if (bindPWParameter != null)
1109    {
1110      bindPassword = bindPWParameter.getStringValue();
1111    }
1112
1113    // Get the DN of the proxy as user.
1114
useProxyAuth = false;
1115    proxyAsDNParameter =
1116         parameters.getStringParameter(proxyAsDNParameter.getName());
1117    if ((proxyAsDNParameter != null) && (proxyAsDNParameter.hasValue()))
1118    {
1119      useProxyAuth = true;
1120      proxyAsDN = proxyAsDNParameter.getStringValue();
1121    }
1122
1123    // Get the base DN under which to add the entries.
1124
baseDN = null;
1125    baseDNParameter = parameters.getStringParameter(baseDNParameter.getName());
1126    if ((baseDNParameter != null) && (baseDNParameter.hasValue()))
1127    {
1128      baseDN = baseDNParameter.getStringValue();
1129    }
1130
1131    // Get the name of the RDN attribute to use.
1132
rdnAttr = "uid";
1133    rdnAttrParameter =
1134         parameters.getStringParameter(rdnAttrParameter.getName());
1135    if ((rdnAttrParameter != null) && (rdnAttrParameter.hasValue()))
1136    {
1137      rdnAttr = rdnAttrParameter.getStringValue().toLowerCase();
1138    }
1139
1140    // Get the initial RDN value to create.
1141
minRDNValue = 0;
1142    rdnStartParameter =
1143         parameters.getIntegerParameter(rdnStartParameter.getName());
1144    if ((rdnStartParameter != null) && (rdnStartParameter.hasValue()))
1145    {
1146      minRDNValue = rdnStartParameter.getIntValue();
1147    }
1148    nextValue = minRDNValue;
1149
1150    // Get the final RDN value to create.
1151
maxRDNValue = Integer.MAX_VALUE;
1152    rdnEndParameter = parameters.getIntegerParameter(rdnEndParameter.getName());
1153    if ((rdnEndParameter != null) && (rdnEndParameter.hasValue()))
1154    {
1155      maxRDNValue = rdnEndParameter.getIntValue();
1156      if (maxRDNValue <= 0)
1157      {
1158        maxRDNValue = Integer.MAX_VALUE;
1159      }
1160    }
1161
1162    // Get the length to use for the attribute values.
1163
valueLength = 80;
1164    lengthParameter = parameters.getIntegerParameter(lengthParameter.getName());
1165    if ((lengthParameter != null) && (lengthParameter.hasValue()))
1166    {
1167      valueLength = lengthParameter.getIntValue();
1168    }
1169
1170    // Get the extra attributes to include in the entry.
1171
attrsToInclude = DEFAULT_ATTR_NAMES;
1172    extraAttrsParameter =
1173         parameters.getMultiLineTextParameter(extraAttrsParameter.getName());
1174    if ((extraAttrsParameter != null) && (extraAttrsParameter.hasValue()))
1175    {
1176      String JavaDoc[] extraAttrs = extraAttrsParameter.getNonBlankLines();
1177      String JavaDoc[] tmpAttrs = new String JavaDoc[attrsToInclude.length + extraAttrs.length];
1178      System.arraycopy(attrsToInclude, 0, tmpAttrs, 0, attrsToInclude.length);
1179      for (int i=attrsToInclude.length, j=0; i < tmpAttrs.length; i++,j++)
1180      {
1181        tmpAttrs[i] = extraAttrs[j].toLowerCase();
1182      }
1183
1184      attrsToInclude = tmpAttrs;
1185    }
1186
1187    // Get the DN of the entry to use in the latency checks.
1188
replicaEntryDN = null;
1189    replicaEntryDNParameter =
1190         parameters.getStringParameter(replicaEntryDNParameter.getName());
1191    if (replicaEntryDNParameter != null)
1192    {
1193      replicaEntryDN = replicaEntryDNParameter.getStringValue();
1194    }
1195
1196    // Get the delay between latency checks.
1197
latencyDelay = 0;
1198    replicaDelayParameter =
1199         parameters.getIntegerParameter(replicaDelayParameter.getName());
1200    if (replicaDelayParameter != null)
1201    {
1202      latencyDelay = replicaDelayParameter.getIntValue();
1203    }
1204
1205    // Get the name of the attribute to modify for the latency checks.
1206
attribute = null;
1207    attributeParameter =
1208         parameters.getStringParameter(attributeParameter.getName());
1209    if (attributeParameter != null)
1210    {
1211      attribute = attributeParameter.getStringValue();
1212    }
1213
1214    // Get the maximum add time limit.
1215
timeLimit = 0;
1216    timeLimitParameter =
1217         parameters.getIntegerParameter(timeLimitParameter.getName());
1218    if (timeLimitParameter != null)
1219    {
1220      timeLimit = timeLimitParameter.getIntValue();
1221    }
1222
1223    // Get the delay between add and delete operations.
1224
deleteDelay = 0;
1225    deleteDelayParameter =
1226         parameters.getIntegerParameter(deleteDelayParameter.getName());
1227    if (deleteDelayParameter != null)
1228    {
1229      deleteDelay = deleteDelayParameter.getIntValue();
1230    }
1231
1232    // Get the delay between requests.
1233
delay = 0;
1234    delayParameter = parameters.getIntegerParameter(delayParameter.getName());
1235    if (delayParameter != null)
1236    {
1237      delay = delayParameter.getIntValue();
1238    }
1239
1240    // Get the flag indicating whether we should disconnect after each op.
1241
alwaysDisconnect = false;
1242    disconnectParameter =
1243         parameters.getBooleanParameter(disconnectParameter.getName());
1244    if (disconnectParameter != null)
1245    {
1246      alwaysDisconnect = disconnectParameter.getBooleanValue();
1247    }
1248
1249    // Get the flag indicating whether we should clean up tombstones.
1250
cleanTombstones = false;
1251    cleanTombstonesParameter =
1252         parameters.getBooleanParameter(cleanTombstonesParameter.getName());
1253    if (cleanTombstonesParameter != null)
1254    {
1255      cleanTombstones = cleanTombstonesParameter.getBooleanValue();
1256    }
1257
1258    // Get the delay to use before cleaning up tombstones.
1259
tombstoneDelay = 0;
1260    tombstoneDelayParameter =
1261         parameters.getIntegerParameter(tombstoneDelayParameter.getName());
1262    if ((tombstoneDelayParameter != null) &&
1263        (tombstoneDelayParameter.hasValue()))
1264    {
1265      tombstoneDelay = tombstoneDelayParameter.getIntValue();
1266    }
1267
1268
1269    // Initialize the parent random number generator
1270
parentRandom = new Random();
1271
1272
1273    // Intialize the add count variables.
1274
numActiveAddThreads = 0;
1275    addThreadMutex = new Object JavaDoc();
1276  }
1277
1278
1279
1280  /**
1281   * Initializes this job thread to be used to actually run the job on the
1282   * client. The provided parameter list should be processed to customize the
1283   * behavior of this job thread, and any other initialization that needs to be
1284   * done in order for the job to run should be performed here as well.
1285   *
1286   * @param clientID The client ID for this job thread.
1287   * @param threadID The thread ID for this job thread.
1288   * @param collectionInterval The length of time in seconds to use as the
1289   * statistics collection interval.
1290   * @param parameters The set of parameters provided to this job that
1291   * can be used to customize its behavior.
1292   *
1293   * @throws UnableToRunException If a problem occurs that prevents the thread
1294   * from being able to run properly.
1295   */

1296  public void initializeThread(String JavaDoc clientID, String JavaDoc threadID,
1297                               int collectionInterval, ParameterList parameters)
1298         throws UnableToRunException
1299  {
1300    // Set up the stat trackers
1301
addCount = new IncrementalTracker(clientID, threadID,
1302                                      STAT_TRACKER_ADD_COUNT,
1303                                      collectionInterval);
1304    addTime = new TimeTracker(clientID, threadID, STAT_TRACKER_ADD_TIME,
1305                              collectionInterval);
1306    addResultCodes = new CategoricalTracker(clientID, threadID,
1307                                            STAT_TRACKER_ADD_RESULT_CODES,
1308                                            collectionInterval);
1309    deleteCount = new IncrementalTracker(clientID, threadID,
1310                                         STAT_TRACKER_DEL_COUNT,
1311                                         collectionInterval);
1312    deleteTime = new TimeTracker(clientID, threadID, STAT_TRACKER_DEL_TIME,
1313                                 collectionInterval);
1314    deleteResultCodes = new CategoricalTracker(clientID, threadID,
1315                                               STAT_TRACKER_DEL_RESULT_CODES,
1316                                               collectionInterval);
1317
1318
1319    // Enable real-time reporting of the data for these stat trackers.
1320
RealTimeStatReporter statReporter = getStatReporter();
1321    if (statReporter != null)
1322    {
1323      String JavaDoc jobID = getJobID();
1324      addCount.enableRealTimeStats(statReporter, jobID);
1325      addTime.enableRealTimeStats(statReporter, jobID);
1326      deleteCount.enableRealTimeStats(statReporter, jobID);
1327      deleteTime.enableRealTimeStats(statReporter, jobID);
1328    }
1329
1330
1331    // Initialize the random number generator for this thread.
1332
random = new Random(parentRandom.nextLong());
1333
1334
1335    // See if this thread should be used to report the replica latency
1336
// information. If so, then start the latency check threads.
1337
if ((! latencyTrackerChosen) && (getClientNumber() == 0))
1338    {
1339      latencyCheckMutex = new Object JavaDoc();
1340      latencyTrackerChosen = true;
1341      reportLatencyTracker = true;
1342
1343      // Create the latency timer stat tracker.
1344
latencyTime = new TimeTracker(clientID, threadID,
1345                                    STAT_TRACKER_REPLICA_LATENCY,
1346                                    collectionInterval);
1347      if (statReporter != null)
1348      {
1349        latencyTime.enableRealTimeStats(statReporter, getJobID());
1350      }
1351
1352      // Create the latency categories stat tracker.
1353
latencyCategories =
1354           new CategoricalTracker(clientID, threadID,
1355                                  STAT_TRACKER_CATEGORIZED_LATENCY,
1356                                  collectionInterval);
1357
1358      // Create the latency thread that will make changes to the master server.
1359
try
1360      {
1361        masterThread =
1362             new LatencyCheckMasterThread(this, masterHost, masterPort, bindDN,
1363                                          bindPassword, replicaEntryDN,
1364                                          attribute, latencyDelay);
1365      }
1366      catch (LDAPException le)
1367      {
1368        throw new UnableToRunException("Could not create the master latency " +
1369                                       "thread: " + le, le);
1370      }
1371
1372      // Create the latency thread that will read changes from the replica.
1373
try
1374      {
1375        replicaThread =
1376             new LatencyCheckReplicaThread(this, replicaHost, replicaPort,
1377                                           bindDN, bindPassword,
1378                                           replicaEntryDN);
1379      }
1380      catch (LDAPException le)
1381      {
1382        throw new UnableToRunException("Could not create the replica latency " +
1383                                       "thread: " + le, le);
1384      }
1385
1386      // Start the latency check threads.
1387
masterThread.start();
1388      replicaThread.start();
1389    }
1390    else
1391    {
1392      reportLatencyTracker = false;
1393    }
1394  }
1395
1396
1397
1398  /**
1399   * Perform the work of this job thread by establishing the connection(s) to
1400   * the directory server and issuing all the appropriate queries. The job will
1401   * continue until the specified number of iterations have been performed, the
1402   * stop time has been reached, the maximum duration has been reached, or the
1403   * SLAMD server indicates that a stop has been requested.
1404   */

1405  public void runJob()
1406  {
1407    // Set a variable that we can use to determine if the connection is alive or
1408
// not
1409
boolean connected = false;
1410
1411    // Set a variable that should be used to determine whether all the entries
1412
// have been added yet or not.
1413
boolean allAdded = false;
1414
1415    // Indicates whether an error has occurred that will prevent the job from
1416
// continuing.
1417
boolean errorOccurred = false;
1418
1419    // Set a variable that can be used to determine how long we should sleep
1420
// between adds.
1421
long addStartTime = 0;
1422
1423    // Create a variable that we will use for the LDAP connection
1424
conn = new LDAPConnection();
1425
1426
1427    // Increment the add reference counter.
1428
synchronized (addThreadMutex)
1429    {
1430      numActiveAddThreads++;
1431    }
1432
1433
1434    // Start the stat trackers for the add tests.
1435
addCount.startTracker();
1436    addTime.startTracker();
1437    addResultCodes.startTracker();
1438
1439    if (reportLatencyTracker)
1440    {
1441      latencyTime.startTracker();
1442      latencyCategories.startTracker();
1443      replicaThread.startChecking();
1444      masterThread.startChecking();
1445    }
1446
1447
1448    // Create a loop that will run until it needs to stop
1449
while ((! shouldStop()) && (! allAdded))
1450    {
1451      // If the connection is currently not connected, then establish it
1452
if (! connected)
1453      {
1454        try
1455        {
1456          conn.connect(3, masterHost, masterPort, bindDN, bindPassword);
1457          connected = true;
1458        }
1459        catch (LDAPException le)
1460        {
1461          logMessage("ERROR -- Could not connect to " + masterHost + ":" +
1462                           masterPort + " (" + le + ") -- aborting thread");
1463          addResultCodes.increment(String.valueOf(le.getLDAPResultCode()));
1464          indicateStoppedDueToError();
1465          errorOccurred = true;
1466          break;
1467        }
1468      }
1469
1470      LDAPConstraints constraints = conn.getConstraints();
1471      if (useProxyAuth)
1472      {
1473        LDAPProxiedAuthControl proxyAuthControl =
1474             new LDAPProxiedAuthControl(proxyAsDN, true);
1475        constraints.setServerControls(proxyAuthControl);
1476      }
1477      constraints.setTimeLimit(1000 * timeLimit);
1478
1479
1480      LDAPEntry entryToAdd = createEntry();
1481      if (entryToAdd == null)
1482      {
1483        allAdded = true;
1484      }
1485      else
1486      {
1487        // Record the current time as the start of the add.
1488
addTime.startTimer();
1489        if (delay > 0)
1490        {
1491          addStartTime = System.currentTimeMillis();
1492        }
1493
1494        // Perform the add
1495
int resultCode = LDAPException.SUCCESS;
1496        try
1497        {
1498          conn.add(entryToAdd, constraints);
1499        }
1500        catch (LDAPException le)
1501        {
1502          resultCode = le.getLDAPResultCode();
1503        }
1504
1505
1506        // Record the current time as the end of the add.
1507
addCount.increment();
1508        addTime.stopTimer();
1509        addResultCodes.increment(String.valueOf(resultCode));
1510      }
1511
1512      // If the connection should be broken, then do so
1513
if (alwaysDisconnect)
1514      {
1515        try
1516        {
1517          conn.disconnect();
1518        } catch (LDAPException le) {}
1519        connected = false;
1520      }
1521
1522      // If we need to sleep, then do so
1523
if ((delay > 0) && (! shouldStop()))
1524      {
1525        long now = System.currentTimeMillis();
1526        long sleepTime = delay - (now - addStartTime);
1527        if (sleepTime > 0)
1528        {
1529          try
1530          {
1531            Thread.sleep(sleepTime);
1532          } catch (InterruptedException JavaDoc ie) {}
1533        }
1534      }
1535    }
1536
1537
1538    // Stop all the stat trackers for the add operations.
1539
addCount.stopTracker();
1540    addTime.stopTracker();
1541    addResultCodes.stopTracker();
1542
1543
1544    // If an error has occurred or we need to stop, then do so now.
1545
if (errorOccurred || shouldStop())
1546    {
1547      // If the connection is still established, then close it
1548
try
1549      {
1550        conn.disconnect();
1551      } catch (LDAPException le) {}
1552
1553
1554      // Decrement the add reference counter
1555
synchronized (addThreadMutex)
1556      {
1557        numActiveAddThreads--;
1558      }
1559
1560      return;
1561    }
1562
1563
1564    // Decrement the add reference counter. If this is the last thread to
1565
// finish with the adds, then also sleep if necessary.
1566
synchronized (addThreadMutex)
1567    {
1568      if (numActiveAddThreads > 1)
1569      {
1570        numActiveAddThreads--;
1571      }
1572      else
1573      {
1574        try
1575        {
1576          Thread.sleep(deleteDelay * 1000);
1577        } catch (InterruptedException JavaDoc ie) {}
1578
1579        // Reset the next value to the minimum so we can start deleting.
1580
nextValue = minRDNValue;
1581
1582        // At this point, signal to all the other threads that the deletes can
1583
// start now.
1584
numActiveAddThreads--;
1585      }
1586    }
1587
1588
1589    // Wait for the other threads to finish their adds.
1590
while ((numActiveAddThreads > 0) && (! shouldStop()))
1591    {
1592      try
1593      {
1594        Thread.sleep(10);
1595      } catch (InterruptedException JavaDoc ie) {}
1596    }
1597
1598
1599    // If we have been requested to stop, then do so.
1600
if (shouldStop())
1601    {
1602      return;
1603    }
1604
1605
1606    // Reset the "allAdded" counter because we'll use it again for deletes.
1607
allAdded = false;
1608
1609
1610    // Start all the delete trackers.
1611
deleteCount.startTracker();
1612    deleteTime.startTracker();
1613    deleteResultCodes.startTracker();
1614
1615
1616    // Create another loop that will run until it needs to stop
1617
while ((! shouldStop()) && (! allAdded))
1618    {
1619      // If the connection is currently not connected, then establish it
1620
if (! connected)
1621      {
1622        try
1623        {
1624          conn.connect(3, masterHost, masterPort, bindDN, bindPassword);
1625          connected = true;
1626        }
1627        catch (LDAPException le)
1628        {
1629          logMessage("ERROR -- Could not connect to " + masterHost + ":" +
1630                           masterPort + " (" + le + ") -- aborting thread");
1631          deleteResultCodes.increment(String.valueOf(le.getLDAPResultCode()));
1632          indicateStoppedDueToError();
1633          errorOccurred = true;
1634          break;
1635        }
1636      }
1637
1638      LDAPConstraints constraints = conn.getConstraints();
1639      if (useProxyAuth)
1640      {
1641        LDAPProxiedAuthControl proxyAuthControl =
1642             new LDAPProxiedAuthControl(proxyAsDN, true);
1643        constraints.setServerControls(proxyAuthControl);
1644      }
1645      constraints.setTimeLimit(1000 * timeLimit);
1646
1647
1648      String JavaDoc dnToDelete = getDNToDelete();
1649      if (dnToDelete == null)
1650      {
1651        allAdded = true;
1652      }
1653      else
1654      {
1655        // Record the current time as the start of the delete.
1656
deleteTime.startTimer();
1657        if (delay > 0)
1658        {
1659          addStartTime = System.currentTimeMillis();
1660        }
1661
1662        // Perform the add
1663
int resultCode = LDAPException.SUCCESS;
1664        try
1665        {
1666          conn.delete(dnToDelete, constraints);
1667        }
1668        catch (LDAPException le)
1669        {
1670          resultCode = le.getLDAPResultCode();
1671        }
1672
1673
1674        // Record the current time as the end of the delete.
1675
deleteCount.increment();
1676        deleteTime.stopTimer();
1677        deleteResultCodes.increment(String.valueOf(resultCode));
1678      }
1679
1680      // If the connection should be broken, then do so
1681
if (alwaysDisconnect)
1682      {
1683        try
1684        {
1685          conn.disconnect();
1686        } catch (LDAPException le) {}
1687        connected = false;
1688      }
1689
1690      // If we need to sleep, then do so
1691
if ((delay > 0) && (! shouldStop()))
1692      {
1693        long now = System.currentTimeMillis();
1694        long sleepTime = delay - (now - addStartTime);
1695        if (sleepTime > 0)
1696        {
1697          try
1698          {
1699            Thread.sleep(sleepTime);
1700          } catch (InterruptedException JavaDoc ie) {}
1701        }
1702      }
1703    }
1704
1705
1706    // Stop all the delete trackers.
1707
deleteCount.stopTracker();
1708    deleteTime.stopTracker();
1709    deleteResultCodes.stopTracker();
1710
1711    if (reportLatencyTracker)
1712    {
1713      latencyTime.stopTracker();
1714      latencyCategories.stopTracker();
1715      replicaThread.stopAndWait();
1716      masterThread.stopAndWait();
1717    }
1718
1719
1720    // If the connection is still established, then close it
1721
try
1722    {
1723      conn.disconnect();
1724    } catch (LDAPException le) {}
1725  }
1726
1727
1728
1729  /**
1730   * Attempts to force this thread to exit by closing the connection to the
1731   * directory server and setting it to <CODE>null</CODE>.
1732   */

1733  public void destroy()
1734  {
1735    if (conn != null)
1736    {
1737      try
1738      {
1739        conn.disconnect();
1740      } catch (Exception JavaDoc e) {}
1741
1742      conn = null;
1743    }
1744
1745    if (masterThread != null)
1746    {
1747      masterThread.masterThread.interrupt();
1748
1749      try
1750      {
1751        masterThread.connection.disconnect();
1752      } catch (Exception JavaDoc e) {}
1753
1754      masterThread.connection = null;
1755      masterThread = null;
1756    }
1757
1758    if (replicaThread != null)
1759    {
1760      replicaThread.replicaThread.interrupt();
1761
1762      try
1763      {
1764        replicaThread.connection.disconnect();
1765      } catch (Exception JavaDoc e) {}
1766
1767      replicaThread.connection = null;
1768      replicaThread = null;
1769    }
1770  }
1771
1772
1773
1774  /**
1775   * Ensures that the master and replica check threads are properly stopped.
1776   */

1777  public void finalizeClient()
1778  {
1779    masterThread.stopAndWait();
1780    replicaThread.stopAndWait();
1781
1782
1783    if (! cleanTombstones)
1784    {
1785      return;
1786    }
1787
1788
1789    // Sleep for the required length of time.
1790
if (tombstoneDelay > 0)
1791    {
1792      try
1793      {
1794        Thread.sleep(1000 * tombstoneDelay);
1795      } catch (InterruptedException JavaDoc ie) {}
1796    }
1797
1798
1799    // Establish the connection to the server.
1800
try
1801    {
1802      conn = new LDAPConnection();
1803      conn.connect(3, masterHost, masterPort, bindDN, bindPassword);
1804    }
1805    catch (LDAPException le)
1806    {
1807      logMessage("Unable to connect to server to clean up tombstones -- " +
1808                 le.getMessage());
1809      indicateCompletedWithErrors();
1810      return;
1811    }
1812
1813
1814    // Perform a search to find the tombstone entries.
1815
LDAPSearchResults results = null;
1816    try
1817    {
1818      results = conn.search(baseDN, LDAPConnection.SCOPE_SUB,
1819                            "(&(objectClass=nsTombstone)(!(nsds50RUV=*)))",
1820                            new String JavaDoc[] { "1.1" }, false);
1821    }
1822    catch (LDAPException le)
1823    {
1824      logMessage("Unable to search for tombstone entries -- " +
1825                 le.getMessage());
1826      indicateCompletedWithErrors();
1827
1828      try
1829      {
1830        conn.disconnect();
1831      } catch (Exception JavaDoc e) {}
1832
1833      return;
1834    }
1835
1836
1837    // Iterate through each matching entry and try to delete it.
1838
while (results.hasMoreElements())
1839    {
1840      Object JavaDoc element = results.nextElement();
1841      if (element instanceof LDAPEntry)
1842      {
1843        String JavaDoc entryDN = ((LDAPEntry) element).getDN();
1844        try
1845        {
1846          conn.delete(entryDN);
1847        } catch (LDAPException le) {}
1848      }
1849    }
1850
1851
1852    // Close the connection to the server.
1853
try
1854    {
1855      conn.disconnect();
1856    } catch (Exception JavaDoc e) {}
1857  }
1858
1859
1860
1861  /**
1862   * Creates a randomly-generated LDAP entry to be added to the directory.
1863   *
1864   * @return The randomly-generated entry.
1865   */

1866  public LDAPEntry createEntry()
1867  {
1868    int nextRDNValue = nextValue++;
1869    if (nextRDNValue > maxRDNValue)
1870    {
1871      return null;
1872    }
1873
1874    LDAPAttribute[] attrs = new LDAPAttribute[attrsToInclude.length + 1];
1875    attrs[0] = new LDAPAttribute("objectClass", OBJECTCLASS_VALUES);
1876    for (int i=0; i < attrsToInclude.length; i++)
1877    {
1878      int colonPos;
1879      if (attrsToInclude[i].equals(rdnAttr))
1880      {
1881        attrs[i+1] = new LDAPAttribute(attrsToInclude[i],
1882                                       String.valueOf(nextRDNValue));
1883      }
1884      else if ((colonPos = attrsToInclude[i].indexOf(':')) > 0)
1885      {
1886        attrs[i+1] = new LDAPAttribute(attrsToInclude[i].substring(0, colonPos),
1887                              attrsToInclude[i].substring(colonPos+1).trim());
1888
1889      }
1890      else
1891      {
1892        attrs[i+1] = new LDAPAttribute(attrsToInclude[i],
1893                                       getRandomString(valueLength));
1894      }
1895    }
1896
1897    return new LDAPEntry(rdnAttr + "=" + nextRDNValue + "," + baseDN,
1898                         new LDAPAttributeSet(attrs));
1899  }
1900
1901
1902
1903  /**
1904   * Retrieves the DN of the next entry to be deleted.
1905   *
1906   * @return The DN of the entry to be deleted, or <CODE>null</CODE> if all
1907   * entries have been processed.
1908   */

1909  public String JavaDoc getDNToDelete()
1910  {
1911    int nextRDNValue = nextValue++;
1912    if (nextRDNValue > maxRDNValue)
1913    {
1914      return null;
1915    }
1916
1917    return (rdnAttr + "=" + nextRDNValue + "," + baseDN);
1918  }
1919
1920
1921
1922  /**
1923   * Retrieves a string containing the specified number of randomly-chosen
1924   * characters.
1925   *
1926   * @param length The number of characters to include in the string.
1927   *
1928   * @return A string containing the specified number of randomly-chosen
1929   * characters.
1930   */

1931  public String JavaDoc getRandomString(int length)
1932  {
1933    char[] returnChars = new char[length];
1934
1935    for (int i=0; i < length; i++)
1936    {
1937      returnChars[i] = ALPHABET[Math.abs((random.nextInt()) & 0x7FFFFFFF) %
1938                                ALPHABET.length];
1939    }
1940
1941    return new String JavaDoc(returnChars);
1942  }
1943}
1944
1945
Popular Tags