001: /*--------------------------------------------------------------------------
002: * <copyright>
003: *
004: * Copyright 2000-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.glm.plugins.inventory;
028:
029: import org.cougaar.core.blackboard.IncrementalSubscription;
030: import org.cougaar.glm.execution.common.InventoryReport;
031: import org.cougaar.glm.ldm.Constants;
032: import org.cougaar.glm.ldm.asset.Inventory;
033: import org.cougaar.glm.ldm.asset.InventoryPG;
034: import org.cougaar.glm.ldm.asset.Organization;
035: import org.cougaar.glm.ldm.plan.QuantityScheduleElement;
036: import org.cougaar.glm.plugins.TaskUtils;
037: import org.cougaar.glm.plugins.TimeUtils;
038: import org.cougaar.planning.ldm.asset.Asset;
039: import org.cougaar.planning.ldm.asset.ItemIdentificationPG;
040: import org.cougaar.planning.ldm.plan.Schedule;
041: import org.cougaar.planning.ldm.plan.Task;
042: import org.cougaar.planning.ldm.plan.Verb;
043: import org.cougaar.util.UnaryPredicate;
044: import org.cougaar.util.log.Logger;
045: import org.cougaar.util.log.Logging;
046:
047: import java.util.Calendar;
048: import java.util.Date;
049: import java.util.Enumeration;
050: import java.util.HashMap;
051: import java.util.Iterator;
052: import java.util.Map;
053:
054: public class DeletionProcessor extends InventoryProcessor {
055: protected IncrementalSubscription tasks_;
056: private Calendar tCalendar_ = Calendar.getInstance();
057: private static Logger logger = Logging
058: .getLogger(DeletionProcessor.class);
059: /** Delete old reports when oldest becomes this old **/
060: private static final long INVENTORY_REPORT_CUTOFF = 8 * TimeUtils.MSEC_PER_WEEK;
061:
062: /** When pruning old reports, remove all older than this age. O
063: disables pruning. This should match the inventoryBG refill
064: task anticipation interval (current 14 days) to provide detail
065: during that interval. Mismatch is not harmful, but leads
066: excess inventory reports or missing detail.
067: **/
068: private static final long INVENTORY_REPORT_PRUNE = 4 * TimeUtils.MSEC_PER_WEEK;
069:
070: private static final boolean DEBUG = true;
071:
072: public DeletionProcessor(InventoryPlugin plugin, Organization org,
073: String type) {
074: super (plugin, org, type);
075: initialize();
076: }
077:
078: static class InventoryTaskPredicate implements UnaryPredicate {
079: String supplyType_;
080: String myOrgName_;
081:
082: public InventoryTaskPredicate(String type, String orgName) {
083: supplyType_ = type;
084: myOrgName_ = orgName;
085: }
086:
087: public boolean execute(Object o) {
088: if (o instanceof Task) {
089: Task task = (Task) o;
090: Verb verb = task.getVerb();
091: if (verb.equals(Constants.Verb.WITHDRAW)
092: || verb.equals(Constants.Verb.PROJECTWITHDRAW)) {
093: return (TaskUtils.isDirectObjectOfType(task,
094: supplyType_) || TaskUtils.isTaskPrepOfType(
095: task, supplyType_));
096: }
097: if (verb.equals(Constants.Verb.SUPPLY)) {
098: if (TaskUtils.isDirectObjectOfType(task,
099: supplyType_)
100: || TaskUtils.isTaskPrepOfType(task,
101: supplyType_)) {
102: return TaskUtils.isMyRefillTask(task,
103: myOrgName_);
104: }
105: }
106: }
107: return false;
108: }
109: }
110:
111: /**
112: * Set up subscriptions,
113: * get the this plugin's organization UIC, and
114: * initialize the OPLAN object.
115: */
116: private void initialize() {
117: // Subscribe to withdraw and refill tasks
118: tasks_ = subscribe(new InventoryTaskPredicate(supplyType_,
119: myOrgName_));
120: }
121:
122: public void update() {
123: super .update(); // set up dates
124: removeTasks(tasks_.getRemovedList());
125: }
126:
127: /**
128: * Keep track of the range of times for which tasks have been
129: * deleted from an inventory. Inventory reports are created to
130: * prop up the inventory over that time range.
131: **/
132: private static class DeletionTimeRange {
133: long earliestTime;
134: long latestTime;
135:
136: DeletionTimeRange(long t) {
137: earliestTime = latestTime = t;
138: }
139: }
140:
141: /**
142: * Process all the removed tasks. For each removed task find the
143: * relevant Inventory and assocate with that inventory the time of
144: * the latest such task. After all tasks have been examined,
145: * create an InventoryReport reflecting the inventory level at the
146: * time of that task (pushed to the end of the day) and add that
147: * inventory report to the InventoryBG.
148: *
149: * Note that we don't perform a publishChange on the inventory
150: * except when old inventory reports are pruned because we are
151: * dealing with ancient history and the inventory level going
152: * forward from the time of the removed tasks should not have been
153: * changed by our actions. We do publishChange when old reports
154: * are pruned as a debugging aid. Otherwise, we might not see any
155: * bad effects.
156: *
157: * Inventory reports may be pruned. When enabled and the oldest
158: * report is older than INVENTORY_REPORT_PRUNE the inventories
159: * older than INVENTORY_REPORT_CUTOFF are removed.
160: **/
161: private void removeTasks(Enumeration tasks) {
162: Map tMap = new HashMap();
163: while (tasks.hasMoreElements()) {
164: Task task = (Task) tasks.nextElement();
165: if (!task.isDeleted()) { // Rescind requires no special handling
166: continue;
167: }
168: Asset proto = (Asset) task.getDirectObject();
169: Inventory inventory = inventoryPlugin_.findOrMakeInventory(
170: supplyType_, proto);
171: if (inventory == null) {
172: String typeID = proto.getTypeIdentificationPG()
173: .getTypeIdentification();
174: if (logger.isErrorEnabled()) {
175: logger.error("Inventory NOT found for " + typeID);
176: }
177: continue;
178: }
179: if (logger.isDebugEnabled()) {
180: logger.debug("Removing task from inventory: "
181: + TaskUtils.taskDesc(task));
182: }
183: long et = TaskUtils.getEndTime(task);
184: et = TimeUtils.pushToEndOfDay(tCalendar_, et);
185: DeletionTimeRange dtr = (DeletionTimeRange) tMap
186: .get(inventory);
187: if (dtr == null) {
188: dtr = new DeletionTimeRange(et);
189: tMap.put(inventory, dtr);
190: } else {
191: dtr.earliestTime = Math.min(dtr.earliestTime, et);
192: dtr.latestTime = Math.max(dtr.latestTime, et);
193: }
194: }
195: long inventoryReportCutoffTime = plugin_.currentTimeMillis()
196: - INVENTORY_REPORT_CUTOFF;
197: long inventoryReportPruneTime = plugin_.currentTimeMillis()
198: - INVENTORY_REPORT_PRUNE;
199: for (Iterator keys = tMap.keySet().iterator(); keys.hasNext();) {
200: boolean needPublishChange = false;
201: Inventory inventory = (Inventory) keys.next();
202: InventoryPG invpg = (InventoryPG) inventory
203: .getInventoryPG();
204: DeletionTimeRange dtr = (DeletionTimeRange) tMap
205: .get(inventory);
206: Schedule schedule = inventory.getScheduledContentPG()
207: .getSchedule();
208: ItemIdentificationPG iipg = inventory
209: .getItemIdentificationPG();
210: String iid = iipg.getItemIdentification();
211: long et;
212: if (true) {
213: et = dtr.earliestTime;
214: } else {
215: et = dtr.latestTime;
216: }
217: for (; et <= dtr.latestTime; et += TimeUtils.MSEC_PER_DAY) {
218: Iterator iter = schedule
219: .getScheduleElementsWithTime(et).iterator();
220: if (iter.hasNext()) { // There should be exactly one element
221: QuantityScheduleElement qse = (QuantityScheduleElement) iter
222: .next();
223: double q = qse.getQuantity();
224: if (logger.isDebugEnabled()) {
225: logger.debug("Adding inventory report to "
226: + inventory + " at " + new Date(et)
227: + " level " + q);
228: }
229: invpg.addInventoryReport(new InventoryReport(iid,
230: et, et, q));
231: needPublishChange = true;
232: } else {
233: if (logger.isErrorEnabled()) {
234: logger.error("No scheduled content");
235: }
236: }
237: }
238: if (INVENTORY_REPORT_PRUNE > 0L) {
239: InventoryReport oldestReport = invpg
240: .getOldestInventoryReport();
241: if (oldestReport != null
242: && oldestReport.theReportDate < inventoryReportCutoffTime) {
243: if (logger.isDebugEnabled()) {
244: logger.debug("Pruning old inventoryReports: "
245: + inventory);
246: }
247: invpg
248: .pruneOldInventoryReports(inventoryReportPruneTime);
249: needPublishChange = true;
250: }
251: if (needPublishChange) {
252: publishChangeAsset(inventory);
253: }
254: }
255: }
256: }
257: }
|