KickJava   Java API By Example, From Geeks To Geeks.

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


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.net.*;
22 import java.text.*;
23 import java.util.*;
24 import com.sun.slamd.job.*;
25 import com.sun.slamd.parameter.*;
26 import com.sun.slamd.stat.*;
27
28
29
30 /**
31  * This class defines a SLAMD job that interacts with an SMTP mail server by
32  * sending messages of a fixed or randomly-chosen size to one or more recipients
33  * at a rate that is as high as possible.
34  *
35  *
36  * @author Neil A. Wilson
37  */

38 public class SMTPSendRateJobClass
39        extends JobClass
40 {
41   /**
42    * The set of characters that will be used when forming random "words".
43    */

44   public static final char[] ALPHABET =
45        "abcdefghijklmnopqrstuvwxyz".toCharArray();
46
47
48   /**
49    * The end-of-line character that is required for SMTP messages.
50    */

51   public static final String JavaDoc EOL = "\r\n";
52
53
54
55   /**
56    * The display name of the stat tracker used to count the number of SMTP
57    * sessions established.
58    */

59   public static final String JavaDoc STAT_TRACKER_SMTP_SESSIONS = "SMTP Sessions";
60
61
62
63   /**
64    * The display name of the stat tracker used to count the total number of
65    * SMTP sessions in which at least one message was sent successfully.
66    */

67   public static final String JavaDoc STAT_TRACKER_SUCCESS_COUNT = "Successful Sessions";
68
69
70
71   /**
72    * The display name of the stat tracker used to count the total number of
73    * SMTP sessions in which no messages were sent successfully.
74    */

75   public static final String JavaDoc STAT_TRACKER_FAILURE_COUNT = "Failed Sessions";
76
77
78
79   /**
80    * The display name of the stat tracker used to keep track of the total number
81    * of recipients specified for each message.
82    */

83   public static final String JavaDoc STAT_TRACKER_TOTAL_RECIPIENTS = "Total Recipients";
84
85
86
87   /**
88    * The display name of the stat tracker used to keep track of the number of
89    * recipient addresses that were accepted by the mail server for each message.
90    */

91   public static final String JavaDoc STAT_TRACKER_ACCEPTED_RECIPIENTS =
92        "Accepted Recipients";
93
94
95
96   /**
97    * The display name of the stat tracker used to keep track of the number of
98    * recipient addresses that were rejected by the mail server for each message.
99    */

100   public static final String JavaDoc STAT_TRACKER_REJECTED_RECIPIENTS =
101        "Rejected Recipients";
102
103
104
105   /**
106    * The display name of the stat tracker used to time the process of
107    * authenticating and retrieving the list of messages.
108    */

109   public static final String JavaDoc STAT_TRACKER_SESSION_DURATION =
110        "Session Duration (ms)";
111
112
113
114   // The length of time between initial requests.
115
IntegerParameter delayParameter =
116        new IntegerParameter("delay", "Time Between SMTP Sessions (ms)",
117                             "The length of time in milliseconds between " +
118                             "attempts to access the SMTP server.",
119                             true, 0, true, 0, false, 0);
120
121   // The maximum number of recipients to include in the message.
122
IntegerParameter maxRecipientsParameter =
123     new IntegerParameter("max_recipients", "Maximum Number of Recipients",
124                          "The maximum number of recipients that should be " +
125                          "used for any single message.", true, 1, true, 1,
126                          false, 0);
127
128   // The minimum number of recipients to include in the message.
129
IntegerParameter minRecipientsParameter =
130     new IntegerParameter("min_recipients", "Minimum Number of Recipients",
131                          "The minimum number of recipients that should be " +
132                          "used for any single message.", true, 1, true, 1,
133                          false, 0);
134
135   // The port number of the SMTP server.
136
IntegerParameter portParameter =
137        new IntegerParameter("smtp_port", "SMTP Server Port",
138                             "The port number on which the SMTP server is " +
139                             "listening for requests.", true, 25, true, 1, true,
140                             65535);
141
142   // The size in bytes that should be used for the message body.
143
IntegerParameter sizeParameter =
144        new IntegerParameter("size", "Message Body Size (bytes)",
145                             "The size in bytes that should be used for the " +
146                             "body of the SMTP message. Note that this will " +
147                             "be used as an approximation -- the actual " +
148                             "message size may deviate by a few bytes. It " +
149                             "should also be noted that this does not " +
150                             "include the SMTP message headers.", true, 1024,
151                             true, 1, false, 0);
152
153   // A placeholder parameter that is only used for formatting.
154
PlaceholderParameter placeholder = new PlaceholderParameter();
155
156   // The address from which messages will originate.
157
StringParameter fromParameter =
158        new StringParameter("from_address", "From Address",
159                            "The e-mail address from which messages sent by " +
160                            "this job will originate.", true, "");
161
162
163   // The address of the SMTP server.
164
StringParameter hostParameter =
165        new StringParameter("smtp_host", "SMTP Server Address",
166                            "The fully-qualified domain name or IP address of " +
167                            "the system running the SMTP server.", true, "");
168
169   // The recipient(s) to use for the messages.
170
StringParameter recipientParameter =
171        new StringParameter("recipient", "Recipient Address",
172                            "The e-mail address of the recipient that should " +
173                            "be used for each mail message. A range of " +
174                            "values may be specified by enclosing the range " +
175                            "in brackets and separating the minimum and " +
176                            "maximum values with a dash (e.g., [1-1000]), or " +
177                            "a sequential range may be specified by " +
178                            "separating the minimum and maximum values with a " +
179                            "colon (e.g., [1:1000]).", true, "");
180
181
182
183   // Static variables used to hold parameter values.
184
static boolean useRecipientRange;
185   static boolean useSequentialRecipient;
186   static int delay;
187   static int messageSize;
188   static int maxRecipients;
189   static int minRecipients;
190   static int recipientSpan;
191   static int nextSequentialRecipient;
192   static int recipientRangeMax;
193   static int recipientRangeMin;
194   static int recipientRangeSpan;
195   static int smtpPort;
196   static String JavaDoc fromAddress;
197   static String JavaDoc messageBody;
198   static String JavaDoc recipientInitial;
199   static String JavaDoc recipientFinal;
200   static String JavaDoc smtpAddress;
201   static String JavaDoc subject;
202
203
204   // The random number generator for the job.
205
static Random parentRandom;
206   Random random;
207
208
209   // The local address associated with this client system.
210
String JavaDoc localAddress;
211
212
213
214   // The stat trackers for the job.
215
IncrementalTracker failureCounter;
216   IncrementalTracker sessionCounter;
217   IncrementalTracker successCounter;
218   IntegerValueTracker acceptedRecipientTracker;
219   IntegerValueTracker rejectedRecipientTracker;
220   IntegerValueTracker totalRecipientTracker;
221   TimeTracker sessionTimer;
222
223
224
225
226   /**
227    * The default constructor used to create a new instance of the job class.
228    * The only thing it should do is to invoke the superclass constructor. All
229    * other initialization should be performed in the <CODE>initialize</CODE>
230    * method.
231    */

232   public SMTPSendRateJobClass()
233   {
234     super();
235   }
236
237
238
239   /**
240    * Retrieves the name of the job performed by this job thread.
241    *
242    * @return The name of the job performed by this job thread.
243    */

244   public String JavaDoc getJobName()
245   {
246     return "SMTP SendRate";
247   }
248
249
250
251   /**
252    * Retrieves a description of the job performed by this job thread.
253    *
254    * @return A description of the job performed by this job thread.
255    */

256   public String JavaDoc getJobDescription()
257   {
258     return "This job can be used to repeatedly establish sessions with an " +
259            "SMTP mail server and send messages to one or more recipients.";
260   }
261
262
263
264   /**
265    * Retrieves the name of the category in which this job class exists. This is
266    * used to help arrange the job classes in the administrative interface.
267    *
268    * @return The name of the category in which this job class exists.
269    */

270   public String JavaDoc getJobCategoryName()
271   {
272     return "Mail";
273   }
274
275
276
277   /**
278    * Retrieve a parameter list that can be used to determine all of the
279    * customizeable options that are available for this job.
280    *
281    * @return A parameter list that can be used to determine all of the
282    * customizeable options that are available for this job.
283    */

284   public ParameterList getParameterStubs()
285   {
286     Parameter[] parameters =
287     {
288       placeholder,
289       hostParameter,
290       portParameter,
291       fromParameter,
292       recipientParameter,
293       minRecipientsParameter,
294       maxRecipientsParameter,
295       sizeParameter,
296       delayParameter
297     };
298
299     return new ParameterList(parameters);
300   }
301
302
303
304   /**
305    * Retrieves the set of stat trackers that will be maintained by this job
306    * class. The stat trackers returned by this method do not have to actually
307    * contain any statistics -- the display name and stat tracker class should
308    * be the only information that callers of this method should rely upon. Note
309    * that this list can be different from the list of statistics actually
310    * collected by the job in some cases (e.g., if the job may not return all the
311    * stat trackers it advertises in all cases, or if the job may return stat
312    * trackers that it did not advertise), but it is a possibility that only the
313    * stat trackers returned by this method will be accessible for some features
314    * in the SLAMD server.
315    *
316    * @param clientID The client ID that should be used for the
317    * returned stat trackers.
318    * @param threadID The thread ID that should be used for the
319    * returned stat trackers.
320    * @param collectionInterval The collection interval that should be used for
321    * the returned stat trackers.
322    *
323    * @return The set of stat trackers that will be maintained by this job
324    * class.
325    */

326   public StatTracker[] getStatTrackerStubs(String JavaDoc clientID, String JavaDoc threadID,
327                                            int collectionInterval)
328   {
329     return new StatTracker[]
330     {
331       new IncrementalTracker(clientID, threadID, STAT_TRACKER_SMTP_SESSIONS,
332                              collectionInterval),
333       new TimeTracker(clientID, threadID, STAT_TRACKER_SESSION_DURATION,
334                       collectionInterval),
335       new IncrementalTracker(clientID, threadID, STAT_TRACKER_SUCCESS_COUNT,
336                              collectionInterval),
337       new IncrementalTracker(clientID, threadID, STAT_TRACKER_FAILURE_COUNT,
338                              collectionInterval),
339       new IntegerValueTracker(clientID, threadID, STAT_TRACKER_TOTAL_RECIPIENTS,
340                               collectionInterval),
341       new IntegerValueTracker(clientID, threadID,
342                               STAT_TRACKER_ACCEPTED_RECIPIENTS,
343                               collectionInterval),
344       new IntegerValueTracker(clientID, threadID,
345                               STAT_TRACKER_REJECTED_RECIPIENTS,
346                               collectionInterval)
347     };
348   }
349
350
351
352   /**
353    * Retrieves the stat trackers that are maintained for this job thread.
354    *
355    * @return The stat trackers that are maintained for this job thread.
356    */

357   public StatTracker[] getStatTrackers()
358   {
359     return new StatTracker[]
360     {
361       sessionCounter,
362       sessionTimer,
363       successCounter,
364       failureCounter,
365       totalRecipientTracker,
366       acceptedRecipientTracker,
367       rejectedRecipientTracker
368     };
369   }
370
371
372
373   /**
374    * Provides a means of validating the information used to schedule the job,
375    * including the scheduling information and list of parameters.
376    *
377    * @param numClients The number of clients that should be used to
378    * run the job.
379    * @param threadsPerClient The number of threads that should be created on
380    * each client to run the job.
381    * @param threadStartupDelay The delay in milliseconds that should be used
382    * when starting the client threads.
383    * @param startTime The time that the job should start running.
384    * @param stopTime The time that the job should stop running.
385    * @param duration The maximum length of time in seconds that the
386    * job should be allowed to run.
387    * @param collectionInterval The collection interval that should be used
388    * when gathering statistics for the job.
389    * @param parameters The set of parameters provided to this job that
390    * can be used to customize its behavior.
391    *
392    * @throws InvalidValueException If the provided information is not
393    * appropriate for running this job.
394    */

395   public void validateJobInfo(int numClients, int threadsPerClient,
396                               int threadStartupDelay, Date startTime,
397                               Date stopTime, int duration,
398                               int collectionInterval, ParameterList parameters)
399          throws InvalidValueException
400   {
401     IntegerParameter minParam =
402          parameters.getIntegerParameter(minRecipientsParameter.getName());
403     if (minParam == null)
404     {
405       throw new InvalidValueException("No value provided for required " +
406                                       "parameter " +
407                                       minRecipientsParameter.getDisplayName());
408     }
409
410     IntegerParameter maxParam =
411          parameters.getIntegerParameter(maxRecipientsParameter.getName());
412     if (maxParam == null)
413     {
414       throw new InvalidValueException("No value provided for required " +
415                                       "parameter " +
416                                       maxRecipientsParameter.getDisplayName());
417     }
418
419     int minRecip = minParam.getIntValue();
420     int maxRecip = maxParam.getIntValue();
421     if ((minRecip <= 0) || (maxRecip <= 0))
422     {
423       throw new InvalidValueException("Minimum and maximum number of " +
424                                       "recipients must be greather than zero.");
425     }
426     else if (minRecip > maxRecip)
427     {
428       throw new InvalidValueException("Maximum number of recipients must be " +
429                                       "greater than or equal to the minimum " +
430                                       "number of recipients.");
431     }
432   }
433
434
435
436   /**
437    * Indicates whether this job class implements logic that makes it possible to
438    * test the validity of job parameters before scheduling the job for execution
439    * (e.g., to see if the server is reachable using the information provided).
440    *
441    * @return <CODE>true</CODE> if this job provides a means of testing the job
442    * parameters, or <CODE>false</CODE> if not.
443    */

444   public boolean providesParameterTest()
445   {
446     return true;
447   }
448
449
450
451   /**
452    * Provides a means of testing the provided job parameters to determine
453    * whether they are valid (e.g., to see if the server is reachable) before
454    * scheduling the job for execution. This method will be executed by the
455    * SLAMD server system itself and not by any of the clients.
456    *
457    * @param parameters The job parameters to be tested.
458    * @param outputMessages The lines of output that were generated as part of
459    * the testing process. Each line of output should
460    * be added to this list as a separate string, and
461    * empty strings (but not <CODE>null</CODE> values)
462    * are allowed to provide separation between
463    * different messages. No formatting should be
464    * provided for these messages, however, since they
465    * may be displayed in either an HTML or plain text
466    * interface.
467    *
468    * @return <CODE>true</CODE> if the test completed successfully, or
469    * <CODE>false</CODE> if not. Note that even if the test did not
470    * complete successfully, the user will be presented with a warning
471    * but will still be allowed to schedule the job using the provided
472    * parameters. This is necessary because the parameters may still be
473    * valid even if the server couldn't validate them at the time the
474    * job was scheduled (e.g., if the server wasn't running or could not
475    * be reached by the SLAMD server even though it could be by the
476    * clients).
477    */

478   public boolean testJobParameters(ParameterList parameters,
479                                    ArrayList outputMessages)
480   {
481     // Get the parameters necessary to perform the test.
482
StringParameter hostParam =
483          parameters.getStringParameter(hostParameter.getName());
484     if ((hostParam == null) || (! hostParam.hasValue()))
485     {
486       outputMessages.add("ERROR: No SMTP server address was provided.");
487       return false;
488     }
489     String JavaDoc host = hostParam.getStringValue();
490
491
492     IntegerParameter portParam =
493          parameters.getIntegerParameter(portParameter.getName());
494     if ((portParam == null) || (! portParam.hasValue()))
495     {
496       outputMessages.add("ERROR: No SMTP server port was provided.");
497       return false;
498     }
499     int port = portParam.getIntValue();
500
501
502     // Try to establish a connection to the SMTP server.
503
Socket socket;
504     BufferedReader reader;
505     BufferedWriter writer;
506     try
507     {
508       outputMessages.add("Trying to establish a connection to SMTP server " +
509                          host + ":" + port + "....");
510
511       socket = new Socket(host, port);
512       reader = new BufferedReader(new InputStreamReader(
513                                            socket.getInputStream()));
514       writer = new BufferedWriter(new OutputStreamWriter(
515                                            socket.getOutputStream()));
516
517       outputMessages.add("Connected successfully.");
518       outputMessages.add("");
519     }
520     catch (Exception JavaDoc e)
521     {
522       outputMessages.add("ERROR: Unable to connect: " +
523                          stackTraceToString(e));
524       return false;
525     }
526
527
528     // Read the initial response line from the server.
529
try
530     {
531       outputMessages.add("Trying to read the hello string from the server....");
532
533       String JavaDoc line = reader.readLine();
534
535       outputMessages.add("Hello string was '" + line + "'.");
536       outputMessages.add("");
537     }
538     catch (Exception JavaDoc e)
539     {
540       outputMessages.add("ERROR: Unable to read the hello string: " +
541                          stackTraceToString(e));
542
543       try
544       {
545         reader.close();
546       } catch (Exception JavaDoc e2) {}
547
548       try
549       {
550         writer.close();
551       } catch (Exception JavaDoc e2) {}
552
553       try
554       {
555         socket.close();
556       } catch (Exception JavaDoc e2) {}
557
558       return false;
559     }
560
561
562     // If we've gotten here, then everything seems to be OK. Close the
563
// connection and return true.
564
try
565     {
566       outputMessages.add("Sending the QUIT request to the server.");
567       outputMessages.add("");
568
569       writer.write("QUIT" + EOL);
570       writer.flush();
571     } catch (Exception JavaDoc e) {}
572
573     try
574     {
575       reader.close();
576     } catch (Exception JavaDoc e) {}
577
578     try
579     {
580       writer.close();
581     } catch (Exception JavaDoc e) {}
582
583     try
584     {
585       socket.close();
586     } catch (Exception JavaDoc e) {}
587
588
589     outputMessages.add("All tests completed.");
590     return true;
591   }
592
593
594
595   /**
596    * Performs initialization for this job on each client immediately before each
597    * thread is created to actually run the job.
598    *
599    * @param clientID The ID assigned to the client running this job.
600    * @param parameters The set of parameters provided to this job that can be
601    * used to customize its behavior.
602    *
603    * @throws UnableToRunException If the client initialization could not be
604    * completed successfully and the job is unable
605    * to run.
606    */

607   public void initializeClient(String JavaDoc clientID, ParameterList parameters)
608          throws UnableToRunException
609   {
610     // Seed the parent random number generator.
611
parentRandom = new Random();
612
613
614     // Get the address of the SMTP server.
615
hostParameter = parameters.getStringParameter(hostParameter.getName());
616     if (hostParameter != null)
617     {
618       smtpAddress = hostParameter.getStringValue();
619     }
620
621     // Get the port for the SMTP server.
622
portParameter = parameters.getIntegerParameter(portParameter.getName());
623     if (portParameter != null)
624     {
625       smtpPort = portParameter.getIntValue();
626     }
627
628     // Get the from address.
629
fromParameter = parameters.getStringParameter(fromParameter.getName());
630     if (fromParameter != null)
631     {
632       fromAddress = fromParameter.getStringValue();
633     }
634
635     // Get the recipient. See if it should be a range of values.
636
useRecipientRange = false;
637     useSequentialRecipient = false;
638
639     recipientParameter =
640          parameters.getStringParameter(recipientParameter.getName());
641     if (recipientParameter != null)
642     {
643       String JavaDoc value = recipientParameter.getStringValue();
644       int openPos = value.indexOf('[');
645       if (openPos < 0)
646       {
647         recipientInitial = value;
648       }
649       else
650       {
651         int closePos = value.indexOf(']', openPos);
652         if (closePos > 0)
653         {
654           int dashPos = value.indexOf('-', openPos);
655           if ((dashPos > 0) && (dashPos < closePos))
656           {
657             useRecipientRange = true;
658             recipientRangeMin = Integer.parseInt(value.substring(openPos+1,
659                                                                   dashPos));
660             recipientRangeMax = Integer.parseInt(value.substring(dashPos+1,
661                                                                   closePos));
662             recipientRangeSpan = recipientRangeMax - recipientRangeMin + 1;
663             recipientInitial = value.substring(0, openPos);
664             recipientFinal = value.substring(closePos+1);
665           }
666           else
667           {
668             dashPos = value.indexOf(':', openPos);
669             if ((dashPos > 0) && (dashPos < closePos))
670             {
671               useRecipientRange = true;
672               useSequentialRecipient = true;
673               recipientRangeMin = Integer.parseInt(value.substring(openPos+1,
674                                                                     dashPos));
675               recipientRangeMax = Integer.parseInt(value.substring(dashPos+1,
676                                                                     closePos));
677               recipientRangeSpan = recipientRangeMax - recipientRangeMin + 1;
678               recipientInitial = value.substring(0, openPos);
679               recipientFinal = value.substring(closePos+1);
680               nextSequentialRecipient = recipientRangeMin;
681             }
682             else
683             {
684               recipientInitial = value;
685             }
686           }
687         }
688         else
689         {
690           recipientInitial = value;
691         }
692       }
693     }
694
695
696     // Get the minimum number of recipients.
697
minRecipientsParameter =
698          parameters.getIntegerParameter(minRecipientsParameter.getName());
699     if (minRecipientsParameter != null)
700     {
701       minRecipients = minRecipientsParameter.getIntValue();
702     }
703
704
705     // Get the maximum number of recipients.
706
maxRecipientsParameter =
707          parameters.getIntegerParameter(maxRecipientsParameter.getName());
708     if (maxRecipientsParameter != null)
709     {
710       maxRecipients = maxRecipientsParameter.getIntValue();
711     }
712     recipientSpan = maxRecipients - minRecipients + 1;
713
714
715     // Get the message size.
716
sizeParameter = parameters.getIntegerParameter(sizeParameter.getName());
717     if (sizeParameter != null)
718     {
719       messageSize = sizeParameter.getIntValue();
720     }
721
722
723     // Get the delay between requests.
724
delayParameter = parameters.getIntegerParameter(delayParameter.getName());
725     if (delayParameter != null)
726     {
727       delay = delayParameter.getIntValue();
728     }
729
730
731     // Create the message that will be used for all the SMTP sessions.
732
generateSubject();
733     generateMessage();
734   }
735
736
737
738   /**
739    * Initializes this job thread to be used to actually run the job on the
740    * client. The provided parameter list should be processed to customize the
741    * behavior of this job thread, and any other initialization that needs to be
742    * done in order for the job to run should be performed here as well.
743    *
744    * @param clientID The client ID for this job thread.
745    * @param threadID The thread ID for this job thread.
746    * @param collectionInterval The length of time in seconds to use as the
747    * statistics collection interval.
748    * @param parameters The set of parameters provided to this job that
749    * can be used to customize its behavior.
750    *
751    * @throws UnableToRunException If a problem occurs that prevents the thread
752    * from being able to run properly.
753    */

754   public void initializeThread(String JavaDoc clientID, String JavaDoc threadID,
755                                int collectionInterval, ParameterList parameters)
756          throws UnableToRunException
757   {
758     // Create the stat trackers for this thread.
759
sessionCounter = new IncrementalTracker(clientID, threadID,
760                                             STAT_TRACKER_SMTP_SESSIONS,
761                                             collectionInterval);
762     sessionTimer = new TimeTracker(clientID, threadID,
763                                    STAT_TRACKER_SESSION_DURATION,
764                                    collectionInterval);
765     successCounter = new IncrementalTracker(clientID, threadID,
766                                             STAT_TRACKER_SUCCESS_COUNT,
767                                             collectionInterval);
768     failureCounter = new IncrementalTracker(clientID, threadID,
769                                             STAT_TRACKER_FAILURE_COUNT,
770                                             collectionInterval);
771     totalRecipientTracker = new IntegerValueTracker(clientID, threadID,
772                                      STAT_TRACKER_TOTAL_RECIPIENTS,
773                                      collectionInterval);
774     acceptedRecipientTracker = new IntegerValueTracker(clientID, threadID,
775                                         STAT_TRACKER_ACCEPTED_RECIPIENTS,
776                                         collectionInterval);
777     rejectedRecipientTracker = new IntegerValueTracker(clientID, threadID,
778                                         STAT_TRACKER_REJECTED_RECIPIENTS,
779                                         collectionInterval);
780
781
782     // Enable real-time reporting of the data for these stat trackers.
783
RealTimeStatReporter statReporter = getStatReporter();
784     if (statReporter != null)
785     {
786       String JavaDoc jobID = getJobID();
787       sessionCounter.enableRealTimeStats(statReporter, jobID);
788       sessionTimer.enableRealTimeStats(statReporter, jobID);
789       successCounter.enableRealTimeStats(statReporter, jobID);
790       failureCounter.enableRealTimeStats(statReporter, jobID);
791       totalRecipientTracker.enableRealTimeStats(statReporter, jobID);
792       acceptedRecipientTracker.enableRealTimeStats(statReporter, jobID);
793       rejectedRecipientTracker.enableRealTimeStats(statReporter, jobID);
794     }
795
796
797     // Get the local address associated with this client.
798
try
799     {
800       localAddress = InetAddress.getLocalHost().getHostName();
801     }
802     catch (IOException ioe)
803     {
804       try
805       {
806         localAddress = InetAddress.getLocalHost().getHostAddress();
807       }
808       catch (IOException ioe2)
809       {
810         localAddress = clientID;
811       }
812     }
813
814
815     // Seed the random number generator for this thread.
816
random = new Random(parentRandom.nextLong());
817   }
818
819
820
821   /**
822    * Perform the work of this job thread by executing the specified command.
823    */

824   public void runJob()
825   {
826     // Define variables that will be used throughout this method.
827
BufferedReader reader;
828     BufferedWriter writer;
829     SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, d MMM yyyy " +
830                                                        "HH:mm:ss Z");
831     int numAccepted;
832     int numRejected;
833     int numRecipients;
834     long lastStartTime = 0;
835     Socket socket;
836     String JavaDoc serverResponse = null;
837     String JavaDoc[] recipients;
838
839
840     // Start the stat trackers.
841
sessionCounter.startTracker();
842     sessionTimer.startTracker();
843     successCounter.startTracker();
844     failureCounter.startTracker();
845     totalRecipientTracker.startTracker();
846     acceptedRecipientTracker.startTracker();
847     rejectedRecipientTracker.startTracker();
848
849
850     // Loop until it is determined that the job should stop.
851
mainLoop:
852     while (! shouldStop())
853     {
854       // If we need to sleep, then do so.
855
if (delay > 0)
856       {
857         long now = System.currentTimeMillis();
858         long prevTestTime = now - lastStartTime;
859         if (prevTestTime < delay)
860         {
861           try
862           {
863             Thread.sleep(delay - prevTestTime);
864           } catch (Exception JavaDoc e) {}
865         }
866       }
867
868
869       lastStartTime = System.currentTimeMillis();
870
871
872       // Put together the list of recipients.
873
numRecipients = ((random.nextInt() & 0x7FFFFFFF) % recipientSpan) +
874                       minRecipients;
875       recipients = new String JavaDoc[numRecipients];
876       for (int i=0; i < numRecipients; i++)
877       {
878         recipients[i] = getRecipientAddress();
879       }
880
881
882       // Start the attempt timer and indicate the beginning of a new attempt.
883
sessionCounter.increment();
884       sessionTimer.startTimer();
885
886
887       // Open the connection to the SMTP server.
888
try
889       {
890         socket = new Socket(smtpAddress, smtpPort);
891         reader = new BufferedReader(new InputStreamReader(
892                                              socket.getInputStream()));
893         writer = new BufferedWriter(new OutputStreamWriter(
894                                              socket.getOutputStream()));
895       }
896       catch (IOException ioe)
897       {
898         sessionTimer.stopTimer();
899         failureCounter.increment();
900         continue;
901       }
902
903
904       // The SMTP server should first introduce itself to the client. Make sure
905
// that the introduction is acceptable -- if so then it should start with
906
// the number "220".
907
try
908       {
909         serverResponse = reader.readLine();
910       }
911       catch (IOException ioe)
912       {
913         sessionTimer.stopTimer();
914         failureCounter.increment();
915         continue;
916       }
917
918       if (! serverResponse.startsWith("220"))
919       {
920         sessionTimer.stopTimer();
921         failureCounter.increment();
922
923         try
924         {
925           sendLine(writer, "QUIT");
926           writer.close();
927           reader.close();
928           socket.close();
929         } catch (IOException ioe) {}
930
931         continue;
932       }
933
934
935       // Send a "HELO" request to the server and read the response. Make sure
936
// that the response starts with a "250".
937
try
938       {
939         sendLine(writer, "HELO " + localAddress);
940         serverResponse = reader.readLine();
941       }
942       catch (IOException ioe)
943       {
944         sessionTimer.stopTimer();
945         failureCounter.increment();
946
947         try
948         {
949           sendLine(writer, "QUIT");
950           writer.close();
951           reader.close();
952           socket.close();
953         } catch (IOException ioe2) {}
954
955         continue;
956       }
957
958       if (! serverResponse.startsWith("250"))
959       {
960         sessionTimer.stopTimer();
961         failureCounter.increment();
962
963         try
964         {
965           sendLine(writer, "QUIT");
966           writer.close();
967           reader.close();
968           socket.close();
969         } catch (IOException ioe) {}
970
971         continue;
972       }
973
974
975       // Specify the from address. The server must provide a response starting
976
// with "250" for this to be acceptable.
977
try
978       {
979         sendLine(writer, "MAIL FROM:<" + fromAddress + ">");
980         serverResponse = reader.readLine();
981       }
982       catch (IOException ioe)
983       {
984         sessionTimer.stopTimer();
985         failureCounter.increment();
986
987         try
988         {
989           sendLine(writer, "QUIT");
990           writer.close();
991           reader.close();
992           socket.close();
993         } catch (IOException ioe2) {}
994
995         continue;
996       }
997
998
999       if (! serverResponse.startsWith("250"))
1000      {
1001        sessionTimer.stopTimer();
1002        failureCounter.increment();
1003
1004        try
1005        {
1006          sendLine(writer, "QUIT");
1007          writer.close();
1008          reader.close();
1009          socket.close();
1010        } catch (IOException ioe) {}
1011
1012        continue;
1013      }
1014
1015      // Specify the recipients. The server should provide a response starting
1016
// with "250" or "251" for each of them.
1017
numAccepted = 0;
1018      numRejected = 0;
1019      for (int i=0; i < recipients.length; i++)
1020      {
1021        try
1022        {
1023          sendLine(writer, "RCPT TO:<" + recipients[i] + ">");
1024          serverResponse = reader.readLine();
1025        }
1026        catch (IOException ioe)
1027        {
1028          sessionTimer.stopTimer();
1029          failureCounter.increment();
1030
1031          try
1032          {
1033            sendLine(writer, "QUIT");
1034            writer.close();
1035            reader.close();
1036            socket.close();
1037          } catch (IOException ioe2) {}
1038
1039          continue mainLoop;
1040        }
1041
1042        if (serverResponse.startsWith("25"))
1043        {
1044          numAccepted++;
1045        }
1046        else
1047        {
1048          numRejected++;
1049        }
1050      }
1051
1052      totalRecipientTracker.addValue(numRecipients);
1053      acceptedRecipientTracker.addValue(numAccepted);
1054      rejectedRecipientTracker.addValue(numRejected);
1055      if (numAccepted == 0)
1056      {
1057        sessionTimer.stopTimer();
1058        failureCounter.increment();
1059
1060        try
1061        {
1062          sendLine(writer, "QUIT");
1063          writer.close();
1064          reader.close();
1065          socket.close();
1066        } catch (IOException ioe) {}
1067
1068        continue;
1069      }
1070
1071
1072      // Send the "DATA" header to the server. The server must provide a
1073
// response starting with "354".
1074
try
1075      {
1076        sendLine(writer, "DATA");
1077        serverResponse = reader.readLine();
1078      }
1079      catch (IOException ioe)
1080      {
1081        sessionTimer.stopTimer();
1082        failureCounter.increment();
1083
1084        try
1085        {
1086          sendLine(writer, "QUIT");
1087          writer.close();
1088          reader.close();
1089          socket.close();
1090        } catch (IOException ioe2) {}
1091
1092        continue;
1093      }
1094
1095      if (! serverResponse.startsWith("354"))
1096      {
1097        sessionTimer.stopTimer();
1098        failureCounter.increment();
1099
1100        try
1101        {
1102          sendLine(writer, "QUIT");
1103          writer.close();
1104          reader.close();
1105          socket.close();
1106        } catch (IOException ioe) {}
1107
1108        continue;
1109      }
1110
1111
1112      // Send the message header. The server will not provide a response to
1113
// this. Also, since we're sending multiple lines at once, there is no
1114
// reason to flush after each one so don't do that.
1115
try
1116      {
1117        writer.write("From: <" + fromAddress + ">" + EOL);
1118        writer.write("MIME-Version: 1.0" + EOL);
1119        writer.write("Content-type: text/plain; charset=us-ascii" + EOL);
1120        writer.write("Date: " + dateFormat.format(new Date()) + EOL);
1121        writer.write("Subject: " + subject + EOL);
1122        for (int i=0; i < recipients.length; i++)
1123        {
1124          writer.write("To: <" + recipients[i] + ">" + EOL);
1125        }
1126        writer.write(EOL);
1127      }
1128      catch (IOException ioe)
1129      {
1130        sessionTimer.stopTimer();
1131        failureCounter.increment();
1132
1133        try
1134        {
1135          sendLine(writer, "QUIT");
1136          writer.close();
1137          reader.close();
1138          socket.close();
1139        } catch (IOException ioe2) {}
1140
1141        continue;
1142      }
1143
1144      // Send the message itself followed by a line containing only a period.
1145
// The server should provide a response starting with "250".
1146
try
1147      {
1148        writer.write(messageBody + EOL);
1149        sendLine(writer, ".");
1150        serverResponse = reader.readLine();
1151      }
1152      catch (IOException ioe)
1153      {
1154        sessionTimer.stopTimer();
1155        failureCounter.increment();
1156
1157        try
1158        {
1159          sendLine(writer, "QUIT");
1160          writer.close();
1161          reader.close();
1162          socket.close();
1163        } catch (IOException ioe2) {}
1164
1165        continue;
1166      }
1167
1168      if (! serverResponse.startsWith("250"))
1169      {
1170        sessionTimer.stopTimer();
1171        failureCounter.increment();
1172
1173        try
1174        {
1175          sendLine(writer, "QUIT");
1176          writer.close();
1177          reader.close();
1178          socket.close();
1179        } catch (IOException ioe) {}
1180
1181        continue;
1182      }
1183
1184
1185      // The message is complete, so end the session with a "QUIT".
1186
try
1187      {
1188        sendLine(writer, "QUIT");
1189        writer.close();
1190        reader.close();
1191        socket.close();
1192      }
1193      catch (IOException ioe)
1194      {
1195        sessionTimer.stopTimer();
1196        failureCounter.increment();
1197
1198        try
1199        {
1200          sendLine(writer, "QUIT");
1201          writer.close();
1202          reader.close();
1203          socket.close();
1204        } catch (IOException ioe2) {}
1205
1206        continue;
1207      }
1208
1209
1210      // If we made it here, then everything was successful.
1211
sessionTimer.stopTimer();
1212      successCounter.increment();
1213    }
1214
1215    sessionCounter.stopTracker();
1216    sessionTimer.stopTracker();
1217    successCounter.stopTracker();
1218    failureCounter.stopTracker();
1219    totalRecipientTracker.stopTracker();
1220    acceptedRecipientTracker.stopTracker();
1221    rejectedRecipientTracker.stopTracker();
1222  }
1223
1224
1225
1226  /**
1227   * Writes the provided line of text to the SMTP server in the appropriate
1228   * format.
1229   *
1230   * @param writer The writer that can be used to communicate with the SMTP
1231   * server.
1232   * @param line The line of text to be written to the server.
1233   *
1234   * @throws IOException If a problem occurs while writing the data to the
1235   * SMTP server.
1236   */

