KickJava   Java API By Example, From Geeks To Geeks.

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


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 modifications
31  * against an LDAP directory server. All of the configuration for this job
32  * thread can be provided through parameters.
33  *
34  *
35  * @author Neil A. Wilson
36  */

37 public class ModRateWithReplicaLatencyJobClass
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 modification.
43    */

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

52   public static final String JavaDoc STAT_TRACKER_MOD_COUNT = "Modifications 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_EXCEPTIONS_CAUGHT =
61        "Exceptions Caught";
62
63
64
65   /**
66    * The display name for the stat tracker that will be used to track the
67    * replicaion latency.
68    */

69   public static final String JavaDoc STAT_TRACKER_REPLICA_LATENCY =
70        "Replication Latency (ms)";
71
72
73
74   /**
75    * The display name for the stat tracker that will be used to categorize the
76    * replication latencies.
77    */

78   public static final String JavaDoc STAT_TRACKER_CATEGORIZED_LATENCY =
79        "Categorized Latency";
80
81
82
83   /**
84    * The characters that are available for use in the randomly-generated values.
85    */

86   public static final char[] ALPHABET =
87        "abcdefghijklmnopqrstuvwxyz".toCharArray();
88
89
90
91
92
93   // The parameter that indicates whether to disconnect after each search
94
BooleanParameter disconnectParameter =
95        new BooleanParameter("disconnect", "Always Disconnect",
96                             "Indicates whether to close the connection after " +
97                             "each modification", false);
98
99   // The parameter that specifies a file containing search filters.
100
FileURLParameter dnURLParameter =
101        new FileURLParameter("dn_url", "DN File URL",
102                             "The URL (FILE or HTTP) that specifies the " +
103                             "DNs of the entries that may be modified.", null,
104                             false);
105
106   // The parmeter that specifies the cool-down time in seconds.
107
IntegerParameter coolDownParameter =
108        new IntegerParameter("cool_down", "Cool Down Time",
109                             "The time in seconds that the job should " +
110                             "continue searching after ending statistics " +
111                             "collection.", true, 0, true, 0, false, 0);
112
113   // The parameter that indicates the delay that should be used between each
114
// request sent by a thread.
115
IntegerParameter delayParameter =
116        new IntegerParameter("delay", "Time Between Requests (ms)",
117                             "Specifies the length of time in milliseconds " +
118                             "each thread should wait between modify " +
119                             "requests. Note that this delay will be " +
120                             "between consecutive requests and not between " +
121                             "the response of one operation and the request " +
122                             "for the next. If a modify takes longer than " +
123                             "this length of time, then there will be no delay.",
124                             true, 0, true, 0, false, 0);
125
126   // The parameter that indicates the number of times the search should be
127
// performed
128
IntegerParameter iterationsParameter =
129     new IntegerParameter("iterations", "Number of Iterations",
130                          "The number of searches that should be performed by " +
131                          "each thread", false, -1);
132
133   // The parameter that indicates the number of characters that should be
134
// included in the replacement value used in the modify.
135
IntegerParameter lengthParameter =
136        new IntegerParameter("length", "Value Length",
137                             "The length that should be used for the new " +
138                             "value of the modified attribute.", true, 80,
139                             true, 1, false, 0);
140
141   // The parameter that indicates the minimum length of time to sleep between
142
// modifications of the latency check entry.
143
IntegerParameter replicaDelayParameter =
144        new IntegerParameter("latency_delay", "Time Between Latency Checks (ms)",
145                             "Specifies the minimum length of time in " +
146                             "milliseconds that should pass between latency " +
147                             "checks. If a replicated operation takes longer " +
148                             "than this length of time, then there will be no " +
149                             "delay.", true, 0, true, 0, false, 0);
150
151   // The parameter that indicates the port number for the master directory.
152
IntegerParameter masterPortParameter =
153        new IntegerParameter("master_port", "Master Directory Port",
154                             "The port number for the master directory server",
155                             true, 389, true, 1, true, 65535);
156
157   // The parameter that indicates the port number for the replica directory.
158
IntegerParameter replicaPortParameter =
159        new IntegerParameter("replica_port", "Replica Directory Port",
160                             "The port number for the replica directory server",
161                             true, 389, true, 1, true, 65535);
162
163   // The parameter that indicates the maximum time limit for modify operations.
164
IntegerParameter timeLimitParameter =
165        new IntegerParameter("time_limit", "Modify Time Limit",
166                             "The maximum length of time in seconds that the " +
167                             "thread should wait for a modify operation to be " +
168                             "performed before cancelling it and trying " +
169                             "another.", false, 0, true, 0, false, 0);
170
171   // The parmeter that specifies the cool-down time in seconds.
172
IntegerParameter warmUpParameter =
173        new IntegerParameter("warm_up", "Warm Up Time",
174                             "The time in seconds that the job should " +
175                             "search before beginning statistics collection.",
176                             true, 0, true, 0, false, 0);
177
178   // The parameter that specifies the attribute to modify
179
MultiLineTextParameter attributeParameter =
180        new MultiLineTextParameter("attribute", "Attribute(s) to Modify",
181                                   "The set of attribute to modify in the " +
182                                   "entry. If multiple attributes are to be " +
183                                   "modified, then a separate attribute " +
184                                   "should be listed per line. Each will be " +
185                                   "given the same value.",
186                                   new String JavaDoc[] { "description" }, true);
187
188   // The placeholder parameter used as a spacer in the admin interface.
189
PlaceholderParameter placeholder = new PlaceholderParameter();
190
191   // The parameter that indicates the DN to use when binding to the server
192
StringParameter bindDNParameter =
193        new StringParameter("binddn", "Bind DN",
194                            "The DN to use to bind to the server", false, "");
195
196   // The parameter that indicates the search base
197
StringParameter entryDNParameter =
198        new StringParameter("entrydn", "Entry DN",
199                            "The DN of the entry to modify", false, "");
200
201   // The parameter that indicates the address of the master directory server
202
StringParameter masterHostParameter =
203        new StringParameter("masterhost", "Master Directory Host",
204                            "The DNS hostname or IP address of the master " +
205                            "directory server", true, "");
206
207   // The parameter that indicates the address of the replica directory server
208
StringParameter replicaHostParameter =
209        new StringParameter("replicahost", "Replica Directory Host",
210                            "The DNS hostname or IP address of the replica " +
211                            "directory server", true, "");
212
213   // The parameter that indicates the DN to use to proxy the modifications.
214
StringParameter proxyAsDNParameter =
215        new StringParameter("proxy_as_dn", "Proxy As DN",
216                            "The DN of the user whose credentials should be " +
217                            "used to perform the modification through the use " +
218                            "of the proxied authorization control.", false, "");
219
220   // The parameter that specifies the DN of the entry to watch for replication
221
// latency.
222
StringParameter replicaEntryDNParameter =
223        new StringParameter("replica_entrydn", "Latency Check Entry DN",
224                            "The DN of the entry that should be periodically " +
225                            "modified to measure the latency of replication. " +
226                            "Note that this DN must not be the same as " +
227                            "the DN of any of the entries to modify, nor " +
228                            "should this entry be modified by any external " +
229                            "process during the test.", true, "");
230
231   // The parameter that indicates the bind password
232
PasswordParameter bindPWParameter =
233        new PasswordParameter("bindpw", "Bind Password",
234                              "The password for the bind DN", false, "");
235
236
237   // Instance variables that correspond to the parameter values
238
static boolean alwaysDisconnect;
239   static boolean useDNFile;
240   static boolean useDNRange;
241   static boolean useProxyAuth;
242   static boolean useSequential;
243   static int coolDownTime;
244   static int dnRangeMax;
245   static int dnRangeMin;
246   static int dnRangeSpan;
247   static int iterations;
248   static int latencyDelay;
249   static int length;
250   static int masterPort;
251   static int replicaPort;
252   static int sequentialCounter;
253   static int timeLimit;
254   static int warmUpTime;
255   static long delay;
256   static String JavaDoc bindDN;
257   static String JavaDoc bindPassword;
258   static String JavaDoc dnInitial;
259   static String JavaDoc dnFinal;
260   static String JavaDoc masterHost;
261   static String JavaDoc proxyAsDN;
262   static String JavaDoc replicaEntryDN;
263   static String JavaDoc replicaHost;
264   static String JavaDoc[] modifyAttrs;
265   static String JavaDoc[] modifyDNs;
266
267
268   // The connection to the directory server to use to perform the modifies.
269
LDAPConnection conn;
270
271
272   // Variables used for status counters
273
IncrementalTracker exceptionsCaught;
274   IncrementalTracker modCount;
275   TimeTracker modTime;
276
277
278   // One random number generator for use throughout the client and another to
279
// use for only the current thread.
280
static Random parentRandom;
281   Random random;
282
283
284   // Variables used for tracking replication latency.
285
static boolean latencyTrackerChosen;
286   boolean reportLatencyTracker;
287   LatencyCheckMasterThread masterThread;
288   LatencyCheckReplicaThread replicaThread;
289
290
291
292   /**
293    * The default constructor used to create a new instance of the search thread.
294    * The only thing it should do is to invoke the superclass constructor. All
295    * other initialization should be performed in the <CODE>initialize</CODE>
296    * method.
297    */

