KickJava   Java API By Example, From Geeks To Geeks.

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


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

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

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

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

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

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

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

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

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

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

103   public static final String JavaDoc STAT_TRACKER_RESULT_CODES = "Result Codes";
104
105
106
107   /**
108    * The set of characters that should be included in numeric values.
109    */

110   public static final char[] NUMERIC_CHARS = "0123456789".toCharArray();
111
112
113
114   /**
115    * The set of characters that should be included in alphabetic values.
116    */

117   public static final char[] ALPHA_CHARS =
118        "abcdefghijklmnopqrstuvwxyz".toCharArray();
119
120
121
122   /**
123    * The set of characters that should be included in alphanumeric values.
124    */

125   public static final char[] ALPHANUMERIC_CHARS =
126        "abcdefghijklmnopqrstuvwxyz0123456789".toCharArray();
127
128
129
130   /**
131    * The set of characters that should be included in hexadecimal values.
132    */

133   public static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
134
135
136
137   /**
138    * The set of characters that should be included in base64 values.
139    */

140   public static final char[] BASE64_CHARS =
141        ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +
142         "0123456789+/").toCharArray();
143
144
145
146   /**
147    * The set of months that will be used if the name of a month is required.
148    */

149   public static final String JavaDoc[] MONTH_NAMES =
150   {
151     "January",
152     "February",
153     "March",
154     "April",
155     "May",
156     "June",
157     "July",
158     "August",
159     "September",
160     "October",
161     "November",
162     "December"
163   };
164
165
166
167   /**
168    * The text of the template that will be used by default.
169    */

170   public static final String JavaDoc[] DEFAULT_TEMPLATE_LINES = new String JavaDoc[]
171   {
172     "objectClass: top",
173     "objectClass: person",
174     "objectClass: organizationalPerson",
175     "objectClass: inetOrgPerson",
176     "employeeNumber: <entrynumber>",
177     "uid: user.{employeeNumber}",
178     "givenName: User",
179     "sn: {employeeNumber}",
180     "cn: User {sn}",
181     "initials: {givenName:1}{sn:1}",
182     "mail: {uid}@example.com",
183     "userPassword: password",
184     "telephoneNumber: <random:telephone>",
185     "mobile: <random:telephone>",
186     "pager: <random:telephone>",
187     "homePhone: <random:telephone>"
188   };
189
190
191
192   // The parameter that indicates whether the client should trust any SSL cert.
193
BooleanParameter blindTrustParameter =
194     new BooleanParameter("blind_trust", "Blindly Trust Any Certificate",
195                          "Indicates whether the client should blindly trust " +
196                          "any certificate presented by the server, or " +
197                          "whether the key and trust stores should be used.",
198                          true);
199
200   // The parameter that indicates whether to disconnect after each add
201
BooleanParameter disconnectParameter =
202        new BooleanParameter("disconnect", "Always Disconnect",
203                             "Indicates whether to close the connection after " +
204                             "each add", false);
205
206   // The parameter that indicates whether the connection should use SSL
207
BooleanParameter useSSLParameter =
208     new BooleanParameter("usessl", "Use SSL",
209                          "Indicates whether SSL should be used for all " +
210                          "communication with the directory server", false);
211
212   // The parmeter that specifies the cool-down time in seconds.
213
IntegerParameter coolDownParameter =
214        new IntegerParameter("cool_down", "Cool Down Time",
215                             "The time in seconds that the job should " +
216                             "continue adding after ending statistics " +
217                             "collection.", true, 0, true, 0, false, 0);
218
219   // The parameter that indicates the delay that should be used between each
220
// request sent by a thread.
221
IntegerParameter delayParameter =
222        new IntegerParameter("delay", "Time Between Requests (ms)",
223                             "Specifies the length of time in milliseconds " +
224                             "each thread should wait between add " +
225                             "requests. Note that this delay will be " +
226                             "between consecutive requests and not between " +
227                             "the response of one operation and the request " +
228                             "for the next. If an add takes longer than " +
229                             "this length of time, then there will be no delay.",
230                             true, 0, true, 0, false, 0);
231
232   // The parameter that indicates the port number for the directory server
233
IntegerParameter portParameter =
234        new IntegerParameter("ldapport", "Directory Server Port",
235                             "The port number for the LDAP directory server",
236                             true, 389, true, 1, true, 65535);
237
238   // The parameter that specifies the starting number for the entries created.
239
IntegerParameter startValueParameter =
240        new IntegerParameter("start_value", "Initial Entry Value Number",
241                             "The number to use as the value of the " +
242                             "entryNumber tag for the first entry to create",
243                             true, 1, false, 0, false, 0);
244
245   // The parameter that specifies the ending number for the entries created.
246
IntegerParameter endValueParameter =
247        new IntegerParameter("end_value", "Final Entry Value Number",
248                             "The number to use as the value of the " +
249                             "entryNumber tag for the last entry to create",
250                             false, 0, false, 0, false, 0);
251
252   // The parameter that indicates the maximum time limit for add operations.
253
IntegerParameter timeLimitParameter =
254        new IntegerParameter("time_limit", "Add Time Limit",
255                             "The maximum length of time in seconds that the " +
256                             "thread should wait for a add operation to be " +
257                             "performed before cancelling it and trying " +
258                             "another.", false, 0, true, 0, false, 0);
259
260   // The parmeter that specifies the cool-down time in seconds.
261
IntegerParameter warmUpParameter =
262        new IntegerParameter("warm_up", "Warm Up Time",
263                             "The time in seconds that the job should " +
264                             "add before beginning statistics collection.",
265                             true, 0, true, 0, false, 0);
266
267   // The template to use to create the entries.
268
MultiLineTextParameter templateParameter =
269        new MultiLineTextParameter("template", "Entry Template",
270                                   "The template to use when creating the " +
271                                   "entries. Consult the job reference guide " +
272                                   "for a description of the tags that may be " +
273                                   "used in the template",
274                                   DEFAULT_TEMPLATE_LINES, true);
275
276   // The placeholder parameter used as a spacer in the admin interface.
277
PlaceholderParameter placeholder = new PlaceholderParameter();
278
279   // The parameter that indicates the DN to use when binding to the server
280
StringParameter bindDNParameter =
281        new StringParameter("binddn", "Bind DN",
282                            "The DN to use to bind to the server", false, "");
283
284   // The parameter that indicates the base below which entries will be added.
285
StringParameter baseDNParameter =
286        new StringParameter("basedn", "Base DN ",
287                            "The base below which to add the entries",
288                            true, "");
289
290   // The parameter that indicates the address of the directory server
291
StringParameter hostParameter =
292        new StringParameter("ldaphost", "Directory Server Host",
293                            "The DNS hostname or IP address of the LDAP " +
294                            "directory server", true, "");
295
296   // The parameter that indicates the DN to use to proxy the adds.
297
StringParameter proxyAsDNParameter =
298        new StringParameter("proxy_as_dn", "Proxy As DN",
299                            "The DN of the user whose credentials should be " +
300                            "used to perform the adds through the use " +
301                            "of the proxied authorization control.", false, "");
302
303   // The parameter that indicates the RDN attribute for the new entries.
304
StringParameter rdnAttrParameter =
305        new StringParameter("rdn_attr", "RDN Attribute",
306                            "The RDN attribute to use when creating the " +
307                            "entries.", true, "uid");
308
309   // The parameter that specifies the location of the SSL key store
310
StringParameter keyStoreParameter =
311     new StringParameter("sslkeystore", "SSL Key Store",
312                         "The path to the JSSE key store to use for an " +
313                         "SSL-based connection", false, "");
314
315   // The parameter that specifies the location of the SSL trust store
316
StringParameter trustStoreParameter =
317     new StringParameter("ssltruststore", "SSL Trust Store",
318                         "The path to the JSSE trust store to use for an " +
319                         "SSL-based connection", false, "");
320
321   // The parameter that indicates the bind password
322
PasswordParameter bindPWParameter =
323        new PasswordParameter("bindpw", "Bind Password",
324                              "The password for the bind DN", false, "");
325
326   // The parameter that specifies the password for the SSL key store
327
PasswordParameter keyPWParameter =
328     new PasswordParameter("sslkeypw", "SSL Key Store Password",
329                           "The password for the JSSE key store", false, "");
330
331   // The parameter that specifies the password for the SSL key store
332
PasswordParameter trustPWParameter =
333     new PasswordParameter("ssltrustpw", "SSL Trust Store Password",
334                           "The password for the JSSE trust store", false, "");
335
336
337   // Instance variables that correspond to the parameter values
338
static boolean alwaysDisconnect;
339   static boolean blindTrust;
340   static boolean useProxyAuth;
341   static boolean useSSL;
342   static int coolDownTime;
343   static int endValue;
344   static int ldapPort;
345   static int nextValue;
346   static int startValue;
347   static int timeLimit;
348   static int warmUpTime;
349   static long delay;
350   static String JavaDoc baseDN;
351   static String JavaDoc bindDN;
352   static String JavaDoc bindPassword;
353   static String JavaDoc ldapHost;
354   static String JavaDoc lowerRDNAttr;
355   static String JavaDoc proxyAsDN;
356   static String JavaDoc rdnAttr;
357   static String JavaDoc sslKeyStore;
358   static String JavaDoc sslKeyPassword;
359   static String JavaDoc sslTrustStore;
360   static String JavaDoc sslTrustPassword;
361   static String JavaDoc[] attrNames;
362   static String JavaDoc[] attrValues;
363   static String JavaDoc[] lowerNames;
364   static String JavaDoc[] separators;
365   static String JavaDoc[] templateLines;
366
367
368   // The connection to the directory server over which the adds will be
369
// performed.
370
LDAPConnection conn;
371
372
373   // Variables used for status counters
374
AccumulatingTracker totalAdds;
375   CategoricalTracker resultCodes;
376   IncrementalTracker addCount;
377   TimeTracker addTime;
378
379
380   // One random number generator for use throughout the client and another to
381
// use for only the current thread.
382
static Random parentRandom = new Random();
383   static String JavaDoc guidBase = null;
384   Random random;
385   char[] chars5000;
386
387
388
389
390   /**
391    * The default constructor used to create a new instance of the add thread.
392    * The only thing it should do is to invoke the superclass constructor. All
393    * other initialization should be performed in the <CODE>initialize</CODE>
394    * method.
395    */