1237  private void sendLine(BufferedWriter writer, String JavaDoc line)
1238          throws IOException
1239  {
1240    writer.write(line);
1241    writer.write(EOL);
1242    writer.flush();
1243  }
1244
1245
1246
1247  /**
1248   * Generates a subject for this mail message. It will be between 3 and 7
1249   * "words" in length.
1250   */

1251  public static void generateSubject()
1252  {
1253    int numWords = (parentRandom.nextInt() & 0x7FFFFFFF) % 5 + 3;
1254    StringBuffer JavaDoc subjectBuffer = new StringBuffer JavaDoc();
1255
1256    String JavaDoc separator = "";
1257    for (int i=0; i < numWords; i++)
1258    {
1259      int wordLength = (parentRandom.nextInt() & 0x7FFFFFFF) % 10 + 3;
1260      subjectBuffer.append(generateWord(wordLength));
1261      subjectBuffer.append(separator);
1262      separator = " ";
1263    }
1264
1265    subject = subjectBuffer.toString();
1266  }
1267
1268
1269
1270  /**
1271   * Creates the e-mail message that will be sent. Although it will not
1272   * contain actual words, it will at least look realistic in terms of
1273   * spacing, word size, punctuation, etc.
1274   */

1275  public static void generateMessage()
1276  {
1277    int totalSize = 0;
1278    int wordsThisSentence = 0;
1279    int charsThisLine = 0;
1280    StringBuffer JavaDoc messageBuffer = new StringBuffer JavaDoc();
1281    int sentenceSize = (parentRandom.nextInt() & 0x7FFFFFFF) % 11 + 5;
1282    while (totalSize < messageSize)
1283    {
1284      int wordLength = (parentRandom.nextInt() & 0x7FFFFFFF) % 10 + 3;
1285      String JavaDoc word = generateWord(wordLength);
1286      messageBuffer.append(word);
1287      totalSize += wordLength;
1288      charsThisLine += wordLength;
1289      wordsThisSentence++;
1290      if ((wordsThisSentence > sentenceSize) || (totalSize > messageSize))
1291      {
1292        messageBuffer.append(". ");
1293        totalSize += 3;
1294        charsThisLine += 3;
1295        wordsThisSentence = 0;
1296        sentenceSize = (parentRandom.nextInt() & 0x7FFFFFFF) % 11 + 5;
1297        if (charsThisLine > 70)
1298        {
1299          messageBuffer.append(EOL);
1300          totalSize += EOL.length();
1301          charsThisLine = 0;
1302        }
1303      }
1304      else if (charsThisLine > 70)
1305      {
1306        messageBuffer.append(EOL);
1307        totalSize += EOL.length();
1308        charsThisLine = 0;
1309      }
1310      else
1311      {
1312        messageBuffer.append(" ");
1313        totalSize++;
1314        charsThisLine++;
1315      }
1316    }
1317
1318    messageBody = messageBuffer.toString();
1319  }
1320
1321
1322
1323  /**
1324   * Generates a word of the specified length comprised of characters randomly
1325   * chosen from the provided character set.
1326   *
1327   * @param numChars The number of characters to include in the word.
1328   *
1329   * @return A word of the specified length comprised of characters randomly
1330   * chosen from the provided character set.
1331   */