298   public ModRateWithReplicaLatencyJobClass()
299   {
300     super();
301   }
302
303
304
305   /**
306    * Retrieves the name of the job performed by this job thread.
307    *
308    * @return The name of the job performed by this job thread.
309    */

310   public String JavaDoc getJobName()
311   {
312     return "LDAP ModRate with Replica Latency";
313   }
314
315
316
317   /**
318    * Retrieves a description of the job performed by this job thread.
319    *
320    * @return A description of the job performed by this job thread.
321    */

322   public String JavaDoc getJobDescription()
323   {
324     return "This job can be used to perform repeated modifications against " +
325            "an LDAP directory server to generate load and measure " +
326            "performance. It also provides the capability to measure the " +
327            "latency associated with replication while the modifications are " +
328            "in progress.";
329   }
330
331
332
333   /**
334    * Retrieves the name of the category in which this job class exists. This is
335    * used to help arrange the job classes in the administrative interface.
336    *
337    * @return The name of the category in which this job class exists.
338    */

339   public String JavaDoc getJobCategoryName()
340   {
341     return "LDAP";
342   }
343
344
345
346   /**
347    * Retrieve a parameter list that can be used to determine all of the
348    * customizeable options that are available for this job.
349    *
350    * @return A parameter list that can be used to determine all of the
351    * customizeable options that are available for this job.
352    */