396   public TemplateBasedAddRateJobClass()
397   {
398     super();
399
400     templateParameter.setVisibleRows(10);
401     templateParameter.setVisibleColumns(80);
402     chars5000 = new char[5000];
403     random = new Random(parentRandom.nextLong());
404
405     if (guidBase == null)
406     {
407       guidBase = generateRandomValue(HEX_CHARS, 12);
408     }
409   }
410
411
412
413   /**
414    * Retrieves the name of the job performed by this job thread.
415    *
416    * @return The name of the job performed by this job thread.
417    */

418   public String JavaDoc getJobName()
419   {
420     return "LDAP Template-Based AddRate";
421   }
422
423
424
425   /**
426    * Retrieves a description of the job performed by this job thread.
427    *
428    * @return A description of the job performed by this job thread.
429    */

430   public String JavaDoc getJobDescription()
431   {
432     return "This job can be used to perform repeated add operations against " +
433            "an LDAP directory server to generate load and measure " +
434            "performance. The entries created will be based on a " +
435            "user-defined template.";
436   }
437
438
439
440   /**
441    * Retrieves the name of the category in which this job class exists. This is
442    * used to help arrange the job classes in the administrative interface.
443    *
444    * @return The name of the category in which this job class exists.
445    */

446   public String JavaDoc getJobCategoryName()
447   {
448     return "LDAP";
449   }
450
451
452
453   /**
454    * Overrides the number of clients that may be used for this job to ensure
455    * that only a single client will be used.
456    *
457    * @return The number of clients that will always be used for this job.
458    */

459   public int overrideNumClients()
460   {
461     return 1;
462   }
463
464
465
466   /**
467    * Retrieve a parameter list that can be used to determine all of the
468    * customizeable options that are available for this job.
469    *
470    * @return A parameter list that can be used to determine all of the
471    * customizeable options that are available for this job.
472    */

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

532   public StatTracker[] getStatTrackerStubs(String JavaDoc clientID, String JavaDoc threadID,
533                                            int collectionInterval)
534   {
535     return new StatTracker[]
536     {
537       new IncrementalTracker(clientID, threadID, STAT_TRACKER_ADD_COUNT,
538                              collectionInterval),
539       new TimeTracker(clientID, threadID, STAT_TRACKER_ADD_TIME,
540                       collectionInterval),
541       new CategoricalTracker(clientID, threadID, STAT_TRACKER_RESULT_CODES,
542                              collectionInterval)
543     };
544   }
545
546
547
548   /**
549    * Retrieves the stat trackers that are maintained for this job thread.
550    *
551    * @return The stat trackers that are maintained for this job thread.
552    */

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

588   public void validateJobInfo(int numClients, int threadsPerClient,
589                               int threadStartupDelay, Date startTime,
590                               Date stopTime, int duration,
591                               int collectionInterval, ParameterList parameters)
592          throws InvalidValueException
593   {
594     // Make sure the job was only scheduled for a single client.
595
if (numClients != 1)
596     {
597       throw new InvalidValueException("An AddRate job may only run on a " +
598                                       "single client.");
599     }
600
601     // Make sure the template provided is parseable.
602
MultiLineTextParameter templateParam =
603          parameters.getMultiLineTextParameter(templateParameter.getName());
604     if (templateParam == null)
605     {
606       throw new InvalidValueException("No value provided for required " +
607                                       "parameter " +
608                                       templateParameter.getDisplayName());
609     }
610
611     templateLines = templateParam.getNonBlankLines();
612     if (templateLines.length == 0)
613     {
614       throw new InvalidValueException("No value provided for required " +
615                                       "parameter " +
616                                       templateParameter.getDisplayName());
617     }
618
619     StringParameter rdnAttrParam =
620          parameters.getStringParameter(rdnAttrParameter.getName());
621     if (rdnAttrParam == null)
622     {
623       throw new InvalidValueException("No value provided for required " +
624                                       "parameter " +
625                                       rdnAttrParameter.getDisplayName());
626     }
627     rdnAttr = rdnAttrParam.getStringValue();
628     lowerRDNAttr = rdnAttr.toLowerCase();
629
630     try
631     {
632       validateTemplate();
633     }
634     catch (UnableToRunException utre)
635     {
636       throw new InvalidValueException(utre.getMessage(), utre);
637     }
638   }
639
640
641
642   /**
643    * Indicates whether this job class implements logic that makes it possible to
644    * test the validity of job parameters before scheduling the job for execution
645    * (e.g., to see if the server is reachable using the information provided).
646    *
647    * @return <CODE>true</CODE> if this job provides a means of testing the job
648    * parameters, or <CODE>false</CODE> if not.
649    */