1332  public static String JavaDoc generateWord(int numChars)
1333  {
1334    char[] chars = new char[numChars];
1335    for (int i=0; i < chars.length; i++)
1336    {
1337      chars[i] = ALPHABET[(parentRandom.nextInt() & 0x7FFFFFFF) %
1338                          ALPHABET.length];
1339    }
1340
1341    return new String JavaDoc(chars);
1342  }
1343
1344
1345
1346  /**
1347   * Chooses a recipient address based on the criteria provided in the job
1348   * parameters.
1349   *
1350   * @return The recipient address that should be used for this attempt.
1351   */

1352  public String JavaDoc getRecipientAddress()
1353  {
1354    if (useRecipientRange)
1355    {
1356      int value;
1357      if (useSequentialRecipient)
1358      {
1359        value = nextSequentialRecipient++;
1360        if (nextSequentialRecipient > recipientRangeMax)
1361        {
1362          nextSequentialRecipient = recipientRangeMin;
1363        }
1364      }
1365      else
1366      {
1367        value = ((random.nextInt() & 0x7FFFFFFF) % recipientRangeSpan) +
1368                recipientRangeMin;
1369      }
1370
1371      return recipientInitial + value + recipientFinal;
1372    }
1373    else
1374    {
1375      return recipientInitial;
1376    }
1377  }
1378}
1379
1380
Popular Tags