353   public ParameterList getParameterStubs()
354   {
355     Parameter[] parameters = new Parameter[]
356     {
357       placeholder,
358       masterHostParameter,
359       masterPortParameter,
360       bindDNParameter,
361       bindPWParameter,
362       proxyAsDNParameter,
363       placeholder,
364       entryDNParameter,
365       dnURLParameter,
366       attributeParameter,
367       lengthParameter,
368       placeholder,
369       replicaHostParameter,
370       replicaPortParameter,
371       replicaEntryDNParameter,
372       replicaDelayParameter,
373       placeholder,
374       warmUpParameter,
375       coolDownParameter,
376       timeLimitParameter,
377       delayParameter,
378       placeholder,
379       iterationsParameter,
380       disconnectParameter
381     };
382
383     return new ParameterList(parameters);
384   }
385
386
387
388   /**
389    * Retrieves the set of stat trackers that will be maintained by this job
390    * class. The stat trackers returned by this method do not have to actually
391    * contain any statistics -- the display name and stat tracker class should
392    * be the only information that callers of this method should rely upon. Note
393    * that this list can be different from the list of statistics actually
394    * collected by the job in some cases (e.g., if the job may not return all the
395    * stat trackers it advertises in all cases, or if the job may return stat
396    * trackers that it did not advertise), but it is a possibility that only the
397    * stat trackers returned by this method will be accessible for some features
398    * in the SLAMD server.
399    *
400    * @param clientID The client ID that should be used for the
401    * returned stat trackers.
402    * @param threadID The thread ID that should be used for the
403    * returned stat trackers.
404    * @param collectionInterval The collection interval that should be used for
405    * the returned stat trackers.
406    *
407    * @return The set of stat trackers that will be maintained by this job
408    * class.
409    */

