001: /*
002:
003: * <copyright>
004: *
005: * Copyright 2002-2007 BBNT Solutions, LLC
006: * under sponsorship of the Defense Advanced Research Projects
007: * Agency (DARPA).
008: *
009: * You can redistribute this software and/or modify it under the
010: * terms of the Cougaar Open Source License as published on the
011: * Cougaar Open Source Website (www.cougaar.org).
012: *
013: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
014: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
015: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
016: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
017: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
018: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
019: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
020: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
021: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
022: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
023: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
024: *
025: * </copyright>
026:
027: */
028:
029: package org.cougaar.qos.qrs;
030:
031: import java.util.ArrayList;
032: import java.util.HashMap;
033: import java.util.List;
034: import java.util.Map;
035:
036: import org.cougaar.qos.ResourceStatus.ResourceNode;
037: import org.cougaar.util.log.Logger;
038:
039: /**
040: * A DataFormula describes a (possibly computed) attribute of a ResourceContext.
041: * DataFormulas are gotten from the corresponding context, by name. In general
042: * the name will be a toplevel static inner class of the context (which implies
043: * only one formula per type).
044: */
045: abstract public class DataFormula implements
046: DataValueChangedCallbackListener, Constants {
047: private final List<DataValueChangedCallbackListener> listeners = new ArrayList<DataValueChangedCallbackListener>();
048: private String name;
049: private String[] args;
050: private DataValue cachedValue;
051: private ResourceContext context;
052: private List<DataFormula> dependencies; // collection of DataFormula's
053: private Map<DataFormula, String> dependencyKeys; // formula->key
054: private Values dependencyValues; // formula_key -> value
055: private boolean deleted;
056: private String pretty_name;
057:
058: /**
059: * Instantiable subclasses provide this to do the formula's work, if there's
060: * any to do.
061: */
062: abstract protected DataValue doCalculation(Values values);
063:
064: protected DataValue computeValue(boolean forwardChain) {
065: synchronized (dependencies) {
066: if (deleted) {
067: return DataValue.NO_VALUE;
068: }
069: for (DataFormula formula : dependencies) {
070: DataValue value = null;
071: if (forwardChain) {
072: value = formula.blockingQuery();
073: } else {
074: value = formula.getCachedValue();
075: }
076: String key = dependencyKeys.get(formula);
077: dependencyValues.put(key, value);
078: }
079: }
080: return doCalculation(dependencyValues);
081: }
082:
083: public String toString() {
084: return pretty_name;
085: }
086:
087: public String getName() {
088: return name;
089: }
090:
091: protected void setName(String name) {
092: this .name = name;
093: }
094:
095: public String[] getArgs() {
096: return args;
097: }
098:
099: protected void setArgs(String[] args) {
100: this .args = args;
101: }
102:
103: // simple version
104: protected boolean hasArgs(String[] args) {
105: if (args == null) {
106: return this .args == null || this .args.length == 0;
107: }
108: if (this .args == null) {
109: return args.length == 0;
110: }
111: if (this .args.length != args.length) {
112: return false;
113: }
114: for (int i = 0; i < this .args.length; i++) {
115: if (!this .args[i].equals(args[i])) {
116: return false;
117: }
118: }
119: return true;
120: }
121:
122: protected List<ResourceNode> getPath() {
123: List<ResourceNode> path = context.getPath();
124: path.add(new ResourceNode(name, args));
125: return path;
126: }
127:
128: protected void initialize(ResourceContext context) {
129: this .context = context;
130: dependencies = new ArrayList<DataFormula>();
131: dependencyValues = new Values();
132: dependencyKeys = new HashMap<DataFormula, String>();
133: cachedValue = DataValue.NO_VALUE;
134: deleted = false;
135: }
136:
137: public void reinitialize() {
138: dependencies = new ArrayList<DataFormula>();
139: }
140:
141: protected void postInitialize() {
142: setCachedValue(blockingQuery());
143: pretty_name = name;
144: if (args != null && args.length > 0) {
145: pretty_name += "[" + args[0];
146: for (int i = 1; i < args.length; i++) {
147: pretty_name += ", " + args[i];
148: }
149: pretty_name += "]";
150: }
151: }
152:
153: protected void registerDependency(DataFormula formula, String key) {
154: synchronized (dependencies) {
155: if (deleted) {
156: return;
157: }
158: dependencies.add(formula);
159: }
160: dependencyKeys.put(formula, key);
161: formula.subscribe(this );
162: }
163:
164: protected DataFormula registerDependency(ResourceContext context,
165: String formulaName) {
166: return registerDependency(context, formulaName, null,
167: formulaName);
168: }
169:
170: protected DataFormula registerDependency(ResourceContext context,
171: String formulaName, String[] formulaArgs) {
172: return registerDependency(context, formulaName, formulaArgs,
173: formulaName);
174: }
175:
176: protected DataFormula registerDependency(ResourceContext context,
177: String formulaName, String key) {
178: return registerDependency(context, formulaName, null, key);
179: }
180:
181: protected DataFormula registerDependency(ResourceContext context,
182: String formulaName, String[] formulaArgs, String key) {
183: DataFormula requiredFormula = context.getFormula(formulaName,
184: formulaArgs);
185: if (requiredFormula != null) {
186: registerDependency(requiredFormula, key);
187: } else {
188: Logger logger = Logging.getLogger(DataFormula.class);
189: logger.error("### Failed to find formula " + formulaName
190: + " in context " + context);
191: }
192: return requiredFormula;
193: }
194:
195: /**
196: * Returns a list of other formulas on which this one depends. Only used by
197: * the ResourceContext gui (so far).
198: */
199: public List<DataFormula> getDependencies() {
200: return dependencies;
201: }
202:
203: /**
204: * Returns a list of listeners on this formula. Only used by the
205: * ResourceContext gui (so far).
206: */
207: public List<DataValueChangedCallbackListener> getSubscribers() {
208: return listeners;
209: }
210:
211: protected ResourceContext getContext() {
212: return context;
213: }
214:
215: protected DataValue getCachedValue() {
216: return cachedValue;
217: }
218:
219: protected synchronized void setCachedValue(DataValue cachedValue) {
220: if (cachedValue != null
221: && !cachedValue.contentsEquals(this .cachedValue)) {
222: this .cachedValue = cachedValue;
223: notifyListeners();
224: }
225: }
226:
227: /**
228: * Implements the DataValueChangedCallbackListener. This would typically be
229: * called here when one of the other formulas on which this one depends has
230: * a new value.
231: */
232: public void dataValueChanged(DataFormula changedFormula) {
233: // Some value I depend on has changed. Backward chain.
234: DataValue newValue = computeValue(false);
235: synchronized (this ) {
236: if (newValue != null
237: && !newValue.contentsEquals(cachedValue)) {
238: cachedValue = newValue;
239: notifyListeners();
240: }
241: }
242: }
243:
244: /**
245: * Returns the currently cached value. This is part of the standard
246: * (non-gui) DataFormula interface.
247: */
248: public DataValue query() {
249: return cachedValue;
250: }
251:
252: /**
253: * Computes and returns a new value. This is part of the standard (non-gui)
254: * DataFormula interface.
255: */
256: public DataValue blockingQuery() {
257: // Forward chain (ignore cache, no notifications)
258: return computeValue(true);
259: }
260:
261: /**
262: * Registers the listener so that will be called back when the value of this
263: * one changes. This is part of standard (non-gui) DataFormula interface.
264: */
265: public void subscribe(DataValueChangedCallbackListener listener) {
266: synchronized (listeners) {
267: if (deleted) {
268: // This should throw an exception or return a status
269: // or something to let the caller know that the
270: // subscription is meaningless.
271: Logger logger = Logging.getLogger(DataFormula.class);
272: logger.warn(listener
273: + " subscribed to deleted formula " + this );
274: return;
275:
276: }
277: listeners.add(listener);
278: }
279: }
280:
281: /**
282: * Unregisters the listener for callbackes. This is part of the standard
283: * (non-gui) DataFormula interface.
284: */
285: public void unsubscribe(DataValueChangedCallbackListener listener) {
286: synchronized (listeners) {
287: if (deleted) {
288: return;
289: }
290: listeners.remove(listener);
291: }
292: }
293:
294: public boolean shouldNotify(DataValue value) {
295: return true;
296: }
297:
298: private void notifyListeners() {
299: synchronized (listeners) {
300: if (deleted) {
301: return;
302: }
303: for (DataValueChangedCallbackListener listener : listeners) {
304: if (listener.shouldNotify(cachedValue)) {
305: listener.dataValueChanged(this );
306: }
307: }
308: }
309: }
310:
311: // Some formula I depend on has been deleted.
312: public void formulaDeleted(DataFormula formula) {
313: // remove formula from dependencies
314: synchronized (dependencies) {
315: if (deleted) {
316: return;
317: }
318: dependencies.remove(formula);
319: }
320: String key = dependencyKeys.get(formula);
321: dependencyKeys.remove(formula);
322: List<ResourceNode> path_list = formula.getPath();
323: ResourceNode[] path = new ResourceNode[path_list.size()];
324: path_list.toArray(path);
325: DataFormula replacement = RSS.instance().getPathFormula(path);
326: if (replacement != null) {
327: registerDependency(replacement, key);
328: dataValueChanged(replacement);
329: } else {
330: Logger logger = Logging.getLogger(DataFormula.class);
331: StringBuffer buf = new StringBuffer();
332: buf.append("Unable to recreate ");
333: RSSUtils.pathToString(buf, path);
334: buf.append(". ");
335: buf.append(this .toString());
336: buf.append(" has lost this dependency forever");
337: logger.warn(buf.toString());
338: }
339: }
340:
341: void contextDeleted() {
342: List<DataValueChangedCallbackListener> listeners_clone;
343: List<DataFormula> dependencies_clone;
344:
345: synchronized (listeners) {
346: synchronized (dependencies) {
347: deleted = true;
348: listeners_clone = new ArrayList<DataValueChangedCallbackListener>(
349: listeners);
350: dependencies_clone = new ArrayList<DataFormula>(
351: dependencies);
352: listeners.clear();
353: dependencies.clear();
354: }
355: }
356:
357: // Unsubscribe from all dependencies
358: for (DataFormula formula : dependencies_clone) {
359: formula.unsubscribe(this );
360: }
361:
362: // Inform listeners
363: for (DataValueChangedCallbackListener listener : listeners_clone) {
364: listener.formulaDeleted(this );
365: }
366: }
367:
368: public static class Values extends HashMap<String, DataValue> {
369: public DataValue get(String key) {
370: return super .get(key);
371: }
372:
373: public double doubleValue(String key) {
374: DataValue dvalue = get(key);
375: if (dvalue == null) {
376: return 0.0;
377: } else {
378: return dvalue.getDoubleValue();
379: }
380: }
381:
382: public String stringValue(String key) {
383: DataValue svalue = get(key);
384: if (svalue == null) {
385: return "";
386: } else {
387: return svalue.getStringValue();
388: }
389: }
390:
391: public boolean booleanValue(String key) {
392: DataValue bvalue = get(key);
393: if (bvalue == null) {
394: return false;
395: } else {
396: return bvalue.getBooleanValue();
397: }
398: }
399:
400: public double credibility(String key) {
401: DataValue dvalue = get(key);
402: if (dvalue == null) {
403: return Constants.NO_CREDIBILITY;
404: } else {
405: return dvalue.getCredibility();
406: }
407: }
408:
409: // ignore 0.0 unless they're all 0.0
410: public double minPositiveCredibility() {
411: double minimum = 2.0;
412: for (DataValue value : values()) {
413: double credibility = value.getCredibility();
414: if (credibility > 0.0) {
415: minimum = Math.min(credibility, minimum);
416: }
417: }
418: return minimum > 1.0 ? Constants.NO_CREDIBILITY : minimum;
419: }
420:
421: public double minCredibility() {
422: double minimum = 2.0;
423: for (DataValue value : values()) {
424: double credibility = value.getCredibility();
425: minimum = Math.min(credibility, minimum);
426: }
427: return minimum > 1.0 ? Constants.NO_CREDIBILITY : minimum;
428: }
429:
430: public double maxCredibility() {
431: double maximum = -1.0;
432: for (DataValue value : values()) {
433: maximum = Math.max(value.getCredibility(), maximum);
434: }
435: return maximum < 0.0 ? Constants.NO_CREDIBILITY : maximum;
436: }
437: }
438: }
|