001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.planning.servlet;
028:
029: import java.io.FileNotFoundException;
030: import java.io.IOException;
031: import java.io.InputStream;
032: import java.io.ObjectInputStream;
033: import java.io.OutputStream;
034: import java.io.PrintWriter;
035: import java.io.StreamCorruptedException;
036: import java.net.URL;
037: import java.net.URLConnection;
038: import java.util.Collection;
039: import java.util.Collections;
040: import java.util.Date;
041: import java.util.HashSet;
042: import java.util.Iterator;
043: import java.util.List;
044: import java.util.Set;
045: import java.util.StringTokenizer;
046:
047: import javax.servlet.ServletException;
048: import javax.servlet.http.HttpServletRequest;
049: import javax.servlet.http.HttpServletResponse;
050:
051: import org.cougaar.core.servlet.SimpleServletSupport;
052: import org.cougaar.planning.ldm.asset.Asset;
053: import org.cougaar.planning.ldm.plan.HasRelationships;
054: import org.cougaar.planning.ldm.plan.Relationship;
055: import org.cougaar.planning.ldm.plan.RelationshipSchedule;
056: import org.cougaar.planning.ldm.plan.Role;
057: import org.cougaar.planning.servlet.data.hierarchy.HierarchyData;
058: import org.cougaar.planning.servlet.data.hierarchy.Organization;
059: import org.cougaar.planning.servlet.data.xml.XMLable;
060: import org.cougaar.util.MutableTimeSpan;
061: import org.cougaar.util.UnaryPredicate;
062:
063: /**
064: * <pre>
065: * Servlet worker that gathers Organizational hierarchy information
066: * from a Cougaar society.
067: *
068: * Takes three parameters :
069: * - recurse, which controls whether to recurse down the hierarchy
070: * - format, which specifies whether the response is a data stream, xml, or html.
071: * - allRelationships, which returns all the relationships for an agent
072: * html is in the familiar CLUSTERS_R format.
073: *
074: * An example URL is :
075: *
076: * http://localhost:8800/$TRANSCOM/hierarchy?recurse=true&format=xml
077: *
078: * This says to recurse from the TRANSCOM agent and return an XML document.
079: *
080: * Example output from this query :
081: *
082: * <?xml version="1.0" ?>
083: * <Hierarchy RootID="TRANSCOM">
084: * <Org>
085: * <OrgID>TRANSCOM</OrgID>
086: * <Name>TRANSCOM</Name>
087: * <Rel OrgID="GlobalAir" Rel="0"/>
088: * <Rel OrgID="GlobalSea" Rel="0"/>
089: * </Org>
090: * <Org>
091: * <OrgID>GlobalAir</OrgID>
092: * <Name>GlobalAir</Name>
093: * <Rel OrgID="PlanePacker" Rel="0"/>
094: * </Org>
095: * ....
096: * </Hierarchy>
097: *
098: * NOTE : If any agent hangs, the whole request will hang...
099: * </pre>
100: */
101: public class HierarchyWorker extends ServletWorker {
102: public static boolean VERBOSE = false;
103: static {
104: VERBOSE = Boolean
105: .getBoolean("org.cougaar.mlm.ui.psp.transit.HierarchyServlet.verbose");
106: }
107: // These constants are from GLM -- they permit us to put this servlet into core
108: // At some future time we may want to have them be parameters to the servlet
109: public static final Role SUBORD_ROLE = Role.getRole("Subordinate");
110: public static final Role ADMIN_SUBORD_ROLE = Role
111: .getRole("AdministrativeSubordinate");
112: public static final String SUPERIOR_SUFFIX = "Superior";
113: public static final String PROVIDER_SUFFIX = "Provider";
114:
115: public static final String CONVERSE_OF_PREFIX = "ConverseOf";
116:
117: protected boolean recurse;
118: protected boolean allRelationships;
119: protected Set visitedOrgs = new HashSet();
120: protected boolean testing = false;
121: protected static final int AGENTS_IN_ROW = 10;
122:
123: /**
124: * This is the path for my Servlet, relative to the
125: * Agent's URLEncoded name.
126: * <p>
127: * For example, on Agent "X" the URI request path
128: * will be "/$X/hello".
129: */
130: private final String myPath = "/Hierarchy";
131:
132: /**
133: * Pretty to-String for debugging.
134: */
135: public String toString() {
136: return getClass().getName() + "(" + myPath + ")";
137: }
138:
139: /**
140: * Main method. <p>
141: * Most of the work is done in getHierarchy.
142: * This method mainly checks that the parameters are the right
143: * number and sets the format and recurse fields.
144: * <p>
145: * @see #getHierarchyData(HttpServletRequest,SimpleServletSupport,HasRelationships,boolean,boolean,Set)
146: */
147: public void execute(HttpServletRequest request,
148: HttpServletResponse response, SimpleServletSupport support)
149: throws IOException, ServletException {
150: super .execute(request, response, support);
151:
152: if (VERBOSE) {
153: System.out.println("BEGIN hierarchy at "
154: + support.getAgentIdentifier());
155: }
156:
157: if (testing) {
158: if (request.getRequestURI().indexOf("PlanePacker") != -1) {
159: try {
160: Thread.sleep(30000);
161: } catch (Exception e) {
162: }
163: }
164: }
165:
166: // generate our response.
167: getHierarchy(response.getOutputStream(), request, support,
168: format, recurse, allRelationships, visitedOrgs);
169:
170: if (VERBOSE) {
171: System.out.println("FINISHED hierarchy at "
172: + support.getAgentIdentifier());
173: }
174: }
175:
176: /**
177: * <pre>
178: * sets both recurse and format
179: *
180: * recurse is either true or false
181: * format is either data, xml, or html
182: *
183: * see class description for what these values mean
184: * </pre>
185: */
186: protected void getSettings(String name, String value) {
187: super .getSettings(name, value);
188: if (eq("recurse", name)) {
189: recurse = ((value == null) || (eq("true", value)));
190: } else if (eq("allRelationships", name)) {
191: if (eq("true", value))
192: allRelationships = true;
193: } else if (eq("visitedOrgs", name)) {
194: StringTokenizer tokenizer = new StringTokenizer(value, ",");
195: while (tokenizer.hasMoreTokens())
196: visitedOrgs.add(tokenizer.nextToken());
197:
198: if (VERBOSE)
199: System.out.println("getParams - Visited Org List is "
200: + visitedOrgs);
201: } else {
202: if (VERBOSE)
203: System.out.println("NOTE : Ignoring parameter named "
204: + name);
205: }
206: }
207:
208: protected String getPrefix() {
209: return "Hierarchy at ";
210: }
211:
212: /**
213: * Fetch HierarchyData and write to output. <p>
214: *
215: * Main work is done by getHierarchyData, which returns a HierarchyData object.
216: *
217: * Output format is either data, xml, or html
218: * @see #getHierarchyData
219: */
220: protected void getHierarchy(OutputStream out,
221: HttpServletRequest request, SimpleServletSupport support,
222: int format, boolean recurse, boolean allRelationships,
223: Set visitedOrgs) {
224: // get self org
225: HasRelationships selfOrg = getSelfOrg(support);
226: if (selfOrg == null) {
227: throw new RuntimeException("No self org?");
228: }
229:
230: // get hierarchy data
231: try {
232: HierarchyData hd = getHierarchyData(request, support,
233: selfOrg, recurse, allRelationships, visitedOrgs);
234:
235: writeResponse(hd, out, request, support, format,
236: allRelationships);
237: } catch (Exception e) {
238: System.err.println("Got exception " + e
239: + " getting hierarchy data for "
240: + support.getAgentIdentifier());
241: e.printStackTrace();
242: }
243: }
244:
245: /**
246: * get the self organization.
247: */
248: protected HasRelationships getSelfOrg(SimpleServletSupport support) {
249: // get self org
250: Collection col = support.queryBlackboard(selfOrgP);
251: if ((col != null) && (col.size() == 1)) {
252: Iterator iter = col.iterator();
253: HasRelationships org = (HasRelationships) iter.next();
254: return org;
255: } else {
256: return null;
257: }
258: }
259:
260: /** test to find which org is yourself */
261: private static final UnaryPredicate selfOrgP = new UnaryPredicate() {
262: public boolean execute(Object o) {
263: return ((o instanceof HasRelationships) && ((HasRelationships) o)
264: .isLocal());
265: }
266: };
267:
268: /** populates a HierarchyData object given the self Org */
269: protected HierarchyData getHierarchyData(
270: HttpServletRequest request, SimpleServletSupport support,
271: HasRelationships selfOrg, boolean recurse,
272: boolean allRelationships, Set visitedOrgs) {
273: // create a "self" org
274: String selfOrgName = ((Asset) selfOrg).getClusterPG()
275: .getMessageAddress().toString();
276: visitedOrgs.add(selfOrgName);
277: // build list of orgs
278: HierarchyData hd = new HierarchyData();
279: Organization toOrg = new Organization();
280: toOrg.setUID(selfOrgName);
281: toOrg.setPrettyName(selfOrgName); // where is the pretty name kept?
282: hd.setRootOrgID(selfOrgName); // set rootId as self
283:
284: MutableTimeSpan mts = new MutableTimeSpan();
285: RelationshipSchedule schedule = selfOrg
286: .getRelationshipSchedule();
287:
288: Collection subordinates = new HashSet();
289:
290: if (!allRelationships) {
291: Collection subordinates1 = schedule
292: .getMatchingRelationships(SUBORD_ROLE, mts
293: .getStartTime(), mts.getEndTime());
294: subordinates.addAll(subordinates1);
295:
296: Collection subordinates2 = schedule
297: .getMatchingRelationships(ADMIN_SUBORD_ROLE, mts
298: .getStartTime(), mts.getEndTime());
299:
300: subordinates.addAll(subordinates2);
301: } else {
302: subordinates.addAll(schedule.getMatchingRelationships(mts));
303: }
304:
305: // add self org to hierarchy
306: hd.addOrgData(toOrg);
307:
308: if (VERBOSE && false)
309: System.out.println("getHierarchyData - " + selfOrgName
310: + " has these subs " + subordinates);
311:
312: if (subordinates.isEmpty()) {
313: // no subordinates
314: return hd;
315: }
316:
317: Set recurseSubOrgSet = null;
318: if (recurse) {
319: recurseSubOrgSet = new HashSet();
320: }
321:
322: // Safe to iterate over subordinates because getSubordinates() returns
323: // a new Collection.
324: for (Iterator schedIter = subordinates.iterator(); schedIter
325: .hasNext();) {
326: Relationship relationship = (Relationship) schedIter.next();
327: Asset subOrg = (Asset) schedule.getOther(relationship);
328: String role = schedule.getOtherRole(relationship).getName();
329:
330: String subOrgName = subOrg.getClusterPG()
331: .getMessageAddress().toString();
332:
333: // client wants a numerical identifier for the role
334: int roleId;
335: if (!allRelationships) {
336: if (role.equalsIgnoreCase("AdministrativeSubordinate")) {
337: // admin_subord
338: roleId = org.cougaar.planning.servlet.data.hierarchy.Organization.ADMIN_SUBORDINATE;
339: } else {
340: // some other subord type
341: // ** add more String.equals cases here **
342: roleId = org.cougaar.planning.servlet.data.hierarchy.Organization.SUBORDINATE;
343: }
344: toOrg.addRelation(subOrgName, roleId);
345: } else if (!role.endsWith("Self")) {
346: toOrg.addRelation(subOrgName, role);
347: }
348: if (recurse && (!(selfOrgName.equals(subOrgName))) && // don't recurse on yourself
349: validRole(role) && // only on customers, subordinates, etc.
350: !visitedOrgs.contains(subOrgName)) { // only ones we haven't visited before
351: if (VERBOSE) // so we don't have circular paths
352: System.out
353: .println("self "
354: + selfOrgName
355: + " sub "
356: + subOrgName
357: + " role "
358: + role
359: + (validRole(role) ? " VALID "
360: : " invalid"));
361: recurseSubOrgSet.add(subOrgName);
362: }
363: }
364:
365: // if we are recursing, recurse on subordinates
366: if (recurse) {
367: visitedOrgs.addAll(recurseSubOrgSet);
368: recurseOnSubords(recurseSubOrgSet, request, support,
369: allRelationships, visitedOrgs, hd);
370: }
371:
372: // return list
373: return hd;
374: }
375:
376: protected void recurseOnSubords(Set recurseSubOrgSet,
377: HttpServletRequest request, SimpleServletSupport support,
378: boolean allRelationships, Set visitedOrgs, HierarchyData hd) {
379: for (Iterator iter = recurseSubOrgSet.iterator(); iter
380: .hasNext();) {
381: String subOrgName = (String) iter.next();
382: // fetch the sub's data
383:
384: HierarchyData subHD = null;
385: int tries = 5;
386: long[] timeToWait = new long[] { 32000, 16000, 8000, 4000,
387: 2000 };
388: while (subHD == null && tries-- > 0) {
389: subHD = fetchForSubordinate(request, support,
390: subOrgName, allRelationships, visitedOrgs);
391: if (subHD == null) {
392: if (VERBOSE && tries > 1) {
393: System.out.println("At " + new Date() + " In "
394: + support.getAgentIdentifier()
395: + ", fetch hierarchy from "
396: + subOrgName
397: + " returned null, retry in "
398: + timeToWait[tries] + " millis.");
399: }
400:
401: synchronized (this ) {
402: try {
403: wait(timeToWait[tries]);
404: } catch (Exception e) {
405: System.out.println("got exception " + e);
406: }
407: }
408: }
409: }
410:
411: if (VERBOSE && (subHD == null)) {
412: System.out.println("In " + support.getAgentIdentifier()
413: + ", fetch hierarchy from " + subOrgName
414: + " returned null.");
415: }
416:
417: // take Orgs from sub's hierarchy data
418: int nSubHD = ((subHD != null) ? subHD.numOrgs() : 0);
419: for (int i = 0; i < nSubHD; i++) {
420: hd.addOrgData(subHD.getOrgDataAt(i));
421: }
422: }
423: }
424:
425: /**
426: * <pre>
427: * This prevents endless recursion, making it so we only follow
428: * relationship links in one direction
429: *
430: * Specifically, we don't follow any agents that are our superiors,
431: * providers, converse of a role, or our self.
432: * </pre>
433: */
434: protected boolean validRole(String role) {
435: return (!role.endsWith(SUPERIOR_SUFFIX)
436: && !role.endsWith(PROVIDER_SUFFIX)
437: && !role.startsWith(CONVERSE_OF_PREFIX) && !role
438: .equals("Self"));
439: }
440:
441: /**
442: * recursion happens here -- for each subOrgName subordinate, a new
443: * servlet in the target agent will be executed
444: */
445: protected HierarchyData fetchForSubordinate(
446: HttpServletRequest request, SimpleServletSupport support,
447: String subOrgName, boolean allRelationships, Set visitedOrgs) {
448: HierarchyData hd = null;
449: InputStream is = null;
450: ObjectInputStream ois = null;
451:
452: try {
453: // build URL for remote connection
454: StringBuffer buf = new StringBuffer();
455: buf.append("http://");
456: buf.append(request.getServerName());
457: buf.append(":");
458: buf.append(request.getServerPort());
459: buf.append("/$");
460: buf.append(subOrgName);
461: buf.append(support.getPath());
462: buf.append("?data=true&recurse=true&visitedOrgs=");
463: for (Iterator iter = visitedOrgs.iterator(); iter.hasNext();) {
464: buf.append(iter.next());
465: if (iter.hasNext())
466: buf.append(",");
467: }
468: if (allRelationships)
469: buf.append("&allRelationships=true");
470:
471: String url = buf.toString();
472:
473: if (VERBOSE) {
474: System.out.println("At " + new Date() + " - in "
475: + support.getAgentIdentifier()
476: + ", fetch hierarchy from " + subOrgName
477: + ", URL:\n" + url);
478: }
479:
480: // open connection
481: URL myURL = new URL(url);
482: URLConnection myConnection = myURL.openConnection();
483: is = myConnection.getInputStream();
484: ois = new ObjectInputStream(is);
485:
486: // read single HierarchyData Object from subordinate
487: hd = (HierarchyData) ois.readObject();
488:
489: if (VERBOSE) {
490: System.out
491: .println("In "
492: + support.getAgentIdentifier()
493: + ", got "
494: + ((hd != null) ? ("hierarchy["
495: + hd.numOrgs() + "]")
496: : ("null")) + " from "
497: + subOrgName);
498: }
499:
500: } catch (StreamCorruptedException sce) {
501: if (VERBOSE) {
502: System.err.println("In " + support.getAgentIdentifier()
503: + ", got exception : ");
504: sce.printStackTrace();
505: }
506: } catch (FileNotFoundException fnf) {
507: if (!allRelationships || true) {
508: System.err.println("In " + support.getAgentIdentifier()
509: + ", got exception : ");
510: fnf.printStackTrace();
511: }
512: } catch (Exception e) {
513: System.err.println("In " + support.getAgentIdentifier()
514: + ", got exception : ");
515: e.printStackTrace();
516: } finally {
517: try {
518: if (ois != null)
519: ois.close();
520: if (is != null)
521: is.close();
522: } catch (Exception e) {
523: }
524: }
525:
526: return hd;
527: }
528:
529: /**
530: * Writes html with the list of found agents across the
531: * top, with links to tables below for each agent. The tables
532: * show each agents relationships to other agents. There are links
533: * in those tables too, to the other referenced agents.
534: */
535: protected void writeResponse(XMLable result, OutputStream out,
536: HttpServletRequest request, SimpleServletSupport support,
537: int format, boolean allRelationships) {
538: if ((format == FORMAT_HTML) && allRelationships) {
539: HierarchyData data = (HierarchyData) result;
540: if (VERBOSE)
541: System.out
542: .println("HierarchyWorker.writeResponse - got data for "
543: + data.numOrgs() + " orgs");
544: PrintWriter writer = new PrintWriter(out);
545: writer.println("<HTML><HEAD>\n<TITLE>" + getPrefix()
546: + support.getAgentIdentifier() + "</TITLE>\n"
547: + "</HEAD><BODY>\n" + "<H2><CENTER>" + getPrefix()
548: + support.getAgentIdentifier()
549: + "</CENTER></H2><p><pre>\n");
550: writer.flush();
551:
552: writer
553: .println("<a name=\"top\"/>"
554: + "<TABLE align=center border=0 cellPadding=1 cellSpacing=1>");
555: boolean rowEnded = false;
556: int k = 0;
557: data.sortOrgs();
558: for (; k < data.numOrgs(); k++) {
559: Organization org = data.getOrgDataAt(k);
560: if ((k % AGENTS_IN_ROW) == 0) {
561: writer.println("<TR>");
562: rowEnded = false;
563: }
564: writer.println("<TD><a href=\"#" + org.getPrettyName()
565: + "\"/>" + org.getPrettyName() + "</a></TD>");
566: if ((k % AGENTS_IN_ROW) == AGENTS_IN_ROW - 1) {
567: writer.println("</TR>");
568: rowEnded = true;
569: }
570: }
571: for (int i = (k % AGENTS_IN_ROW); i < AGENTS_IN_ROW; i++)
572: writer.print("<TD></TD>");
573: if (!rowEnded)
574: writer.println("</TR>");
575: writer.println("</TABLE><P/><P/><br/>");
576:
577: for (int i = 0; i < data.numOrgs(); i++) {
578: Organization org = data.getOrgDataAt(i);
579: writer
580: .println("<P>Agent relationships for <B><a name=\""
581: + org.getPrettyName()
582: + "\" />"
583: + org.getPrettyName()
584: + "</B> <a href=\"#top\">[top]</a></P>");
585: writer
586: .println("<TABLE align=center border=1 cellPadding=1 cellSpacing=1");
587: writer
588: .println("width=75% bordercolordark=#660000 bordercolorlight=#cc9966>");
589: writer.println("<TR>");
590: // writer.println("<TD width=\"25%\"> <FONT color=mediumblue ><B>Agent</FONT></B> </TD>");
591: writer
592: .println("<TD width=\"25%\"> <FONT color=mediumblue ><B>Assigned Agent</FONT></B></TD>");
593: writer
594: .println("<TD width=\"75%\"> <FONT color=mediumblue ><B>Role </FONT></B></TD>");
595: writer.println("</TR>");
596:
597: List relations = org.getRelations();
598: Collections.sort(relations);
599: for (Iterator iter = relations.iterator(); iter
600: .hasNext();) {
601: Organization.OrgRelation relation = (Organization.OrgRelation) iter
602: .next();
603: String relatedOrg = relation.getRelatedOrg();
604: writer.println("<TR><TD><a href=\"#" + relatedOrg
605: + "\">" + relatedOrg + "</a></TD><TD>"
606: + relation.getName() + "</TD></TR>");
607: }
608: writer.println("</TABLE><P>");
609: }
610: writer.println("\n</BODY></HTML>\n");
611: writer.flush();
612: } else {
613: try {
614: writeResponse(result, out, request, support, format);
615: } catch (Exception e) {
616: System.err.println("Got exception " + e
617: + " writing out response for "
618: + support.getAgentIdentifier());
619: e.printStackTrace();
620: }
621: }
622: }
623: }
|