410   public StatTracker[] getStatTrackerStubs(String JavaDoc clientID, String JavaDoc threadID,
411                                            int collectionInterval)
412   {
413     return new StatTracker[]
414     {
415       new IncrementalTracker(clientID, threadID, STAT_TRACKER_MOD_COUNT,
416                              collectionInterval),
417       new TimeTracker(clientID, threadID, STAT_TRACKER_MOD_TIME,
418                       collectionInterval),
419       new IncrementalTracker(clientID, threadID, STAT_TRACKER_EXCEPTIONS_CAUGHT,
420                              collectionInterval),
421       new TimeTracker(clientID, threadID, STAT_TRACKER_REPLICA_LATENCY,
422                       collectionInterval),
423       new CategoricalTracker(clientID, threadID,
424                              STAT_TRACKER_CATEGORIZED_LATENCY,
425                              collectionInterval)
426     };
427   }
428
429
430
431   /**
432    * Retrieves the stat trackers that are maintained for this job thread.
433    *
434    * @return The stat trackers that are maintained for this job thread.
435    */

436   public StatTracker[] getStatTrackers()
437   {
438     if (reportLatencyTracker)
439     {
440       return new StatTracker[]
441       {
442         modCount,
443         modTime,
444         exceptionsCaught,
445         latencyTime,
446         latencyCategories
447       };
448     }
449     else
450     {
451       return new StatTracker[]
452       {
453         modCount,
454         modTime,
455         exceptionsCaught
456       };
457     }
458   }
459
460
461
462   /**
463    * Provides a means of validating the information used to schedule the job,
464    * including the scheduling information and list of parameters.
465    *
466    * @param numClients The number of clients that should be used to
467    * run the job.
468    * @param threadsPerClient The number of threads that should be created on
469    * each client to run the job.
470    * @param threadStartupDelay The delay in milliseconds that should be used
471    * when starting the client threads.
472    * @param startTime The time that the job should start running.
473    * @param stopTime The time that the job should stop running.
474    * @param duration The maximum length of time in seconds that the
475    * job should be allowed to run.
476    * @param collectionInterval The collection interval that should be used
477    * when gathering statistics for the job.
478    * @param parameters The set of parameters provided to this job that
479    * can be used to customize its behavior.
480    *
481    * @throws InvalidValueException If the provided information is not
482    * appropriate for running this job.
483    */

484   public void validateJobInfo(int numClients, int threadsPerClient,
485                               int threadStartupDelay, Date startTime,
486                               Date stopTime, int duration,
487                               int collectionInterval, ParameterList parameters)
488          throws InvalidValueException
489   {
490     StringParameter dnParameter =
491          parameters.getStringParameter(entryDNParameter.getName());
492     FileURLParameter dnFileParameter =
493          parameters.getFileURLParameter(dnURLParameter.getName());
494     if (((dnParameter == null) || (! dnParameter.hasValue())) &&
495         ((dnFileParameter == null) || (! dnFileParameter.hasValue())))
496     {
497       throw new InvalidValueException("Either a single entry DN or a DN file " +
498                                       "URL must be specified.");
499     }
500   }
501
502
503
504   /**
505    * Indicates whether this job class implements logic that makes it possible to
506    * test the validity of job parameters before scheduling the job for execution
507    * (e.g., to see if the server is reachable using the information provided).
508    *
509    * @return <CODE>true</CODE> if this job provides a means of testing the job
510    * parameters, or <CODE>false</CODE> if not.
511    */

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