650   public boolean providesParameterTest()
651   {
652     return true;
653   }
654
655
656
657   /**
658    * Provides a means of testing the provided job parameters to determine
659    * whether they are valid (e.g., to see if the server is reachable) before
660    * scheduling the job for execution. This method will be executed by the
661    * SLAMD server system itself and not by any of the clients.
662    *
663    * @param parameters The job parameters to be tested.
664    * @param outputMessages The lines of output that were generated as part of
665    * the testing process. Each line of output should
666    * be added to this list as a separate string, and
667    * empty strings (but not <CODE>null</CODE> values)
668    * are allowed to provide separation between
669    * different messages. No formatting should be
670    * provided for these messages, however, since they
671    * may be displayed in either an HTML or plain text
672    * interface.
673    *
674    * @return <CODE>true</CODE> if the test completed successfully, or
675    * <CODE>false</CODE> if not. Note that even if the test did not
676    * complete successfully, the user will be presented with a warning
677    * but will still be allowed to schedule the job using the provided
678    * parameters. This is necessary because the parameters may still be
679    * valid even if the server couldn't validate them at the time the
680    * job was scheduled (e.g., if the server wasn't running or could not
681    * be reached by the SLAMD server even though it could be by the
682    * clients).
683    */

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

1003  public void initializeClient(String JavaDoc clientID, ParameterList parameters)
1004         throws UnableToRunException
1005  {
1006    // Get the address of the target directory server
1007
ldapHost = null;
1008    hostParameter = parameters.getStringParameter(hostParameter.getName());
1009    if (hostParameter != null)
1010    {
1011      ldapHost = hostParameter.getStringValue();
1012    }
1013
1014    // Get the port for the target directory server
1015
ldapPort = 389;
1016    portParameter = parameters.getIntegerParameter(portParameter.getName());
1017    if (portParameter != null)
1018    {
1019      ldapPort = portParameter.getIntValue();
1020    }
1021
1022    // Get the bind DN for the target directory server
1023
bindDN = "";
1024    bindDNParameter = parameters.getStringParameter(bindDNParameter.getName());
1025    if (bindDNParameter != null)
1026    {
1027      bindDN = bindDNParameter.getStringValue();
1028    }
1029
1030    // Get the bind password for the target directory server
1031
bindPassword = "";
1032    bindPWParameter =
1033         parameters.getPasswordParameter(bindPWParameter.getName());
1034    if (bindPWParameter != null)
1035    {
1036      bindPassword = bindPWParameter.getStringValue();
1037    }
1038
1039    // Get the DN of the proxy as user.
1040
useProxyAuth = false;
1041    proxyAsDNParameter =
1042         parameters.getStringParameter(proxyAsDNParameter.getName());
1043    if ((proxyAsDNParameter != null) && (proxyAsDNParameter.hasValue()))
1044    {
1045      useProxyAuth = true;
1046      proxyAsDN = proxyAsDNParameter.getStringValue();
1047    }
1048
1049    // Get the base DN under which to add the entries.
1050
baseDN = null;
1051    baseDNParameter = parameters.getStringParameter(baseDNParameter.getName());
1052    if ((baseDNParameter != null) && (baseDNParameter.hasValue()))
1053    {
1054      baseDN = baseDNParameter.getStringValue();
1055    }
1056
1057    // Get the name of the RDN attribute to use.
1058
rdnAttr = "uid";
1059    rdnAttrParameter =
1060         parameters.getStringParameter(rdnAttrParameter.getName());
1061    if ((rdnAttrParameter != null) && (rdnAttrParameter.hasValue()))
1062    {
1063      rdnAttr = rdnAttrParameter.getStringValue().toLowerCase();
1064    }
1065    lowerRDNAttr = rdnAttr.toLowerCase();
1066
1067    // Get the initial value of the entry to create.
1068
startValue = 0;
1069    startValueParameter =
1070         parameters.getIntegerParameter(startValueParameter.getName());
1071    if ((startValueParameter != null) && (startValueParameter.hasValue()))
1072    {
1073      startValue = startValueParameter.getIntValue();
1074    }
1075    nextValue = startValue;
1076
1077    // Get the final RDN value to create.
1078
endValue = Integer.MAX_VALUE;
1079    endValueParameter =
1080         parameters.getIntegerParameter(endValueParameter.getName());
1081    if ((endValueParameter != null) && (endValueParameter.hasValue()))
1082    {
1083      endValue = endValueParameter.getIntValue();
1084      if (endValue <= 0)
1085      {
1086        endValue = Integer.MAX_VALUE;
1087      }
1088    }
1089
1090    // Get the template to use to create the entry.
1091
templateLines = DEFAULT_TEMPLATE_LINES;
1092    templateParameter =
1093         parameters.getMultiLineTextParameter(templateParameter.getName());
1094    if ((templateParameter != null) && templateParameter.hasValue())
1095    {
1096      templateLines = templateParameter.getNonBlankLines();
1097    }
1098    validateTemplate();
1099
1100    // Get the warm up time.
1101
warmUpTime = 0;
1102    warmUpParameter = parameters.getIntegerParameter(warmUpParameter.getName());
1103    if (warmUpParameter != null)
1104    {
1105      warmUpTime = warmUpParameter.getIntValue();
1106    }
1107
1108    // Get the cool down time.
1109
coolDownTime = 0;
1110    coolDownParameter =
1111         parameters.getIntegerParameter(coolDownParameter.getName());
1112    if (coolDownParameter != null)
1113    {
1114      coolDownTime = coolDownParameter.getIntValue();
1115    }
1116
1117    // Get the maximum add time limit.
1118
timeLimit = 0;
1119    timeLimitParameter =
1120         parameters.getIntegerParameter(timeLimitParameter.getName());
1121    if (timeLimitParameter != null)
1122    {
1123      timeLimit = timeLimitParameter.getIntValue();
1124    }
1125
1126    // Get the delay between requests.
1127
delay = 0;
1128    delayParameter = parameters.getIntegerParameter(delayParameter.getName());
1129    if (delayParameter != null)
1130    {
1131      delay = delayParameter.getIntValue();
1132    }
1133
1134    // Get the flag indicating whether we should use SSL or not
1135
useSSL = false;
1136    useSSLParameter = parameters.getBooleanParameter(useSSLParameter.getName());
1137    if (useSSLParameter != null)
1138    {
1139      useSSL = useSSLParameter.getBooleanValue();
1140    }
1141
1142    // If we are to use SSL, then get all the other SSL-related info
1143
if (useSSL)
1144    {
1145      blindTrustParameter =
1146           parameters.getBooleanParameter(blindTrustParameter.getName());
1147      if (blindTrustParameter != null)
1148      {
1149        blindTrust = blindTrustParameter.getBooleanValue();
1150      }
1151
1152      // The location of the JSSE key store
1153
sslKeyStore = null;
1154      keyStoreParameter =
1155           parameters.getStringParameter(keyStoreParameter.getName());
1156      if ((keyStoreParameter != null) && (keyStoreParameter.hasValue()))
1157      {
1158        sslKeyStore = keyStoreParameter.getStringValue();
1159        System.setProperty(SSL_KEY_STORE_PROPERTY, sslKeyStore);
1160      }
1161
1162      // The JSSE key store password
1163
sslKeyPassword = null;
1164      keyPWParameter =
1165           parameters.getPasswordParameter(keyPWParameter.getName());
1166      if ((keyPWParameter != null) && (keyPWParameter.hasValue()))
1167      {
1168        sslKeyPassword = keyPWParameter.getStringValue();
1169        System.setProperty(SSL_KEY_PASSWORD_PROPERTY, sslKeyPassword);
1170      }
1171
1172      // The location of the JSSE trust store
1173
sslTrustStore = null;
1174      trustStoreParameter =
1175           parameters.getStringParameter(trustStoreParameter.getName());
1176      if ((trustStoreParameter != null) && (trustStoreParameter.hasValue()))
1177      {
1178        sslTrustStore = trustStoreParameter.getStringValue();
1179        System.setProperty(SSL_TRUST_STORE_PROPERTY, sslTrustStore);
1180      }
1181
1182      // The JSSE trust store password
1183
sslTrustPassword = null;
1184      trustPWParameter =
1185           parameters.getPasswordParameter(trustPWParameter.getName());
1186      if ((trustPWParameter != null) && (trustPWParameter.hasValue()))
1187      {
1188        sslTrustPassword = trustPWParameter.getStringValue();
1189        System.setProperty(SSL_TRUST_PASSWORD_PROPERTY, sslTrustPassword);
1190      }
1191    }
1192
1193    // Get the flag indicating whether we should disconnect after each add.
1194
alwaysDisconnect = false;
1195    disconnectParameter =
1196         parameters.getBooleanParameter(disconnectParameter.getName());
1197    if (disconnectParameter != null)
1198    {
1199      alwaysDisconnect = disconnectParameter.getBooleanValue();
1200    }
1201  }
1202
1203
1204
1205  /**
1206   * Initializes this job thread to be used to actually run the job on the
1207   * client. The provided parameter list should be processed to customize the
1208   * behavior of this job thread, and any other initialization that needs to be
1209   * done in order for the job to run should be performed here as well.
1210   *
1211   * @param clientID The client ID for this job thread.
1212   * @param threadID The thread ID for this job thread.
1213   * @param collectionInterval The length of time in seconds to use as the
1214   * statistics collection interval.
1215   * @param parameters The set of parameters provided to this job that
1216   * can be used to customize its behavior.
1217   *
1218   * @throws UnableToRunException If a problem occurs that prevents the thread
1219   * from being able to run properly.
1220   */

