001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.logistics.plugin.utils;
028:
029: import org.cougaar.planning.ldm.plan.Task;
030: import org.cougaar.planning.ldm.plan.AspectType;
031: import org.cougaar.planning.ldm.plan.Preference;
032: import org.cougaar.util.TimeSpan;
033: import org.cougaar.logistics.plugin.inventory.TaskUtils;
034: import org.cougaar.logistics.plugin.inventory.TimeUtils;
035: import org.cougaar.logistics.plugin.inventory.UtilsProvider;
036: import java.util.*;
037: import java.io.*;
038: import org.cougaar.core.service.LoggingService;
039: import org.cougaar.core.service.AlarmService;
040: import org.apache.xerces.parsers.SAXParser;
041: import org.xml.sax.helpers.DefaultHandler;
042: import org.xml.sax.Attributes;
043: import org.xml.sax.InputSource;
044: import org.xml.sax.SAXException;
045:
046: /**
047: * A TaskSchedulingPolicy instance prioritizes tasks as to which
048: * should be scheduled first.
049: * There are n priority levels, each with an accompanying predicate/test
050: * that tells whether a task is at that priority level.
051: * If a task matches the predicate for more than one level, it is
052: * assigned the highest level.
053: * Note that the highest level corresponds to the lowest number (e.g.,
054: * priority 0 is the highest priority and should be scheduled first).
055: * <p> A TaskSchedulingPolicy also can contain a sequence of time phases.
056: * This specified how to break a task into different time periods without
057: * actually expanding the task, hence allowing a plugin to handle
058: * particular time periods before others.
059: **/
060:
061: public class TaskSchedulingPolicy {
062:
063: public final static int UNKNOWN_PRIORITY = -1;
064:
065: // list of Predicates with 0th element corresponding to priority 0, etc.
066: private Predicate[] priorityTests;
067:
068: // specifies phases of processing of tasks, including prioritization
069: private PriorityPhaseMix[] ordering;
070:
071: /** Like UnaryPredicate except that object must always be a Task */
072: public static interface Predicate {
073: public boolean execute(Task t);
074: }
075:
076: /** class that allows specification of a priority and phase jointly */
077: public static class PriorityPhaseMix {
078: private int priority;
079: private TimeSpan timeSpan;
080:
081: public PriorityPhaseMix(int priority, TimeSpan timeSpan) {
082: this .priority = priority;
083: this .timeSpan = timeSpan;
084: }
085:
086: public int getPriority() {
087: return priority;
088: }
089:
090: public TimeSpan getTimeSpan() {
091: return timeSpan;
092: }
093: }
094:
095: /**
096: * Constructor when using just priorities and not phases
097: * @param priorityTests A set of tests that determines what priority
098: * a given task is. If a task passes the test in the i<sup>th</sup>
099: * entry of the array but not previous test, then the task is
100: * of priority i.
101: */
102: public TaskSchedulingPolicy(Predicate[] priorityTests) {
103: this .priorityTests = priorityTests;
104: ordering = new PriorityPhaseMix[priorityTests.length];
105: for (int i = 0; i < ordering.length; i++)
106: ordering[i] = new PriorityPhaseMix(i, null);
107: }
108:
109: /**
110: * Constructor when using just phases and not priorities
111: * @param phases A set of time intervals in order of how to handle
112: */
113: public TaskSchedulingPolicy(TimeSpan[] phases) {
114: priorityTests = new Predicate[] { PASSALL };
115: ordering = new PriorityPhaseMix[phases.length];
116: for (int i = 0; i < ordering.length; i++)
117: ordering[i] = new PriorityPhaseMix(0, phases[i]);
118: }
119:
120: /**
121: * Constructor specifying both priorities and phases
122: */
123: public TaskSchedulingPolicy(Predicate[] priorityTests,
124: PriorityPhaseMix[] ordering) {
125: this .priorityTests = priorityTests;
126: this .ordering = ordering;
127: }
128:
129: /* Specifies a time period over which to process tasks */
130: private static class TimePeriod implements TimeSpan {
131: private long start;
132: private long end;
133:
134: public TimePeriod(long start, long end) {
135: this .start = start;
136: this .end = end;
137: }
138:
139: public long getStartTime() {
140: return start;
141: }
142:
143: public long getEndTime() {
144: return end;
145: }
146: }
147:
148: /** Get a time span object */
149: public static TimeSpan makeTimeSpan(long start, long end) {
150: return new TimePeriod(start, end);
151: }
152:
153: /** Predicate that lets everything pass */
154: public static Predicate PASSALL = new Predicate() {
155: public boolean execute(Task task) {
156: return true;
157: }
158: };
159:
160: /** Predicate that tests if task is for level 2 detail */
161: public static class Level2Predicate implements Predicate {
162: private boolean isLevel2;
163: private TaskUtils taskUtils;
164:
165: public Level2Predicate(boolean isLevel2, TaskUtils taskUtils) {
166: this .isLevel2 = isLevel2;
167: this .taskUtils = taskUtils;
168: }
169:
170: public boolean execute(Task task) {
171: return (!isLevel2) ^ taskUtils.isLevel2(task);
172: }
173: }
174:
175: /** Predicate that tests if task is within given time span */
176: public static class TimeSpanPredicate implements Predicate {
177: private long earliestStart;
178: private long latestStart;
179:
180: public TimeSpanPredicate(long earliestStart, long latestStart) {
181: this .earliestStart = earliestStart;
182: this .latestStart = latestStart;
183: }
184:
185: public boolean execute(Task task) {
186: Preference pref = task.getPreference(AspectType.START_TIME);
187: if (pref == null)
188: pref = task.getPreference(AspectType.END_TIME);
189: if (pref == null)
190: return false;
191: long time = pref.getScoringFunction().getBest()
192: .getAspectValue().longValue();
193: return (time >= earliestStart) && (time < latestStart);
194: }
195: }
196:
197: public static TaskSchedulingPolicy fromXML(String filename,
198: UtilsProvider plugin, AlarmService alarm) {
199: try {
200: return fromXML(new FileInputStream(filename), plugin, alarm);
201: } catch (Exception e) {
202: plugin.getLoggingService(plugin).error(
203: "Could not open file " + filename);
204: return null;
205: }
206: }
207:
208: public static TaskSchedulingPolicy fromXML(InputStream stream,
209: UtilsProvider plugin, AlarmService alarm) {
210: try {
211: SAXParser parser = new SAXParser();
212: ParmsHandler handler = new ParmsHandler(plugin, alarm);
213: parser.setContentHandler(handler);
214: parser.parse(new InputSource(stream));
215: return handler.getPolicy();
216: } catch (Exception e) {
217: plugin.getLoggingService(plugin).error(e.getMessage());
218: return null;
219: }
220: }
221:
222: public PriorityPhaseMix[] getOrdering() {
223: return ordering;
224: }
225:
226: /** number of different phases for a particular priority */
227: public int numPhases(int priority) {
228: int count = 0;
229: for (int i = 0; i < ordering.length; i++)
230: if (ordering[i].getPriority() == priority)
231: count++;
232: return count;
233: }
234:
235: /** number of different priorities */
236: public int numPriorities() {
237: return priorityTests.length;
238: }
239:
240: public int taskPriority(Task task) {
241: return findPriority(task, 0, priorityTests.length - 1);
242: }
243:
244: public boolean isPriority(int priority, Task task) {
245: return findPriority(task, 0, priority) == priority;
246: }
247:
248: private int findPriority(Task task, int lowest, int highest) {
249: for (int i = lowest; i <= highest; i++) {
250: if (priorityTests[i].execute(task))
251: return i;
252: }
253: return UNKNOWN_PRIORITY;
254: }
255:
256: private static class ParmsHandler extends DefaultHandler {
257: private ArrayList criteria = null;
258: private ArrayList phases = null;
259: private ArrayList ordering = null;
260: private TaskUtils taskUtils;
261: private TimeUtils timeUtils;
262: private long now;
263: private TaskSchedulingPolicy policy = null;
264:
265: public ParmsHandler(UtilsProvider plugin, AlarmService alarm) {
266: taskUtils = plugin.getTaskUtils();
267: timeUtils = plugin.getTimeUtils();
268: now = alarm.currentTimeMillis();
269: }
270:
271: public TaskSchedulingPolicy getPolicy() {
272: return policy;
273: }
274:
275: public void startElement(String uri, String local, String name,
276: Attributes atts) throws SAXException {
277: if (name.equals("PRIORITIES")) {
278: criteria = new ArrayList();
279: } else if (name.equals("PHASES")) {
280: phases = new ArrayList();
281: } else if (name.equals("ORDERING")) {
282: if ((criteria == null) || (phases == null))
283: throw new SAXException(
284: "In scheduling policy, should "
285: + "have criteria and phases defined before ordering");
286: ordering = new ArrayList();
287: } else if (name.equals("CRITERION")) {
288: String passAll = atts.getValue("passall");
289: String level = atts.getValue("level");
290: String earliest = atts.getValue("earliest");
291: String latest = atts.getValue("latest");
292: if (passAll != null)
293: criteria.add(PASSALL);
294: else if (level != null)
295: criteria.add(new Level2Predicate("2".equals(level
296: .trim()), taskUtils));
297: else if ((earliest != null) || (latest != null)) {
298: try {
299: int t1 = (earliest == null) ? 0 : Integer
300: .parseInt(earliest);
301: int t2 = (latest == null) ? 0 : Integer
302: .parseInt(latest);
303: criteria.add(new TimeSpanPredicate(timeUtils
304: .addNDays(now, t1), timeUtils.addNDays(
305: now, t2)));
306: } catch (NumberFormatException e) {
307: throw new SAXException(
308: "In scheduling policy criterion, the "
309: + "values of earliest and latest of a criterion must be integers");
310: }
311: } else
312: throw new SAXException(
313: "In scheduling policy criterion, "
314: + "need to define at least one expected attribute");
315: } else if (name.equals("PHASE")) {
316: String start = atts.getValue("start");
317: String end = atts.getValue("end");
318: if ((start == null) || (end == null))
319: throw new SAXException(
320: "In scheduling policy criterion, "
321: + "must define start and end attributes for a phase");
322: try {
323: int istart = Integer.parseInt(start);
324: int iend = Integer.parseInt(end);
325: phases.add(makeTimeSpan(timeUtils.addNDays(now,
326: istart), timeUtils.addNDays(now, iend)));
327: } catch (NumberFormatException e) {
328: throw new SAXException(
329: "In scheduling policy criterion, "
330: + "the values of start and end of a phase must be integers");
331: }
332: } else if (name.equals("PRIORITYPHASEMIX")) {
333: String priority = atts.getValue("priority");
334: String phase = atts.getValue("phase");
335: if ((priority == null) || (phase == null))
336: throw new SAXException(
337: "In scheduling policy criterion, "
338: + "must define priority and phase attributes for a priphasemix");
339: try {
340: int ipri = Integer.parseInt(priority);
341: int iphase = Integer.parseInt(phase);
342: ordering.add(new PriorityPhaseMix(ipri,
343: (TimeSpan) phases.get(iphase)));
344: } catch (NumberFormatException e) {
345: throw new SAXException(
346: "In scheduling policy criterion, the "
347: + "values of priority and phase of a priphasemix must be integers");
348: }
349: }
350: }
351:
352: public void endElement(String uri, String local, String name) {
353: if (name.equals("POLICY")) {
354: if (ordering != null)
355: policy = new TaskSchedulingPolicy(
356: (Predicate[]) criteria
357: .toArray(new Predicate[criteria
358: .size()]),
359: (PriorityPhaseMix[]) ordering
360: .toArray(new PriorityPhaseMix[ordering
361: .size()]));
362: else if (phases != null)
363: policy = new TaskSchedulingPolicy(
364: (TimeSpan[]) phases
365: .toArray(new TimeSpan[phases.size()]));
366: else if (criteria != null)
367: policy = new TaskSchedulingPolicy(
368: (Predicate[]) criteria
369: .toArray(new Predicate[criteria
370: .size()]));
371: }
372: }
373: }
374:
375: }
|