546   public boolean testJobParameters(ParameterList parameters,
547                                    ArrayList outputMessages)
548   {
549     // Get all the parameters that we might need to perform the test.
550
StringParameter masterHostParam =
551          parameters.getStringParameter(masterHostParameter.getName());
552     if ((masterHostParam == null) || (! masterHostParam.hasValue()))
553     {
554       outputMessages.add("ERROR: No master directory server address was " +
555                          "provided.");
556       return false;
557     }
558     String JavaDoc masterHost = masterHostParam.getStringValue();
559
560
561     IntegerParameter masterPortParam =
562          parameters.getIntegerParameter(masterPortParameter.getName());
563     if ((masterPortParam == null) || (! masterPortParam.hasValue()))
564     {
565       outputMessages.add("ERROR: No master directory server port was " +
566                          "provided.");
567       return false;
568     }
569     int masterPort = masterPortParam.getIntValue();
570
571
572     StringParameter replicaHostParam =
573          parameters.getStringParameter(replicaHostParameter.getName());
574     if ((replicaHostParam == null) || (! replicaHostParam.hasValue()))
575     {
576       outputMessages.add("ERROR: No replica directory server address was " +
577                          "provided.");
578       return false;
579     }
580     String JavaDoc replicaHost = replicaHostParam.getStringValue();
581
582
583     IntegerParameter replicaPortParam =
584          parameters.getIntegerParameter(replicaPortParameter.getName());
585     if ((replicaPortParam == null) || (! replicaPortParam.hasValue()))
586     {
587       outputMessages.add("ERROR: No replica directory server port was " +
588                          "provided.");
589       return false;
590     }
591     int replicaPort = replicaPortParam.getIntValue();
592
593
594     String JavaDoc bindDN = "";
595     StringParameter bindDNParam =
596          parameters.getStringParameter(bindDNParameter.getName());
597     if ((bindDNParam != null) && bindDNParam.hasValue())
598     {
599       bindDN = bindDNParam.getStringValue();
600     }
601
602
603     String JavaDoc bindPassword = "";
604     PasswordParameter bindPWParam =
605          parameters.getPasswordParameter(bindPWParameter.getName());
606     if ((bindPWParam != null) && bindPWParam.hasValue())
607     {
608       bindPassword = bindPWParam.getStringValue();
609     }
610
611
612     String JavaDoc proxyAsDN = null;
613     StringParameter proxyAsDNParam =
614          parameters.getStringParameter(proxyAsDNParameter.getName());
615     if ((proxyAsDNParam != null) && proxyAsDNParam.hasValue())
616     {
617       proxyAsDN = proxyAsDNParam.getStringValue();
618     }
619
620
621     StringParameter replicaEntryDNParam =
622          parameters.getStringParameter(replicaEntryDNParameter.getName());
623     if ((replicaEntryDNParam == null) || (! replicaEntryDNParam.hasValue()))
624     {
625       outputMessages.add("ERROR: No replica check entry DN was provided.");
626       return false;
627     }
628     String JavaDoc replicaEntryDN = replicaEntryDNParam.getStringValue();
629
630
631     // Create the LDAPConnection object that we will use to communicate with the
632
// directory server.
633
LDAPConnection conn = new LDAPConnection();
634
635
636     // Attempt to establish a connection to the master server.
637
try
638     {
639       outputMessages.add("Attempting to establish a connection to master " +
640                          masterHost + ":" + masterPort + "....");
641       conn.connect(masterHost, masterPort);
642       outputMessages.add("Connected successfully.");
643       outputMessages.add("");
644     }
645     catch (Exception JavaDoc e)
646     {
647       outputMessages.add("ERROR: Unable to connect to the master server: " +
648                          stackTraceToString(e));
649       return false;
650     }
651
652
653     // Attempt to bind to the master server using the bind DN and password.
654
try
655     {
656       outputMessages.add("Attempting to perform an LDAPv3 bind to the " +
657                          "master server with a DN of '" + bindDN + "'....");
658       conn.bind(3, bindDN, bindPassword);
659       outputMessages.add("Bound successfully.");
660       outputMessages.add("");
661     }
662     catch (Exception JavaDoc e)
663     {
664       try
665       {
666         conn.disconnect();
667       } catch (Exception JavaDoc e2) {}
668
669       outputMessages.add("ERROR: Unable to bind to the master server: " +
670                          stackTraceToString(e));
671       return false;
672     }
673
674
675     // If a proxy user was specified, make sure that it exists.
676
if (proxyAsDN != null)
677     {
678       try
679       {
680         outputMessages.add("Checking to make sure that the proxied user '" +
681                            proxyAsDN + "' exists in the master....");
682         LDAPEntry proxyUserEntry = conn.read(proxyAsDN, new String JavaDoc[] { "1.1" });
683         if (proxyUserEntry == null)
684         {
685           try
686           {
687             conn.disconnect();
688           } catch (Exception JavaDoc e2) {}
689
690           outputMessages.add("ERROR: Unable to retrieve the proxied user's " +
691                              "entry.");
692           return false;
693         }
694         else
695         {
696           outputMessages.add("Successfully read the proxied user's entry.");
697           outputMessages.add("");
698         }
699       }
700       catch (Exception JavaDoc e)
701       {
702         try
703         {
704           conn.disconnect();
705         } catch (Exception JavaDoc e2) {}
706
707         outputMessages.add("ERROR: Unable to retrieve the proxied user's " +
708                            "entry: " + stackTraceToString(e));
709         return false;
710       }
711     }
712
713
714     // Make sure that the entry specified as the replica check entry exists.
715
try
716     {
717       outputMessages.add("Checking to make sure that the replica check " +
718                          "entry '" + replicaEntryDN +
719                          "' exists in the master....");
720       LDAPEntry baseDNEntry = conn.read(replicaEntryDN, new String JavaDoc[] { "1.1" });
721       if (baseDNEntry == null)
722       {
723         try
724         {
725           conn.disconnect();
726         } catch (Exception JavaDoc e2) {}
727
728         outputMessages.add("ERROR: Unable to retrieve the replica check " +
729                            "entry.");
730         return false;
731       }
732       else
733       {
734         outputMessages.add("Successfully read the replica check entry.");
735         outputMessages.add("");
736       }
737     }
738     catch (Exception JavaDoc e)
739     {
740       try
741       {
742         conn.disconnect();
743       } catch (Exception JavaDoc e2) {}
744
745       outputMessages.add("ERROR: Unable to retrieve the replica check " +
746                          "entry: " + stackTraceToString(e));
747       return false;
748     }
749
750
751     // At this point, all tests on the master have passed. Close the connection
752
// and prepare to start on the replica.
753
try
754     {
755       conn.disconnect();
756     } catch (Exception JavaDoc e) {}
757
758
759     // Attempt to establish a connection to the replica server.
760
try
761     {
762       outputMessages.add("Attempting to establish a connection to replica " +
763                          replicaHost + ":" + replicaPort + "....");
764       conn.connect(replicaHost, replicaPort);
765       outputMessages.add("Connected successfully.");
766       outputMessages.add("");
767     }
768     catch (Exception JavaDoc e)
769     {
770       outputMessages.add("ERROR: Unable to connect to the replica server: " +
771                          stackTraceToString(e));
772       return false;
773     }
774
775
776     // Attempt to bind to the replica server using the bind DN and password.
777
try
778     {
779       outputMessages.add("Attempting to perform an LDAPv3 bind to the " +
780                          "replica 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 replica server: " +
793                          stackTraceToString(e));
794       return false;
795     }
796
797
798     // Make sure that the entry specified as the replica check entry exists.
799
try
800     {
801       outputMessages.add("Checking to make sure that the replica check " +
802                          "entry '" + replicaEntryDN +
803                          "' exists in the replica....");
804       LDAPEntry baseDNEntry = conn.read(replicaEntryDN, new String JavaDoc[] { "1.1" });
805       if (baseDNEntry == null)
806       {
807         try
808         {
809           conn.disconnect();
810         } catch (Exception JavaDoc e2) {}
811
812         outputMessages.add("ERROR: Unable to retrieve the replica check " +
813                            "entry.");
814         return false;
815       }
816       else
817       {
818         outputMessages.add("Successfully read the replica check entry.");
819         outputMessages.add("");
820       }
821     }
822     catch (Exception JavaDoc e)
823     {
824       try
825       {
826         conn.disconnect();
827       } catch (Exception JavaDoc e2) {}
828
829       outputMessages.add("ERROR: Unable to retrieve the replica check " +
830                          "entry: " + stackTraceToString(e));
831       return false;
832     }
833
834
835     // At this point, all tests on the replica have passed. Close the
836
// connection and return true.
837
try
838     {
839       conn.disconnect();
840     } catch (Exception JavaDoc e) {}
841
842
843     outputMessages.add("All tests completed successfully.");
844     return true;
845   }
846
847
848
849   /**
850    * Initializes all of the instance variables that correspond to job
851    * parameters.
852    *
853    * @param clientID The client ID for the current client.
854    * @param parameters The set of parameters that have been defined for this
855    * job.
856    *
857    * @throws UnableToRunException If any part of the initialization fails.
858    */

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