1221  public void initializeThread(String JavaDoc clientID, String JavaDoc threadID,
1222                               int collectionInterval, ParameterList parameters)
1223         throws UnableToRunException
1224  {
1225    // Set up the stat trackers
1226
addCount = new IncrementalTracker(clientID, threadID,
1227                                      STAT_TRACKER_ADD_COUNT,
1228                                      collectionInterval);
1229    addTime = new TimeTracker(clientID, threadID, STAT_TRACKER_ADD_TIME,
1230                              collectionInterval);
1231    resultCodes = new CategoricalTracker(clientID, threadID,
1232                                         STAT_TRACKER_RESULT_CODES,
1233                                         collectionInterval);
1234    totalAdds = new AccumulatingTracker(clientID, threadID,
1235                                        STAT_TRACKER_ADD_TOTAL,
1236                                        collectionInterval);
1237
1238
1239    // Enable real-time reporting of the data for these stat trackers.
1240
RealTimeStatReporter statReporter = getStatReporter();
1241    if (statReporter != null)
1242    {
1243      String JavaDoc jobID = getJobID();
1244      addCount.enableRealTimeStats(statReporter, jobID);
1245      addTime.enableRealTimeStats(statReporter, jobID);
1246      totalAdds.enableRealTimeStats(statReporter, jobID);
1247    }
1248
1249
1250    // If the connection is to use SSL, then establish a preliminary connection
1251
// now. The first connection can take a significant amount of time to
1252
// establish, and we want to get it out of the way early before the timer
1253
// starts (if a duration is specified). Don't worry about any exceptions
1254
// that may get thrown here because there's no easy way to report them back
1255
// but they will be repeated and handled when the job starts running anyway,
1256
// so no big deal.
1257
if (useSSL)
1258    {
1259      try
1260      {
1261        if (blindTrust)
1262        {
1263          conn = new LDAPConnection(new JSSEBlindTrustSocketFactory());
1264        }
1265        else
1266        {
1267          conn = new LDAPConnection(new JSSESocketFactory(null));
1268        }
1269        conn.connect(3, ldapHost, ldapPort, bindDN, bindPassword);
1270        conn.disconnect();
1271      }
1272      catch (Exception JavaDoc e) {}
1273    }
1274  }
1275
1276
1277
1278  /**
1279   * Perform the work of this job thread by establishing the connection(s) to
1280   * the directory server and issuing all the appropriate queries. The job will
1281   * continue until the specified number of iterations have been performed, the
1282   * stop time has been reached, the maximum duration has been reached, or the
1283   * SLAMD server indicates that a stop has been requested.
1284   */

1285  public void runJob()
1286  {
1287    // Determine the range of time for which we should collect statistics.
1288
long currentTime = System.currentTimeMillis();
1289    boolean collectingStats = false;
1290    long startCollectingTime = currentTime + (1000 * warmUpTime);
1291    long stopCollectingTime = Long.MAX_VALUE;
1292    if ((coolDownTime > 0) && (getShouldStopTime() > 0))
1293    {
1294      stopCollectingTime = getShouldStopTime() - (1000 * coolDownTime);
1295    }
1296
1297    // Set a variable that we can use to determine if the connection is alive or
1298
// not
1299
boolean connected = false;
1300
1301    // Set a variable that should be used to determine whether all the entries
1302
// have been added yet or not.
1303
boolean allAdded = false;
1304
1305    // Set a variable that can be used to determine how long we should sleep
1306
// between adds.
1307
long addStartTime = 0;
1308
1309    // Create a variable that we will use for the LDAP connection
1310
if (useSSL)
1311    {
1312      if (blindTrust)
1313      {
1314        try
1315        {
1316          conn = new LDAPConnection(new JSSEBlindTrustSocketFactory());
1317        }
1318        catch (LDAPException le)
1319        {
1320          logMessage(le.getMessage());
1321          indicateStoppedDueToError();
1322          return;
1323        }
1324      }
1325      else
1326      {
1327        conn = new LDAPConnection(new JSSESocketFactory(null));
1328      }
1329    }
1330    else
1331    {
1332      conn = new LDAPConnection();
1333    }
1334
1335
1336    // Create a loop that will run until it needs to stop
1337
while ((! shouldStop()) && (! allAdded))
1338    {
1339      currentTime = System.currentTimeMillis();
1340      if ((! collectingStats) && (currentTime >= startCollectingTime) &&
1341          (currentTime < stopCollectingTime))
1342      {
1343        // Tell the stat trackers that they should start tracking now
1344
addCount.startTracker();
1345        addTime.startTracker();
1346        resultCodes.startTracker();
1347        totalAdds.startTracker();
1348        collectingStats = true;
1349      }
1350      else if ((collectingStats) && (currentTime >= stopCollectingTime))
1351      {
1352        addCount.stopTracker();
1353        addTime.stopTracker();
1354        resultCodes.stopTracker();
1355        totalAdds.stopTracker();
1356        collectingStats = false;
1357      }
1358
1359      // If the connection is currently not connected, then establish it
1360
if (! connected)
1361      {
1362        try
1363        {
1364          conn.connect(3, ldapHost, ldapPort, bindDN, bindPassword);
1365          connected = true;
1366        }
1367        catch (LDAPException le)
1368        {
1369          logMessage("ERROR -- Could not connect to " + ldapHost + ":" +
1370                           ldapPort + " (" + le + ") -- aborting thread");
1371          if (collectingStats)
1372          {
1373            resultCodes.increment(String.valueOf(le.getLDAPResultCode()));
1374          }
1375          indicateStoppedDueToError();
1376          break;
1377        }
1378      }
1379
1380      LDAPConstraints constraints = conn.getConstraints();
1381      if (useProxyAuth)
1382      {
1383        LDAPProxiedAuthControl proxyAuthControl =
1384             new LDAPProxiedAuthControl(proxyAsDN, true);
1385        constraints.setServerControls(proxyAuthControl);
1386      }
1387      constraints.setTimeLimit(1000 * timeLimit);
1388
1389
1390      // Create a flag used to determine if the add was successful.
1391
LDAPEntry entryToAdd = createEntry();
1392      if (entryToAdd == null)
1393      {
1394        allAdded = true;
1395      }
1396      else
1397      {
1398        // Record the current time as the start of the add.
1399
if (collectingStats)
1400        {
1401          addTime.startTimer();
1402        }
1403        if (delay > 0)
1404        {
1405          addStartTime = System.currentTimeMillis();
1406        }
1407
1408        // Perform the add
1409
int resultCode = LDAPException.SUCCESS;
1410        try
1411        {
1412          conn.add(entryToAdd, constraints);
1413        }
1414        catch (LDAPException le)
1415        {
1416          resultCode = le.getLDAPResultCode();
1417        }
1418
1419
1420        // Record the current time as the end of the add.
1421
if (collectingStats)
1422        {
1423          addCount.increment();
1424          totalAdds.increment();
1425          addTime.stopTimer();
1426          resultCodes.increment(String.valueOf(resultCode));
1427        }
1428      }
1429
1430      // If the connection should be broken, then do so
1431
if (alwaysDisconnect)
1432      {
1433        try
1434        {
1435          conn.disconnect();
1436        } catch (LDAPException le) {}
1437        connected = false;
1438      }
1439
1440      // If we need to sleep, then do so
1441
if ((delay > 0) && (! shouldStop()))
1442      {
1443        long now = System.currentTimeMillis();
1444        long sleepTime = delay - (now - addStartTime);
1445        if (sleepTime > 0)
1446        {
1447          try
1448          {
1449            Thread.sleep(sleepTime);
1450          } catch (InterruptedException JavaDoc ie) {}
1451        }
1452      }
1453    }
1454
1455
1456    // If the connection is still established, then close it
1457
try
1458    {
1459      conn.disconnect();
1460    } catch (LDAPException le) {}
1461
1462
1463    // Tell the stat trackers that they should stop tracking
1464
if (collectingStats)
1465    {
1466      addCount.stopTracker();
1467      addTime.stopTimer();
1468      resultCodes.stopTracker();
1469      totalAdds.stopTracker();
1470    }
1471  }
1472
1473
1474
1475  /**
1476   * Attempts to force this thread to exit by closing the connection to the
1477   * directory server and setting it to <CODE>null</CODE>.
1478   */

