001: /*
002: * <copyright>
003: *
004: * Copyright 2003-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: package org.cougaar.core.plugin.deletion;
027:
028: import java.text.SimpleDateFormat;
029: import java.util.Collection;
030: import java.util.Comparator;
031: import java.util.HashMap;
032: import java.util.Iterator;
033: import java.util.Map;
034: import java.util.SortedSet;
035: import java.util.TreeSet;
036:
037: import org.cougaar.core.agent.service.alarm.Alarm;
038: import org.cougaar.core.blackboard.CollectionSubscription;
039: import org.cougaar.core.component.ServiceBroker;
040: import org.cougaar.core.logging.LoggingServiceWithPrefix;
041: import org.cougaar.core.persist.PersistenceNotEnabledException;
042: import org.cougaar.core.plugin.ComponentPlugin;
043: import org.cougaar.core.plugin.PluginAlarm;
044: import org.cougaar.core.service.BlackboardService;
045: import org.cougaar.core.service.LoggingService;
046: import org.cougaar.core.service.UIDService;
047: import org.cougaar.util.UnaryPredicate;
048:
049: /**
050: * This component removes {@link Deletable} blackboard objects
051: * according to policy and their age.
052: * <p>
053: * How to Configure the DeletionPlugin:
054: * <p>
055: * The DeletionPlugin runs according to a prescribed schedule to find
056: * Deletable objects on the blackboard and delete them if they are ready
057: * to be deleted. These actions are controlled by a
058: * DelectionSchedulePolicy and one or more DeletionPolicy objects on the
059: * blackboard. The DeletionPlugin guarantees that there is always a
060: * DefaultDeletionPolicy present by creating on at startup (or restart)
061: * if there is not one already present.
062: * <p>
063: * DeletionSchedulePolicy:
064: * <p>
065: * The deletion schedule is characterized primarily by a period (how
066: * often) and a phase (at what time of day). The public interface of the
067: * standard DeletionSchedulePolicy allows all aspects of the schedule to
068: * be altered including adding specific times for deletion to occur. The
069: * current DeletionPlugin implementation insures that exactly one
070: * DeletionSchedulePolicy exists on the blackboard creating one if
071: * necessary and deleting extraneous ones. If an existing policy is
072: * found, it is left as is. Subclasses could choose to insure that the
073: * periodic schedule parameters agree with the values specified as plugin
074: * parameters.
075: * <p>
076: * DefaultDeletionPolicy:
077: * <p>
078: * The DeletionPlugin also guarantees that there is exactly one default
079: * DeletionPolicy on the blackboard. If there are none, one is created
080: * using plugin parameters. If there are multiples (should never happen),
081: * extras are deleted.
082: * <p>
083: * DeletionPlugin Parameters:
084: * <p>
085: * The DeletionPlug accepts four named parameters as follows:
086: * <pre>
087: * deletionDelay=<long default 15 days>
088: * deletionPeriod=<long default 7 days>
089: * deletionPhase=<long 0 (midnight)>
090: * archivingEnabled=<boolean>
091: * </pre>
092: * <p>
093: * The archivingEnable parameter causes a persistence snapshot to be
094: * taken for archiving purposes prior to doing deletions.
095: */
096: public class DeletionPlugin extends ComponentPlugin {
097:
098: protected void setupSubscriptions() {
099: long deletionDelay = DEFAULT_DELETION_DELAY;
100: long deletionPeriod = DEFAULT_DELETION_PERIOD;
101: long deletionPhase = DEFAULT_DELETION_PHASE;
102: archivingEnabled = DEFAULT_ARCHIVING_ENABLED;
103: for (Iterator i = getParameters().iterator(); i.hasNext();) {
104: String param = (String) i.next();
105: deletionDelay = parseLongParameter(param,
106: DELETION_DELAY_PREFIX, deletionDelay);
107: deletionPeriod = parseLongParameter(param,
108: DELETION_PERIOD_PREFIX, deletionPeriod);
109: deletionPhase = parseLongParameter(param,
110: DELETION_PHASE_PREFIX, deletionPhase);
111: archivingEnabled = parseBooleanParameter(param,
112: ARCHIVING_ENABLED_PREFIX, archivingEnabled);
113: }
114: deletionPolicies = (CollectionSubscription) blackboard
115: .subscribe(deletionPolicyPredicate, false);
116: checkDefaultDeletionPolicy(deletionDelay);
117: deletionSchedulePolicies = (CollectionSubscription) blackboard
118: .subscribe(deletionSchedulePolicyPredicate, false);
119: checkDeletionSchedulePolicies(deletionPeriod, deletionPhase);
120: getBlackboardService().setShouldBePersisted(false);
121: // All subscriptions are created as needed
122: setAlarm();
123: }
124:
125: /**
126: * Runs only when the alarm expires.
127: *
128: * The procedure is:
129: * Find new allocations for tasks that deletable and mark the allocations
130: * Find tasks with deletable dispositions and mark them
131: * Find deletable tasks that are subtasks of an expansion and
132: * remove them from the expansion and remove them from the
133: * logplan.
134: */
135: public void execute() {
136: scenarioNow = currentTimeMillis();
137: systemNow = System.currentTimeMillis();
138: if (alarm.hasExpired()) { // Time to make the donuts
139: if (logger.isDebugEnabled())
140: logger.debug("Time to make the donuts");
141: if (archivingEnabled) {
142: try {
143: getBlackboardService().persistNow(); // Record our state
144: } catch (PersistenceNotEnabledException pnee) {
145: pnee.printStackTrace();
146: logger.error("Archiving disabled");
147: archivingEnabled = false;
148: }
149: }
150: checkDeletables();
151: setAlarm();
152: }
153: }
154:
155: protected void checkDeletables() {
156: Collection deletables = blackboard.query(deletablePredicate);
157: deletablesLoop: for (Iterator i = deletables.iterator(); i
158: .hasNext();) {
159: Deletable element = (Deletable) i.next();
160: policiesLoop: for (Iterator j = deletionPolicies.iterator(); j
161: .hasNext();) {
162: DeletionPolicy policy = (DeletionPolicy) j.next();
163: if (policy.getPredicate().execute(element)) {
164: long deletionTime = element.getDeletionTime()
165: + policy.getDeletionDelay();
166: long now = element.useSystemTime() ? systemNow
167: : scenarioNow;
168: if (deletionTime < now) {
169: element.setDeleted();
170: blackboard.publishRemove(element);
171: }
172: break policiesLoop;
173: }
174: }
175: }
176: }
177:
178: protected static final SimpleDateFormat deletionTimeFormat;
179: static {
180: deletionTimeFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
181: }
182: protected static UnaryPredicate truePredicate = new UnaryPredicate() {
183: public boolean execute(Object o) {
184: return true;
185: }
186: };
187:
188: private static UnaryPredicate deletablePredicate = new UnaryPredicate() {
189: public boolean execute(Object o) {
190: if (o instanceof Deletable) {
191: Deletable d = (Deletable) o;
192: return d.isDeletable();
193: }
194: return false;
195: }
196: };
197:
198: protected static final String DELETION_DELAY_PREFIX = "deletionDelay=";
199:
200: protected static final String DELETION_PERIOD_PREFIX = "deletionPeriod=";
201:
202: protected static final String DELETION_PHASE_PREFIX = "deletionPhase=";
203:
204: protected static final String ARCHIVING_ENABLED_PREFIX = "archivingEnabled=";
205:
206: protected static final long DEFAULT_DELETION_PERIOD = 7 * 86400000L;
207:
208: protected static final long DEFAULT_DELETION_DELAY = 15 * 86400000L;
209:
210: protected static final long DEFAULT_DELETION_PHASE = 0L;
211:
212: protected static final boolean DEFAULT_ARCHIVING_ENABLED = true;
213:
214: protected static final long subscriptionExpirationTime = 10L * 60L * 1000L;
215:
216: protected boolean archivingEnabled = true;
217:
218: protected Alarm alarm;
219:
220: protected long scenarioNow;
221: protected long systemNow;
222:
223: protected DeletionSchedulePolicy theDeletionSchedulePolicy;
224:
225: protected UnaryPredicate deletionPolicyPredicate = new UnaryPredicate() {
226: public boolean execute(Object o) {
227: return o instanceof DeletionPolicy;
228: }
229: };
230:
231: protected UnaryPredicate deletionSchedulePolicyPredicate = new UnaryPredicate() {
232: public boolean execute(Object o) {
233: return o instanceof DeletionSchedulePolicy;
234: }
235: };
236:
237: protected SortedSet deletionPolicySet = new TreeSet(
238: new Comparator() {
239: public int compare(Object o1, Object o2) {
240: DeletionPolicy p1 = (DeletionPolicy) o1;
241: DeletionPolicy p2 = (DeletionPolicy) o2;
242: int diff = p1.getPriority() - p2.getPriority();
243: if (diff != 0)
244: return diff;
245: String n1 = p1.getName();
246: String n2 = p2.getName();
247: if (n1 != n2) {
248: if (n1 == null)
249: return -1;
250: if (n2 == null)
251: return +1;
252: diff = n1.compareTo(n2);
253: if (diff != 0)
254: return diff;
255: }
256: return o1.hashCode() - o2.hashCode();
257: }
258: });
259:
260: protected CollectionSubscription deletionPolicies;
261:
262: protected CollectionSubscription deletionSchedulePolicies;
263:
264: protected LoggingService logger;
265:
266: protected UIDService uidService;
267:
268: public void setLoggingService(LoggingService ls) {
269: logger = ls;
270: }
271:
272: public void setUIDService(UIDService uidService) {
273: this .uidService = uidService;
274: }
275:
276: public void load() {
277: super .load();
278: if (!(logger instanceof LoggingServiceWithPrefix)) {
279: logger = LoggingServiceWithPrefix.add(logger,
280: getAgentIdentifier().toString() + ": ");
281: }
282: }
283:
284: /**
285: * The known unit names
286: */
287: private static Map intervals = new HashMap(11);
288: static {
289: intervals.put("seconds", new Long(1000L));
290: intervals.put("minutes", new Long(1000L * 60L));
291: intervals.put("hours", new Long(1000L * 60L * 60L));
292: intervals.put("days", new Long(1000L * 60L * 60L * 24L));
293: intervals.put("weeks", new Long(1000L * 60L * 60L * 24L * 7L));
294: }
295:
296: private static long parseInterval(String param) {
297: param = param.trim();
298: int spacePos = param.indexOf(' ');
299: long mul = 1L;
300: if (spacePos >= 0) {
301: String units = param.substring(spacePos + 1).toLowerCase();
302: param = param.substring(0, spacePos);
303: Long factor = (Long) intervals.get(units);
304: if (factor != null) {
305: mul = factor.longValue();
306: }
307: }
308: return Long.parseLong(param) * mul;
309: }
310:
311: protected long parseLongParameter(String param, String prefix,
312: long dflt) {
313: if (param.startsWith(prefix)) {
314: try {
315: return parseInterval(param.substring(prefix.length()));
316: } catch (Exception e) {
317: if (logger.isWarnEnabled())
318: logger.warn("Could not parseInterval " + param);
319: return dflt;
320: }
321: }
322: return dflt;
323: }
324:
325: protected boolean parseBooleanParameter(String param,
326: String prefix, boolean dflt) {
327: if (param.startsWith(prefix)) {
328: return Boolean.valueOf(param.substring(prefix.length()))
329: .booleanValue();
330: }
331: return dflt;
332: }
333:
334: /**
335: * Check to see if the DefaultDeletionPolicy is present and matches the
336: * current deletionDelay, If a DefaultDeletionPolicy is found for
337: * which the deletionDelay does not match the current deletionDelay,
338: * it is removed. If no DefaultDeletionPolicy having the correct
339: * deletionDelay is found, a new one created and added.
340: */
341: private void checkDefaultDeletionPolicy(long deletionDelay) {
342: for (Iterator i = deletionPolicies.iterator(); i.hasNext();) {
343: DeletionPolicy policy = (DeletionPolicy) i.next();
344: if (SimpleDeletionPolicy.isDefaultDeletionPolicy(policy)) {
345: if (policy.getDeletionDelay() == deletionDelay) {
346: return; // ok
347: }
348: blackboard.publishRemove(policy);
349: }
350: }
351: SimpleDeletionPolicy policy = new SimpleDeletionPolicy(
352: "Default Deletion Policy", truePredicate,
353: deletionDelay, DeletionPolicy.MIN_PRIORITY);
354: uidService.registerUniqueObject(policy);
355: blackboard.publishAdd(policy);
356: }
357:
358: /**
359: * Check to see if the default schedule policy is present. If a
360: * DeletionSchedulePolicy is found then it is left as is. If no
361: * DeletionSchedulePolicy is found, a new one created and added.
362: * If multiple schedule policies are found, all but one is deleted.
363: * Subclasses may wish to set the periodic schedule parameters to
364: * match the specified values, but the base implementation only uses
365: * the values if a new policy must be created.
366: */
367: protected void checkDeletionSchedulePolicies(long deletionPeriod,
368: long deletionPhase) {
369: for (Iterator i = deletionSchedulePolicies.iterator(); i
370: .hasNext();) {
371: DeletionSchedulePolicy policy = (DeletionSchedulePolicy) i
372: .next();
373: if (theDeletionSchedulePolicy == null) {
374: theDeletionSchedulePolicy = policy;
375: } else {
376: // Remove extraneous policies
377: blackboard.publishRemove(policy);
378: }
379: }
380: if (theDeletionSchedulePolicy == null) {
381: DeletionSchedulePolicy policy = new DeletionSchedulePolicy(
382: deletionPeriod, deletionPhase);
383: blackboard.publishAdd(policy);
384: theDeletionSchedulePolicy = policy;
385: }
386: }
387:
388: private Alarm createAlarm(long time) {
389: return new PluginAlarm(time) {
390: public BlackboardService getBlackboardService() {
391: return blackboard;
392: }
393: };
394: }
395:
396: /**
397: * Set the alarm so that it expires when the time is next congruent to the
398: * deletionPhase modulo the deletionPeriod
399: */
400: protected void setAlarm() {
401: long now = currentTimeMillis();
402: long nextAlarm = theDeletionSchedulePolicy
403: .getNextDeletionTime(now);
404: if (logger.isDebugEnabled())
405: logger.debug(getAgentIdentifier() + " Make the donuts in "
406: + (nextAlarm - now) + "msec.");
407: alarm = createAlarm(nextAlarm);
408: getAlarmService().addAlarm(alarm);
409: }
410:
411: private static class DefaultDeletionPolicy extends
412: SimpleDeletionPolicy {
413: DefaultDeletionPolicy(long deletionDelay) {
414: super(deletionDelay);
415: }
416: }
417: }
|