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:
016: package org.griphyn.cPlanner.namespace;
017:
018: import org.griphyn.cPlanner.classes.Profile;
019: import org.griphyn.cPlanner.common.PegasusProperties;
020:
021: import java.util.Iterator;
022: import java.util.Map;
023: import java.util.TreeMap;
024:
025: /**
026: * This profile namespace is the placeholder for the keys that go into the .dag
027: * file . Keys like RETRY that trigger retries in dagman in the event of a job
028: * failing would go in here.
029: * All the keys stored in it are in UPPERCASE irrespective of the case specified
030: * by the user in the various catalogs. To specify a post script or a pre script
031: * use POST and PRE keys.
032: *
033: * @author Karan Vahi
034: * @author Gaurang Mehta
035: * @version $Revision: 50 $
036: */
037:
038: public class Dagman extends Namespace {
039:
040: /**
041: * The name of the namespace that this class implements.
042: */
043: public static final String NAMESPACE_NAME = Profile.DAGMAN;
044:
045: /**
046: * The name of the key that determines what post script is to be invoked
047: * when the job completes.
048: */
049: public static final String POST_SCRIPT_KEY = "POST";
050:
051: /**
052: * The name of the key that determines the arguments that need to be passed
053: * to the postscript.
054: */
055: public static final String POST_SCRIPT_ARGUMENTS_KEY = "POST_ARGS";
056:
057: /**
058: * The name of the key that determines the file on the submit host on
059: * which postscript is to be invoked.
060: */
061: public static final String OUTPUT_KEY = "OUTPUT";
062:
063: /**
064: * The name of the key that determines what pre script is to be invoked
065: * when the job is run.
066: */
067: public static final String PRE_SCRIPT_KEY = "PRE";
068:
069: /**
070: * The name of the key that determines the arguments that need to be passed
071: * to the postscript.
072: */
073: public static final String PRE_SCRIPT_ARGUMENTS_KEY = "PRE_ARGS";
074:
075: /**
076: * The name of the key that determines how many times DAGMAN should be
077: * retrying the job.
078: */
079: public static final String RETRY_KEY = "RETRY";
080:
081: /**
082: * The name of the key that indicates the path to the corresponding
083: * submit file for the job.
084: */
085: public static final String JOB_KEY = "JOB";
086:
087: /**
088: * The key name for the post script that is put in the .dag file.
089: */
090: private static final String POST_SCRIPT_REPLACEMENT_KEY = "SCRIPT POST";
091:
092: /**
093: * The key name for the pre script that is put in the .dag file.
094: */
095: private static final String PRE_SCRIPT_REPLACEMENT_KEY = "SCRIPT PRE";
096:
097: /**
098: * The name of the job (jobname) to which the profiles for this namespace
099: * belong.
100: *
101: * @see org.griphyn.cPlanner.classes.SubInfo#jobName
102: */
103: private String mJobName;
104:
105: /**
106: * The name of the implementing namespace. It should be one of the valid
107: * namespaces always.
108: *
109: * @see Namespace#isNamespaceValid(String)
110: */
111: protected String mNamespace;
112:
113: /**
114: * The default constructor.
115: * We always initialize the map, as the map is guarenteed to store at least
116: * the postscript value for a job.
117: */
118: public Dagman() {
119: mProfileMap = new TreeMap();
120: mNamespace = NAMESPACE_NAME;
121: mJobName = null;
122: }
123:
124: /**
125: * The overloaded constructor.
126: *
127: * @param mp the initial map containing the profile keys for this namespace.
128: */
129: public Dagman(Map mp) {
130: mProfileMap = new TreeMap(mp);
131: mNamespace = NAMESPACE_NAME;
132: mJobName = null;
133: }
134:
135: /**
136: * The overloaded constructor.
137: *
138: * @param mp the initial map containing the profile keys for this namespace.
139: * @param name name of the job with which these profile keys are associated.
140: */
141: public Dagman(Map mp, String name) {
142: mProfileMap = new TreeMap(mp);
143: mNamespace = NAMESPACE_NAME;
144: mJobName = name;
145: }
146:
147: /**
148: * Returns the name of the namespace associated with the profile implementations.
149: *
150: * @return the namespace name.
151: * @see #NAMESPACE_NAME
152: */
153: public String namespaceName() {
154: return mNamespace;
155: }
156:
157: /**
158: * It sets the name of the job that is associated with the profiles contained
159: * in this placeholder.
160: *
161: * @param name name of the job with which these profile keys are associated.
162: */
163: public void setJobName(String name) {
164: mJobName = name;
165: }
166:
167: /**
168: * Constructs a new element of the format (key=value).
169: * The underlying map is allocated memory in the constructors always.
170: * All the keys are converted to UPPER CASE before storing.
171: *
172: * @param key is the left-hand-side
173: * @param value is the right hand side
174: */
175: public void construct(String key, String value) {
176: //convert to uppercase the key
177: mProfileMap.put(key.toUpperCase(), value);
178: }
179:
180: /**
181: * This checks whether the key passed by the user is valid in the current
182: * namespace or not. All keys are assumed valid currently.
183: *
184: * @param key (left hand side)
185: * @param value (right hand side)
186: *
187: * @return Namespace.VALID_KEY
188: *
189: */
190: public int checkKey(String key, String value) {
191: //all are valid because of certain keys
192: //are defined in SCRIPT POST, that needs
193: //to be corrected
194: int res = 0;
195: if (key == null || key.length() < 2 || value == null
196: || value.length() < 2) {
197: res = MALFORMED_KEY;
198: }
199:
200: //convert key to lower case
201: key = key.toUpperCase();
202:
203: switch (key.charAt(0)) {
204:
205: case 'J':
206: if (key.compareTo(this .JOB_KEY) == 0) {
207: res = VALID_KEY;
208: } else {
209: res = NOT_PERMITTED_KEY;
210: }
211: break;
212:
213: case 'O':
214: if (key.compareTo(this .OUTPUT_KEY) == 0) {
215: res = VALID_KEY;
216: } else {
217: res = NOT_PERMITTED_KEY;
218: }
219: break;
220:
221: case 'P':
222: if ((key.compareTo(this .POST_SCRIPT_KEY) == 0)
223: || (key.compareTo(this .POST_SCRIPT_ARGUMENTS_KEY) == 0)
224: || (key.compareTo(this .PRE_SCRIPT_KEY) == 0)
225: || (key.compareTo(this .PRE_SCRIPT_ARGUMENTS_KEY) == 0)) {
226: res = VALID_KEY;
227: } else {
228: res = NOT_PERMITTED_KEY;
229: }
230: break;
231:
232: case 'R':
233: if (key.compareTo(this .RETRY_KEY) == 0) {
234: res = VALID_KEY;
235: } else {
236: res = NOT_PERMITTED_KEY;
237: }
238: break;
239:
240: default:
241: res = NOT_PERMITTED_KEY;
242: }
243:
244: return res;
245: }
246:
247: /**
248: * It puts in the namespace specific information specified in the properties
249: * file into the namespace. The profile information is populated only if the
250: * corresponding key does not exist in the object already.
251: *
252: * @param properties the <code>PegasusProperties</code> object containing
253: * all the properties that the user specified at various
254: * places (like .chimerarc, properties file, command line).
255: * @param pool the pool name where the job is scheduled to run.
256: */
257: public void checkKeyInNS(PegasusProperties properties, String pool) {
258: //check if RETRY key already exists
259: if (!this .containsKey(this .RETRY_KEY)) {
260: //try to get one from the condor file
261: String val = properties.getCondorRetryValue();
262: if (val != null && Integer.parseInt(val) > 0)
263: //construct the RETRY key and put it in
264: //assuming val is a proper integer
265: this .checkKeyInNS(this .RETRY_KEY, val);
266: }
267:
268: //check if the arguments for the
269: //post script are specified or not
270: if (!this .containsKey(this .POST_SCRIPT_ARGUMENTS_KEY)) {
271: //push in the default arguments for the post script
272: this .checkKeyInNS(this .POST_SCRIPT_ARGUMENTS_KEY,
273: properties.getPOSTScriptArguments());
274: }
275:
276: //check if the arguments for the
277: //pre script are specified or not
278: if (!this .containsKey(this .PRE_SCRIPT_ARGUMENTS_KEY)) {
279: //push in the default arguments for the post script
280: this .checkKeyInNS(this .PRE_SCRIPT_ARGUMENTS_KEY, properties
281: .getPrescriptArguments());
282: }
283:
284: //what type of postscript needs to be invoked for the job
285: if (!this .containsKey(this .POST_SCRIPT_KEY)) {
286: //get one from the properties
287: String ps = properties.getPOSTScript();
288: if (ps != null) {
289: checkKeyInNS(this .POST_SCRIPT_KEY, properties
290: .getPOSTScript());
291: }
292: }
293:
294: }
295:
296: /**
297: * Merge the profiles in the namespace in a controlled manner.
298: * In case of intersection, the new profile value overrides, the existing
299: * profile value.
300: *
301: * @param profiles the <code>Namespace</code> object containing the profiles.
302: */
303: public void merge(Namespace profiles) {
304: //check if we are merging profiles of same type
305: if (!(profiles instanceof Dagman)) {
306: //throw an error
307: throw new IllegalArgumentException(
308: "Profiles mismatch while merging");
309: }
310: String key;
311: for (Iterator it = profiles.getProfileKeyIterator(); it
312: .hasNext();) {
313: //construct directly. bypassing the checks!
314: key = (String) it.next();
315: this .construct(key, (String) profiles.get(key));
316: }
317: }
318:
319: /**
320: * Converts the contents of the map into the string that can be put in the
321: * Condor file for printing.
322: *
323: * @return the the textual description.
324: */
325: public String toString() {
326: return toString(mJobName);
327: }
328:
329: /**
330: * Converts the contents of the map into the string that can be put in the
331: * Condor file for printing.
332: *
333: * @param name the name of the condor job that contains these variables.
334: *
335: * @return the textual description.
336: */
337: public String toString(String name) {
338: StringBuffer sb = new StringBuffer();
339:
340: if (mProfileMap == null) {
341: //no profile keys were stored in here
342: return sb.toString();
343: }
344: String key = null;
345: for (Iterator it = mProfileMap.keySet().iterator(); it
346: .hasNext();) {
347: key = (String) it.next();
348:
349: //continue to next if the key has to be ignored.
350: if (ignore(key)) {
351: continue;
352: }
353:
354: sb.append(replacementKey(key)).append(" ").append(name)
355: .append(" ").
356: /*append((String)mProfileMap.get(key))*/
357: append(replacementValue(key)).append("\n");
358: }
359: return sb.toString();
360:
361: }
362:
363: /**
364: * Helper method to decide whether a key has to be ignored or not.
365: *
366: * @return boolean
367: */
368: private boolean ignore(String key) {
369: return key.equals(this .POST_SCRIPT_ARGUMENTS_KEY)
370: || key.equals(this .PRE_SCRIPT_ARGUMENTS_KEY)
371: || key.equals(this .OUTPUT_KEY);
372: }
373:
374: /**
375: * Returns the replacement key that needs to be printed in .dag file in
376: * lieu of the key.
377: *
378: * @return the replacement key.
379: */
380: private String replacementKey(String key) {
381: String replacement = key;
382: if (key.equalsIgnoreCase(this .POST_SCRIPT_KEY)) {
383: replacement = this .POST_SCRIPT_REPLACEMENT_KEY;
384: } else if (key.equalsIgnoreCase(this .PRE_SCRIPT_KEY)) {
385: replacement = this .PRE_SCRIPT_REPLACEMENT_KEY;
386: }
387: return replacement;
388: }
389:
390: /**
391: * Returns the replacement value that needs to be printed in .dag file for
392: * a key. This helps us tie the post script path to the arguments, and same
393: * for prescript.
394: *
395: * @return the replacement value
396: */
397: private String replacementValue(String key) {
398: StringBuffer value = new StringBuffer();
399:
400: //append the value for the key
401: value.append((String) mProfileMap.get(key));
402:
403: //for postscript and prescript in addition put in the arguments.
404: if (key.equalsIgnoreCase(this .POST_SCRIPT_KEY)) {
405: //append the postscript arguments
406: value.append(" ").append(
407: (String) this .get(this .POST_SCRIPT_ARGUMENTS_KEY));
408: //append the output file
409: value.append(" ")
410: .append((String) this .get(this .OUTPUT_KEY));
411:
412: } else if (key.equalsIgnoreCase(this .PRE_SCRIPT_KEY)) {
413: //append the prescript arguments
414: value.append(" ").append(
415: (String) this .get(this .PRE_SCRIPT_ARGUMENTS_KEY));
416: }
417: return value.toString();
418: }
419:
420: /**
421: * Returns a copy of the current namespace object.
422: *
423: * @return the Cloned object
424: */
425: public Object clone() {
426: Dagman ns = (mProfileMap == null) ? new Dagman() : new Dagman(
427: this .mProfileMap);
428: ns.mJobName = (mJobName == null) ? null : new String(
429: this.mJobName);
430: return ns;
431: }
432:
433: }
|