1479  public void destroy()
1480  {
1481    if (conn != null)
1482    {
1483      try
1484      {
1485        conn.disconnect();
1486      } catch (Exception JavaDoc e) {}
1487
1488      conn = null;
1489    }
1490  }
1491
1492
1493
1494  /**
1495   * Parses the provided template and ensures that it is acceptable for use in
1496   * creating entries.
1497   *
1498   * @throws UnableToRunException If the provided template is not acceptable
1499   * for some reason.
1500   */

1501  public void validateTemplate()
1502         throws UnableToRunException
1503  {
1504    boolean rdnAttrFound = false;
1505    attrNames = new String JavaDoc[templateLines.length];
1506    lowerNames = new String JavaDoc[templateLines.length];
1507    separators = new String JavaDoc[templateLines.length];
1508    attrValues = new String JavaDoc[templateLines.length];
1509
1510    for (int i=0; i < templateLines.length; i++)
1511    {
1512      int colonPos = templateLines[i].indexOf(':');
1513      if (colonPos < 0)
1514      {
1515        throw new UnableToRunException("No colon found in template line \"" +
1516                                       templateLines[i] + "\" to separate " +
1517                                       "the attribute name from the value.");
1518      }
1519      else if (colonPos == 0)
1520      {
1521        throw new UnableToRunException("No attribute name found in template " +
1522                                       "line \"" + templateLines[i] + "\".");
1523      }
1524      else if (colonPos == (templateLines[i].length() - 1))
1525      {
1526        throw new UnableToRunException("No attribute value found in template " +
1527                                       "line \"" + templateLines[i] + "\".");
1528      }
1529      attrNames[i] = templateLines[i].substring(0, colonPos);
1530      lowerNames[i] = attrNames[i].toLowerCase();
1531      if (lowerNames[i].equals(lowerRDNAttr))
1532      {
1533        rdnAttrFound = true;
1534      }
1535
1536      char nextChar = templateLines[i].charAt(colonPos+1);
1537      if (nextChar == ' ')
1538      {
1539        separators[i] = ":";
1540        attrValues[i] = templateLines[i].substring(colonPos+2).trim();
1541      }
1542      else if (nextChar == ':')
1543      {
1544        if (colonPos == (templateLines[i].length() - 2))
1545        {
1546          throw new UnableToRunException("No attribute value found in " +
1547                                         "template line \"" + templateLines[i] +
1548                                         "\".");
1549        }
1550        else if (templateLines[i].charAt(colonPos+2) == ' ')
1551        {
1552          separators[i] = "::";
1553          attrValues[i] = templateLines[i].substring(colonPos+3).trim();
1554        }
1555        else
1556        {
1557          throw new UnableToRunException("Invalid character sequence found " +
1558                                         "in template line \"" +
1559                                         templateLines[i] + "\" -- illegal " +
1560                                         "character '" +
1561                                         templateLines[i].charAt(colonPos+2) +
1562                                         "' in column " + (colonPos+2));
1563        }
1564      }
1565      else
1566      {
1567        throw new UnableToRunException("Invalid character sequence found " +
1568                                       "in template line \"" +
1569                                       templateLines[i] + "\" -- illegal " +
1570                                       "character '" +
1571                                       templateLines[i].charAt(colonPos+1) +
1572                                       "' in column " + (colonPos+1));
1573      }
1574    }
1575
1576    if (! rdnAttrFound)
1577    {
1578      throw new UnableToRunException("No value provided for RDN attribute \"" +
1579                                     rdnAttr + "\" in template definition.");
1580    }
1581  }
1582
1583
1584
1585  /**
1586   * Creates a randomly-generated LDAP entry to be added to the directory.
1587   *
1588   * @return The randomly-generated entry.
1589   */

1590  public LDAPEntry createEntry()
1591  {
1592    int entryValue = nextValue++;
1593    int entryNumCreated = entryValue - startValue + 1;
1594    if (nextValue > endValue)
1595    {
1596      return null;
1597    }
1598
1599    LDAPAttributeSet attributeSet = new LDAPAttributeSet();
1600    String JavaDoc rdnValue = null;
1601    for (int i=0; i < attrNames.length; i++)
1602    {
1603      String JavaDoc value = processValue(attrValues[i], attributeSet, entryValue,
1604                                  entryNumCreated);
1605      if (value == null)
1606      {
1607        continue;
1608      }
1609
1610      if ((rdnValue == null) && (lowerNames[i].equals(lowerRDNAttr)))
1611      {
1612        rdnValue = value;
1613      }
1614      attributeSet.add(new LDAPAttribute(attrNames[i], value));
1615    }
1616
1617    return new LDAPEntry(rdnAttr + "=" + rdnValue + "," + baseDN,
1618                         attributeSet);
1619  }
1620
1621
1622
1623  /**
1624   * Generates the appropriate value from the given line in the template.
1625   *
1626   * @param value The value to be processed.
1627   * @param attributeSet The attribute set containing the values of the
1628   * attributes previously defined.
1629   * @param entryNumber The unique number assigned to the entry being
1630   * created.
1631   * @param entryInSequence A counter used to determine how many entries have
1632   * been created so far.
1633   *
1634   * @return The generated value.
1635   */