1089  public void initializeThread(String JavaDoc clientID, String JavaDoc threadID,
1090                               int collectionInterval, ParameterList parameters)
1091         throws UnableToRunException
1092  {
1093    // Set up the stat trackers
1094
modCount = new IncrementalTracker(clientID, threadID,
1095                                      STAT_TRACKER_MOD_COUNT,
1096                                      collectionInterval);
1097    exceptionsCaught = new IncrementalTracker(clientID, threadID,
1098                                              STAT_TRACKER_EXCEPTIONS_CAUGHT,
1099                                              collectionInterval);
1100    modTime = new TimeTracker(clientID, threadID, STAT_TRACKER_MOD_TIME,
1101                              collectionInterval);
1102
1103
1104    // Enable real-time reporting of the data for these stat trackers.
1105
RealTimeStatReporter statReporter = getStatReporter();
1106    if (statReporter != null)
1107    {
1108      String JavaDoc jobID = getJobID();
1109      modCount.enableRealTimeStats(statReporter, jobID);
1110      exceptionsCaught.enableRealTimeStats(statReporter, jobID);
1111      modTime.enableRealTimeStats(statReporter, jobID);
1112    }
1113
1114
1115    // Initialize the random number generator for this thread.
1116
random = new Random(parentRandom.nextLong());
1117
1118    // See if this thread should be used to report the replica latency
1119
// information. If so, then start the latency check threads.
1120
if ((! latencyTrackerChosen) && (getClientNumber() == 0))
1121    {
1122      latencyCheckMutex = new Object JavaDoc();
1123      latencyTrackerChosen = true;
1124      reportLatencyTracker = true;
1125
1126      // Create the latency timer stat tracker.
1127
latencyTime = new TimeTracker(clientID, threadID,
1128                                    STAT_TRACKER_REPLICA_LATENCY,
1129                                    collectionInterval);
1130      if (statReporter != null)
1131      {
1132        latencyTime.enableRealTimeStats(statReporter, getJobID());
1133      }
1134
1135      // Create the latency categories stat tracker.
1136
latencyCategories =
1137           new CategoricalTracker(clientID, threadID,
1138                                  STAT_TRACKER_CATEGORIZED_LATENCY,
1139                                  collectionInterval);
1140
1141      // Create the latency thread that will make changes to the master server.
1142
try
1143      {
1144        masterThread =
1145             new LatencyCheckMasterThread(this, masterHost, masterPort, bindDN,
1146                                          bindPassword, replicaEntryDN,
1147                                          modifyAttrs[0], latencyDelay);
1148      }
1149      catch (LDAPException le)
1150      {
1151        throw new UnableToRunException("Could not create the master latency " +
1152                                       "thread: " + le, le);
1153      }
1154
1155      // Create the latency thread that will read changes from the replica.
1156
try
1157      {
1158        replicaThread =
1159             new LatencyCheckReplicaThread(this, replicaHost, replicaPort,
1160                                           bindDN, bindPassword,
1161                                           replicaEntryDN);
1162      }
1163      catch (LDAPException le)
1164      {
1165        throw new UnableToRunException("Could not create the replica latency " +
1166                                       "thread: " + le, le);
1167      }
1168
1169      // Start the latency check threads.
1170
masterThread.start();
1171      replicaThread.start();
1172    }
1173    else
1174    {
1175      reportLatencyTracker = false;
1176    }
1177  }
1178
1179
1180
1181  /**
1182   * Perform the work of this job thread by establishing the connection(s) to
1183   * the directory server and issuing all the appropriate queries. The job will
1184   * continue until the specified number of iterations have been performed, the
1185   * stop time has been reached, the maximum duration has been reached, or the
1186   * SLAMD server indicates that a stop has been requested.
1187   */

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

