0001: /*
0002: * <copyright>
0003: *
0004: * Copyright 1997-2004 BBNT Solutions, LLC
0005: * under sponsorship of the Defense Advanced Research Projects
0006: * Agency (DARPA).
0007: *
0008: * You can redistribute this software and/or modify it under the
0009: * terms of the Cougaar Open Source License as published on the
0010: * Cougaar Open Source Website (www.cougaar.org).
0011: *
0012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
0015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
0016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0023: *
0024: * </copyright>
0025: */
0026:
0027: package org.cougaar.core.qos.frame;
0028:
0029: import java.io.File;
0030: import java.io.FileWriter;
0031: import java.io.IOException;
0032: import java.io.PrintWriter;
0033: import java.net.URL;
0034: import java.util.ArrayList;
0035: import java.util.Collection;
0036: import java.util.Collections;
0037: import java.util.HashMap;
0038: import java.util.HashSet;
0039: import java.util.Iterator;
0040: import java.util.List;
0041: import java.util.Map;
0042: import java.util.Observer;
0043: import java.util.Properties;
0044: import java.util.Set;
0045:
0046: import org.cougaar.core.component.ServiceBroker;
0047: import org.cougaar.core.qos.metrics.Metric;
0048: import org.cougaar.core.qos.metrics.MetricsService;
0049: import org.cougaar.core.service.BlackboardService;
0050: import org.cougaar.core.service.LoggingService;
0051: import org.cougaar.core.service.UIDService;
0052: import org.cougaar.core.util.UID;
0053: import org.cougaar.core.util.UniqueObject;
0054: import org.cougaar.util.UnaryPredicate;
0055: import org.xml.sax.Attributes;
0056:
0057: /**
0058: * Currently the only implementation of FrameSet, this class enforces
0059: * single inheritance in both the prototype hierarchy and the
0060: * containment hierarchy.
0061: */
0062: public class SingleInheritanceFrameSet implements FrameSet {
0063:
0064: private static final long LOOKUP_WARN_TIME = 10000;
0065:
0066: private final String domain;
0067: private final String name;
0068: private final String pkg;
0069: private final LoggingService log;
0070: private final UIDService uids;
0071: private final BlackboardService bbs;
0072: private final MetricsService metrics;
0073: private final Object change_queue_lock, relation_lock;
0074: // This slot is assumed to be immutable
0075: private final String primaryIndexSlot;
0076:
0077: private List<ChangeQueueEntry> change_queue;
0078: private Map<UID, Object> kb; // values can be either Frames or Paths
0079: private Map<String, Class> cached_classes;
0080: private Map<String, PrototypeFrame> prototypes;
0081: private Map<RelationFrame, DataFrame> parent_cache, child_cache;
0082: private Map<DataFrame, Set<RelationFrame>> inverse_parent_cache,
0083: inverse_child_cache;
0084: private Map<String, Path> paths;
0085: private Set<DataFrame> frames;
0086:
0087: // Containment hackery
0088: private Set<RelationFrame> pending_relations;
0089: private Map<DataFrame, DataFrame> containers;
0090: private String container_relation;
0091:
0092: private Map<PrototypeFrame, Map<Object, DataFrame>> primaryIndexCache;
0093:
0094: private final Object transactionLock = new TransactionLock();
0095:
0096: private Set<SlotAggregation> aggregations;
0097:
0098: //eclispe friendly lock names
0099: public class TransactionLock extends Object {
0100: };
0101:
0102: public class ChangeQueueLock extends Object {
0103: };
0104:
0105: public class RelationLock extends Object {
0106: };
0107:
0108: public class PendingRelationships extends HashSet<RelationFrame> {
0109: };
0110:
0111: public class Containers extends HashMap<DataFrame, DataFrame> {
0112: };
0113:
0114: public class KB extends HashMap<UID, Object> {
0115: };
0116:
0117: public class Frames extends HashSet<DataFrame> {
0118: };
0119:
0120: public class CachedClasses extends HashMap<String, Class> {
0121: };
0122:
0123: public class Paths extends HashMap<String, Path> {
0124: };
0125:
0126: public class Prototypes extends HashMap<String, PrototypeFrame> {
0127: };
0128:
0129: public SingleInheritanceFrameSet(String pkg, ServiceBroker sb,
0130: BlackboardService bbs, String domain, String name,
0131: String container_relation, String primaryIndexSlot) {
0132: this .domain = domain;
0133: this .name = name;
0134: this .primaryIndexSlot = primaryIndexSlot;
0135: this .container_relation = container_relation;
0136: this .cached_classes = new CachedClasses();
0137: this .parent_cache = new HashMap<RelationFrame, DataFrame>();
0138: this .child_cache = new HashMap<RelationFrame, DataFrame>();
0139: this .inverse_child_cache = new HashMap<DataFrame, Set<RelationFrame>>();
0140: this .inverse_parent_cache = new HashMap<DataFrame, Set<RelationFrame>>();
0141: this .paths = new Paths();
0142: this .frames = new Frames();
0143: this .primaryIndexCache = new HashMap<PrototypeFrame, Map<Object, DataFrame>>();
0144: this .pkg = pkg;
0145: this .bbs = bbs;
0146: this .change_queue = new ArrayList();
0147: this .change_queue_lock = new ChangeQueueLock();
0148: this .relation_lock = new RelationLock();
0149: log = (LoggingService) sb.getService(this ,
0150: LoggingService.class, null);
0151: uids = (UIDService) sb.getService(this , UIDService.class, null);
0152: metrics = (MetricsService) sb.getService(this ,
0153: MetricsService.class, null);
0154:
0155: this .kb = new KB();
0156: this .prototypes = new Prototypes();
0157:
0158: this .pending_relations = new PendingRelationships();
0159: this .containers = new Containers();
0160: this .aggregations = new HashSet<SlotAggregation>();
0161: }
0162:
0163: // Slot aggregation
0164:
0165: public void addAggregator(String slot, String relatedSlot,
0166: String relation, String role, String aggregator) {
0167: try {
0168: SlotAggregation agg = new SlotAggregation(this , slot,
0169: relatedSlot, relation, role, aggregator);
0170: aggregations.add(agg);
0171: } catch (Exception e) {
0172: log.error("Couldn't instantiate " + aggregator);
0173: }
0174: }
0175:
0176: public void initializeAggregators() {
0177: for (SlotAggregation aggregation : aggregations) {
0178: aggregation.setupSubscriptions(bbs);
0179: }
0180: }
0181:
0182: private void executeAggregators() {
0183: for (SlotAggregation aggregation : aggregations) {
0184: aggregation.execute(bbs);
0185: }
0186: }
0187:
0188: // Object creation
0189: private void addObject(UniqueObject object) {
0190: if (log.isInfoEnabled())
0191: log.info("Adding " + object);
0192: synchronized (kb) {
0193: kb.put(object.getUID(), object);
0194: }
0195: if (object instanceof DataFrame) {
0196: synchronized (frames) {
0197: frames.add((DataFrame) object);
0198: }
0199: if (object instanceof RelationFrame) {
0200: cacheRelation((RelationFrame) object);
0201: } else {
0202: // Any new DataFrame could potentially resolve as yet
0203: // unfilled values in relations.
0204: synchronized (relation_lock) {
0205: Iterator<RelationFrame> itr = pending_relations
0206: .iterator();
0207: while (itr.hasNext()) {
0208: boolean success = cacheRelation(itr.next());
0209: if (success)
0210: itr.remove();
0211: }
0212: }
0213: }
0214: }
0215: }
0216:
0217: public DataFrame makeFrame(String proto, Properties values) {
0218: UID uid = uids.nextUID();
0219: return makeFrame(proto, values, uid);
0220: }
0221:
0222: public DataFrame makeFrame(String proto, Properties values, UID uid) {
0223: Object key = null;
0224: if (primaryIndexSlot != null) {
0225: // Look for an existing frame with the same key
0226: key = values.get(primaryIndexSlot);
0227: if (key != null) {
0228: DataFrame oldFrame = getIndexedFrame(proto, key);
0229: if (oldFrame != null) {
0230: log.error("A " + proto + " frame with "
0231: + primaryIndexSlot + " " + key
0232: + " already exists");
0233: return oldFrame;
0234: }
0235: }
0236: }
0237: DataFrame frame = DataFrame.newFrame(this , proto, uid, values);
0238: if (key != null) {
0239: PrototypeFrame pframe = frame.getPrototype();
0240: Map<Object, DataFrame> cache = primaryIndexCache
0241: .get(pframe);
0242: if (cache == null) {
0243: cache = new HashMap<Object, DataFrame>();
0244: primaryIndexCache.put(pframe, cache);
0245: }
0246: cache.put(key, frame);
0247: }
0248: addObject(frame);
0249: publishAdd(frame);
0250: return frame;
0251:
0252: }
0253:
0254: public DataFrame makeFrame(DataFrame frame) {
0255: if (primaryIndexSlot != null) {
0256: Object key = frame.getValue(primaryIndexSlot);
0257: if (key != null) {
0258: PrototypeFrame pframe = frame.getPrototype();
0259:
0260: String proto = pframe.getName();
0261: DataFrame oldFrame = getIndexedFrame(proto, key);
0262: if (oldFrame != null) {
0263: log.error("A " + proto + " frame with "
0264: + primaryIndexSlot + " " + key
0265: + " already exists");
0266: return oldFrame;
0267: }
0268:
0269: Map<Object, DataFrame> cache = primaryIndexCache
0270: .get(pframe);
0271: if (cache == null) {
0272: cache = new HashMap<Object, DataFrame>();
0273: primaryIndexCache.put(pframe, cache);
0274: }
0275: cache.put(key, frame);
0276: }
0277: }
0278: addObject(frame);
0279: publishAdd(frame);
0280: return frame;
0281: }
0282:
0283: public boolean isResolved(RelationFrame frame) {
0284: synchronized (pending_relations) {
0285: return pending_relations.contains(frame);
0286: }
0287: }
0288:
0289: public RelationFrame makeRelationship(String kind,
0290: Properties values, DataFrame parent, DataFrame child) {
0291: UID uid = uids.nextUID();
0292: RelationFrame rel = (RelationFrame) DataFrame.newFrame(this ,
0293: kind, uid, values);
0294: synchronized (relation_lock) {
0295: cacheRelation(rel, parent, child);
0296: }
0297: synchronized (kb) {
0298: kb.put(uid, rel);
0299: }
0300: synchronized (frames) {
0301: frames.add(rel);
0302: }
0303: publishAdd(rel);
0304: return rel;
0305: }
0306:
0307: // In this case the proto argument refers to what the prototype
0308: // should be a prototype of.
0309: public PrototypeFrame makePrototype(String proto, String parent,
0310: Attributes attrs, Map<String, Attributes> slots) {
0311: UID uid = uids.nextUID();
0312: return makePrototype(proto, parent, attrs, slots, uid);
0313: }
0314:
0315: public PrototypeFrame makePrototype(String proto, String parent,
0316: Attributes attrs, Map<String, Attributes> slots, UID uid) {
0317: PrototypeFrame frame = null;
0318: synchronized (prototypes) {
0319: if (prototypes.containsKey(proto)) {
0320: if (log.isWarnEnabled())
0321: log.warn("Ignoring prototype " + proto);
0322: return null;
0323: } else {
0324: frame = new PrototypeFrame(this , proto, parent, uid,
0325: attrs, slots);
0326: if (log.isDebugEnabled())
0327: log.debug("Adding prototype " + frame + " for "
0328: + proto);
0329: prototypes.put(proto, frame);
0330: }
0331: }
0332: addObject(frame);
0333: publishAdd(frame);
0334: return frame;
0335: }
0336:
0337: public Path makePath(String name, Path.Fork[] forks, String slot) {
0338: UID uid = uids.nextUID();
0339: Path path = new Path(uid, name, forks, slot);
0340: synchronized (paths) {
0341: paths.put(name, path);
0342: }
0343: addObject(path);
0344: publishAdd(path);
0345: return path;
0346: }
0347:
0348: // Removing and modifying frames
0349: public void valueUpdated(DataFrame frame, String slot, Object value) {
0350: if (frame instanceof RelationFrame) {
0351: RelationFrame rframe = (RelationFrame) frame;
0352: synchronized (relation_lock) {
0353: if (slot.startsWith(CHILD)) {
0354: DataFrame child = child_cache.get(rframe);
0355: child_cache.remove(rframe);
0356: Set<RelationFrame> rframes = inverse_child_cache
0357: .get(child);
0358: rframes.remove(rframe);
0359: } else if (slot.startsWith(PARENT)) {
0360: DataFrame parent = parent_cache.get(rframe);
0361: parent_cache.remove(frame);
0362: Set<RelationFrame> rframes = inverse_parent_cache
0363: .get(parent);
0364: rframes.remove(rframe);
0365: }
0366: }
0367: cacheRelation(rframe);
0368: }
0369:
0370: // Publish the frame itself as the change, or just a change
0371: // record for the specific slot?
0372: Frame.Change change = new Frame.Change(frame.getUID(), slot,
0373: value);
0374: publishChange(frame, change);
0375: }
0376:
0377: // Caller should synchronize on kb
0378: private void removeFromKB(DataFrame frame) {
0379: synchronized (kb) {
0380: if (primaryIndexSlot != null) {
0381: PrototypeFrame pframe = frame.getPrototype();
0382: Map<Object, DataFrame> cache = primaryIndexCache
0383: .get(pframe);
0384: if (cache != null) {
0385: Object key = frame.getValue(primaryIndexSlot);
0386: cache.remove(key);
0387: }
0388: }
0389: kb.remove(frame.getUID());
0390: }
0391: }
0392:
0393: public void removeFrame(DataFrame frame) {
0394: synchronized (kb) {
0395: removeFromKB(frame);
0396: }
0397: synchronized (frames) {
0398: frames.remove(frame);
0399: }
0400:
0401: // Handle the removal of containment relationship frames
0402: if (frame instanceof RelationFrame) {
0403: RelationFrame rframe = (RelationFrame) frame;
0404: decacheRelation(rframe);
0405: } else {
0406: // TODO: If frame is some other frame's container, that other frame needs to be told!
0407: synchronized (relation_lock) {
0408: Set<RelationFrame> rframes = inverse_child_cache
0409: .get(frame);
0410: inverse_child_cache.remove(frame);
0411: if (rframes != null) {
0412: for (RelationFrame rframe : rframes) {
0413: child_cache.remove(rframe);
0414: pending_relations.add(rframe);
0415: }
0416: }
0417: rframes = inverse_parent_cache.get(frame);
0418: inverse_parent_cache.remove(frame);
0419: if (rframes != null) {
0420: for (RelationFrame rframe : rframes) {
0421: parent_cache.remove(rframe);
0422: pending_relations.add(rframe);
0423: }
0424: }
0425: }
0426: }
0427:
0428: publishRemove(frame);
0429: }
0430:
0431: public void removeFrameAndRelations(DataFrame frame) {
0432: Set<DataFrame> removedFrames = new HashSet<DataFrame>();
0433: removedFrames.add(frame);
0434: synchronized (kb) {
0435: removeFromKB(frame);
0436: synchronized (relation_lock) {
0437: containers.remove(frame);
0438: Set<RelationFrame> rframes = inverse_child_cache
0439: .get(frame);
0440: inverse_child_cache.remove(frame);
0441: if (rframes != null) {
0442: for (RelationFrame rframe : rframes) {
0443: child_cache.remove(rframe);
0444: parent_cache.remove(rframe);
0445: pending_relations.remove(rframe);
0446: removeFromKB(rframe);
0447: }
0448: removedFrames.addAll(rframes);
0449: }
0450: rframes = inverse_parent_cache.get(frame);
0451: inverse_parent_cache.remove(frame);
0452: if (rframes != null) {
0453: for (RelationFrame rframe : rframes) {
0454: child_cache.remove(rframe);
0455: parent_cache.remove(rframe);
0456: pending_relations.remove(rframe);
0457: removeFromKB(rframe);
0458: }
0459: removedFrames.addAll(rframes);
0460: }
0461: }
0462: }
0463: synchronized (frames) {
0464: frames.removeAll(removedFrames);
0465: }
0466: publishRemove(removedFrames);
0467: }
0468:
0469: public String getName() {
0470: return name;
0471: }
0472:
0473: public String getPackageName() {
0474: return pkg;
0475: }
0476:
0477: public Collection<PrototypeFrame> getPrototypes() {
0478: synchronized (prototypes) {
0479: return Collections.unmodifiableCollection(prototypes
0480: .values());
0481: }
0482: }
0483:
0484: // Hierarchy
0485:
0486: public PrototypeFrame getPrototype(Frame frame) {
0487: String proto = frame.getKind();
0488: if (proto == null)
0489: return null;
0490: synchronized (prototypes) {
0491: return prototypes.get(proto);
0492: }
0493: }
0494:
0495: // Old version, replaced by reflection
0496: public boolean descendsFromOld(Frame frame, String prototype) {
0497: String proto = frame.getKind();
0498: if (proto == null)
0499: return false;
0500: if (proto.equals(prototype)) {
0501: return true;
0502: }
0503: Frame proto_frame = null;
0504: synchronized (prototypes) {
0505: proto_frame = prototypes.get(proto);
0506: }
0507: boolean result = proto_frame != null
0508: && descendsFromOld(proto_frame, prototype);
0509: return result;
0510: }
0511:
0512: private static final Class CNF = Object.class;
0513:
0514: public Class classForPrototype(String prototype) {
0515: // cache these!
0516: synchronized (cached_classes) {
0517: Class klass = cached_classes.get(prototype);
0518: if (klass == CNF)
0519: return null;
0520: if (klass != null)
0521: return klass;
0522:
0523: String classname = pkg + "."
0524: + FrameGen.fixName(prototype, true);
0525: try {
0526: Class klass2 = Class.forName(classname);
0527: cached_classes.put(prototype, klass2);
0528: return klass2;
0529: } catch (Exception ex) {
0530: if (log.isWarnEnabled())
0531: log.warn("Couldn't find class for prototype "
0532: + prototype);
0533: cached_classes.put(prototype, CNF);
0534: return null;
0535: }
0536: }
0537: }
0538:
0539: public Class classForPrototype(PrototypeFrame pframe) {
0540: return classForPrototype(pframe.getName());
0541: }
0542:
0543: public boolean descendsFrom(DataFrame frame, String prototype) {
0544: Class klass = classForPrototype(prototype);
0545: return klass != null ? descendsFrom(frame, klass, prototype)
0546: : false;
0547: }
0548:
0549: boolean descendsFrom(DataFrame frame, Class klass, String prototype) {
0550: boolean result = klass.isInstance(frame);
0551: if (log.isDebugEnabled())
0552: log.debug(frame
0553: + (result ? " descends from "
0554: : " does not descend from ") + prototype);
0555: return result;
0556: }
0557:
0558: public boolean descendsFrom(PrototypeFrame frame, String prototype) {
0559: boolean result;
0560: Class klass1 = classForPrototype(prototype);
0561: Class klass2 = classForPrototype(frame.getName());
0562: if (klass1 != null && klass2 != null)
0563: result = klass1.isAssignableFrom(klass2);
0564: else
0565: result = false;
0566:
0567: if (log.isDebugEnabled())
0568: log.debug(frame
0569: + (result ? " descends from "
0570: : " does not descend from ") + prototype);
0571: return result;
0572: }
0573:
0574: // Relationships
0575:
0576: private boolean isContainmentRelation(DataFrame frame) {
0577: return descendsFrom(frame, container_relation);
0578: }
0579:
0580: public DataFrame getContainer(DataFrame frame) {
0581: synchronized (containers) {
0582: return containers.get(frame);
0583: }
0584: }
0585:
0586: // Caller should synchronize on relation_lock
0587: private DataFrame getRelate(RelationFrame relationship,
0588: Map<RelationFrame, DataFrame> cache,
0589: Map<DataFrame, Set<RelationFrame>> inverseCache,
0590: String proto, String slot, Object value) {
0591: DataFrame result = findFrame(proto, slot, value);
0592: if (result == null) {
0593: long time = relationship.failed_lookup_time();
0594: if (time > LOOKUP_WARN_TIME) {
0595: // reset the timer
0596: relationship.clear_failed_lookup_time();
0597: if (log.isWarnEnabled())
0598: log.warn(" Proto = " + proto + " Slot = " + slot
0599: + " Value = " + value
0600: + " matches nothing in " + name);
0601: } else if (log.isDebugEnabled()) {
0602: log.debug(" Proto = " + proto + " Slot = " + slot
0603: + " Value = " + value + " matches nothing in "
0604: + name);
0605: }
0606: } else {
0607: if (log.isDebugEnabled())
0608: log.debug(" Caching: Proto = " + proto + " Slot = "
0609: + slot + " Value = " + value + " Result = "
0610: + result + "in " + name);
0611: relationship.clear_failed_lookup_time();
0612: cache.put(relationship, result);
0613: Set<RelationFrame> rframes = inverseCache.get(result);
0614: if (rframes == null) {
0615: rframes = new HashSet<RelationFrame>();
0616: inverseCache.put(result, rframes);
0617: }
0618: rframes.add(relationship);
0619: }
0620: return result;
0621: }
0622:
0623: public DataFrame getRelationshipParent(RelationFrame relationship) {
0624: synchronized (relation_lock) {
0625: DataFrame result = parent_cache.get(relationship);
0626: if (result != null) {
0627: if (log.isInfoEnabled())
0628: log.info(" Found cached relation value " + result);
0629: return result;
0630: }
0631:
0632: String proto = relationship.getParentPrototype();
0633: String slot = relationship.getParentSlot();
0634: Object value = relationship.getParentValue();
0635: return getRelate(relationship, parent_cache,
0636: inverse_parent_cache, proto, slot, value);
0637: }
0638: }
0639:
0640: public DataFrame getRelationshipChild(RelationFrame relationship) {
0641: synchronized (relation_lock) {
0642: DataFrame result = child_cache.get(relationship);
0643: if (result != null) {
0644: if (log.isInfoEnabled())
0645: log.info(" Found cached relation value " + result);
0646: return result;
0647: }
0648:
0649: String proto = relationship.getChildPrototype();
0650: String slot = relationship.getChildSlot();
0651: Object value = relationship.getChildValue();
0652: return getRelate(relationship, child_cache,
0653: inverse_child_cache, proto, slot, value);
0654: }
0655: }
0656:
0657: private boolean cacheRelation(RelationFrame relationship) {
0658: // cache a containment relationship
0659:
0660: DataFrame parent = getRelationshipParent(relationship);
0661: DataFrame child = getRelationshipChild(relationship);
0662: return cacheRelation(relationship, parent, child);
0663: }
0664:
0665: private boolean cacheRelation(RelationFrame relationship,
0666: DataFrame parent, DataFrame child) {
0667: if (parent == null || child == null) {
0668: synchronized (relation_lock) {
0669: // Queue for later
0670: synchronized (pending_relations) {
0671: pending_relations.add(relationship);
0672: if (log.isDetailEnabled())
0673: log.detail("Relation of type "
0674: + relationship.getPrototype().getName()
0675: + " between "
0676: + relationship.getParentValue()
0677: + " and "
0678: + relationship.getChildValue()
0679: + " is unresolved");
0680: }
0681: return false;
0682: }
0683: } else {
0684: if (isContainmentRelation(relationship)) {
0685: DataFrame old;
0686: synchronized (relation_lock) {
0687: synchronized (containers) {
0688: old = containers.get(child);
0689: containers.put(child, parent);
0690: }
0691: }
0692: // JAZ container Change potentially walks all child paths so can't hold relation_lock
0693: child.containerChange(old, parent);
0694: if (log.isInfoEnabled())
0695: log.info("Parent of " + child + " is " + parent);
0696: }
0697: return true;
0698: }
0699: }
0700:
0701: private void decacheRelation(RelationFrame relationship) {
0702: synchronized (relation_lock) {
0703: DataFrame child = child_cache.get(relationship);
0704: if (child != null) {
0705: Set<RelationFrame> rframes = inverse_child_cache
0706: .get(child);
0707: if (rframes != null) {
0708: rframes.remove(relationship);
0709: }
0710: }
0711: DataFrame parent = parent_cache.get(relationship);
0712: if (parent != null) {
0713: Set<RelationFrame> rframes = inverse_parent_cache
0714: .get(parent);
0715: if (rframes != null) {
0716: rframes.remove(relationship);
0717: }
0718: }
0719: child_cache.remove(relationship);
0720: parent_cache.remove(relationship);
0721:
0722: pending_relations.remove(relationship);
0723: }
0724: if (isContainmentRelation(relationship)) {
0725: synchronized (containers) {
0726: Frame child = getRelationshipChild(relationship);
0727: if (child != null)
0728: containers.remove(child);
0729: }
0730: }
0731: }
0732:
0733: // Metrics
0734: public void subscribeToMetric(DataFrame frame, Observer observer,
0735: String path) {
0736: metrics.subscribeToValue(path, observer, frame);
0737: }
0738:
0739: public Metric getMetricValue(DataFrame frame, String path) {
0740: return metrics.getValue(path, frame);
0741: }
0742:
0743: // Query
0744:
0745: public PrototypeFrame findPrototypeFrame(String name) {
0746: return prototypes.get(name);
0747: }
0748:
0749: public Frame findFrame(UID uid) {
0750: synchronized (kb) {
0751: Object raw = kb.get(uid);
0752: if (raw instanceof Frame)
0753: return (Frame) raw;
0754: else
0755: return null;
0756: }
0757: }
0758:
0759: private DataFrame getIndexedFrame(String proto, Object key) {
0760: PrototypeFrame pframe = findPrototypeFrame(proto);
0761: Map<Object, DataFrame> cache = primaryIndexCache.get(pframe);
0762: if (cache != null) {
0763: DataFrame value = cache.get(key);
0764: if (value != null)
0765: return value;
0766: }
0767: // walk up the proto hierarchy
0768: String parent = pframe.getKind();
0769: if (parent != null) {
0770: return getIndexedFrame(parent, key);
0771: } else {
0772: return null;
0773: }
0774: }
0775:
0776: public DataFrame findFrame(String proto, String slot, Object value) {
0777: if (slot == null || proto == null || value == null)
0778: return null;
0779: if (primaryIndexSlot != null && slot.equals(primaryIndexSlot)) {
0780: DataFrame frame = getIndexedFrame(proto, value);
0781: if (frame != null) {
0782: return frame;
0783: }
0784: }
0785: Class klass = classForPrototype(proto);
0786: if (klass == null)
0787: return null;
0788:
0789: synchronized (frames) {
0790: for (DataFrame frame : frames) {
0791: if (descendsFrom(frame, klass, proto)) {
0792: Object candidate = frame.getValue(slot);
0793: if (candidate != null && candidate.equals(value))
0794: return frame;
0795: }
0796: }
0797: }
0798: return null;
0799: }
0800:
0801: public Set<DataFrame> findFrames(String proto,
0802: Properties slot_value_pairs) {
0803: Class klass = classForPrototype(proto);
0804: if (klass == null)
0805: return null;
0806:
0807: Set<DataFrame> results = new HashSet<DataFrame>();
0808: synchronized (frames) {
0809: for (DataFrame frame : frames) {
0810: if (descendsFrom(frame, klass, proto)) {
0811: if (slot_value_pairs == null
0812: || frame.matchesSlots(slot_value_pairs)) {
0813: results.add(frame);
0814: }
0815: }
0816: }
0817: }
0818: return results;
0819: }
0820:
0821: Set<DataFrame> findChildren(DataFrame parent,
0822: String relation_prototype, Map<RelationFrame, DataFrame> map) {
0823: Class klass = classForPrototype(relation_prototype);
0824: if (klass == null)
0825: return null;
0826: Set<DataFrame> results = map != null ? null
0827: : new HashSet<DataFrame>();
0828: synchronized (relation_lock) {
0829: Set<RelationFrame> rframes = inverse_parent_cache
0830: .get(parent);
0831: if (rframes != null) {
0832: for (RelationFrame rframe : rframes) {
0833: if (descendsFrom(rframe, klass, relation_prototype)) {
0834: DataFrame child = child_cache.get(rframe);
0835: if (child != null) {
0836: if (map != null)
0837: map.put(rframe, child);
0838: else
0839: results.add(child);
0840: }
0841: }
0842: }
0843: }
0844: }
0845: return results;
0846: }
0847:
0848: Set<DataFrame> findParents(DataFrame child,
0849: String relation_prototype, Map<RelationFrame, DataFrame> map) {
0850: Class klass = classForPrototype(relation_prototype);
0851: if (klass == null)
0852: return null;
0853: Set<DataFrame> results = map != null ? null
0854: : new HashSet<DataFrame>();
0855: synchronized (relation_lock) {
0856: Set<RelationFrame> rframes = inverse_child_cache.get(child);
0857: if (rframes != null) {
0858: for (RelationFrame rframe : rframes) {
0859: if (descendsFrom(rframe, klass, relation_prototype)) {
0860: DataFrame parent = parent_cache.get(rframe);
0861: if (parent != null) {
0862: if (map != null)
0863: map.put(rframe, parent);
0864: else
0865: results.add(parent);
0866: }
0867: }
0868: }
0869: }
0870: }
0871: return results;
0872: }
0873:
0874: int countChildren(DataFrame parent, String relation_prototype) {
0875: Class klass = classForPrototype(relation_prototype);
0876: if (klass == null)
0877: return 0;
0878: int count = 0;
0879: synchronized (relation_lock) {
0880: Set<RelationFrame> rframes = inverse_parent_cache
0881: .get(parent);
0882: if (rframes != null) {
0883: for (RelationFrame rframe : rframes) {
0884: if (descendsFrom(rframe, klass, relation_prototype)) {
0885: DataFrame child = child_cache.get(rframe);
0886: if (child != null) {
0887: ++count;
0888: }
0889: }
0890: }
0891: }
0892: }
0893: return count;
0894: }
0895:
0896: int countParents(DataFrame child, String relation_prototype) {
0897: Class klass = classForPrototype(relation_prototype);
0898: if (klass == null)
0899: return 0;
0900: int count = 0;
0901: synchronized (relation_lock) {
0902: Set<RelationFrame> rframes = inverse_child_cache.get(child);
0903: if (rframes != null) {
0904: for (RelationFrame rframe : rframes) {
0905: if (descendsFrom(rframe, klass, relation_prototype)) {
0906: DataFrame parent = parent_cache.get(rframe);
0907: if (parent != null) {
0908: ++count;
0909: }
0910: }
0911: }
0912: }
0913: }
0914: return count;
0915: }
0916:
0917: DataFrame findFirstChild(DataFrame parent, String relation_prototype) {
0918: Class klass = classForPrototype(relation_prototype);
0919: if (klass == null)
0920: return null;
0921: synchronized (relation_lock) {
0922: Set<RelationFrame> rframes = inverse_parent_cache
0923: .get(parent);
0924: if (rframes != null) {
0925: for (RelationFrame rframe : rframes) {
0926: if (descendsFrom(rframe, klass, relation_prototype)) {
0927: DataFrame child = child_cache.get(rframe);
0928: if (child != null) {
0929: return child;
0930: }
0931: }
0932: }
0933: }
0934: }
0935: return null;
0936: }
0937:
0938: DataFrame findFirstParent(DataFrame child, String relation_prototype) {
0939: Class klass = classForPrototype(relation_prototype);
0940: if (klass == null)
0941: return null;
0942: synchronized (relation_lock) {
0943: Set<RelationFrame> rframes = inverse_child_cache.get(child);
0944: if (rframes != null) {
0945: for (RelationFrame rframe : rframes) {
0946: if (descendsFrom(rframe, klass, relation_prototype)) {
0947: DataFrame parent = parent_cache.get(rframe);
0948: if (parent != null) {
0949: return parent;
0950: }
0951: }
0952: }
0953: }
0954: }
0955: return null;
0956: }
0957:
0958: public Set<DataFrame> findRelations(Frame frame, // should be DataFrame
0959: String role, String relation_proto) {
0960: if (role.equals(PARENT)) {
0961: return findParents((DataFrame) frame, relation_proto, null);
0962: } else if (role.equals(CHILD)) {
0963: return findChildren((DataFrame) frame, relation_proto, null);
0964: } else {
0965: if (log.isWarnEnabled())
0966: log.warn("Role " + role + " should be " + PARENT
0967: + " or " + CHILD);
0968: return null;
0969: }
0970: }
0971:
0972: public DataFrame findFirstRelation(Frame frame, // should be DataFrame
0973: String role, String relation_proto) {
0974: if (role.equals(PARENT)) {
0975: return findFirstParent((DataFrame) frame, relation_proto);
0976: } else if (role.equals(CHILD)) {
0977: return findFirstChild((DataFrame) frame, relation_proto);
0978: } else {
0979: if (log.isWarnEnabled())
0980: log.warn("Role " + role + " should be " + PARENT
0981: + " or " + CHILD);
0982: return null;
0983: }
0984: }
0985:
0986: public int countRelations(Frame frame, // should be DataFrame
0987: String role, String relation_proto) {
0988: if (role.equals(PARENT)) {
0989: return countParents((DataFrame) frame, relation_proto);
0990: } else if (role.equals(CHILD)) {
0991: return countChildren((DataFrame) frame, relation_proto);
0992: } else {
0993: if (log.isWarnEnabled())
0994: log.warn("Role " + role + " should be " + PARENT
0995: + " or " + CHILD);
0996: return 0;
0997: }
0998: }
0999:
1000: public Map<RelationFrame, DataFrame> findRelationshipFrames(
1001: DataFrame frame, String role, String relation_proto) {
1002: Map<RelationFrame, DataFrame> map = new HashMap<RelationFrame, DataFrame>();
1003: if (role.equals(PARENT)) {
1004: findParents(frame, relation_proto, map);
1005: } else if (role.equals(CHILD)) {
1006: findChildren(frame, relation_proto, map);
1007: } else {
1008: if (log.isWarnEnabled())
1009: log.warn("Role " + role + " should be " + PARENT
1010: + " or " + CHILD);
1011: }
1012: return map;
1013:
1014: }
1015:
1016: public Path findPath(UID uid) {
1017: synchronized (kb) {
1018: Object raw = kb.get(uid);
1019: if (raw instanceof Path)
1020: return (Path) raw;
1021: else
1022: return null;
1023: }
1024: }
1025:
1026: public Path findPath(String name) {
1027: synchronized (paths) {
1028: return paths.get(name);
1029: }
1030: }
1031:
1032: // BBS queue
1033:
1034: private static class ChangeQueueEntry {
1035: final UniqueObject object;
1036:
1037: public ChangeQueueEntry(UniqueObject object) {
1038: this .object = object;
1039: }
1040: }
1041:
1042: private static class Add extends ChangeQueueEntry {
1043: Add(UniqueObject object) {
1044: super (object);
1045: }
1046: }
1047:
1048: private static class Change extends ChangeQueueEntry {
1049: Object change;
1050:
1051: Change(UniqueObject object, Object change) {
1052: super (object);
1053: this .change = change;
1054: }
1055: }
1056:
1057: private static class Remove extends ChangeQueueEntry {
1058: Remove(UniqueObject object) {
1059: super (object);
1060: }
1061: }
1062:
1063: public void runInTransaction(Runnable r) {
1064: synchronized (transactionLock) {
1065: r.run();
1066: }
1067: }
1068:
1069: // Synchronized for a shorter time but doesn't work reliably.
1070: // Sometimes items are added while this is in progress and
1071: // execute doesn't run again.
1072: public void processQueue() {
1073:
1074: synchronized (transactionLock) {
1075: executeAggregators();
1076: List<ChangeQueueEntry> changes = null;
1077: synchronized (change_queue_lock) {
1078: changes = new ArrayList<ChangeQueueEntry>(change_queue);
1079: change_queue = new ArrayList<ChangeQueueEntry>();
1080: }
1081: int count = changes.size();
1082: for (int i = 0; i < count; i++) {
1083: ChangeQueueEntry change = changes.get(i);
1084: if (log.isDebugEnabled())
1085: log.debug("about to publish " + change);
1086: if (change instanceof Change) {
1087: Change chng = (Change) change;
1088: List<Object> changes_list = new ArrayList<Object>(1);
1089: changes_list.add(chng.change);
1090: if (log.isDebugEnabled())
1091: log.debug("Publish change " + chng.change);
1092: bbs.publishChange(chng.object, changes_list);
1093: } else if (change instanceof Add) {
1094: Add add = (Add) change;
1095: bbs.publishAdd(add.object);
1096: } else if (change instanceof Remove) {
1097: Remove rem = (Remove) change;
1098: bbs.publishRemove(rem.object);
1099: }
1100: }
1101: }
1102: }
1103:
1104: // Old version
1105: public void processQueueSlow() {
1106: synchronized (change_queue_lock) {
1107: int count = change_queue.size();
1108: for (int i = 0; i < count; i++) {
1109: ChangeQueueEntry change = change_queue.get(i);
1110: if (change instanceof Change) {
1111: Change chng = (Change) change;
1112: List<Object> changes_list = new ArrayList<Object>(1);
1113: changes_list.add(chng.change);
1114: bbs.publishChange(chng.object, changes_list);
1115: } else if (change instanceof Add) {
1116: Add add = (Add) change;
1117: bbs.publishAdd(add.object);
1118: } else if (change instanceof Remove) {
1119: Remove rem = (Remove) change;
1120: bbs.publishRemove(rem.object);
1121: }
1122: }
1123: change_queue.clear();
1124: }
1125: }
1126:
1127: void publishAdd(UniqueObject object) {
1128: synchronized (change_queue_lock) {
1129: change_queue.add(new Add(object));
1130: bbs.signalClientActivity();
1131: }
1132: }
1133:
1134: void publishChange(UniqueObject object, Object change) {
1135: synchronized (change_queue_lock) {
1136: change_queue.add(new Change(object, change));
1137: bbs.signalClientActivity();
1138: }
1139: }
1140:
1141: void publishRemove(UniqueObject object) {
1142: synchronized (change_queue_lock) {
1143: change_queue.add(new Remove(object));
1144: bbs.signalClientActivity();
1145: }
1146: }
1147:
1148: void publishRemove(Collection<DataFrame> objects) {
1149: synchronized (change_queue_lock) {
1150: for (UniqueObject object : objects)
1151: change_queue.add(new Remove(object));
1152: }
1153: bbs.signalClientActivity();
1154: }
1155:
1156: // XML dumping
1157:
1158: void writeDataFrames(PrintWriter writer, int indentation,
1159: int offset, UnaryPredicate filter, boolean allSlots) {
1160: synchronized (kb) {
1161: for (Object raw : kb.values()) {
1162: if (raw instanceof DataFrame
1163: && !(raw instanceof RelationFrame)) {
1164: if (filter != null && !filter.execute(raw)) {
1165: // wrong kind of frame
1166: continue;
1167: }
1168: DataFrame frame = (DataFrame) raw;
1169: frame.write(writer, indentation, offset, allSlots);
1170: }
1171: }
1172: for (Object raw : kb.values()) {
1173: if (raw instanceof RelationFrame) {
1174: if (filter != null && !filter.execute(raw)) {
1175: // wrong kind of frame
1176: continue;
1177: }
1178: DataFrame frame = (DataFrame) raw;
1179: frame.write(writer, indentation, offset, allSlots);
1180: }
1181: }
1182: }
1183: }
1184:
1185: void writeData(File file, int indentation, int offset,
1186: UnaryPredicate filter, boolean allSlots)
1187: throws java.io.IOException {
1188: FileWriter fwriter = new FileWriter(file);
1189: PrintWriter writer = new PrintWriter(fwriter);
1190:
1191: indentation += offset;
1192: writer.println("<!-- !DOCTYPE " + domain + " SYSTEM \""
1193: + domain + ".dtd\" -->");
1194: for (int i = 0; i < indentation; i++)
1195: writer.print(' ');
1196: writer.println("<" + domain + ">");
1197: indentation += offset;
1198:
1199: writeDataFrames(writer, indentation, offset, filter, allSlots);
1200:
1201: indentation -= offset;
1202: writer.println("</" + domain + ">");
1203:
1204: writer.close();
1205: }
1206:
1207: private void exportFramesOfType(File file, Set<String> prototypes,
1208: boolean allSlots) throws IOException {
1209: Set<Class> classes = new HashSet<Class>();
1210: for (String prototype : prototypes) {
1211: classes.add(classForPrototype(prototype));
1212: }
1213: final Class[] pclasses = new Class[classes.size()];
1214: classes.toArray(pclasses);
1215: UnaryPredicate filter = new UnaryPredicate() {
1216: public boolean execute(Object o) {
1217: Class cls = o.getClass();
1218: for (Class cl : pclasses) {
1219: if (cl.isAssignableFrom(cls)) {
1220: return true;
1221: }
1222: }
1223: return false;
1224: }
1225: };
1226: writeData(file, 0, 2, filter, allSlots);
1227: }
1228:
1229: public void importFrames(URL location) throws IOException {
1230: FrameSetParser parser = new FrameSetParser();
1231: parser.parseFrameSetData(name, location, this );
1232: }
1233:
1234: public void exportFrames(File file) throws IOException {
1235: writeData(file, 0, 2, null, false);
1236: }
1237:
1238: public void exportFrames(File file, Set<String> prototypes)
1239: throws IOException {
1240: if (prototypes != null) {
1241: exportFramesOfType(file, prototypes, false);
1242: } else {
1243: writeData(file, 0, 2, null, false);
1244: }
1245: }
1246:
1247: public void exportFrames(File file, Set<String> prototypes,
1248: boolean allSlots) throws IOException {
1249: if (prototypes != null) {
1250: exportFramesOfType(file, prototypes, allSlots);
1251: } else {
1252: writeData(file, 0, 2, null, allSlots);
1253: }
1254: }
1255: }
|