1636  public String JavaDoc processValue(String JavaDoc value, LDAPAttributeSet attributeSet,
1637                             int entryNumber, int entryInSequence)
1638  {
1639    boolean needReprocess = true;
1640    int pos;
1641
1642    // If the value contains "<presence:", then determine if it should
1643
// actually be included in this entry. If not, then just go to the next
1644
// attribute
1645
if ((pos = value.indexOf("<presence:")) >= 0)
1646    {
1647      int closePos = value.indexOf(">", pos);
1648      if (closePos > pos)
1649      {
1650        String JavaDoc numStr = value.substring(pos+10, closePos);
1651        try
1652        {
1653          int percentage = Integer.parseInt(numStr);
1654          int randomValue = ((random.nextInt() & 0x7FFFFFFF) % 100) + 1;
1655          if (randomValue <= percentage)
1656          {
1657            // We have determined that this value should be included in the
1658
// entry, so remove the "<presence:x>" tag and let it go on to do
1659
// the rest of the processing on this entry
1660
value = value.substring(0, pos) + value.substring(closePos+1);
1661          }
1662          else
1663          {
1664            // We have determined that this value should not be included in
1665
// the entry, so return null.
1666
return null;
1667          }
1668        }
1669        catch (NumberFormatException JavaDoc nfe)
1670        {
1671          return null;
1672        }
1673      }
1674    }
1675
1676    // If the value contains "<ifpresent:{attrname}>", then determine if it
1677
// should actually be included in this entry. If not, then just go to the
1678
// next attribute.
1679
if ((pos = value.indexOf("<ifpresent:")) >= 0)
1680    {
1681      int closePos = value.indexOf(">", pos);
1682      if (closePos > pos)
1683      {
1684        int colonPos = value.indexOf(":", pos+11);
1685        if ((colonPos > 0) && (colonPos < closePos))
1686        {
1687          // Look for a specific value to be present.
1688
boolean matchFound = false;
1689          String JavaDoc attrName = value.substring(pos+11, colonPos);
1690          String JavaDoc matchValue = value.substring(colonPos+1, closePos);
1691
1692          String JavaDoc[] values = null;
1693          LDAPAttribute attr = attributeSet.getAttribute(attrName);
1694          if (attr != null)
1695          {
1696            values = attr.getStringValueArray();
1697          }
1698
1699          for (int j=0; ((values != null) && (j < values.length)); j++)
1700          {
1701            if (matchValue.equalsIgnoreCase(values[j]))
1702            {
1703              value = value.substring(0, pos) + value.substring(closePos+1);
1704              matchFound = true;
1705              break;
1706            }
1707          }
1708
1709          if (! matchFound)
1710          {
1711            return null;
1712          }
1713        }
1714        else
1715        {
1716          // Just look for the attribute to be present.
1717
String JavaDoc attrName = value.substring(pos+11, closePos);
1718          String JavaDoc[] values = null;
1719          LDAPAttribute attr = attributeSet.getAttribute(attrName);
1720          if (attr != null)
1721          {
1722            values = attr.getStringValueArray();
1723          }
1724          if ((values == null) || (values.length == 0))
1725          {
1726            // The requested attribute is not present, so skip this line.
1727
return null;
1728          }
1729          else
1730          {
1731            value = value.substring(0, pos) + value.substring(closePos+1);
1732          }
1733        }
1734      }
1735    }
1736
1737    // If the value contains "<ifabsent:{attrname}>", then determine if it
1738
// should actually be included in this entry. If not, then just go to the
1739
// next attribute.
1740
if ((pos = value.indexOf("<ifabsent:")) >= 0)
1741    {
1742      int closePos = value.indexOf(">", pos);
1743      if (closePos > pos)
1744      {
1745        int colonPos = value.indexOf(":", pos+10);
1746        if ((colonPos > 0) && (colonPos < closePos))
1747        {
1748          // Look for a specific value to be present.
1749
boolean matchFound = false;
1750          String JavaDoc attrName = value.substring(pos+10, colonPos);
1751          String JavaDoc matchValue = value.substring(colonPos+1, closePos);
1752
1753          String JavaDoc[] values = null;
1754          LDAPAttribute attr = attributeSet.getAttribute(attrName);
1755          if (attr != null)
1756          {
1757            values = attr.getStringValueArray();
1758          }
1759
1760          for (int j=0; ((values != null) && (j < values.length)); j++)
1761          {
1762            if (matchValue.equalsIgnoreCase(values[j]))
1763            {
1764              matchFound = true;
1765              break;
1766            }
1767          }
1768
1769          if (matchFound)
1770          {
1771            return null;
1772          }
1773          else
1774          {
1775            value = value.substring(0, pos) + value.substring(closePos+1);
1776          }
1777        }
1778        else
1779        {
1780          // Just look for the attribute to be present.
1781
String JavaDoc attrName = value.substring(pos+10, closePos);
1782          String JavaDoc[] values = null;
1783          LDAPAttribute attr = attributeSet.getAttribute(attrName);
1784          if (attr != null)
1785          {
1786            values = attr.getStringValueArray();
1787          }
1788          if ((values != null) && (values.length > 0))
1789          {
1790            // The requested attribute is present, so skip this line.
1791
return null;
1792          }
1793          else
1794          {
1795            value = value.substring(0, pos) + value.substring(closePos+1);
1796          }
1797        }
1798      }
1799    }
1800
1801    while (needReprocess && (value.indexOf("<") >= 0))
1802    {
1803      needReprocess = false;
1804
1805
1806      // If the value contains "<entryNumber>" then replace that with the first
1807
// name
1808
if ((pos = value.indexOf("<entrynumber>")) >= 0)
1809      {
1810        value = value.substring(0, pos) + entryNumber +
1811                value.substring(pos + 13);
1812        needReprocess = true;
1813      }
1814      if ((pos = value.indexOf("<entryNumber>")) >= 0)
1815      {
1816        value = value.substring(0, pos) + entryNumber +
1817                value.substring(pos + 13);
1818        needReprocess = true;
1819      }
1820
1821      // If the value contains "<random:chars:characters:length>" then
1822
// generate a random string of length characters from the provided
1823
// character set.
1824
if ((pos = value.indexOf("<random:chars:")) >= 0)
1825      {
1826        // Get the set of characters to use in the resulting value.
1827
int colonPos = value.indexOf(":", pos+14);
1828        int closePos = value.indexOf(">", colonPos+1);
1829        String JavaDoc charSet = value.substring(pos+14, colonPos);
1830
1831        // See if there is an additional colon followed by a number. If so,
1832
// then the length will be a random number between the two.
1833
int count;
1834        int colonPos2 = value.indexOf(":", colonPos+1);
1835        if ((colonPos2 > 0) && (colonPos2 < closePos))
1836        {
1837          int minValue = Integer.parseInt(value.substring(colonPos+1,
1838                                                          colonPos2));
1839          int maxValue = Integer.parseInt(value.substring(colonPos2+1,
1840                                                          closePos));
1841          int span = maxValue - minValue + 1;
1842          count = (random.nextInt() & 0x7FFFFFFF) % span + minValue;
1843        }
1844        else
1845        {
1846          count = Integer.parseInt(value.substring(colonPos+1, closePos));
1847        }
1848
1849        String JavaDoc randVal = generateRandomValue(charSet.toCharArray(), count);
1850        value = value.substring(0, pos) + randVal +
1851                value.substring(closePos+1);
1852        needReprocess = true;
1853      }
1854
1855      // If the value contains "<random:alpha:num>" then generate a random
1856
// alphabetic value and use it.
1857
if ((pos = value.indexOf("<random:alpha:")) >= 0)
1858      {
1859        // See if there is an additional colon followed by a number. If so,
1860
// then the length will be a random number between the two.
1861
int count;
1862        int closePos = value.indexOf(">", pos+14);
1863        int colonPos = value.indexOf(":", pos+14);
1864        if ((colonPos > 0) && (colonPos < closePos))
1865        {
1866          int minValue = Integer.parseInt(value.substring(pos+14, colonPos));
1867          int maxValue = Integer.parseInt(value.substring(colonPos+1,
1868                                                          closePos));
1869          int span = maxValue - minValue + 1;
1870          count = (random.nextInt() & 0x7FFFFFFF) % span + minValue;
1871         }
1872        else
1873        {
1874          count = Integer.parseInt(value.substring(pos+14, closePos));
1875        }
1876
1877        // Generate the new value.
1878
String JavaDoc randVal = generateRandomValue(ALPHA_CHARS, count);
1879        value = value.substring(0, pos) + randVal +
1880                value.substring(closePos + 1);
1881        needReprocess = true;
1882      }
1883
1884      // If the value contains "<random:numeric:num>" then generate a random
1885
// numeric value and use it. This can also take the form
1886
// "<random:numeric:min:max>" or "<random:numeric:min:max:length>".
1887
if ((pos = value.indexOf("<random:numeric:")) >= 0)
1888      {
1889        int closePos = value.indexOf('>', pos);
1890
1891        // See if there is an extra colon. If so, then generate a random
1892
// number between x and y. Otherwise, generate a random number with
1893
// the specified number of digits.
1894
int extraColonPos = value.indexOf(':', pos+16);
1895        if ((extraColonPos > 0) && (extraColonPos < closePos))
1896        {
1897          // See if there is one more colon separating the max from the
1898
// length. If so, then get it and create a padded value of at least
1899
// length digts. If not, then just generate the random value.
1900
int extraColonPos2 = value.indexOf(':', extraColonPos+1);
1901          if ((extraColonPos2 > 0) && (extraColonPos2 < closePos))
1902          {
1903            String JavaDoc lowerBoundStr = value.substring(pos+16, extraColonPos);
1904            String JavaDoc upperBoundStr = value.substring(extraColonPos+1,
1905                                                   extraColonPos2);
1906            String JavaDoc lengthStr = value.substring(extraColonPos2+1, closePos);
1907            int lowerBound = Integer.parseInt(lowerBoundStr);
1908            int upperBound = Integer.parseInt(upperBoundStr);
1909            int length = Integer.parseInt(lengthStr);
1910            int span = (upperBound - lowerBound + 1);
1911            int randomValue = (random.nextInt() & 0x7FFFFFFF) % span +
1912                              lowerBound;
1913            String JavaDoc valueStr = String.valueOf(randomValue);
1914            while (valueStr.length() < length)
1915            {
1916              valueStr = "0" + valueStr;
1917            }
1918            value = value.substring(0, pos) + valueStr +
1919                    value.substring(closePos+1);
1920          }
1921          else
1922          {
1923            String JavaDoc lowerBoundStr = value.substring(pos+16, extraColonPos);
1924            String JavaDoc upperBoundStr = value.substring(extraColonPos+1, closePos);
1925            int lowerBound = Integer.parseInt(lowerBoundStr);
1926            int upperBound = Integer.parseInt(upperBoundStr);
1927            int span = (upperBound - lowerBound + 1);
1928            int randomValue = (random.nextInt() & 0x7FFFFFFF) % span +
1929                              lowerBound;
1930            value = value.substring(0, pos) + randomValue +
1931                    value.substring(closePos+1);
1932          }
1933        }
1934        else
1935        {
1936          // Get the number of characters to include in the value
1937
int numPos = pos + 16;
1938          int count = Integer.parseInt(value.substring(numPos, closePos));
1939          String JavaDoc randVal = generateRandomValue(NUMERIC_CHARS, count);
1940          value = value.substring(0, pos) + randVal +
1941                  value.substring(closePos+1);
1942        }
1943
1944        needReprocess = true;
1945      }
1946
1947      // If the value contains "<random:alphanumeric:num>" then generate a
1948
// random alphanumeric value and use it
1949
if ((pos = value.indexOf("<random:alphanumeric:")) >= 0)
1950      {
1951        // See if there is an additional colon followed by a number. If so,
1952
// then the length will be a random number between the two.
1953
int count;
1954        int closePos = value.indexOf(">", pos+21);
1955        int colonPos = value.indexOf(":", pos+21);
1956        if ((colonPos > 0) && (colonPos < closePos))
1957        {
1958          int minValue = Integer.parseInt(value.substring(pos+21, colonPos));
1959          int maxValue = Integer.parseInt(value.substring(colonPos+1,
1960                                                          closePos));
1961          int span = maxValue - minValue + 1;
1962          count = (random.nextInt() & 0x7FFFFFFF) % span + minValue;
1963         }
1964        else
1965        {
1966          count = Integer.parseInt(value.substring(pos+21, closePos));
1967        }
1968
1969        // Generate the new value.
1970
String JavaDoc randVal = generateRandomValue(ALPHANUMERIC_CHARS, count);
1971        value = value.substring(0, pos) + randVal +
1972                value.substring(closePos + 1);
1973        needReprocess = true;
1974      }
1975
1976      // If the value contains "<random:hex:num>" then generate a random
1977
// hexadecimal value and use it
1978
if ((pos = value.indexOf("<random:hex:")) >= 0)
1979      {
1980        // See if there is an additional colon followed by a number. If so,
1981
// then the length will be a random number between the two.
1982
int count;
1983        int closePos = value.indexOf(">", pos+12);
1984        int colonPos = value.indexOf(":", pos+12);
1985        if ((colonPos > 0) && (colonPos < closePos))
1986        {
1987          int minValue = Integer.parseInt(value.substring(pos+12, colonPos));
1988          int maxValue = Integer.parseInt(value.substring(colonPos+1,
1989                                                          closePos));
1990          int span = maxValue - minValue + 1;
1991          count = (random.nextInt() & 0x7FFFFFFF) % span + minValue;
1992         }
1993        else
1994        {
1995          count = Integer.parseInt(value.substring(pos+12, closePos));
1996        }
1997
1998        // Generate the new value.
1999
String JavaDoc randVal = generateRandomValue(HEX_CHARS, count);
2000        value = value.substring(0, pos) + randVal +
2001                value.substring(closePos + 1);
2002        needReprocess = true;
2003      }
2004
2005      // If the value contains "<random:base64:num>" then generate a random
2006
// base64 value and use it
2007
if ((pos = value.indexOf("<random:base64:")) >= 0)
2008      {
2009        // See if there is an additional colon followed by a number. If so,
2010
// then the length will be a random number between the two.
2011
int count;
2012        int closePos = value.indexOf(">", pos+15);
2013        int colonPos = value.indexOf(":", pos+15);
2014        if ((colonPos > 0) && (colonPos < closePos))
2015        {
2016          int minValue = Integer.parseInt(value.substring(pos+15, colonPos));
2017          int maxValue = Integer.parseInt(value.substring(colonPos+1,
2018                                                          closePos));
2019          int span = maxValue - minValue + 1;
2020          count = (random.nextInt() & 0x7FFFFFFF) % span + minValue;
2021         }
2022        else
2023        {
2024          count = Integer.parseInt(value.substring(pos+15, closePos));
2025        }
2026
2027        // Generate the new value.
2028
String JavaDoc randVal = generateRandomValue(BASE64_CHARS, count);
2029        switch (count % 4)
2030        {
2031          case 1: randVal += "===";
2032                   break;
2033          case 2: randVal += "==";
2034                   break;
2035          case 3: randVal += "=";
2036                   break;
2037        }
2038        value = value.substring(0, pos) + randVal +
2039                value.substring(closePos + 1);
2040        needReprocess = true;
2041      }
2042
2043      // If the value contains "<random:telephone>" then generate a random
2044
// telephone number and use it
2045
if ((pos = value.indexOf("<random:telephone>")) >= 0)
2046      {
2047        // Get the number of characters to include in the value
2048
String JavaDoc randVal = generateRandomValue(NUMERIC_CHARS, 10);
2049        value = value.substring(0, pos) + randVal.substring(0, 3) + "-" +
2050                randVal.substring(3, 6) + "-" + randVal.substring(6) +
2051                value.substring(pos + 18);
2052        needReprocess = true;
2053      }
2054
2055      // If the value contains "<random:month>" then choose a random month
2056
// name. Optionally, look for "<random:month:length>" and use at most
2057
// length characters of the month name.
2058
if ((pos = value.indexOf("<random:month")) >= 0)
2059      {
2060        int closePos = value.indexOf('>', pos+13);
2061        String JavaDoc monthStr = MONTH_NAMES[(random.nextInt() & 0x7FFFFFFF) % 12];
2062
2063        // See if there is another colon that specifies the length.
2064
int colonPos = value.indexOf(':', pos+13);
2065        if ((colonPos > 0) && (colonPos < closePos))
2066        {
2067          String JavaDoc lengthStr = value.substring(colonPos+1, closePos);
2068          int length = Integer.parseInt(lengthStr);
2069          if (monthStr.length() > length)
2070          {
2071            monthStr = monthStr.substring(0, length);
2072          }
2073        }
2074
2075        value = value.substring(0, pos) + monthStr +
2076                value.substring(closePos+1);
2077        needReprocess = true;
2078      }
2079
2080      // If the value contains "<guid>" then generate a GUID and use it
2081
if ((pos = value.indexOf("<guid>")) >= 0)
2082      {
2083        // Get the number of characters to include in the value
2084
value = value.substring(0, pos) + generateGUID() +
2085                value.substring(pos + 6);
2086        needReprocess = true;
2087      }
2088
2089      // If the value contains "<sequential>" then use the next sequential
2090
// value for that attribute
2091
if ((pos = value.indexOf("<sequential")) >= 0)
2092      {
2093        int closePos = value.indexOf(">", pos);
2094
2095        // If a starting point was specified, then use it. If not, then use 0.
2096
int colonPos = value.indexOf(":", pos);
2097        int startingValue = 0;
2098        if ((colonPos > pos) && (colonPos < closePos))
2099        {
2100          startingValue = Integer.parseInt(value.substring(colonPos+1,
2101                                                           closePos));
2102        }
2103
2104        value = value.substring(0, pos) + (startingValue + entryInSequence) +
2105                value.substring(closePos+1);
2106        needReprocess = true;
2107      }
2108    }
2109
2110    needReprocess = true;
2111    while (needReprocess && ((pos = value.indexOf("{")) >= 0))
2112    {
2113      // If there is a backslash in front of the curly brace, then we don't
2114
// want to consider it an attribute name.
2115
if ((pos > 0) && (value.charAt(pos-1) == '\\'))
2116      {
2117        boolean keepGoing = true;
2118        boolean nonEscaped = false;
2119        while (keepGoing)
2120        {
2121          value = value.substring(0, pos-1) + value.substring(pos);
2122
2123          pos = value.indexOf('{', pos);
2124          if (pos < 0)
2125          {
2126            keepGoing = false;
2127          }
2128          else if (value.charAt(pos-1) != '\\')
2129          {
2130            nonEscaped = true;
2131          }
2132        }
2133
2134        if (! nonEscaped)
2135        {
2136          break;
2137        }
2138      }
2139
2140
2141      // If the value has "{attr}", then try to replace it with the value of
2142
// that attribute. Note that attribute replacement will only work
2143
// properly for attributes that are defined in the template before the
2144
// attribute that attempts to use its value. If the specified attribute
2145
// has more than one value, then the first value found will be used.
2146
int closePos = value.indexOf("}", pos);
2147      if (closePos > 0)
2148      {
2149        int colonPos = value.indexOf(":", pos);
2150        int substringChars = -1;
2151        String JavaDoc attrName = null;
2152        if ((colonPos > 0) && (colonPos < closePos))
2153        {
2154          attrName = value.substring(pos+1, colonPos).toLowerCase();
2155          String JavaDoc numStr = value.substring(colonPos+1, closePos);
2156          try
2157          {
2158            substringChars = Integer.parseInt(numStr);
2159          }
2160          catch (NumberFormatException JavaDoc nfe)
2161          {
2162            writeVerbose(nfe.toString());
2163            return null;
2164          }
2165        }
2166        else
2167        {
2168          attrName = value.substring(pos+1, closePos).toLowerCase();
2169        }
2170
2171        String JavaDoc attrValue = "";
2172        LDAPAttribute attr = attributeSet.getAttribute(attrName);
2173        if (attr != null)
2174        {
2175          String JavaDoc[] values = attr.getStringValueArray();
2176          if ((values != null) && (values.length > 0))
2177          {
2178            attrValue = values[0];
2179          }
2180        }
2181        if ((colonPos > 0) && (colonPos < closePos) && (substringChars > 0) &&
2182            (attrValue.length() > substringChars))
2183        {
2184          attrValue = attrValue.substring(0, substringChars);
2185        }
2186
2187        StringBuffer JavaDoc valueBuffer = new StringBuffer JavaDoc(1000);
2188        valueBuffer.append(value.substring(0, pos)).append(attrValue).
2189                         append(value.substring(closePos+1));
2190        value = valueBuffer.toString();
2191        needReprocess = true;
2192      }
2193    }
2194
2195    if ((pos = value.indexOf("<base64:")) >= 0)
2196    {
2197      String JavaDoc charset;
2198      String JavaDoc valueToEncode;
2199
2200      int closePos = value.indexOf('>', pos+8);
2201      int colonPos = value.indexOf(':', pos+8);
2202      if ((closePos > 0) && (colonPos > 0) && (colonPos < closePos))
2203      {
2204        charset = value.substring(pos+8, colonPos);
2205        valueToEncode = value.substring(colonPos+1, closePos);
2206      }
2207      else
2208      {
2209        charset = "UTF-8";
2210        valueToEncode = value.substring(pos+8, closePos);
2211      }
2212
2213      try
2214      {
2215        String JavaDoc encodedStr =
2216                    Base64Encoder.encode(valueToEncode.getBytes(charset));
2217        value = value.substring(0, pos) + encodedStr +
2218                value.substring(closePos+1);
2219      }
2220      catch (UnsupportedEncodingException uee)
2221      {
2222        writeVerbose(uee.toString());
2223        return null;
2224      }
2225    }
2226
2227    return value;
2228  }
2229
2230
2231
2232  /**
2233   * Retrieves a string containing the specified number of randomly-chosen
2234   * characters.
2235   *
2236   * @param charSet The character set from which to take the characters to use
2237   * in the generated value.
2238   * @param length The number of characters to include in the string.
2239   *
2240   * @return A string containing the specified number of randomly-chosen
2241   * characters.
2242   */