1394  public void destroy()
1395  {
1396    if (conn != null)
1397    {
1398      try
1399      {
1400        conn.disconnect();
1401      } catch (Exception JavaDoc e) {}
1402
1403      conn = null;
1404    }
1405
1406    if (masterThread != null)
1407    {
1408      masterThread.masterThread.interrupt();
1409
1410      try
1411      {
1412        masterThread.connection.disconnect();
1413      } catch (Exception JavaDoc e) {}
1414
1415      masterThread.connection = null;
1416      masterThread = null;
1417    }
1418
1419    if (replicaThread != null)
1420    {
1421      replicaThread.replicaThread.interrupt();
1422
1423      try
1424      {
1425        replicaThread.connection.disconnect();
1426      } catch (Exception JavaDoc e) {}
1427
1428      replicaThread.connection = null;
1429      replicaThread = null;
1430    }
1431  }
1432
1433
1434
1435  /**
1436   * Ensures that the master and replica check threads are properly stopped.
1437   */

1438  public void finalizeClient()
1439  {
1440    masterThread.stopAndWait();
1441    replicaThread.stopAndWait();
1442  }
1443
1444
1445
1446  /**
1447   * Retrieves a random DN from the list of DNs that can be modified.
1448   *
1449   * @return A randomly-chosen DN of an entry to modify.
1450   */

