0001: /*
0002: *
0003: * Copyright 2002-2004 BBNT Solutions, LLC
0004: * under sponsorship of the Defense Advanced Research Projects
0005: * Agency (DARPA).
0006: *
0007: * You can redistribute this software and/or modify it under the
0008: * terms of the Cougaar Open Source License as published on the
0009: * Cougaar Open Source Website (www.cougaar.org).
0010: *
0011: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0012: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0013: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
0014: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
0015: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0016: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0017: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0018: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0019: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0020: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0021: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0022: *
0023: * </copyright>
0024: */
0025:
0026: package org.cougaar.pizza.servlet;
0027:
0028: import org.cougaar.community.CommunityDescriptor;
0029: import org.cougaar.community.manager.Request;
0030: import org.cougaar.core.blackboard.Claimable;
0031: import org.cougaar.core.blackboard.IncrementalSubscription;
0032: import org.cougaar.core.component.ServiceBroker;
0033: import org.cougaar.core.mts.MessageAddress;
0034: import org.cougaar.core.plugin.ComponentPlugin;
0035: import org.cougaar.core.relay.Relay;
0036: import org.cougaar.core.service.LoggingService;
0037: import org.cougaar.core.service.ServletService;
0038: import org.cougaar.core.service.community.Community;
0039: import org.cougaar.core.service.community.CommunityResponse;
0040: import org.cougaar.core.util.UID;
0041: import org.cougaar.core.util.UniqueObject;
0042: import org.cougaar.multicast.AttributeBasedAddress;
0043: import org.cougaar.planning.ldm.asset.Asset;
0044: import org.cougaar.planning.ldm.asset.Entity;
0045: import org.cougaar.planning.ldm.plan.*;
0046: import org.cougaar.util.Arguments;
0047: import org.cougaar.util.UnaryPredicate;
0048:
0049: import javax.servlet.Servlet;
0050: import javax.servlet.ServletException;
0051: import javax.servlet.http.HttpServlet;
0052: import javax.servlet.http.HttpServletRequest;
0053: import javax.servlet.http.HttpServletResponse;
0054: import java.io.IOException;
0055: import java.io.PrintWriter;
0056: import java.net.URLEncoder;
0057: import java.text.FieldPosition;
0058: import java.text.SimpleDateFormat;
0059: import java.util.*;
0060:
0061: /**
0062: * Generic debugging servlet/plugin that displays all Adds/Changes/Removes
0063: * on Blackboard objects, accessed at "/history".
0064: * <p/>
0065: * Specifically tracks changes on Relays, Tasks, PlanElements, Assets,
0066: * UniqueObjects, and implementations of {@link HistoryServletFriendly}.
0067: * For every event, attempts to explain the event.
0068: * For instance, when a Relay is first published, it shows which Agent
0069: * the Relay is being sent to.
0070: * <p/>
0071: * The Servlet lets the user sort events by time, then by uid, or by uid
0072: * only. Sorting by uid shows the complete lifecycle of a Blackboard
0073: * object; especially useful for transient objects that live on the
0074: * Blackboard only a short time. When sorting by time, each group of
0075: * changes that happens in the same execute cycle is drawn with
0076: * the same background color. When sorting by uid, each distinct
0077: * object is drawn with the same background color.
0078: * <p/>
0079: * The servlet has a "show details" link which will show the toString for
0080: * the blackboard object at that time. This is a spot where developers
0081: * can tune how this servlet displays their objects.
0082: * <p/>
0083: * The "Meaning" column uses the HistoryServletFriendly's toHTML() method
0084: * to fill in content when available.
0085: * <p/>
0086: * Also shows which plugin initially published an object to the Blackboard
0087: * if that information is available (for Claimables).
0088: * <p/>
0089: * <p/>
0090: * Try it in any Cougaar Application! Simply add this as a Plugin
0091: * in any Agent!
0092: * <p/>
0093: * Has a default limit of 1000 events, but this can be set by the
0094: * MAX_EVENTS_REMEMBERED component argument to the servlet.
0095: * E.g. :
0096: * <argument>MAX_EVENTS_REMEMBERED=5000</argument>
0097: * <p> Another limit: 5 RoleSchedule elements displayed by default,
0098: * set with MAX_ROLE_SCHEDULE_ELEMENTS argument.
0099: * And a default of 5 child Tasks shown per Expansion, changed with the
0100: * MAX_CHILD_TASKS argument.
0101: * <p/>
0102: * Note that this Servlet is actually a Plugin, so that it can subscribe
0103: * to all the blackboard changes, and keeps a SortedSet of these Events,
0104: * ready for display. It then provides an inner Servlet to the ServletService,
0105: * so a user can view the pre-collected Events Set.
0106: * <p/>
0107: * Note that this Servlet has heavy Planning dependencies. It has minor
0108: * Community dependencies, to allow printing details in the Meaning column.
0109: * By commenting out those items, this dependency could be removed.
0110: */
0111: public class HistoryServlet extends ComponentPlugin {
0112: // Some defaults
0113: protected final int INITIAL_MAX_ENTRIES = 1000; // Default max # events to track
0114: protected final int INITIAL_MAX_ROLE_SCHEDULE_ELEMENTS = 5;
0115: protected final int INITIAL_MAX_CHILD_TASKS = 5;
0116:
0117: // Actual value of parametrized preferences
0118: private int maxEvents;
0119: private int maxRoleScheduleElements;
0120: private int maxChildTasks;
0121:
0122: /**
0123: * initialize args to the empty instance
0124: */
0125: private Arguments args = Arguments.EMPTY_INSTANCE;
0126: protected MessageAddress localAgent;
0127:
0128: protected LoggingService logger;
0129: private ServletService servletService;
0130:
0131: // Subscribe to various kinds of objects
0132: private IncrementalSubscription relaysSubscription;
0133: private IncrementalSubscription tasksSubscription;
0134: private IncrementalSubscription planElementsSubscription;
0135: private IncrementalSubscription assetsSubscription;
0136: // Note that this is an everything-but-the-above subscription
0137: private IncrementalSubscription uniqueObjectsSubscription;
0138:
0139: protected static SimpleDateFormat format = new SimpleDateFormat(
0140: "MM-dd hh:mm:ss,SSS");
0141: protected String encAgentName;
0142:
0143: /**
0144: * The actual Blackboard history we collect
0145: */
0146: protected SortedSet events = new TreeSet();
0147:
0148: // Has the events list been trimmed?
0149: private boolean didDropOldEntries = false;
0150:
0151: // User preferences
0152: boolean showChangeReport = false;
0153: boolean sortByUID = false;
0154: boolean showDetails = false;
0155:
0156: private SimpleDateFormat myDateFormat = new SimpleDateFormat(
0157: "MM_dd_yyyy_h:mma");
0158: private Date myDateInstance = new Date();
0159: private FieldPosition myFieldPos = new FieldPosition(
0160: SimpleDateFormat.YEAR_FIELD);
0161:
0162: // what are we looking at?
0163: public static final int TASK = 0;
0164: public static final int PLAN_ELEMENT = 1;
0165: public static final int ASSET = 2;
0166: public static final int UNIQUE_OBJECT = 3;
0167: public static final int DIRECT_OBJECT = 4;
0168:
0169: // What kind of event was this?
0170: public static final int ADDED = 0;
0171: public static final int CHANGED = 1;
0172: public static final int REMOVED = 2;
0173:
0174: private UnaryPredicate relayPredicate = new UnaryPredicate() {
0175: public boolean execute(Object o) {
0176: return (o instanceof Relay);
0177: }
0178: };
0179:
0180: private UnaryPredicate taskPredicate = new UnaryPredicate() {
0181: public boolean execute(Object o) {
0182: return (o instanceof Task);
0183: }
0184: };
0185:
0186: private UnaryPredicate allocationPredicate = new UnaryPredicate() {
0187: public boolean execute(Object o) {
0188: return (o instanceof PlanElement);
0189: }
0190: };
0191:
0192: private UnaryPredicate assetPredicate = new UnaryPredicate() {
0193: public boolean execute(Object o) {
0194: return (o instanceof Asset);
0195: }
0196: };
0197:
0198: // Subscribe to anything not in the above subscriptions
0199: private UnaryPredicate uniqueObjectPredicate = new UnaryPredicate() {
0200: public boolean execute(Object o) {
0201: return (!(o instanceof Relay) && !(o instanceof Task)
0202: && !(o instanceof PlanElement)
0203: && !(o instanceof Asset) && (o instanceof UniqueObject));
0204: }
0205: };
0206:
0207: /**
0208: * Only called if a plugin has parameters.
0209: * We over-ride this to use the Arguments utility, since all our
0210: * arguments are NAME=VALUE format.
0211: */
0212: public void setParameter(Object o) {
0213: args = new Arguments(o);
0214: }
0215:
0216: /**
0217: * Get the ServletService via reflection
0218: */
0219: public void setServletService(ServletService servletService) {
0220: this .servletService = servletService;
0221: }
0222:
0223: public void load() {
0224: super .load();
0225: ServiceBroker sb = getServiceBroker();
0226: logger = (LoggingService) sb.getService(this ,
0227: LoggingService.class, null);
0228: if (logger == null)
0229: logger = LoggingService.NULL;
0230:
0231: encAgentName = encodeAgentName(agentId.getAddress());
0232:
0233: // when plugin is added to agent you can set this to a smaller or
0234: // larger value depending on how much of the heap you want
0235: // to fill up with events
0236: maxEvents = args.getInt("MAX_EVENTS_REMEMBERED",
0237: INITIAL_MAX_ENTRIES);
0238: maxRoleScheduleElements = args.getInt(
0239: "MAX_ROLE_SCHEDULE_ELEMENTS",
0240: INITIAL_MAX_ROLE_SCHEDULE_ELEMENTS);
0241: maxChildTasks = args.getInt("MAX_CHILD_TASKS",
0242: INITIAL_MAX_CHILD_TASKS);
0243: if (logger.isInfoEnabled()) {
0244: logger.info("max events "
0245: + args.getInt("MAX_EVENTS_REMEMBERED", 55));
0246: logger.info("args is " + args);
0247: }
0248: }
0249:
0250: // Every load() should be matched with an unload()
0251: public void unload() {
0252: if (servletService != null) {
0253: getServiceBroker().releaseService(this ,
0254: ServletService.class, servletService);
0255: servletService = null;
0256: }
0257: if (logger != LoggingService.NULL) {
0258: getServiceBroker().releaseService(this ,
0259: LoggingService.class, logger);
0260: logger = null;
0261: }
0262:
0263: super .unload();
0264: }
0265:
0266: /*
0267: * Create subscriptions to all the BBoard changes, and register the servlet.
0268: */
0269: protected void setupSubscriptions() {
0270: relaysSubscription = (IncrementalSubscription) blackboard
0271: .subscribe(relayPredicate);
0272: tasksSubscription = (IncrementalSubscription) blackboard
0273: .subscribe(taskPredicate);
0274: planElementsSubscription = (IncrementalSubscription) blackboard
0275: .subscribe(allocationPredicate);
0276: assetsSubscription = (IncrementalSubscription) blackboard
0277: .subscribe(assetPredicate);
0278: uniqueObjectsSubscription = (IncrementalSubscription) blackboard
0279: .subscribe(uniqueObjectPredicate);
0280:
0281: // register with servlet service
0282: try {
0283: servletService.register(getPath(), createServlet());
0284: } catch (Exception e) {
0285: if (logger.isWarnEnabled())
0286: logger.warn("could not register servlet?", e);
0287: }
0288:
0289: }
0290:
0291: /**
0292: * Load this servlet at "/history"
0293: */
0294: protected String getPath() {
0295: return "/history";
0296: }
0297:
0298: /**
0299: * Get a new {@link HistoryWorker} to be the Servlet
0300: */
0301: protected Servlet createServlet() {
0302: return new HistoryWorker();
0303: }
0304:
0305: /**
0306: * Whenever a BBoard item changes, it adds that event to the list of Events
0307: * (trimming the set if we reach the MAX size). Then when a user invokes
0308: * the servlet, the set of events is ready for quick display.
0309: */
0310: public void execute() {
0311: // Grab the time with which to mark the events. Note that this time
0312: // is not when the event appeneded therefore, but when this plugin saw it.
0313: // If the agent were busy (persistence?), this could be much later.
0314: long now = currentTimeMillis(); // this is scenario time.
0315:
0316: try {
0317: // Look at our subscriptions for changes, adding to our
0318: // set of known events if necessary. This actually builds of the EventInfo
0319: // objects (see definition toward the bottom) which contain some HTML
0320: // strings -- making the later servlet viewing quicker, but taking
0321: // up more memory
0322:
0323: // synchronized prevents servlet from iterating over events
0324: // and adding to list of known events at same time
0325: synchronized (events) {
0326: checkTasks(now);
0327: checkPlanElements(now);
0328: checkRelays(now);
0329: checkAssets(now);
0330: checkUniqueObjects(now);
0331: }
0332: } catch (Exception e) {
0333: if (logger.isWarnEnabled())
0334: logger.warn("Got exception adding to events list: ", e);
0335: }
0336:
0337: // Print the current set of Events...
0338: if (logger.isDebugEnabled()) {
0339: StringBuffer buf = new StringBuffer();
0340: synchronized (events) {
0341: for (Iterator iter = events.iterator(); iter.hasNext();) {
0342: buf.append(iter.next() + "\n");
0343: }
0344: }
0345:
0346: logger.debug("Current events: \n" + buf);
0347: }
0348: }
0349:
0350: /////////////////
0351: // Below are all the helper methods to collect the Events....
0352:
0353: public String encodeAgentName(String name) {
0354: try {
0355: return URLEncoder.encode(name, "UTF-8");
0356: } catch (java.io.UnsupportedEncodingException e) {
0357: // should never happen
0358: throw new RuntimeException("Unable to encode to UTF-8?");
0359: }
0360: }
0361:
0362: protected String encode(String s) {
0363: try {
0364: return URLEncoder.encode(s, "UTF-8");
0365: } catch (Exception e) {
0366: throw new IllegalArgumentException("Unable to encode URL ("
0367: + s + ")");
0368: }
0369: }
0370:
0371: /**
0372: * Generate a link to the PlanView (/tasks) servlet
0373: * for full details on the objet.
0374: */
0375: protected String getURL(UID uid, int which) {
0376: int mode = 0;//PlanViewServlet.MODE_FRAME;
0377: switch (which) {
0378: case TASK:
0379: mode = 3;//PlanViewServlet.MODE_TASK_DETAILS;
0380: break;
0381: case PLAN_ELEMENT:
0382: mode = 5;//PlanViewServlet.MODE_PLAN_ELEMENT_DETAILS;
0383: break;
0384: case ASSET:
0385: mode = 7;//PlanViewServlet.MODE_PLAN_ELEMENT_DETAILS;
0386: break;
0387: case UNIQUE_OBJECT:
0388: mode = 10;//PlanViewServlet.MODE_XML_HTML_DETAILS;
0389: break;
0390: case DIRECT_OBJECT:
0391: mode = 15;//PlanViewServlet.MODE_XML_HTML_DETAILS;
0392: break;
0393: }
0394:
0395: StringBuffer buf = new StringBuffer();
0396: buf.append("<a href=\"/$");
0397: buf.append(encAgentName);
0398: buf.append("/tasks");
0399: buf.append("?" + "mode" + //PlanViewServlet.MODE+
0400: "=" + mode + "&" + "uid" + //PlanViewServlet.ITEM_UID+
0401: "=");
0402: buf.append(encode(uid.toString()));
0403: // buf.append("\" target=\"itemFrame\">");
0404: buf.append("\" target=\"_blank\">");
0405: // buf.append("\"");
0406: buf.append(uid);
0407: buf.append("</a>");
0408:
0409: return buf.toString();
0410: }
0411:
0412: // Keep a running counter of events seen
0413: // Can later be used to number the rows for readability.
0414: private int eventNum = 0;
0415:
0416: /**
0417: * Get the next number for a new Event
0418: */
0419: protected int nextEventNum() {
0420: return eventNum++;
0421: }
0422:
0423: /**
0424: * Check the Tasks subscription, adding any new events to the list
0425: */
0426: protected void checkTasks(long now) {
0427: // If there were any Task events
0428: if (tasksSubscription.hasChanged()) {
0429: // First look at Added Tasks
0430: Collection added = tasksSubscription.getAddedCollection();
0431: // For each
0432: for (Iterator iter = added.iterator(); iter.hasNext();) {
0433: Task task = (Task) iter.next();
0434: // Basic description is the UID, verb, publisher
0435: String event = "Task " + getURL(task.getUID(), TASK)
0436: + " - " + task.getVerb();
0437: event += getClaimerString((Claimable) task);
0438:
0439: // Add the event to the set
0440: addEvent(new EventInfo(ADDED, nextEventNum(), task
0441: .getUID().toString(), event, now,
0442: getAddedTaskComment(task), encodeHTML(task
0443: .toString())));
0444: }
0445:
0446: // Now the Changed tasks
0447: Collection changed = tasksSubscription
0448: .getChangedCollection();
0449: for (Iterator iter = changed.iterator(); iter.hasNext();) {
0450: Task task = (Task) iter.next();
0451: addEvent(new EventInfo(CHANGED, nextEventNum(), task
0452: .getUID().toString(), "Task "
0453: + getURL(task.getUID(), TASK), now, "",
0454: tasksSubscription.getChangeReports(task),
0455: encodeHTML(task.toString())));
0456: }
0457:
0458: // Now the removed tasks
0459: Collection removed = tasksSubscription
0460: .getRemovedCollection();
0461: for (Iterator iter = removed.iterator(); iter.hasNext();) {
0462: Task task = (Task) iter.next();
0463: addEvent(new EventInfo(REMOVED, nextEventNum(), task
0464: .getUID().toString(), "Task "
0465: + getURL(task.getUID(), TASK), now, ""));
0466: }
0467: }
0468: }
0469:
0470: /**
0471: * description of an added Task shows the Verb, DirectObject, and Preferences.
0472: */
0473: protected String getAddedTaskComment(Task task) {
0474: StringBuffer buf = new StringBuffer();
0475:
0476: buf.append(task.getVerb());
0477: buf.append(" Task, ");
0478: if (task.getDirectObject() != null) {
0479: buf.append(" with Direct Object ");
0480: buf.append(getTypeAndItemInfo(task.getDirectObject()));
0481: buf.append(".");
0482: }
0483: buf.append(getTaskPreferences(task));
0484:
0485: return buf.toString();
0486: }
0487:
0488: /**
0489: * Print out the Claimer of a Claimable, if any.
0490: */
0491: private String getClaimerString(Claimable cl) {
0492: if (cl == null)
0493: return "";
0494:
0495: Object claimer = cl.getClaim();
0496: if (claimer != null && !claimer.toString().trim().equals(""))
0497: return "<br/>was published by " + claimer.toString();
0498: else
0499: return "";
0500: }
0501:
0502: /**
0503: * Add any PlanElement events to the Set
0504: */
0505: protected void checkPlanElements(long now) {
0506: if (planElementsSubscription.hasChanged()) {
0507: Collection added = planElementsSubscription
0508: .getAddedCollection();
0509: for (Iterator iter = added.iterator(); iter.hasNext();) {
0510: PlanElement planElement = (PlanElement) iter.next();
0511: String event = "" + getClassName(planElement) + " "
0512: + getURL(planElement.getUID(), PLAN_ELEMENT)
0513: + "<br/>of Task "
0514: + getURL(planElement.getTask().getUID(), TASK)
0515: + getClaimerString(planElement.getClaimable());
0516:
0517: addEvent(new EventInfo(ADDED, nextEventNum(),
0518: planElement.getUID().toString(), event, now,
0519: getAddedPEComment(planElement),
0520: encodeHTML(planElement.toString())));
0521: }
0522:
0523: Collection changed = planElementsSubscription
0524: .getChangedCollection();
0525: for (Iterator iter = changed.iterator(); iter.hasNext();) {
0526: PlanElement planElement = (PlanElement) iter.next();
0527: String event = getClassName(planElement) + " "
0528: + getURL(planElement.getUID(), PLAN_ELEMENT);
0529:
0530: addEvent(new EventInfo(CHANGED, nextEventNum(),
0531: planElement.getUID().toString(), event, now,
0532: getChangedPEComment(planElement),
0533: planElementsSubscription
0534: .getChangeReports(planElement),
0535: encodeHTML(planElement.toString())));
0536: }
0537:
0538: Collection removed = planElementsSubscription
0539: .getRemovedCollection();
0540: for (Iterator iter = removed.iterator(); iter.hasNext();) {
0541: PlanElement planElement = (PlanElement) iter.next();
0542: String event = getClassName(planElement) + " "
0543: + getURL(planElement.getUID(), PLAN_ELEMENT);
0544:
0545: addEvent(new EventInfo(REMOVED, nextEventNum(),
0546: planElement.getUID().toString(), event, now,
0547: // Alternative to empty comment would be getAddedPEComment(planElement)
0548: " ", planElementsSubscription
0549: .getChangeReports(planElement),
0550: encodeHTML(planElement.toString())));
0551: }
0552: }
0553: }
0554:
0555: /**
0556: * Add any Relay events
0557: */
0558: protected void checkRelays(long now) {
0559: if (relaysSubscription.hasChanged()) {
0560: Collection added = relaysSubscription.getAddedCollection();
0561: for (Iterator iter = added.iterator(); iter.hasNext();) {
0562: Relay relay = (Relay) iter.next();
0563: addEvent(new EventInfo(ADDED, nextEventNum(), relay
0564: .getUID().toString(), "Relay "
0565: + getURL(relay.getUID(), UNIQUE_OBJECT), now,
0566: getAddedRelayComment(relay), encodeHTML(relay
0567: .toString())));
0568: }
0569:
0570: Collection changed = relaysSubscription
0571: .getChangedCollection();
0572: for (Iterator iter = changed.iterator(); iter.hasNext();) {
0573: Relay relay = (Relay) iter.next();
0574: addEvent(new EventInfo(CHANGED, nextEventNum(), relay
0575: .getUID().toString(), "Relay "
0576: + getURL(relay.getUID(), UNIQUE_OBJECT), now,
0577: getChangedRelayComment(relay),
0578: relaysSubscription.getChangeReports(relay),
0579: encodeHTML(relay.toString())));
0580: }
0581:
0582: Collection removed = relaysSubscription
0583: .getRemovedCollection();
0584: for (Iterator iter = removed.iterator(); iter.hasNext();) {
0585: Relay relay = (Relay) iter.next();
0586: addEvent(new EventInfo(REMOVED, nextEventNum(), relay
0587: .getUID().toString(), "Relay "
0588: + getURL(relay.getUID(), UNIQUE_OBJECT), now,
0589: "Relay removed from blackboard.",
0590: relaysSubscription.getChangeReports(relay),
0591: encodeHTML(relay.toString())));
0592: }
0593: }
0594: }
0595:
0596: /**
0597: * Add any Asset events to the Set
0598: */
0599: protected void checkAssets(long now) {
0600: if (assetsSubscription.hasChanged()) {
0601: Collection added = assetsSubscription.getAddedCollection();
0602: for (Iterator iter = added.iterator(); iter.hasNext();) {
0603: Asset asset = (Asset) iter.next();
0604: addEvent(new EventInfo(ADDED, nextEventNum(), asset
0605: .getUID().toString(), "Asset "
0606: + getURL(asset.getUID(), ASSET), now,
0607: getAddedAssetComment(asset), encodeHTML(asset
0608: .toString())));
0609: }
0610:
0611: Collection changed = assetsSubscription
0612: .getChangedCollection();
0613: for (Iterator iter = changed.iterator(); iter.hasNext();) {
0614: Asset asset = (Asset) iter.next();
0615: addEvent(new EventInfo(CHANGED, nextEventNum(), asset
0616: .getUID().toString(), "Asset "
0617: + getURL(asset.getUID(), ASSET), now,
0618: getChangedAssetComment(asset), encodeHTML(asset
0619: .toString())));
0620: }
0621:
0622: Collection removed = assetsSubscription
0623: .getRemovedCollection();
0624: for (Iterator iter = removed.iterator(); iter.hasNext();) {
0625: Asset asset = (Asset) iter.next();
0626: addEvent(new EventInfo(REMOVED, nextEventNum(), asset
0627: .getUID().toString(), "Asset "
0628: + getURL(asset.getUID(), ASSET), now, ""));
0629: }
0630: }
0631: }
0632:
0633: /**
0634: * Get the ChangedAssetComment
0635: */
0636: protected String getAddedAssetComment(Asset asset) {
0637: return getChangedAssetComment(asset);
0638: }
0639:
0640: /**
0641: * Show the asset class, typeID, ItemID, if an Entity then any Roles, Relationships, RoleSchedule, and the HistoryServletFriendly content
0642: */
0643: protected String getChangedAssetComment(Asset asset) {
0644: StringBuffer buf = new StringBuffer();
0645:
0646: buf.append(getClassName(asset));
0647: buf.append(": <b>");
0648: buf.append(getTypeAndItemInfo(asset));
0649: buf.append("</b><br/>");
0650:
0651: if (asset instanceof Entity) {
0652: Entity entity = (Entity) asset;
0653:
0654: if (!entity.getEntityPG().getRoles().isEmpty()) {
0655: buf.append(" with Roles : ");
0656: }
0657:
0658: for (Iterator iter = entity.getEntityPG().getRoles()
0659: .iterator(); iter.hasNext();) {
0660: buf.append("<font size=small color=mediumblue>"
0661: + "<li>");
0662: Object obj = iter.next();
0663: buf.append(getClassName(obj) + " - " + obj);
0664: buf.append("</li>" + "</font>\n");
0665: } // end of loop over roles
0666: } // end of block for Entity
0667:
0668: if (asset instanceof HasRelationships) {
0669: HasRelationships entity = (HasRelationships) asset;
0670:
0671: Collection relationships = entity.getRelationshipSchedule()
0672: .getMatchingRelationships(new UnaryPredicate() {
0673: public boolean execute(Object o) {
0674: return true;
0675: }
0676: });
0677:
0678: if (!relationships.isEmpty()) {
0679: buf.append("<br/>with Relationships : ");
0680: }
0681:
0682: for (Iterator iter = relationships.iterator(); iter
0683: .hasNext();) {
0684: buf.append("<font size=small color=mediumblue>"
0685: + "<li>");
0686:
0687: Object obj = iter.next();
0688: // buf.append (getClassName(obj) + " - " + obj);
0689: if (obj instanceof Relationship) {
0690: Relationship relation = (Relationship) obj;
0691: buf.append(relation.getRoleA().toString());
0692: buf.append("=");
0693:
0694: if (relation.getA() instanceof Asset) {
0695: buf.append(getTypeAndItemInfo((Asset) relation
0696: .getA()));
0697: }
0698:
0699: buf
0700: .append("<br/>"
0701: + relation.getRoleB().toString());
0702: buf.append("=");
0703:
0704: if (relation.getB() instanceof Asset) {
0705: buf.append(getTypeAndItemInfo((Asset) relation
0706: .getB()));
0707: }
0708: }
0709:
0710: buf.append("</li>" + "</font>\n");
0711: } // end of loop over relationships
0712:
0713: } // end of HasRelationships
0714:
0715: buf.append(getRoleSchedule(asset));
0716:
0717: if (asset instanceof HistoryServletFriendly) {
0718: buf
0719: .append(((HistoryServletFriendly) asset)
0720: .toHTML(CHANGED));
0721: }
0722:
0723: return buf.toString();
0724: } // end of getChangedAssetComment
0725:
0726: /**
0727: * Print the RoleSchedule of the Asset if any, up to the
0728: * maxRoleScheduleElements
0729: */
0730: protected String getRoleSchedule(Asset asset) {
0731: StringBuffer buf = new StringBuffer();
0732:
0733: if (asset.getRoleSchedule().getRoleScheduleElements()
0734: .hasMoreElements()) {
0735: buf.append("<br/>which has Role Schedule: ");
0736: }
0737:
0738: int numShown = 0;
0739: for (Enumeration en = asset.getRoleSchedule()
0740: .getRoleScheduleElements(); en.hasMoreElements(); numShown++) {
0741: buf.append("<font size=small color=mediumblue>" + "<li>");
0742: Object elem = en.nextElement();
0743: if (numShown >= maxRoleScheduleElements) {
0744: buf.append("... (more than ");
0745: buf.append(maxRoleScheduleElements);
0746: buf.append(")");
0747: buf.append("</li>" + "</font>\n");
0748: break;
0749: } else {
0750: if (elem instanceof Allocation) {
0751: buf.append("Allocation to "
0752: + getTypeAndItemInfo(((Allocation) elem)
0753: .getAsset()));
0754: } else if (elem instanceof PlanElement) {
0755: buf.append(getAddedPEComment((PlanElement) elem));
0756: } else {
0757: buf.append(en.nextElement());
0758: }
0759: }
0760:
0761: buf.append("</li>" + "</font>\n");
0762: }
0763:
0764: return buf.toString();
0765: }
0766:
0767: /**
0768: * Add any added/changed/removed UniqueObjects
0769: */
0770: protected void checkUniqueObjects(long now) {
0771: if (uniqueObjectsSubscription.hasChanged()) {
0772: Collection added = uniqueObjectsSubscription
0773: .getAddedCollection();
0774: for (Iterator iter = added.iterator(); iter.hasNext();) {
0775: UniqueObject uniqueObject = (UniqueObject) iter.next();
0776: addEvent(new EventInfo(ADDED, nextEventNum(),
0777: uniqueObject.getUID().toString(),
0778: "UniqueObject "
0779: + getURL(uniqueObject.getUID(),
0780: UNIQUE_OBJECT), now,
0781: getAddedUniqueObjectComment(uniqueObject),
0782: encodeHTML(uniqueObject.toString())));
0783: }
0784:
0785: Collection changed = uniqueObjectsSubscription
0786: .getChangedCollection();
0787: for (Iterator iter = changed.iterator(); iter.hasNext();) {
0788: UniqueObject uniqueObject = (UniqueObject) iter.next();
0789: addEvent(new EventInfo(CHANGED, nextEventNum(),
0790: uniqueObject.getUID().toString(),
0791: "UniqueObject "
0792: + getURL(uniqueObject.getUID(),
0793: UNIQUE_OBJECT) + " changed.",
0794: now,
0795: getChangedUniqueObjectComment(uniqueObject),
0796: encodeHTML(uniqueObject.toString())));
0797: }
0798:
0799: Collection removed = uniqueObjectsSubscription
0800: .getRemovedCollection();
0801: for (Iterator iter = removed.iterator(); iter.hasNext();) {
0802: UniqueObject uniqueObject = (UniqueObject) iter.next();
0803: addEvent(new EventInfo(REMOVED, nextEventNum(),
0804: uniqueObject.getUID().toString(),
0805: "UniqueObject "
0806: + getURL(uniqueObject.getUID(),
0807: UNIQUE_OBJECT)
0808: + " was removed.", now,
0809: getRemovedUniqueObjectComment(uniqueObject)));
0810: }
0811: }
0812: }
0813:
0814: /**
0815: * For an Added unique object, print it's name, and any
0816: * HistoryServletFriendly content
0817: */
0818: protected String getAddedUniqueObjectComment(UniqueObject unique) {
0819: StringBuffer buf = new StringBuffer();
0820:
0821: buf.append("A ");
0822: buf.append(getClassName(unique));
0823: buf.append(" object.<br/>");
0824:
0825: if (unique instanceof HistoryServletFriendly) {
0826: buf.append(((HistoryServletFriendly) unique).toHTML(ADDED));
0827: }
0828:
0829: return buf.toString();
0830: }
0831:
0832: /**
0833: * Changed/Removed unique objects use the same comment as for Add
0834: */
0835: protected String getChangedUniqueObjectComment(UniqueObject unique) {
0836: return getAddedUniqueObjectComment(unique);
0837: }
0838:
0839: /**
0840: * Changed/Removed unique objects use the same comment as for Add
0841: */
0842: protected String getRemovedUniqueObjectComment(UniqueObject unique) {
0843: return getAddedUniqueObjectComment(unique);
0844: }
0845:
0846: /**
0847: * Get the non-Package name of the Class
0848: */
0849: protected String getClassName(Object obj) {
0850: String classname = obj.getClass().getName();
0851: int index = classname.lastIndexOf(".");
0852: classname = classname.substring(index + 1, classname.length());
0853: return classname;
0854: }
0855:
0856: /**
0857: * For a Source: show the target addresses, any community content or request, and the
0858: * relay's Content.
0859: * <p/>
0860: * For a Target, show any community info, the relay Source, and any HistoryServletFriendly content.
0861: */
0862: protected String getAddedRelayComment(Relay relay) {
0863: StringBuffer buf = new StringBuffer();
0864:
0865: if (relay instanceof Relay.Source) {
0866: buf.append("Sent new ");
0867: Relay.Source sourceRelay = (Relay.Source) relay;
0868: buf.append(showTargetAddresses(sourceRelay));
0869:
0870: // Special case community relays
0871: if (sourceRelay.getContent() instanceof CommunityDescriptor) {
0872: CommunityDescriptor response = (CommunityDescriptor) sourceRelay
0873: .getContent();
0874: Community community = null;
0875: if (response != null)
0876: community = (Community) response.getCommunity();
0877: if (buf.length() != 0)
0878: buf.append("<br/>");
0879: buf
0880: .append(getCommunityText(
0881: "Relay Source sending Description: ",
0882: community));
0883: } else if (sourceRelay.getContent() instanceof Request) {
0884: if (buf.length() != 0)
0885: buf.append("<br/>");
0886: buf.append("Community Request: ");
0887: Request request = (Request) sourceRelay.getContent();
0888: buf.append(request.getRequestTypeAsString(request
0889: .getRequestType()));
0890: buf.append("<br/>Source: ");
0891: buf.append(request.getSource());
0892: buf.append("<br/>Entity: ");
0893: buf.append(request.getEntity());
0894: } else {
0895: if (buf.length() != 0)
0896: buf.append("<br/>");
0897: buf.append("Content: ");
0898: buf.append(encodeHTML(sourceRelay.getContent()
0899: .toString()));
0900: }
0901: buf.append("<br/>");
0902: }
0903:
0904: if (relay instanceof Relay.Target) {
0905: Relay.Target targetRelay = (Relay.Target) relay;
0906: // buf.append("instanceof " + getClassName(targetRelay.getResponse()) + " ");
0907: if (targetRelay.getResponse() instanceof CommunityResponse) {
0908: CommunityResponse response = (CommunityResponse) targetRelay
0909: .getResponse();
0910: Community community = null;
0911: if (response != null)
0912: community = (Community) response.getContent();
0913:
0914: buf.append("Entity "
0915: + targetRelay.getSource()
0916: + " registers with Community "
0917: + (community != null ? community.getName()
0918: : "[null]"));
0919: } else {
0920: // Other than community relays, no good general way to print
0921: // the relay response
0922: String targetSource = "-NO SOURCE SET-";
0923:
0924: if (targetRelay.getSource() != null) {
0925: targetSource = targetRelay.getSource().toString();
0926: }
0927:
0928: buf.append("Received Relay Target from Source : "
0929: + encodeHTML(targetSource));
0930: }
0931: }
0932:
0933: if (relay instanceof HistoryServletFriendly) {
0934: buf.append(((HistoryServletFriendly) relay).toHTML(ADDED));
0935: }
0936:
0937: return buf.toString();
0938: }
0939:
0940: /**
0941: * Show the target(s) of a relay, including ABAs
0942: */
0943: protected String showTargetAddresses(Relay.Source sourceRelay) {
0944: StringBuffer buf = new StringBuffer();
0945:
0946: if (!sourceRelay.getTargets().isEmpty()) {
0947: buf.append("Relay Source with Targets: ");
0948: for (Iterator iter = sourceRelay.getTargets().iterator(); iter
0949: .hasNext();) {
0950: buf.append("<font size=small color=mediumblue>"
0951: + "<li>");
0952: MessageAddress address = (MessageAddress) iter.next();
0953: if (address instanceof AttributeBasedAddress) {
0954: AttributeBasedAddress aba = (AttributeBasedAddress) address;
0955: buf
0956: .append("AttributeBasedAddress: Broadcast to Community=");
0957: buf.append(aba.getCommunityName());
0958: buf.append(" attribute type=");
0959: buf.append(aba.getAttributeType());
0960: buf.append(" value=");
0961: buf.append(aba.getAttributeValue());
0962: } else {
0963: buf.append(address);
0964: }
0965: // buf.append (encodeHTML(address.toString()));
0966: buf.append("</li>" + "</font>\n");
0967: }
0968: }
0969:
0970: return buf.toString();
0971: }
0972:
0973: /**
0974: * For a Source, show any community specific information, the Content.
0975: * <p/>
0976: * For a Target, show any community specific information, and the response, and
0977: * any HistoryServletFriendly content.
0978: */
0979: protected String getChangedRelayComment(Relay relay) {
0980: StringBuffer buf = new StringBuffer();
0981: if (relay instanceof Relay.Source) {
0982: buf.append("Response Returned</br>");
0983:
0984: Relay.Source sourceRelay = (Relay.Source) relay;
0985: if (!(sourceRelay.getContent() instanceof Relay))
0986: buf.append("Relay Source's Query : "
0987: + encodeHTML(sourceRelay.getContent()
0988: .toString()) + "<br/>");
0989:
0990: if (sourceRelay.getContent() instanceof CommunityDescriptor) {
0991: CommunityDescriptor response = (CommunityDescriptor) sourceRelay
0992: .getContent();
0993: Community community = null;
0994: if (response != null)
0995: community = (Community) response.getCommunity();
0996: buf.append(getCommunityText(
0997: "Relay Source received Response: ", community));
0998: }
0999: }
1000:
1001: if (relay instanceof Relay.Target) {
1002: Relay.Target targetRelay = (Relay.Target) relay;
1003: // buf.append("instanceof " + getClassName(targetRelay.getResponse()) + " ");
1004: if (targetRelay.getResponse() instanceof CommunityResponse) {
1005: CommunityResponse response = (CommunityResponse) targetRelay
1006: .getResponse();
1007: Community community = null;
1008: if (response != null)
1009: community = (Community) response.getContent();
1010: buf.append(getCommunityText(
1011: "Relay Target sent Response: ", community));
1012: } else if (targetRelay.getResponse() instanceof CommunityDescriptor) {
1013: CommunityDescriptor response = (CommunityDescriptor) targetRelay
1014: .getResponse();
1015: Community community = null;
1016: if (response != null)
1017: community = (Community) response.getCommunity();
1018: buf.append(getCommunityText(
1019: "Relay Target sent Response: ", community));
1020: } else {
1021: buf.append("Relay Target sent Response: "
1022: + encodeHTML(targetRelay.getResponse()
1023: .toString()));
1024: }
1025: }
1026:
1027: if (relay instanceof HistoryServletFriendly) {
1028: buf
1029: .append(((HistoryServletFriendly) relay)
1030: .toHTML(CHANGED));
1031: }
1032:
1033: return buf.toString();
1034: }
1035:
1036: /**
1037: * Describe this Community by name, entities
1038: */
1039: protected String getCommunityText(String prefix, Community community) {
1040: if (community == null)
1041: return "[empty response]";
1042:
1043: StringBuffer buf = new StringBuffer();
1044: buf.append(prefix);
1045: buf.append(" Community <b>" + community.getName()
1046: + "</b> with members : ");
1047: for (Iterator iter = community.getEntities().iterator(); iter
1048: .hasNext();) {
1049: buf.append("<font size=small color=mediumblue>" + "<li>");
1050: buf.append(iter.next());
1051: buf.append("</li>" + "</font>\n");
1052: }
1053: return buf.toString();
1054: }
1055:
1056: /**
1057: * Show the results on the PE
1058: */
1059: protected String getChangedPEComment(PlanElement planElement) {
1060: StringBuffer buf = new StringBuffer();
1061:
1062: if (planElement.getEstimatedResult() != null) {
1063: buf.append(getAllocResult("Estimated", planElement
1064: .getEstimatedResult()));
1065: }
1066:
1067: if (planElement.getReportedResult() != null) {
1068: buf.append(getAllocResult("Reported", planElement
1069: .getReportedResult()));
1070: buf.append("<br/>");
1071: if (!planElement.getReportedResult().isSuccess()) {
1072: if (planElement.getTask().getPreferences()
1073: .hasMoreElements()) {
1074: buf
1075: .append("<br>Failed because reported Aspect Values did not satisfy Task Preferences:");
1076: buf
1077: .append(getTaskPreferences(planElement
1078: .getTask()));
1079: } else {
1080: buf
1081: .append("<br>Failed because one or more Child Tasks failed.");
1082: }
1083: }
1084: }
1085:
1086: return buf.toString();
1087: }
1088:
1089: /**
1090: * Get an HTML table of these Aspect Values (id'd by the prefix)
1091: */
1092: protected String getAspectValues(AspectValue[] values, String prefix) {
1093: StringBuffer buf = new StringBuffer();
1094: buf.append("<table>");
1095: buf.append("<tr><td>");
1096: buf.append(prefix + " Aspect Values:");
1097: buf.append("</td></tr>");
1098: for (int i = 0; i < values.length; i++) {
1099: buf.append("<tr><td>");
1100: buf.append("Type ");
1101: buf.append(AspectValue.aspectTypeToString(values[i]
1102: .getType()));
1103: buf.append(" - Value ");
1104: buf.append(values[i].getValue());
1105: buf.append("</td></tr>");
1106: }
1107: buf.append("</table>");
1108: return buf.toString();
1109: }
1110:
1111: /**
1112: * Get String representation of these Aspect Values
1113: */
1114: protected String getAspectValues2(AspectValue[] values) {
1115: StringBuffer buf = new StringBuffer();
1116: // for all (type, result) pairs
1117: for (int i = 0; i < values.length; i++) {
1118: AspectValue avi = values[i];
1119: buf.append(getAspectValue(avi));
1120: }
1121: return buf.toString();
1122: }
1123:
1124: /**
1125: * Get a String representation of this AspectValue
1126: */
1127: protected String getAspectValue(AspectValue avi) {
1128: StringBuffer buf = new StringBuffer();
1129: buf.append("<font size=small color=mediumblue>" + "<li>");
1130: // show type
1131: buf.append(AspectValue.aspectTypeToString(avi.getType()));
1132: buf.append("= ");
1133: // show value
1134: if (avi instanceof TimeAspectValue) {
1135: // print the date in our format
1136: long time = ((TimeAspectValue) avi).timeValue();
1137: buf.append(getTimeString(time));
1138: } else {
1139: buf.append(avi.getValue());
1140: }
1141: buf.append("</li>" + "</font>\n");
1142: return buf.toString();
1143: }
1144:
1145: /**
1146: * Formats long millis time to Date Format String.
1147: */
1148: protected String getTimeString(long time) {
1149: synchronized (myDateFormat) {
1150: myDateInstance.setTime(time);
1151: return myDateFormat.format(myDateInstance,
1152: new StringBuffer(20), myFieldPos).toString();
1153: }
1154: }
1155:
1156: /**
1157: * Get a String HTML table of the Task's preferences
1158: */
1159: protected String getTaskPreferences(Task task) {
1160: Enumeration prefs = task.getPreferences();
1161: StringBuffer buf = new StringBuffer();
1162: boolean hasPrefs = false;
1163: buf.append("<table>");
1164: buf.append("<tr><td>");
1165: buf.append("Preferences: ");
1166: buf.append("</td></tr>");
1167: while (prefs.hasMoreElements()) {
1168: Preference pref = (Preference) prefs.nextElement();
1169: hasPrefs = true;
1170: AspectValue prefav = pref.getScoringFunction().getBest()
1171: .getAspectValue();
1172: buf.append("<tr><td>");
1173: buf.append(getAspectValue(prefav));
1174: /*
1175: buf.append ("Type ");
1176: buf.append (AspectValue.aspectTypeToString(prefav.getType()));
1177: buf.append (" - Value");
1178: buf.append (prefav.getValue());
1179: */
1180: buf.append("</td></tr>");
1181: }
1182: buf.append("</table>");
1183:
1184: if (!hasPrefs)
1185: return "";
1186: else
1187: return buf.toString();
1188: }
1189:
1190: /**
1191: * Return HTML of the success or failure (with Aspect Values) of the given AllocationResult
1192: */
1193: private String getAllocResult(String type, AllocationResult ar) {
1194: if (ar == null)
1195: return "";
1196:
1197: StringBuffer buf = new StringBuffer();
1198: AspectValue[] values = ar.getAspectValueResults();
1199: boolean success = ar.isSuccess();
1200: String prefix = "<br/>"
1201: + type
1202: + " Allocation Result - "
1203: + (success ? "<font color=\"green\">Success</font>"
1204: : "<font color=\"red\">Failure</font>");
1205: buf.append(prefix + "<br/>");
1206: buf.append(getAspectValues2(values));
1207:
1208: return buf.toString();
1209: }
1210:
1211: /**
1212: * If it's an Allocation, show the Task Verb and the allocated Asset's Type
1213: * and ItemID, RoleSchedule.
1214: * For an Expansion, show the child tasks (up to the MAX).
1215: * For an AssetTransfer, show the moving Asset and destination.
1216: * For an Aggregation, show just the count of parent tasks.
1217: * For a Disposition, show whether it succeed or failed.
1218: * Also show any Annotation, Estimated Result, and Reported Result.
1219: */
1220: protected String getAddedPEComment(PlanElement planElement) {
1221: StringBuffer buf = new StringBuffer();
1222:
1223: if (planElement instanceof Allocation) {
1224: Allocation allocation = (Allocation) planElement;
1225:
1226: if (allocation.getAsset() != null) {
1227: buf.append("Satisfy ");
1228: buf.append(allocation.getTask().getVerb());
1229: buf.append(" Task with ");
1230: if (allocation.getAsset() instanceof Entity) {
1231: buf.append(" <i>remote Agent</i> ");
1232: }
1233: buf.append(getTypeAndItemInfo(allocation.getAsset()));
1234: buf.append("<br/>");
1235: buf.append(getRoleSchedule(allocation.getAsset()));
1236: }
1237: } else if (planElement instanceof Expansion) {
1238: Expansion expansion = (Expansion) planElement;
1239:
1240: buf.append("<table>");
1241: buf.append("<tr><td>");
1242: buf.append("Expansion's Child Tasks are:");
1243: buf.append("</td></tr>");
1244:
1245: int numShown = 0;
1246: for (Enumeration e = expansion.getWorkflow().getTasks(); e
1247: .hasMoreElements(); numShown++) {
1248: Task task = (Task) e.nextElement();
1249: buf.append("<tr><td>");
1250:
1251: if (numShown >= maxChildTasks) {
1252: buf.append("... (more than ");
1253: buf.append(maxChildTasks);
1254: buf.append(")");
1255: buf.append("</td></tr>");
1256: break;
1257: } else {
1258: buf.append("Task " + getURL(task.getUID(), TASK)
1259: + " " + task.getVerb());
1260:
1261: if (task.getDirectObject() != null) {
1262: buf.append(" with Direct Object ");// + getURL(task.getDirectObject().getUID(), DIRECT_OBJECT) + " ");
1263: buf.append(getTypeAndItemInfo(task
1264: .getDirectObject()));
1265: buf.append(".");
1266: }
1267:
1268: buf.append("</td></tr>");
1269: }
1270: }
1271:
1272: buf.append("</table>");
1273: } else if (planElement instanceof AssetTransfer) {
1274: AssetTransfer transfer = (AssetTransfer) planElement;
1275: buf.append("Transfer of <b>");
1276: buf.append(getTypeAndItemInfo(transfer.getAsset()));
1277: buf.append("</b> from <b>");
1278: buf.append(transfer.getAssignor());
1279: buf.append("</b> to <b>");
1280: buf.append(getTypeAndItemInfo(transfer.getAssignee()));
1281: buf.append("</b>");
1282: } else if (planElement instanceof Aggregation) {
1283: buf.append("Aggregation of ");
1284: buf.append(((Aggregation) planElement).getComposition()
1285: .getParentTasks().size());
1286: buf.append(" parent tasks.");
1287: } else if (planElement instanceof Disposition) {
1288: boolean suc = ((Disposition) planElement).isSuccess();
1289: buf.append("Disposition as ");
1290: if (suc)
1291: buf.append("<font color=green>Success</font>");
1292: else
1293: buf.append("<font color=red>Failure</font>");
1294: }
1295:
1296: if (planElement.getAnnotation() != null) {
1297: buf
1298: .append("<br>Annotation: "
1299: + planElement.getAnnotation());
1300: }
1301:
1302: if (planElement.getEstimatedResult() != null) {
1303: buf.append(getAllocResult("Estimated", planElement
1304: .getEstimatedResult()));
1305: }
1306: if (planElement.getReportedResult() != null) {
1307: buf.append(getAllocResult("Reported", planElement
1308: .getReportedResult()));
1309: }
1310:
1311: return buf.toString();
1312: }
1313:
1314: /**
1315: * Get the type & item ID of an Asset -- to ID it in the display
1316: */
1317: protected String getTypeAndItemInfo(Asset asset) {
1318: StringBuffer buf = new StringBuffer();
1319:
1320: if (asset.getTypeIdentificationPG() != null) {
1321: buf.append(asset.getTypeIdentificationPG()
1322: .getTypeIdentification()
1323: + " - ");
1324: }
1325: if (asset.getItemIdentificationPG() != null) {
1326: buf.append(asset.getItemIdentificationPG()
1327: .getItemIdentification());
1328: }
1329:
1330: return buf.toString();
1331: }
1332:
1333: // End of the various methods to fill the Events Set during plugin Execution
1334: /////////////////////////////////////////////////////////////////
1335: /////////////////////////////////////////////////////////////////
1336: // Now, the methods to handle the Servlet:
1337:
1338: /**
1339: * Inner-class that's registered as the servlet.
1340: */
1341: protected class HistoryWorker extends HttpServlet {
1342: public void doGet(HttpServletRequest request,
1343: HttpServletResponse response) throws IOException,
1344: ServletException {
1345: doPost(request, response);
1346: }
1347:
1348: public void doPost(HttpServletRequest request,
1349: HttpServletResponse response) throws IOException,
1350: ServletException {
1351: // returns a new instance with each query. Perhaps inefficient?
1352: new HistoryFormatter(request, response);
1353: }
1354: }
1355:
1356: /**
1357: * Add a new event to the list
1358: */
1359: protected void addEvent(EventInfo newEvent) {
1360: events.add(newEvent);
1361:
1362: // If we've reached the Max, trim the oldest event
1363: if (events.size() > maxEvents) {
1364: // if the events are sorted by uid, we want to sort them
1365: // by time first and then remove the oldest entry
1366: if (sortByUID) {
1367: // switch back to sorting by time, then uid
1368: sortByUID = false;
1369:
1370: SortedSet set = new TreeSet();
1371: set.addAll(events);
1372: setEvents(set);
1373: }
1374:
1375: if (logger.isInfoEnabled()) {
1376: logger.info("removing " + events.first()
1377: + " since more than max " + events.size()
1378: + " elements.");
1379: }
1380:
1381: // Drop the oldest event
1382: events.remove(events.first());
1383: didDropOldEntries = true;
1384: }
1385: }
1386:
1387: /**
1388: * Reset the stored event list -- used when we've resorted the list.
1389: */
1390: protected void setEvents(SortedSet events) {
1391: this .events = events;
1392: }
1393:
1394: /**
1395: * The inner class used by the servlet to format the request results.
1396: */
1397: protected class HistoryFormatter {
1398: public static final int FORMAT_DATA = 0;
1399: public static final int FORMAT_XML = 1;
1400: public static final int FORMAT_HTML = 2;
1401:
1402: private int format = FORMAT_HTML;
1403:
1404: public HistoryFormatter(HttpServletRequest request,
1405: HttpServletResponse response) throws IOException,
1406: ServletException {
1407: // Handle the parameters to the request
1408: processParams(request);
1409: // Then print the resulting page
1410: execute(response);
1411: }
1412:
1413: /**
1414: * Interpret the Format requested (only HTML supported),
1415: * sort the events as requested before we do printing.
1416: */
1417: protected void processParams(HttpServletRequest request) {
1418: // Set page format. Default is HTML
1419: String formatParam = request.getParameter("format");
1420: if ("data".equals(formatParam)) {
1421: format = FORMAT_DATA;
1422: } else if ("xml".equals(formatParam)) {
1423: format = FORMAT_XML;
1424: } else {
1425: format = FORMAT_HTML;
1426: }
1427:
1428: // Does the user want the detailed toString column?
1429: String showDetailsParam = request
1430: .getParameter("showDetails");
1431: // Note the pattern below -- do the .equals on the constant,
1432: // avoiding the need to check for null in the variable String
1433: showDetails = "true".equals(showDetailsParam);
1434:
1435: // Re-sort the events if necessary.
1436: String sortByUIDParam = request.getParameter("sortByUID");
1437: boolean oldValue = sortByUID;
1438: sortByUID = "true".equals(sortByUIDParam);
1439:
1440: if (sortByUID != oldValue) {
1441: if (sortByUID) {
1442: if (logger.isInfoEnabled()) {
1443: logger.info("sorting by uid, then time.");
1444: }
1445: sortByUIDThenTime();
1446: } else {
1447: SortedSet set = new TreeSet();
1448: set.addAll(events);
1449: //logger.warn ("events before " + events.size() + " vs now "+ set.size());
1450: setEvents(set);
1451: }
1452: }
1453: } // done processParams
1454:
1455: /**
1456: * Execute the request - print the web page
1457: */
1458: public void execute(HttpServletResponse response)
1459: throws IOException {
1460: if (format == FORMAT_HTML) {
1461: response.setContentType("text/html");
1462:
1463: PrintWriter out = response.getWriter();
1464: String sortLink = "<a href=\"/$" + encAgentName
1465: + getPath() + "?";
1466:
1467: if (sortByUID) {
1468: sortLink = sortLink + "sortByUID=false"
1469: + "\">Sort by time</a>";
1470: } else {
1471: sortLink = sortLink + "sortByUID=true"
1472: + "\">Sort by uid</a>";
1473: }
1474:
1475: String detailsLink = "<a href=\"/$" + encAgentName
1476: + getPath() + "?";
1477:
1478: if (showDetails) {
1479: detailsLink = detailsLink + "showDetails=false"
1480: + "\">hide object details</a>";
1481: } else {
1482: detailsLink = detailsLink + "showDetails=true"
1483: + "\">Show object details</a>";
1484: }
1485:
1486: // Now print the actual page
1487: out.print("<html><head><title>"
1488: + "History Servlet for " + agentId.getAddress()
1489: + "</title></head>" + "<body>"
1490: + "<p><center><h1>" + agentId.getAddress()
1491: + " Blackboard History</h1></center>" + "<p>" +
1492:
1493: "<center>" + sortLink + " "
1494: + detailsLink + "</center>" +
1495: // Here we print the actual table of events
1496: getHtmlForState() + "</body>" + "</html>\n");
1497: out.flush();
1498: }
1499: // FIXME: Add support for FORMAT_XML and FORMAT_DATA
1500: } // end of execute()
1501:
1502: /**
1503: * Print the actual table of events
1504: */
1505: protected String getHtmlForState() {
1506: StringBuffer buf = new StringBuffer();
1507:
1508: // First we print the Table header....
1509: if (didDropOldEntries) {
1510: buf
1511: .append("<center>Note: Too many changes have occurred; only newest ");
1512: buf.append(maxEvents);
1513: buf.append(" are shown.</center><br/>");
1514: }
1515:
1516: // tell user what the gray-white highlighting means
1517: buf
1518: .append("<center>Gray-white alternation indicates different ");
1519:
1520: if (sortByUID) {
1521: buf.append("blackboard objects.");
1522: } else {
1523: buf.append("plugin execute cycles.");
1524: }
1525: buf.append("<br/>Time shown is scenario time.</center>");
1526:
1527: buf.append("<table border=1 align=center>");
1528: buf.append("<tr>");
1529: buf.append("<th>");
1530: // Event Number
1531: // Note that this may never appear in order, since the table is
1532: // sorted by the comparator
1533: buf.append("#");
1534: buf.append("</th>");
1535: buf.append("<th>");
1536: buf.append("When"); // Scenario time
1537: buf.append("</th>");
1538: buf.append("<th>");
1539: buf.append("Type"); // Add/Change/Remove
1540: buf.append("</th>");
1541: buf.append("<th>");
1542: buf.append("Event"); // Basic summary
1543: buf.append("</th>");
1544: if (showChangeReport) {
1545: buf.append("<th>");
1546: buf.append("Change Report");
1547: buf.append("</th>");
1548: }
1549: buf.append("<th>");
1550: buf.append("Comment"); // Human readable descriptio in context
1551: buf.append("</th>");
1552:
1553: if (showDetails) {
1554: buf.append("<th>");
1555: buf.append("Object Details"); // Full toString
1556: buf.append("</th>");
1557: }
1558:
1559: buf.append("</tr>");
1560: long lastTime = -1;
1561: String lastUID = "";
1562: boolean colorRowGrey = false;
1563:
1564: // Now Loop over the events and print them
1565: synchronized (events) {
1566: for (Iterator iter = events.iterator(); iter.hasNext();) {
1567: EventInfo event = (EventInfo) iter.next();
1568:
1569: // Alternate colors in rows
1570: if (sortByUID) {
1571: // Alternate based on changing UID
1572: if (!event.uid.equals(lastUID)) {
1573: colorRowGrey = !colorRowGrey;
1574: lastUID = event.uid;
1575: }
1576: } else {
1577: // Alternate based on changing timestamp
1578: if (event.timeStamp != lastTime
1579: && event.timeStamp != -1) {
1580: colorRowGrey = !colorRowGrey;
1581: lastTime = event.timeStamp;
1582: }
1583: }
1584:
1585: // Add the next Event
1586: buf.append(event.toHTML(colorRowGrey,
1587: showChangeReport, showDetails));
1588: buf.append("\n");
1589: }
1590: }
1591: buf.append("</table>");
1592: return buf.toString();
1593: } // end of getHTMLForState helper method, called by execute()
1594: } // end of HistoryFormatter inner class
1595:
1596: /**
1597: * Sort the events collection by UID and then time. Then type, then meaning.
1598: * Updates the single events collection Set
1599: */
1600: protected void sortByUIDThenTime() {
1601: SortedSet events2 = new TreeSet(new Comparator() {
1602: public int compare(Object o1, Object o2) {
1603: EventInfo e1 = (EventInfo) o1;
1604: EventInfo e2 = (EventInfo) o2;
1605: int comp = e1.uid.compareTo(e2.uid);
1606:
1607: if (comp == 0) {
1608: if (e1.timeStamp < e2.timeStamp) {
1609: comp = -1;
1610: } else if (e1.timeStamp > e2.timeStamp) {
1611: comp = 1;
1612: }
1613: }
1614:
1615: if (comp == 0) {
1616: comp = e1.type - e2.type;
1617: }
1618:
1619: if (comp == 0) {
1620: comp = e1.meaning.compareTo(e2.meaning);
1621: }
1622:
1623: if (comp == 0) {
1624: if (logger.isInfoEnabled()) {
1625: logger.info(e1 + "\nequals\n" + e2);
1626: }
1627: }
1628:
1629: return comp;
1630: }
1631: });
1632:
1633: events2.addAll(events);
1634: setEvents(events2);
1635: }
1636:
1637: /**
1638: * Encodes a string that may contain HTML syntax-significant
1639: * characters.
1640: */
1641: protected String encodeHTML(String s) {
1642: if (s == null)
1643: return "";
1644:
1645: boolean noBreakSpaces = true;
1646: // Commenting out sawEquals for now -- a broken
1647: // attempt to break up long lines....
1648: // boolean sawEquals = false;
1649: StringBuffer buf = null; // In case we need to edit the string
1650: int ix = 0; // Beginning of uncopied part of s
1651: for (int i = 0, n = s.length(); i < n; i++) {
1652: String replacement = null;
1653: switch (s.charAt(i)) {
1654: case '"':
1655: replacement = """;
1656: break;
1657: //case '[': replacement = "["; break;
1658: case '<':
1659: if (i != 0 && s.charAt(i - 1) == ' ')
1660: replacement = "<br/> <";
1661: else
1662: replacement = "<";
1663: break;
1664: case '>':
1665: // put in a line break after '> ' as in the toString of many objects
1666: if (i + 1 < n && s.charAt(i + 1) == ' ')
1667: replacement = "><br/>";
1668: else
1669: replacement = ">";
1670: break;
1671: case ',':
1672: // put in a line break after '>,' as in the toString of ServiceContractRelays
1673: if (i > 0 && i + 1 < n && s.charAt(i - 1) == '>'
1674: && s.charAt(i + 1) == ' ')
1675: replacement = ",<br/>";
1676: else
1677: replacement = ",";
1678: break;
1679: case '&':
1680: replacement = "&";
1681: break;
1682: // case '=': sawEquals = true; break;
1683: case ' ':
1684: // if (sawEquals && (i+1 < n) && s.charAt(i+1) != '>') {
1685: // replacement = "<br/>";
1686: // }
1687: //else if (noBreakSpaces) {
1688: if (noBreakSpaces) {
1689: replacement = " ";
1690: }
1691: //sawEquals=false;
1692: break;
1693: }
1694: if (replacement != null) {
1695: if (buf == null)
1696: buf = new StringBuffer();
1697: buf.append(s.substring(ix, i));
1698: buf.append(replacement);
1699: ix = i + 1;
1700: }
1701: }
1702: if (buf != null) {
1703: buf.append(s.substring(ix));
1704: return buf.toString();
1705: } else {
1706: return s;
1707: }
1708: }
1709:
1710: /**
1711: * This is the class that stores the information about an event
1712: * Basic information about a particular event in a Transaction
1713: */
1714: private static class EventInfo implements Comparable {
1715: public int num; // Event number
1716: public String uid; // The UID of the object
1717: public String event; // Summary descripton of the event
1718: public long timeStamp; // The scenario time of the event
1719:
1720: // Goes in the Comment column - basic
1721: // human readable description of the Event
1722: public String meaning = " ";
1723: public Set changeReports = null; // Any ChangeReports
1724: public String toStringResult = " "; // Full toString of the Event
1725: public int type; // Add, Change, or Remove
1726:
1727: public EventInfo(int type, int num, String uid, String event,
1728: long timeStamp, String meaning) {
1729: this .type = type;
1730: this .num = num;
1731: this .uid = uid;
1732: this .event = event;
1733: this .timeStamp = timeStamp;
1734: this .meaning = meaning;
1735: }
1736:
1737: public EventInfo(int type, int num, String uid, String event,
1738: long timeStamp, String meaning, Set changeReports) {
1739: this (type, num, uid, event, timeStamp, meaning);
1740: this .changeReports = changeReports;
1741: }
1742:
1743: public EventInfo(int type, int num, String uid, String event,
1744: long timeStamp, String meaning, String toStringResult) {
1745: this (type, num, uid, event, timeStamp, meaning);
1746: this .toStringResult = toStringResult;
1747: }
1748:
1749: public EventInfo(int type, int num, String uid, String event,
1750: long timeStamp, String meaning, Set changeReports,
1751: String toStringResult) {
1752: this (type, num, uid, event, timeStamp, meaning,
1753: changeReports);
1754: this .toStringResult = toStringResult;
1755: }
1756:
1757: /**
1758: * compare first by timestamp, and withing that, by UID
1759: * Then by type (A/C/R), and finally by the String meaning.
1760: */
1761: public int compareTo(Object other) {
1762: EventInfo otherEvent = (EventInfo) other;
1763:
1764: if (timeStamp < otherEvent.timeStamp)
1765: return -1;
1766: if (timeStamp > otherEvent.timeStamp)
1767: return 1;
1768:
1769: int uidComp = uid.compareTo(otherEvent.uid);
1770: if (uidComp != 0)
1771: return uidComp;
1772:
1773: int val = type - otherEvent.type;
1774:
1775: if (val != 0)
1776: return val;
1777:
1778: val = meaning.compareTo(otherEvent.meaning);
1779:
1780: return val;
1781: }
1782:
1783: public String toString() {
1784: StringBuffer buf = new StringBuffer();
1785: buf.append("<EventInfo " + num + " at ");
1786: buf.append(format.format(new Date(timeStamp)));
1787: String typeS = " Added ";
1788: if (type == CHANGED)
1789: typeS = " Changed ";
1790: else if (type == REMOVED)
1791: typeS = " Removed ";
1792: buf.append(typeS);
1793: buf.append(meaning);
1794: buf.append(">");
1795: return buf.toString();
1796: }
1797:
1798: /**
1799: * Produce HTML for the Servlet display. "odd" boolean used to alternate colors for rows
1800: */
1801: public String toHTML(boolean odd, boolean showChangeReport,
1802: boolean showDetails) {
1803: String color = odd ? "#FFFFFF" : "#c0c0c0";
1804: StringBuffer buf = new StringBuffer();
1805: buf.append("<tr BGCOLOR=" + color + ">");
1806: buf.append("<td>" + num + "</td>");
1807: buf.append("<td>" + format.format(new Date(timeStamp))
1808: + "</td>");
1809: String typeString = "<font color=green>Added</font>";
1810: if (type == CHANGED)
1811: typeString = "<font color=blue>Changed</font>";
1812: else if (type == REMOVED)
1813: typeString = "<font color=red>Removed</font>";
1814:
1815: buf.append("<td>" + typeString + "</td>");
1816: buf.append("<td>" + event + "</td>");
1817:
1818: if (showChangeReport) {
1819: buf.append("<td>");
1820:
1821: if (changeReports != null) {
1822: buf.append(htmlForChangeReport());
1823: }
1824:
1825: buf.append("</td>");
1826: }
1827:
1828: if (meaning != null) {
1829: buf.append("<td>" + meaning + "</td>");
1830: }
1831:
1832: if (showDetails) {
1833: buf.append("<td>" + toStringResult + "</td>");
1834: }
1835:
1836: buf.append("</tr>");
1837:
1838: return buf.toString();
1839: }
1840:
1841: /**
1842: * Produce pretty HTML to show the ChangeReports on a Transaction
1843: *
1844: * @return HTML Table of ChangeReport data
1845: */
1846: public String htmlForChangeReport() {
1847: StringBuffer buf = new StringBuffer();
1848: buf.append("<table>");
1849:
1850: for (Iterator iter = changeReports.iterator(); iter
1851: .hasNext();) {
1852: Object obj = iter.next();
1853: buf.append("<tr><td>" + obj + "</td></tr>");
1854: }
1855:
1856: buf.append("</table>");
1857:
1858: return buf.toString();
1859: }
1860: } // end of EventInfo class
1861: } // end of HistoryServlet
|