KickJava   Java API By Example, From Geeks To Geeks.

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


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.util.*;
22 import com.sun.slamd.client.*;
23 import com.sun.slamd.job.*;
24 import com.sun.slamd.parameter.*;
25 import com.sun.slamd.stat.*;
26
27
28
29 /**
30  * This class defines a job that has the ability to execute a command on the
31  * client system.
32  *
33  *
34  * @author Neil A. Wilson
35  */

36 public class ExecJobClass
37        extends JobClass
38 {
39   /**
40    * The size to use for the read buffer.
41    */

42   public static final int READ_BUFFER_SIZE = 4096;
43
44
45
46   // The parameter that indicates whether to log command output.
47
BooleanParameter logOutputParameter =
48        new BooleanParameter("log_output", "Log Command Output",
49                             "Indicates whether the output of the command " +
50                             "should be logged.", false);
51
52   // Specifies the list of environment variables to define for the job
53
// execution.
54
MultiLineTextParameter environmentParameter =
55        new MultiLineTextParameter("env_variables", "Environment Variables",
56                                   "A set of environment variables that " +
57                                   "should be defined when the job is " +
58                                   "executed. The environment variables " +
59                                   "should be specified one per line, using " +
60                                   "the format name=value", null, false);
61
62   // The parameter that specifies the command to execute.
63
StringParameter commandParameter =
64       new StringParameter("command", "Command to Execute",
65                           "Specifies the command to execute.", true, "");
66
67   // THe parameter that specifies the working directory.
68
StringParameter workingDirParameter =
69        new StringParameter("working_dir", "Working Directory",
70                            "The path to the working directory to use when " +
71                            "executing the command.", false, "");
72
73   // Indicates whether the output of the command should be captured and logged.
74
static boolean logOutput;
75
76   // The placeholder parameter.
77
PlaceholderParameter placeholder = new PlaceholderParameter();
78
79   // The command to be executed.
80
static String JavaDoc command;
81
82   // The path to the working directory for the job.
83
static File workingDir;
84
85   // The environment variables to use for the job.
86
static String JavaDoc[] environmentVariables;
87
88   // The buffer used to hold data read from the process output.
89
byte[] readBuffer;
90
91
92
93   /**
94    * The default constructor used to create a new instance of the job class.
95    * The only thing it should do is to invoke the superclass constructor. All
96    * other initialization should be performed in the <CODE>initialize</CODE>
97    * method.
98    */

99   public ExecJobClass()
100   {
101     super();
102   }
103
104
105
106   /**
107    * Retrieves the name of the job performed by this job thread.
108    *
109    * @return The name of the job performed by this job thread.
110    */

111   public String JavaDoc getJobName()
112   {
113     return "Exec Job";
114   }
115
116
117
118   /**
119    * Retrieves a description of the job performed by this job thread.
120    *
121    * @return A description of the job performed by this job thread.
122    */

123   public String JavaDoc getJobDescription()
124   {
125     return "This job can be used to execute a specified command on a client " +
126            "system. The output of the command can be captured and logged.";
127   }
128
129
130
131   /**
132    * Retrieves the name of the category in which this job class exists. This is
133    * used to help arrange the job classes in the administrative interface.
134    *
135    * @return The name of the category in which this job class exists.
136    */

137   public String JavaDoc getJobCategoryName()
138   {
139     return "Utility";
140   }
141
142
143
144   /**
145    * Retrieve a parameter list that can be used to determine all of the
146    * customizeable options that are available for this job.
147    *
148    * @return A parameter list that can be used to determine all of the
149    * customizeable options that are available for this job.
150    */

151   public ParameterList getParameterStubs()
152   {
153     Parameter[] parameters = new Parameter[]
154     {
155       placeholder,
156       commandParameter,
157       workingDirParameter,
158       environmentParameter,
159       logOutputParameter
160     };
161
162     return new ParameterList(parameters);
163   }
164
165
166
167   /**
168    * Retrieves the set of stat trackers that will be maintained by this job
169    * class. The stat trackers returned by this method do not have to actually
170    * contain any statistics -- the display name and stat tracker class should
171    * be the only information that callers of this method should rely upon. Note
172    * that this list can be different from the list of statistics actually
173    * collected by the job in some cases (e.g., if the job may not return all the
174    * stat trackers it advertises in all cases, or if the job may return stat
175    * trackers that it did not advertise), but it is a possibility that only the
176    * stat trackers returned by this method will be accessible for some features
177    * in the SLAMD server.
178    *
179    * @param clientID The client ID that should be used for the
180    * returned stat trackers.
181    * @param threadID The thread ID that should be used for the
182    * returned stat trackers.
183    * @param collectionInterval The collection interval that should be used for
184    * the returned stat trackers.
185    *
186    * @return The set of stat trackers that will be maintained by this job
187    * class.
188    */

189   public StatTracker[] getStatTrackerStubs(String JavaDoc clientID, String JavaDoc threadID,
190                                            int collectionInterval)
191   {
192     return new StatTracker[0];
193   }
194
195
196
197   /**
198    * Retrieves the stat trackers that are maintained for this job thread.
199    *
200    * @return The stat trackers that are maintained for this job thread.
201    */

202   public StatTracker[] getStatTrackers()
203   {
204     return new StatTracker[0];
205   }
206
207
208
209   /**
210    * Provides a means of validating the information used to schedule the job,
211    * including the scheduling information and list of parameters.
212    *
213    * @param numClients The number of clients that should be used to
214    * run the job.
215    * @param threadsPerClient The number of threads that should be created on
216    * each client to run the job.
217    * @param threadStartupDelay The delay in milliseconds that should be used
218    * when starting the client threads.
219    * @param startTime The time that the job should start running.
220    * @param stopTime The time that the job should stop running.
221    * @param duration The maximum length of time in seconds that the
222    * job should be allowed to run.
223    * @param collectionInterval The collection interval that should be used
224    * when gathering statistics for the job.
225    * @param parameters The set of parameters provided to this job that
226    * can be used to customize its behavior.
227    *
228    * @throws InvalidValueException If the provided information is not
229    * appropriate for running this job.
230    */

231   public void validateJobInfo(int numClients, int threadsPerClient,
232                               int threadStartupDelay, Date startTime,
233                               Date stopTime, int duration,
234                               int collectionInterval, ParameterList parameters)
235          throws InvalidValueException
236   {
237     // See if the environment variables were specified. If so, make sure they
238
// were specified properly.
239
MultiLineTextParameter envParameter =
240          parameters.getMultiLineTextParameter(environmentParameter.getName());
241     if (envParameter != null)
242     {
243       String JavaDoc[] lines = envParameter.getNonBlankLines();
244       for (int i=0; ((lines != null) && (i < lines.length)); i++)
245       {
246         if (lines[i].indexOf('=') <= 0)
247         {
248           throw new InvalidValueException("Invalid environment variable " +
249                                           "specified: \"" + lines[i] +
250                                           "\". Expected {name}={value}");
251         }
252       }
253     }
254   }
255
256
257
258   /**
259    * Initializes all of the instance variables that correspond to job
260    * parameters.
261    *
262    * @param clientID The client ID for the current client.
263    * @param parameters The set of parameters that have been defined for this
264    * job.
265    *
266    * @throws UnableToRunException If any part of the initialization fails.
267    */

268   public void initializeClient(String JavaDoc clientID, ParameterList parameters)
269          throws UnableToRunException
270   {
271     command = null;
272     commandParameter =
273          parameters.getStringParameter(commandParameter.getName());
274     if (commandParameter != null)
275     {
276       command = commandParameter.getStringValue();
277     }
278
279
280     workingDir = null;
281     workingDirParameter =
282          parameters.getStringParameter(workingDirParameter.getName());
283     if ((workingDirParameter != null) && (workingDirParameter.hasValue()))
284     {
285       String JavaDoc workingDirStr = workingDirParameter.getStringValue();
286       workingDir = new File(workingDirStr);
287
288       try
289       {
290         if (! workingDir.exists())
291         {
292           throw new UnableToRunException("Working directory \"" +
293                                          workingDirStr + "\" does not exist.");
294         }
295
296         if (! workingDir.isDirectory())
297         {
298           throw new UnableToRunException("Working directory \"" +
299                                          workingDirStr +
300                                          "\" is not a directory.");
301         }
302       }
303       catch (UnableToRunException utre)
304       {
305         throw utre;
306       }
307       catch (Exception JavaDoc e)
308       {
309         throw new UnableToRunException("Unable to verify existence of " +
310                                        "working directory \"" + workingDirStr +
311                                        "\": " + e, e);
312       }
313     }
314
315
316     environmentVariables = null;
317     environmentParameter =
318          parameters.getMultiLineTextParameter(environmentParameter.getName());
319     if (environmentParameter != null)
320     {
321       environmentVariables = environmentParameter.getNonBlankLines();
322     }
323
324
325     logOutput = false;
326     logOutputParameter =
327          parameters.getBooleanParameter(logOutputParameter.getName());
328     if (logOutputParameter != null)
329     {
330       logOutput = logOutputParameter.getBooleanValue();
331     }
332   }
333
334
335
336   /**
337    * Initializes this job thread to be used to actually run the job on the
338    * client. The provided parameter list should be processed to customize the
339    * behavior of this job thread, and any other initialization that needs to be
340    * done in order for the job to run should be performed here as well.
341    *
342    * @param clientID The client ID for this job thread.
343    * @param threadID The thread ID for this job thread.
344    * @param collectionInterval The length of time in seconds to use as the
345    * statistics collection interval.
346    * @param parameters The set of parameters provided to this job that
347    * can be used to customize its behavior.
348    *
349    * @throws UnableToRunException If a problem occurs that prevents the thread
350    * from being able to run properly.
351    */

352   public void initializeThread(String JavaDoc clientID, String JavaDoc threadID,
353                                int collectionInterval, ParameterList parameters)
354          throws UnableToRunException
355   {
356     // Initialize the read buffer.
357
readBuffer = new byte[READ_BUFFER_SIZE];
358   }
359
360
361
362   /**
363    * Perform the work of this job thread by executing the specified command.
364    */

365   public void runJob()
366   {
367     Runtime JavaDoc runtime = Runtime.getRuntime();
368     Process JavaDoc process = null;
369
370     try
371     {
372       process = runtime.exec(command, environmentVariables, workingDir);
373     }
374     catch (IOException ioe)
375     {
376       logMessage("Unable to execute command \"" + command + "\": " + ioe);
377       indicateStoppedDueToError();
378       return;
379     }
380
381
382     BufferedInputStream stdOutStream =
383          new BufferedInputStream(process.getInputStream());
384     BufferedInputStream stdErrStream =
385          new BufferedInputStream(process.getErrorStream());
386
387
388     while (true)
389     {
390       try
391       {
392         if (logOutput)
393         {
394           if (stdOutStream.available() > 0)
395           {
396             while ((! shouldStop()) && (stdOutStream.available() > 0))
397             {
398               int bytesRead = stdOutStream.read(readBuffer);
399               String JavaDoc[] outputStrs = byteArrayToStrings(readBuffer, bytesRead);
400               for (int i=0; i < outputStrs.length; i++)
401               {
402                 logMessage("STDOUT: " + outputStrs[i]);
403               }
404             }
405           }
406
407           if (stdErrStream.available() > 0)
408           {
409             while ((! shouldStop()) && (stdErrStream.available() > 0))
410             {
411               int bytesRead = stdErrStream.read(readBuffer);
412               String JavaDoc[] errorStrs = byteArrayToStrings(readBuffer, bytesRead);
413               for (int i=0; i < errorStrs.length; i++)
414               {
415                 logMessage("STDERR: " + errorStrs[i]);
416               }
417             }
418           }
419         }
420
421         if (shouldStop())
422         {
423           try
424           {
425             stdOutStream.close();
426             stdErrStream.close();
427           } catch (Exception JavaDoc e) {}
428
429           process.destroy();
430           logMessage("Terminated process because the client determined it " +
431                      "should stop running.");
432           return;
433         }
434
435         try
436         {
437           int returnCode = process.exitValue();
438           if (returnCode == 0)
439           {
440             logMessage("Command completed successfully (exit code 0)");
441           }
442           else
443           {
444             logMessage("Command completed abnormally (exit code " +
445                        returnCode + ")");
446             indicateCompletedWithErrors();
447           }
448
449           try
450           {
451             stdOutStream.close();
452             stdErrStream.close();
453           } catch (Exception JavaDoc e) {}
454
455           return;
456         } catch (IllegalThreadStateException JavaDoc itse) {}
457
458         try
459         {
460           Thread.sleep(100);
461         } catch (InterruptedException JavaDoc ie) {}
462       }
463       catch (IOException ioe)
464       {
465         // This could mean that the command is done or that some other error
466
// occurred. Ty to get the return code to see if it completed.
467
boolean completedSuccessfully = false;
468         try
469         {
470           int returnCode = process.exitValue();
471           completedSuccessfully = (returnCode == 0);
472           if (completedSuccessfully)
473           {
474             logMessage("Command completed successfully (exit code 0)");
475           }
476           else
477           {
478             logMessage("Command completed abnormally (exit code " + returnCode +
479                        ")");
480             indicateCompletedWithErrors();
481           }
482         }
483         catch (IllegalThreadStateException JavaDoc itse)
484         {
485           logMessage("Attempt to read process output failed: " + ioe);
486           indicateCompletedWithErrors();
487         }
488
489         return;
490       }
491     }
492   }
493
494
495
496   /**
497    * Converts the provided byte array into an array of strings, with one string
498    * per line.
499    *
500    * @param byteArray The byte array containing the data to convert to an
501    * array of strings.
502    * @param length The number of bytes to actually use in the byte array.
503    *
504    * @return The array of strings containing the data from the provided byte
505    * array.
506    */

507   private static String JavaDoc[] byteArrayToStrings(byte[] byteArray, int length)
508   {
509     ArrayList stringList = new ArrayList();
510
511     String JavaDoc byteStr = new String JavaDoc(byteArray, 0, length);
512     StringTokenizer tokenizer = new StringTokenizer(byteStr, "\r\n");
513     while (tokenizer.hasMoreTokens())
514     {
515       stringList.add(tokenizer.nextToken());
516     }
517
518     String JavaDoc[] returnStrings = new String JavaDoc[stringList.size()];
519     stringList.toArray(returnStrings);
520     return returnStrings;
521   }
522 }
523
524
Popular Tags