1451  public String JavaDoc getRandomEntryDN()
1452  {
1453    if (useDNFile)
1454    {
1455      return modifyDNs[(random.nextInt() & 0x7FFFFFFF) % modifyDNs.length];
1456    }
1457    else
1458    {
1459      if (useDNRange)
1460      {
1461        int value;
1462        if (useSequential)
1463        {
1464          value = sequentialCounter++;
1465          if (sequentialCounter > dnRangeMax)
1466          {
1467            sequentialCounter = dnRangeMin;
1468          }
1469        }
1470        else
1471        {
1472          value = ((random.nextInt() & 0x7FFFFFFF) % dnRangeSpan) + dnRangeMin;
1473        }
1474        return dnInitial + value + dnFinal;
1475      }
1476      else
1477      {
1478        return dnInitial;
1479      }
1480    }
1481  }
1482
1483
1484
1485  /**
1486   * Retrieves a string containing the specified number of randomly-chosen
1487   * characters.
1488   *
1489   * @param length The number of characters to include in the string.
1490   *
1491   * @return A string containing the specified number of randomly-chosen
1492   * characters.
1493   */

1494  public String JavaDoc getRandomString(int length)
1495  {
1496    char[] returnChars = new char[length];
1497
1498    for (int i=0; i < length; i++)
1499    {
1500      returnChars[i] = ALPHABET[Math.abs((random.nextInt()) & 0x7FFFFFFF) %
1501                                ALPHABET.length];
1502    }
1503
1504    return new String JavaDoc(returnChars);
1505  }
1506}
1507
1508
Popular Tags