001/* 002 * Copyright (c) 2004-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: jianwu $' 006 * '$Date: 2012-10-11 21:29:56 +0000 (Thu, 11 Oct 2012) $' 007 * '$Revision: 30866 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler.job; 031 032import java.io.File; 033import java.util.Vector; 034 035import org.apache.commons.logging.Log; 036import org.apache.commons.logging.LogFactory; 037 038/** 039 * Class Job to describe a standalone job. It is assumed that a job will be 040 * submitted only once and then forgotten. Do not resubmit the same Job object 041 * but create another one. 042 */ 043 044public class Job { 045 046 public JobStatusInfo status; 047 048 private String myID; // the key of this object in the hash table 049 // becomes set in setJobID() called from jobFactory; 050 private String workdirPath; // path of (remote) working directory for 051 // job submission and files 052 053 private File localWorkdir; // local working directory for the job (output) 054 private String localWorkdirPath; // path of local working directory for 055 // output files 056 057 // private String submitFile; // the submitFile of the job; 058 // It is to be created or given by the user. 059 // private boolean submitFileIsLocal; // is the submitFile created here, or 060 // is it on the remote site? default=true (i.e.local); 061 062 private JobManager jmgr; // the jobmanager which handles this job 063 064 private String arguments; // argument string for the job 065 066 private int numTasks = 0; //number of tasks 067 068 private static final Log log = LogFactory.getLog(Job.class.getName()); 069 private static final boolean isDebugging = log.isDebugEnabled(); 070 071 /** An array of jobs that must successfully complete before this one can run. */ 072 private Job[] _dependentJobs; 073 074 public class JobFile { 075 String filename; 076 boolean isLocal; 077 078 JobFile(String filename, boolean isLocal) { 079 this.filename = filename; 080 this.isLocal = isLocal; 081 } 082 }; 083 084 public class JobFiles { 085 JobFile executable; // the executable file, local or remote 086 JobFile submitfile; // the submitfile, local or remote, given by user or 087 // created by supporter class 088 Vector inputfiles; // of type JobFile, input files to be declared in 089 // submit file 090 Vector otherinputfiles; // of type String, other files to be staged 091 // before submission 092 JobFile binfile; // scheduler script to be staged to binpath 093 // if bin path is not given stage to workingdir 094 Vector outputfiles; // of type String 095 096 }; 097 098 public JobFiles jobfiles; 099 100 /** 101 * Constructor is called from JobFactory. jobID is a unique id for the jobs. 102 * Currently only for the current applications. Later it will be an uuid. 103 */ 104 protected Job(String jobID) { 105 status = new JobStatusInfo(); 106 // submitFileIsLocal = true; 107 jobfiles = new JobFiles(); 108 jobfiles.inputfiles = new Vector(); 109 jobfiles.otherinputfiles = new Vector(); 110 jobfiles.outputfiles = new Vector(); 111 myID = jobID; 112 status.statusCode = JobStatusCode.NotInQueue; 113 } 114 115 /** 116 * Get the key of the Job object in the hash table. Used by JobManager to 117 * create unique files based on Job's key Do not mix it with status.jobID, 118 * which is the real job id in the remote queue. 119 */ 120 public String getJobID() { 121 return myID; 122 } 123 124 public void setNumTasks(int nt) { 125 numTasks = nt; 126 } 127 128 public int getNumTasks() { 129 return numTasks; 130 } 131 132 133 /** 134 * Set the executable for the job. <i>executablePath</i> is the path to the 135 * executable <i>isLocal</i> true means a local file, false means the 136 * executable is on the remote site <i>arguments</i> are the arguments to 137 * be passed to the job. This will work only if the submission file is 138 * created by the JobManager and not provided as is in the setSubmitFile() 139 * method. 140 * 141 * @return true if succeeds. Throws JobException if isLocal is true but 142 * executable is not found locally. 143 */ 144 public boolean setExecutable(String executablePath, boolean isLocal, 145 String arguments) throws JobException { 146 147 if (isLocal) { 148 File f = new File(executablePath); 149 if (!f.isFile()) { 150 throw new JobException("Local executable file " 151 + executablePath + " is actually not a file." 152 + " Did you want to declare it remote file?"); 153 } 154 } 155 156 jobfiles.executable = new JobFile(executablePath, isLocal); 157 this.arguments = arguments; 158 return true; 159 } 160 161 /** 162 * Set an input file for job. It can be either locally present or remotely. 163 * 164 * @return true at success, but throws JobException if file is assumed to be 165 * a local file and it does not exist. 166 */ 167 public boolean setInputFile(String path, boolean isLocal) 168 throws JobException { 169 170 if (isLocal) { 171 File f = new File(path); 172 if (!f.isFile()) { 173 throw new JobException("Local input file " + path 174 + " is actually not a file." 175 + " Did you want to declare it remote file?"); 176 } 177 } 178 if (path == null || path.trim().length() == 0) 179 throw new JobException( 180 "Your parameter as input file string is empty"); 181 182 jobfiles.inputfiles.add(new JobFile(path, isLocal)); 183 return true; 184 } 185 186 /** 187 * Set file to be staged to bin path. It can be either locally present or 188 * remotely. It is currently used for staging default fork script 189 * jmgr-fork.sh from job/resources 190 * 191 * @return true at success, but throws JobException if file is assumed to be 192 * a local file and it does not exist. 193 */ 194 public void setBinFile(String binFile, boolean isLocal) throws JobException { 195 196 if (isLocal) { 197 File f = new File(binFile); 198 if (!f.isFile()) { 199 throw new JobException("Local file " + binFile 200 + " is actually not a file." 201 + " Did you want to declare it remote file?"); 202 } 203 } 204 jobfiles.binfile = new JobFile(binFile, isLocal); 205 } 206 207 /** 208 * Set an "other" input file for job. It will be staged to the remote site 209 * into the remote working dir but it will not be used in creating the 210 * submission file. 211 * 212 * @return true at success, but throws JobException if file is assumed to be 213 * a local file and it does not exist. 214 */ 215 public boolean setOtherInputFile(String path) throws JobException { 216 217 File f = new File(path); 218 if (!f.isFile()) { 219 throw new JobException("'Other' input file " + path 220 + " is actually not a file."); 221 } 222 if (path == null || path.trim().length() == 0) 223 throw new JobException( 224 "Your parameter as input file string is empty"); 225 226 jobfiles.otherinputfiles.add(path); 227 return true; 228 } 229 230 /** 231 * Set an output file for job. It should be the name of output file which 232 * will be produced by the job. 233 */ 234 public void setOutputFile(String path) throws JobException { 235 236 if (path == null || path.trim().length() == 0) 237 throw new JobException( 238 "Your parameter as output file string is empty"); 239 240 jobfiles.outputfiles.add(path); 241 } 242 243 /** 244 * Set and create the local working directory. For local execution, it will 245 * be the same as the working directory, so it is not necessary to set. For 246 * remote execution, the result of the remote job will be brought to this 247 * directory (not implemented!). 248 * 249 * @return true if creation succeeded, throws JobException otherwise 250 */ 251 public boolean setLocalWorkdir(String path) throws JobException { 252 253 File dir = new File(path); 254 if (!dir.exists()) { 255 if (!dir.mkdirs()) 256 throw new JobException( 257 "Path " 258 + path 259 + " cannot be created to be the local working directory for the job"); 260 } else if (!dir.isDirectory()) { 261 throw new JobException("Path " + path 262 + " exists but is not a directory"); 263 } 264 265 localWorkdirPath = path; 266 localWorkdir = dir; 267 return true; 268 } 269 270 /** 271 * Set the (remote) working directory. The directory given as path must 272 * exist already. The actual working directory of a job will be this path 273 * appended with the job's myID. That directory will be created 274 * automatically and the job submission will be issued from this directory. 275 * I.e. relative input/output filenames in the job submission refer files 276 * under this directory. To get back the actual working directory name, use 277 * getWorkdirPath(). 278 * 279 * @return if path==null, it returns false, otherwise true 280 */ 281 public boolean setWorkdir(String path) { 282 return setWorkdir(path, true); 283 } 284 285 /** 286 * Set the (remote) working directory. If "createUniqueSubdir" is set to 287 * false, then the given dir is used as working dir. If "createUniqueSubdir" 288 * is set to true, the directory given as path must exist already and tThe 289 * actual working directory will be this path appended with the job's myID. 290 * That directory will be created automatically and the job submission will 291 * be issued from this directory. I.e. relative input/output filenames in 292 * the job submission refer files under this directory. To get back the 293 * actual working directory name, use getWorkdirPath(). 294 * 295 * @return if path==null, it returns false, otherwise true 296 */ 297 public boolean setWorkdir(String path, boolean createUniqueSubdir) { 298 String sep; 299 if (path == null) 300 return false; 301 if (createUniqueSubdir) { 302 if (path.endsWith("/")) 303 sep = ""; 304 else 305 sep = "/"; 306 workdirPath = path + sep + myID; 307 } else { 308 workdirPath = path; 309 } 310 return true; 311 } 312 313 public String getLocalWorkdirPath() { 314 return localWorkdirPath; 315 } 316 317 public String getWorkdirPath() { 318 return workdirPath; 319 } 320 321 /** 322 * Give a predefined submit file for the job. This is the most general 323 * possibility for advanced users, to submit jobs to a specific job manager. 324 * 325 * For basic users, do not use this method. Just define the executable, 326 * input and output files for the Job and the JobManager will create an 327 * appropriate submit file for the selected job manager. !!!That is not 328 * implemented in the support classes, so you must use this method always!!! 329 * 330 * @input submitFile: The predefined submit file. It will be directly used 331 * at job submission without altering it. 332 * @input isItLocal: Is it here locally, or already you refer to a remote 333 * submission file. In the first case, it will be copied to the 334 * remote site before job submission. It throws JobException if the 335 * submitFile is to be a local file but does not exist. 336 */ 337 public void setSubmitFile(String submitFile, boolean isItLocal) 338 throws JobException { 339 340 if (isItLocal) { 341 File f = new File(submitFile); 342 if (!f.isFile()) { 343 throw new JobException("Local file " + submitFile 344 + " is actually not a file." 345 + " If you want to declare it remote file, uncheck the cmdFileLocal option."); 346 } 347 } 348 jobfiles.submitfile = new JobFile(submitFile, isItLocal); 349 } 350 351 /** 352 * Submit a job, called from Job.submit(); boolean <i>overwrite</i> 353 * indicates whether old files that exist on the same directory should be 354 * removed before staging new files. As long jobIDs are not really unique, 355 * this is worth to be true. <i>options</i> can be a special options string 356 * for the actual jobmanager. 357 * 358 * @return: jobID as String if submission is successful (it is submitted and 359 * real jobID of the submitted job can be retrieved) Real jobID can 360 * also be found in job.status.jobID, but you probably do not need 361 * it except for logging. on error throws JobException 362 */ 363 public String submit(JobManager jobmanager, boolean overwrite, 364 String options) throws JobException { 365 366 if (jobmanager == null) { 367 throw new JobException( 368 "Valid Jobmanager is needed at job submission"); 369 } 370 this.jmgr = jobmanager; 371 372 // set defaults if things are unset or throw exception 373 checkDefaults(); 374 375 status.jobID = jmgr.submit(this, overwrite, options); 376 377 // The job has been successfully submitted (or an exception has been 378 // thrown) 379 status.statusCode = JobStatusCode.Wait; 380 381 return status.jobID; 382 } 383 384 /** 385 * Reconnect to a executing job. As long jobIDs are not really unique, this 386 * is worth to be true. <i>options</i> can be a special options string for 387 * the actual jobmanager. 388 * 389 * @return: jobID as String if submission is successful (it is submitted and 390 * real jobID of the submitted job can be retrieved) Real jobID can 391 * also be found in job.status.jobID, but you probably do not need 392 * it except for logging. on error throws JobException 393 */ 394 public boolean reconnect(JobManager jobmanager) throws JobException { 395 396 if (jobmanager == null) { 397 throw new JobException( 398 "Valid Jobmanager is needed at job reconnect"); 399 } 400 this.jmgr = jobmanager; 401 402 // set defaults if things are unset or throw exception 403 checkDefaults(); 404 status.statusCode = JobStatusCode.Wait; 405 status(); // successful query if not throws exception 406 return true; 407 } 408 409 /** 410 * If something important is not set, set default here or throw an exception 411 */ 412 private void checkDefaults() throws JobException { 413 414 if (localWorkdirPath == null) { 415 String home = System.getProperty("user.home"); 416 if ( System.getProperty("os.name").toLowerCase().indexOf("win") >= 0 ) { 417 home = System.getenv().get("HOMEPATH"); 418 } 419 setLocalWorkdir(new String( 420 home + File.separator + ".hpcc" + File.separator + myID)); 421 } 422 if (workdirPath == null) 423 setWorkdir(new String(".hpcc")); 424 425 // if ( jobfiles.executable == null ) 426 // throw new JobException("Job has not executable file specified."); 427 428 } 429 430 /** 431 * Check the status of the job 432 * 433 * @return: true if succeeded The JobStatusInfo data struct of Job is 434 * altered: (job.status) throws JobException on error, or you call 435 * for a non-submitted job 436 */ 437 public boolean status() throws JobException { 438 439 if (jmgr == null) { 440 throw new JobException( 441 "Valid Jobmanager is needed before checking job status"); 442 } 443 444 if (status.jobID == null) { 445 throw new JobException("The job is not submitted. " 446 + "Or at least, it has no real jobID, so we lost it."); 447 } 448 449 if (status.statusCode == JobStatusCode.Error) // no need to check it 450 // again 451 return true; 452 453 JobStatusInfo stat = jmgr.status(status.jobID,numTasks); 454 status.statusCode = stat.statusCode; 455 if(numTasks > 0) { 456 ((TaskParallelJobStatusInfo)status).taskStatusCodes = 457 ((TaskParallelJobStatusInfo)stat).taskStatusCodes; 458 } 459 460 return true; 461 } 462 463 /** 464 * Remove job from the queue (either running or waiting) 465 * 466 * @return: true if succeeded, false is not throws JobException on error, or 467 * you call for a non-submitted job 468 */ 469 public boolean deleteFromQueue() throws JobException { 470 471 if (jmgr == null) { 472 throw new JobException( 473 "Valid Jobmanager is needed before removing the job"); 474 } 475 476 if (status.jobID == null) { 477 throw new JobException("The job is not submitted. " 478 + "Or at least, it has no real jobID, so we lost it."); 479 } 480 481 boolean stat = jmgr.delete(status.jobID); 482 return stat; 483 } 484 485 /** Specify a set of jobs that must successfully complete before this 486 * can start. 487 */ 488 public void setDependentJobs(Job[] dependentJobs) { 489 _dependentJobs = dependentJobs; 490 } 491 492 /** Get a set of jobs that must successfully complete before this job 493 * can start. If there are none, returns null. 494 */ 495 public Job[] getDependentJobs() { 496 return _dependentJobs; 497 } 498 499 /* 500 * ---------------- Protected methods for JobManager and JobSupport classes 501 * ------------ 502 */ 503 public String getSubmitFile() { 504 if (jobfiles.submitfile != null) 505 return jobfiles.submitfile.filename; 506 return null; 507 } 508 509 protected boolean isSubmitFileLocal() { 510 if (jobfiles.submitfile != null) 511 return jobfiles.submitfile.isLocal; 512 return false; 513 } 514 515 protected String getBinFile() { 516 if (jobfiles.binfile != null) 517 return jobfiles.binfile.filename; 518 return null; 519 } 520 521 protected boolean isBinFileLocal() { 522 if (jobfiles.binfile != null) 523 return jobfiles.binfile.isLocal; 524 return false; 525 } 526 527 /** 528 * Return vector of local files that should be staged to the remote site for 529 * a job. Vector elements are type of File! 530 * 531 * @return: Vector of File objects. On error throws JobException 532 */ 533 protected Vector getLocalFiles() { 534 535 Vector files = new Vector(); 536 537 if (jobfiles.executable != null && jobfiles.executable.isLocal) 538 files.add(new File(jobfiles.executable.filename)); 539 540 int i; 541 int inputs = jobfiles.inputfiles.size(); 542 for (i = 0; i < inputs; i++) { 543 Job.JobFile f = (Job.JobFile) (jobfiles.inputfiles.get(i)); 544 if (f.isLocal) 545 files.add(new File(f.filename)); 546 } 547 548 int others = jobfiles.otherinputfiles.size(); 549 for (i = 0; i < others; i++) { 550 files.add(new File((String) jobfiles.otherinputfiles.get(i))); 551 } 552 553 if (jobfiles.submitfile != null && jobfiles.submitfile.isLocal) 554 files.add(new File(jobfiles.submitfile.filename)); 555 556 return files; 557 } 558 559 /** 560 * Return vector of remote files that should be copied into to remote job 561 * directory before submission. Vector elements are type of Strings! 562 * 563 * @return: Vector of File objects. On error throws JobException 564 */ 565 protected Vector getRemoteFiles() { 566 567 Vector files = new Vector(); 568 569 if (jobfiles.executable != null && !jobfiles.executable.isLocal) 570 files.add(jobfiles.executable.filename); 571 572 int i; 573 int inputs = jobfiles.inputfiles.size(); 574 for (i = 0; i < inputs; i++) { 575 Job.JobFile f = (Job.JobFile) (jobfiles.inputfiles.get(i)); 576 if (!f.isLocal) 577 files.add(f.filename); 578 } 579 580 if (jobfiles.submitfile != null && !jobfiles.submitfile.isLocal) 581 files.add(jobfiles.submitfile.filename); 582 583 return files; 584 } 585 586} // end-of-class-Job 587