2243  public String JavaDoc generateRandomValue(char[] charSet, int length)
2244  {
2245    // A performance optimization in an attempt to avoid continually
2246
// re-allocating character arrays. If the requested array is less than
2247
// 5000 characters long, then use the space we've already allocated for this
2248
// purpose. If it is longer than 5000 characters, then we'll take the
2249
// hit of re-allocating the memory.
2250
if (length <= 5000)
2251    {
2252      for (int i=0; i < length; i++)
2253      {
2254        chars5000[i] = charSet[(random.nextInt() & 0x7FFFFFFF) %
2255                               charSet.length];
2256      }
2257
2258      return new String JavaDoc(chars5000, 0, length);
2259    }
2260    else
2261    {
2262      char[] retArray = new char[length];
2263
2264      for (int i=0; i < length; i++)
2265      {
2266        retArray[i] = charSet[(random.nextInt() & 0x7FFFFFFF) % charSet.length];
2267      }
2268
2269      return new String JavaDoc(retArray);
2270    }
2271  }
2272
2273
2274
2275  /**
2276   * Generates a globally-unique identifier. Technically speaking, it's not
2277   * guaranteed to be globally unique, but this should be good enough for most
2278   * purposes.
2279   *
2280   * @return The GUID that was generated.
2281   */

2282  public String JavaDoc generateGUID()
2283  {
2284    String JavaDoc timeStr = Long.toHexString(System.currentTimeMillis());
2285    String JavaDoc tmpStr = timeStr +
2286                    generateRandomValue(HEX_CHARS, 20-timeStr.length());
2287    return tmpStr.substring(0, 8) + "-" + tmpStr.substring(8, 12) + "-" +
2288           tmpStr.substring(12,16) + "-" + tmpStr.substring(16) + "-" +
2289           guidBase;
2290  }
2291}
2292
2293
Popular Tags