001: /*
002: * This file or a portion of this file is licensed under the terms of
003: * the Globus Toolkit Public License, found in file GTPL, or at
004: * http://www.globus.org/toolkit/download/license.html. This notice must
005: * appear in redistributions of this file, with or without modification.
006: *
007: * Redistributions of this Software, with or without modification, must
008: * reproduce the GTPL in: (1) the Software, or (2) the Documentation or
009: * some other similar material which is provided with the Software (if
010: * any).
011: *
012: * Copyright 1999-2004 University of Chicago and The University of
013: * Southern California. All rights reserved.
014: */
015: package org.griphyn.cPlanner.classes;
016:
017: import org.griphyn.cPlanner.common.LogManager;
018:
019: import org.griphyn.common.util.Currently;
020: import org.griphyn.common.util.Version;
021:
022: import java.io.File;
023:
024: import java.util.Enumeration;
025: import java.util.Iterator;
026: import java.util.Map;
027: import java.util.Set;
028: import java.util.HashSet;
029: import java.util.TreeMap;
030: import java.util.Vector;
031:
032: /**
033: * Holds the information needed to make one dag file corresponding to a Abstract
034: * Dag. It holds information to generate the .dax file which is submitted to
035: * Condor.
036: *
037: *
038: * @author Karan Vahi
039: * @author Gaurang Mehta
040: * @version $Revision: 314 $
041: */
042:
043: public class DagInfo extends Data {
044:
045: /**
046: * The default name for the ADag object, if not supplied in the DAX.
047: */
048: private static final String DEFAULT_NAME = "PegasusRun";
049:
050: /**
051: * Vector of String objects containing the jobname_id of jobs making
052: * the abstract dag.
053: */
054: public Vector dagJobs;
055:
056: /**
057: * Captures the parent child relations making up the DAG. It is a Vector of
058: * <code>PCRelation</code> objects.
059: */
060: public Vector relations;
061:
062: /**
063: * The name of the Abstract Dag taken from the adag element of the DAX
064: * generated by the Abstract Planner.
065: */
066: public String nameOfADag;
067:
068: /**
069: * Refers to the number of the Abstract Dags which are being sent to the
070: * Concrete Planner in response to the user's request.
071: */
072: public String count;
073:
074: /**
075: * Refers to the number of the Dag. Index can vary from 0 to count - 1.
076: */
077: public String index;
078:
079: /**
080: * It is a unique identifier identifying the concrete DAG generated by Pegasus.
081: * It consists of the dag name and the timestamp.
082: *
083: * @see #flowIDName
084: * @see #mFlowTimestamp
085: */
086: public String flowID;
087:
088: /**
089: * It is the name of the dag as generated by Chimera in the dax. If none is
090: * specified then a default name of PegasusRun is assigned.
091: */
092: public String flowIDName;
093:
094: /**
095: * The ISO timestamp corresponding to the time when Pegasus is invoked for a
096: * dax. It is used to generate the random directory names also if required.
097: */
098: private String mFlowTimestamp;
099:
100: /**
101: * Keeps the last modified time of the DAX.
102: */
103: private String mDAXMTime;
104:
105: /**
106: * Identifies the release version of the VDS software that was
107: * used to generate the workflow. It is populated from Version.java.
108: *
109: * @see org.griphyn.common.util.Version
110: */
111: public String releaseVersion;
112:
113: /**
114: * The workflow metric objects that contains metrics about the workflow being
115: * planned.
116: */
117: private WorkflowMetrics mWFMetrics;
118:
119: /**
120: * Contains a unique ordered listing of the logical names referred
121: * to by the dag. The TreeMap implementation guarentees us a log(n) execution
122: * time for the basic operations. Hence should scale well. The key for the
123: * map is the lfn name. The value is a String flag denoting whether this
124: * file is an input(i) or output(o) or both (b) or none(n). A value of
125: * none(n) would denote an error condition.
126: */
127: public TreeMap lfnMap;
128:
129: //for scripts later
130:
131: /**
132: * The default constructor.
133: */
134: public DagInfo() {
135: dagJobs = new Vector();
136: relations = new Vector();
137: nameOfADag = new String();
138: count = new String();
139: index = new String();
140: flowID = new String();
141: flowIDName = new String();
142: mFlowTimestamp = new String();
143: mDAXMTime = new String();
144: releaseVersion = new String();
145: lfnMap = new TreeMap();
146: mWFMetrics = new WorkflowMetrics();
147: }
148:
149: /**
150: * Adds a new job to the dag.
151: *
152: * @param job the job to be added
153: */
154: public void addNewJob(SubInfo job) {
155: dagJobs.add(job.getID());
156: //increment the various metrics
157: mWFMetrics.increment(job);
158: }
159:
160: /**
161: * Adds a new PCRelation pair to the Vector of <code>PCRelation</code>
162: * pairs. Since we are adding a new relation the isDeleted parameter should
163: * be false.
164: *
165: * @param parent The parent in the relation pair
166: * @param child The child in the relation pair
167: *
168: * @see #relations
169: */
170: public void addNewRelation(String parent, String child) {
171: PCRelation newRelation = new PCRelation(parent, child);
172: relations.addElement(newRelation);
173: }
174:
175: /**
176: * Adds a new PCRelation pair to the Vector of <code>PCRelation</code> pairs.
177: *
178: * @param parent The parent in the relation pair
179: * @param child The child in the relation pair
180: * @param isDeleted Whether the relation has been deleted due to the
181: * reduction algorithm or not
182: *
183: * @see #relations
184: */
185: public void addNewRelation(String parent, String child,
186: boolean isDeleted) {
187: PCRelation newRelation = new PCRelation(parent, child,
188: isDeleted);
189: relations.addElement(newRelation);
190: }
191:
192: /**
193: * Removes a job from the dag/graph structure. It however does not
194: * delete the relations the edges that refer to the job.
195: *
196: * @param job the job to be removed
197: *
198: * @return boolean indicating whether removal was successful or not.
199: */
200: public boolean remove(SubInfo job) {
201: mWFMetrics.decrement(job);
202: return dagJobs.remove(job.getID());
203: }
204:
205: /**
206: * It returns the list of lfns referred to by the DAG. The list is unique
207: * as it is gotten from iterating through the lfnMap.
208: *
209: * @return a Set of <code>String<code> objects corresponding to the
210: * logical filenames
211: */
212: public Set getLFNs() {
213: return this .getLFNs(false);
214: }
215:
216: /**
217: * Returns the list of lfns referred to by the DAG. The list is unique
218: * as it is gotten from iterating through the lfnMap. The contents of the list
219: * are determined on the basis of the command line options passed by the user
220: * at runtime. For e.g. if the user has specified force, then one needs to
221: * search only for the input files.
222: *
223: * @param onlyInput a boolean flag indicating that you need only the input
224: * files to the whole workflow
225: *
226: * @return a set of logical filenames.
227: */
228: public Set getLFNs(boolean onlyInput) {
229:
230: Set lfns = onlyInput ? new HashSet(lfnMap.size() / 3)
231: : new HashSet(lfnMap.size());
232: String key = null;
233: String val = null;
234:
235: //if the force option is set we
236: //need to search only for the
237: //input files in the dag i.e
238: //whose link is set to input in
239: //the dag.
240: if (onlyInput) {
241: for (Iterator it = lfnMap.keySet().iterator(); it.hasNext();) {
242: key = (String) it.next();
243: val = (String) lfnMap.get(key);
244:
245: if (val.equals("i")) {
246: lfns.add(key);
247: }
248: }
249: } else {
250: lfns = new HashSet(lfnMap.keySet());
251: }
252:
253: return lfns;
254: }
255:
256: /**
257: * Returns the label of the workflow, that was specified in the DAX.
258: *
259: * @return the label of the workflow.
260: */
261: public String getLabel() {
262: return (nameOfADag == null) ? this .DEFAULT_NAME : nameOfADag;
263: }
264:
265: /**
266: * Returns the last modified time for the file containing the workflow
267: * description.
268: *
269: * @return the MTime
270: */
271: public String getMTime() {
272: return mDAXMTime;
273: }
274:
275: /**
276: * Returns the flow timestamp for the workflow.
277: *
278: * @return the flowtimestamp
279: */
280: public String getFlowTimestamp() {
281: return mFlowTimestamp;
282: }
283:
284: /**
285: * Sets the flow timestamp for the workflow.
286: *
287: * @param timestamp the flowtimestamp
288: */
289: public void setFlowTimestamp(String timestamp) {
290: mFlowTimestamp = timestamp;
291: }
292:
293: /**
294: * Returns the number of jobs in the dag on the basis of number of elements
295: * in the <code>dagJobs</code> Vector.
296: *
297: * @return the number of the jobs.
298: */
299: public int getNoOfJobs() {
300: return dagJobs.size();
301: }
302:
303: /**
304: * Gets all the parents of a particular node.
305: *
306: * @param node the name of the job whose parents are to be found.
307: *
308: * @return Vector corresponding to the parents of the node.
309: */
310: public Vector getParents(String node) {
311: //getting the parents of that node
312: Enumeration ePcRel = this .relations.elements();
313: Vector vParents = new Vector();
314: PCRelation currentRelPair;
315: while (ePcRel.hasMoreElements()) {
316: currentRelPair = (PCRelation) ePcRel.nextElement();
317: if (currentRelPair.child.trim().equalsIgnoreCase(node)) {
318: vParents.addElement(new String(currentRelPair.parent));
319: }
320: }
321:
322: return vParents;
323: }
324:
325: /**
326: * Get all the children of a particular node.
327: *
328: * @param node the name of the node whose children we want to find.
329: *
330: * @return Vector containing the children of the node.
331: */
332: public Vector getChildren(String node) {
333: Enumeration ePcRel = this .relations.elements();
334: Vector vChildren = new Vector();
335: PCRelation currentRelPair;
336:
337: while (ePcRel.hasMoreElements()) {
338: currentRelPair = (PCRelation) ePcRel.nextElement();
339: if (currentRelPair.parent.trim().equalsIgnoreCase(node)) {
340: vChildren.addElement(new String(currentRelPair.child));
341: }
342: }
343:
344: return vChildren;
345: }
346:
347: /**
348: * This returns all the leaf nodes of the dag. The way the structure of Dag
349: * is specified in terms of the parent child relationship pairs, the
350: * determination of the leaf nodes can be computationally intensive. The
351: * complexity if of order n^2.
352: *
353: * @return Vector of <code>String</code> corresponding to the job names of
354: * the leaf nodes.
355: *
356: * @see org.griphyn.cPlanner.classes.PCRelation
357: * @see org.griphyn.cPlanner.classes.DagInfo#relations
358: */
359: public Vector getLeafNodes() {
360: Vector leafJobs = new Vector();
361: Vector vJobs = this .dagJobs;
362: Vector vRelations = this .relations;
363: Enumeration eRel;
364: String job;
365: PCRelation pcRel;
366: boolean isLeaf = false;
367:
368: //search for all the jobs which are Roots i.e are not child in relation
369: Enumeration e = vJobs.elements();
370:
371: while (e.hasMoreElements()) {
372: //traverse through all the relations
373: job = (String) e.nextElement();
374: eRel = vRelations.elements();
375:
376: isLeaf = true;
377: while (eRel.hasMoreElements()) {
378: pcRel = (PCRelation) eRel.nextElement();
379:
380: if (pcRel.parent.equalsIgnoreCase(job)) { //means not a Child job
381: isLeaf = false;
382: break;
383: }
384: }
385:
386: //adding if leaf to vector
387: if (isLeaf) {
388: mLogger.log("Leaf job is " + job,
389: LogManager.DEBUG_MESSAGE_LEVEL);
390: leafJobs.addElement(new String(job));
391: }
392: }
393:
394: return leafJobs;
395: }
396:
397: /**
398: * It determines the root Nodes for the ADag looking at the relation pairs
399: * of the adag. The way the structure of Dag is specified in terms
400: * of the parent child relationship pairs, the determination of the leaf
401: * nodes can be computationally intensive. The complexity if of
402: * order n^2.
403: *
404: *
405: * @return the root jobs of the Adag
406: *
407: * @see org.griphyn.cPlanner.classes.PCRelation
408: * @see org.griphyn.cPlanner.classes.DagInfo#relations
409: *
410: */
411: public Vector getRootNodes() {
412: Vector rootJobs = new Vector();
413: Vector vJobs = this .dagJobs;
414: Vector vRelations = this .relations;
415: Enumeration eRel;
416: String job;
417: PCRelation pcRel;
418: boolean isRoot = false;
419:
420: //search for all the jobs which are Roots
421: //i.e are not child in relation
422: Enumeration e = vJobs.elements();
423:
424: while (e.hasMoreElements()) {
425: //traverse through all the relations
426: job = (String) e.nextElement();
427: eRel = vRelations.elements();
428:
429: isRoot = true;
430: while (eRel.hasMoreElements()) {
431: pcRel = (PCRelation) eRel.nextElement();
432:
433: if (pcRel.child.equalsIgnoreCase(job)) { //means not a Root job
434: isRoot = false;
435: break;
436: }
437: }
438: //adding if Root to vector
439: if (isRoot) {
440: mLogger.log("Root job is " + job,
441: LogManager.DEBUG_MESSAGE_LEVEL);
442: rootJobs.addElement(new String(job));
443: }
444: }
445:
446: return rootJobs;
447: }
448:
449: /**
450: * Returns the workflow metrics so far.
451: *
452: * @return the workflow metrics
453: */
454: public WorkflowMetrics getWorkflowMetrics() {
455: return this .mWFMetrics;
456: }
457:
458: /**
459: * Generates the flow id for this current run. It is made of the name of the
460: * dag and a timestamp. This is a simple concat of the mFlowTimestamp and the
461: * flowName. For it work correctly the function needs to be called after the
462: * flow name and timestamp have been generated.
463: */
464: public void generateFlowID() {
465: StringBuffer sb = new StringBuffer(40);
466:
467: sb.append(flowIDName).append("-").append(mFlowTimestamp);
468:
469: flowID = sb.toString();
470: }
471:
472: /**
473: * Generates the name of the flow. It is same as the nameOfADag if specified
474: * in dax generated by Chimera.
475: */
476: public void generateFlowName() {
477: StringBuffer sb = new StringBuffer();
478:
479: if (nameOfADag != null)
480: sb.append(nameOfADag);
481: else
482: sb.append(this .DEFAULT_NAME);
483:
484: //append the count. that is important for deffered planning
485: sb.append("-").append(index);
486:
487: flowIDName = sb.toString();
488:
489: }
490:
491: /**
492: * Sets the label for the workflow.
493: *
494: * @param label the label to be assigned to the workflow
495: */
496: public void setLabel(String label) {
497: this .nameOfADag = label;
498: mWFMetrics.setLabel(label);
499: }
500:
501: /**
502: * Sets the mtime (last modified time) for the DAX. It is the time, when
503: * the DAX file was last modified. If the DAX file does not exist or an
504: * IO error occurs, the MTime is set to OL i.e . The DAX mTime is always
505: * generated in an extended format. Generating not in extended format, leads
506: * to the XML parser tripping while parsing the invocation record generated
507: * by Kickstart.
508: *
509: * @param path the path to the DAX|PDAX file.
510: */
511: public void setDAXMTime(String path) {
512: File f = new File(path);
513: long time = f.lastModified();
514: mDAXMTime = Currently.iso8601(false, true, false,
515: new java.util.Date(time));
516: }
517:
518: /**
519: * Grabs the release version from VDS.Properties file.
520: *
521: * @see org.griphyn.common.util.Version
522: */
523: public void setReleaseVersion() {
524: this .releaseVersion = Version.instance().toString();
525: }
526:
527: /**
528: * Updates the lfn map, that contains the mapping of an lfn with the type.
529: *
530: * @param lfn the logical file name.
531: * @param type type the type of lfn (i|o|b). usually a character.
532: */
533: public void updateLFNMap(String lfn, String type) {
534: Object entry = lfnMap.get(lfn);
535: if (entry == null) {
536: lfnMap.put(lfn, type);
537: return;
538: } else {
539: //there is a preexisting entry in the map, check if it needs to be
540: //updated
541: if (!(entry.equals("b") || entry.equals(type))) {
542: //types do not match. so upgrade the type to both
543: lfnMap.put(lfn, "b");
544: }
545: }
546: }
547:
548: /**
549: * Returns a new copy of the Object.
550: *
551: * @return a copy of the object.
552: */
553: public Object clone() {
554: DagInfo dag = new DagInfo();
555:
556: dag.dagJobs = (Vector) this .dagJobs.clone();
557: dag.relations = (Vector) this .relations.clone();
558: dag.nameOfADag = new String(this .nameOfADag);
559: dag.count = new String(this .count);
560: dag.index = new String(this .index);
561: dag.flowID = new String(this .flowID);
562: dag.flowIDName = new String(this .flowIDName);
563: dag.mFlowTimestamp = new String(this .mFlowTimestamp);
564: dag.mDAXMTime = new String(this .mDAXMTime);
565: dag.releaseVersion = new String(this .releaseVersion);
566: dag.lfnMap = (TreeMap) this .lfnMap.clone();
567: dag.mWFMetrics = (WorkflowMetrics) this .mWFMetrics.clone();
568: return dag;
569: }
570:
571: /**
572: * Returns the a textual description of the object.
573: *
574: * @return textual description.
575: */
576: public String toString() {
577: String st = "\n "
578: + "\n Name of ADag : "
579: + this .nameOfADag
580: + "\n Index : "
581: + this .index
582: + " Count :"
583: + this .count
584: +
585: //"\n FlowId : " + this.flowID +
586: "\n FlowName : "
587: + this .flowIDName
588: + "\n FlowTimestamp: "
589: + this .mFlowTimestamp
590: + "\n Release Ver : "
591: + this .releaseVersion
592: + vectorToString(" Relations making the Dag ",
593: this .relations) + "\n LFN List is "
594: + this.lfnMap;
595:
596: return st;
597: }
598:
599: }
|