001: /*
002: * <copyright>
003: *
004: * Copyright 2002-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.core.util;
028:
029: import java.io.IOException;
030: import java.io.PrintWriter;
031: import java.util.Collection;
032: import java.util.Collections;
033: import java.util.Iterator;
034: import java.util.List;
035: import java.util.Map;
036:
037: import javax.servlet.http.HttpServlet;
038: import javax.servlet.http.HttpServletRequest;
039: import javax.servlet.http.HttpServletResponse;
040:
041: import org.cougaar.core.component.ComponentDescription;
042: import org.cougaar.core.component.ComponentView;
043: import org.cougaar.core.component.ContainerView;
044: import org.cougaar.core.component.ServiceView;
045: import org.cougaar.core.component.ViewService;
046: import org.cougaar.core.mts.MessageAddress;
047: import org.cougaar.core.service.LoggingService;
048: import org.cougaar.core.servlet.ComponentServlet;
049:
050: /**
051: * This component loads the "/components" servlet, which displays
052: * information from the component model's {@link ViewService}.
053: * <p>
054: * Load with:<pre>
055: * <component class="org.cougaar.core.util.ComponentViewServlet">
056: * <argument>/components</argument>
057: * </component>
058: * </pre>
059: * <p>
060: * The supported URL-parameter arguments can easily be seen
061: * in the zero-parameter <code>printUsage</code> method's page.
062: * <p>
063: * This servlet generates XML. A future client-side GUI could use
064: * this XML to draw pretty graphical displays of the component model.
065: * Another idea is to generate output in a standard graph
066: * node/edge format (e.g. GXL for client-side JGraph viewing, or
067: * DOT for <a href="http://www.graphviz.org/webdot/">webdot</a>
068: * PNGs).
069: * <p>
070: * <pre>
071: * In the generated XML, there are <b><component-view></b>s and
072: * <b><container-view></b>s, where the container view includes all
073: * the fields of the component view, which are:
074: * <b>id=..</b> unique identifier, also useful for sorting
075: * <b><load-time></b> millis when loaded
076: * <b><component></b> the component description
077: * <b><advertised-services></b> "addService" classes and info
078: * <b><obtained-services></b> "getService" class info
079: * and the <b><container-view></b> adds:
080: * <b><children></b> child component/container views.
081: * The <b><advertised-services></b> contains:
082: * <b>class=..</b> service class name
083: * <b>id=..</b> unique identifier
084: * <b><advertise-time></b> timestamp when advertised, if not
085: * revoked yet
086: * and <b><obtained-services></b> contains:
087: * <b>class=..</b> service class name
088: * <b>id=..</b> unique identifier
089: * <b>sp-id=..</b> if the URL parameters <i>?hideT=true</i> and
090: * <i>?hideSP=true</i> are enabled and the service provider's
091: * <b>id</b> is known
092: * <b><obtain-time></b> timestamp when obtained, if not
093: * released yet
094: * <b><service-provider></b> service provider of this service,
095: * plus the provider's <b>id</b> and <b><component></b>
096: * description.
097: * <b><indirectly-advertised-services></b> if the service
098: * contains a "get.*ServiceBroker" method,
099: * such as {@link org.cougaar.core.node.NodeControlService},
100: * this will show services indirectly advertised through
101: * that ServiceBroker.
102: * </pre>
103: */
104: public class ComponentViewServlet extends ComponentServlet {
105:
106: private LoggingService log;
107:
108: private ComponentView view;
109:
110: public void load() {
111: super .load();
112:
113: log = (LoggingService) serviceBroker.getService(this ,
114: LoggingService.class, null);
115:
116: ViewService viewService = (ViewService) serviceBroker
117: .getService(this , ViewService.class, null);
118: if (viewService != null) {
119: view = viewService.getComponentView();
120: serviceBroker.releaseService(this , ViewService.class,
121: viewService);
122: }
123: if (view == null && log.isWarnEnabled()) {
124: log.warn("Unable to obtain ViewService");
125: }
126: }
127:
128: // find the top-most parent, which is usually the AgentManager
129: private ContainerView findRoot() {
130: if (view == null) {
131: return null;
132: }
133: ContainerView curr = view.getParentView();
134: ContainerView next = curr;
135: while (next != null) {
136: curr = next;
137: next = next.getParentView();
138: }
139: return curr;
140: }
141:
142: public void doGet(HttpServletRequest request,
143: HttpServletResponse response) throws IOException {
144: (new Worker(request, response)).execute();
145: }
146:
147: private class Worker {
148:
149: private final HttpServletRequest request;
150: private final HttpServletResponse response;
151:
152: private PrintWriter out;
153: private XMLWriter xml;
154:
155: private String format;
156: private String agent;
157: private boolean hideCV;
158: private boolean hideS;
159: private boolean hideSP;
160: private boolean hideT;
161:
162: public Worker(HttpServletRequest request,
163: HttpServletResponse response) {
164: this .request = request;
165: this .response = response;
166: }
167:
168: public void execute() throws IOException {
169: getParams();
170: if ("xml".equals(format)) {
171: printXML();
172: } else {
173: printUsage();
174: }
175: }
176:
177: private void getParams() {
178: format = getParam("format");
179: agent = getParam("agent");
180: if ("*".equals(agent)) {
181: agent = null;
182: }
183: hideCV = "true".equals(getParam("hideCV"));
184: if (hideCV) {
185: hideS = true;
186: hideSP = true;
187: hideT = true;
188: } else {
189: hideS = "true".equals(getParam("hideS"));
190: hideSP = "true".equals(getParam("hideSP"));
191: hideT = "true".equals(getParam("hideT"));
192: }
193: }
194:
195: private String getParam(String name) {
196: String ret = request.getParameter(name);
197: if (ret != null) {
198: ret = ret.trim();
199: if (ret.length() == 0) {
200: ret = null;
201: }
202: }
203: return ret;
204: }
205:
206: private void printUsage() throws IOException {
207: response.setContentType("text/html");
208: out = response.getWriter();
209: String title = getEncodedAgentName()
210: + " Component Model View";
211: out
212: .println("<html><head><title>"
213: + title
214: + "</title></head><body>"
215: + "<h2>"
216: + title
217: + "</h2>\n"
218: + "<p>\n"
219: + "This servlet displays component model information based on"
220: + " on the <code>"
221: + "org.cougaar.core.component.<b>ViewService</b>"
222: + "</code>.<br>\n"
223: + " In addition to showing which components are currently"
224: + " loaded, this servlet can show each component's load"
225: + " timestamp, advertised services, and obtained services"
226: + " (including the component that advertised each obtained"
227: + " service).\n"
228: + "<p>\n"
229: + "The minimal view to show just the component descriptions"
230: + " can be enabled by selecting:<br>\n<b>"
231: + " Hide all <code><component-view></code>"
232: + " details</b></br>\n"
233: + "<p>\n"
234: + "Note that the component model will show infrastructure"
235: + " components specified in the agent template (e.g. in"
236: + " <code>"
237: + "$COUGAAR_INSTALL_PATH/configs/common/SimpleAgent.xsl"
238: + "</code>). For more information about agent templates,"
239: + " see the <a href=\"http://docs.cougaar.org\">"
240: + "Cougaar Developers' Guide</a>.\n"
241: + "<p>\n"
242: + "<form method=\"GET\" action=\""
243: + request.getRequestURI()
244: + "\">\n"
245: + "<table border=0>\n"
246: + "<tr><td>Agent Filter</td><td>"
247: + "<input type=\"text\" name=\"agent\" size=\"50\""
248: + " value=\""
249: + (agent == null ? "*" : agent)
250: + "\""
251: + "/></td></tr>\n"
252: + "<tr><td>Hide all <code><component-view></code>"
253: + " details</td><td>"
254: + "<input type=\"checkbox\" name=\"hideCV\" value=\"true\""
255: + (hideCV ? " checked" : "")
256: + "/></td></tr>\n"
257: + "<tr><td>Hide advertised/obtained services</td><td>"
258: + "<input type=\"checkbox\" name=\"hideS\" value=\"true\""
259: + (hideS ? " checked" : "")
260: + "/></td></tr>\n"
261: + "<tr><td>Hide <code><service-provider></code>"
262: + " component info</td><td>"
263: + "<input type=\"checkbox\" name=\"hideSP\" value=\"true\""
264: + (hideSP ? " checked" : "")
265: + "/></td></tr>\n"
266: + "<tr><td>Hide timestamps</td><td>"
267: + "<input type=\"checkbox\" name=\"hideT\""
268: + " value=\"true\""
269: + (hideT ? " checked" : "")
270: + "/></td></tr>\n"
271: + "<tr><td> </td><td>"
272: + "<input type=\"hidden\" name=\"format\" value=\"xml\"/>\n"
273: + "<input type=\"submit\" name=\"action\" value=\"Submit\"/>\n"
274: + "</td></tr>\n" + "</table></form>\n"
275: + "</body></html>\n");
276: out.flush();
277: out.close();
278: }
279:
280: private void printXML() throws IOException {
281: response.setContentType("text/xml");
282: out = response.getWriter();
283: xml = new XMLWriter(out);
284: xml.header();
285: printComments();
286: xml.begin("component-model-view");
287: xml.attr("agent", getEncodedAgentName());
288: ContainerView root = findRoot();
289: printView(root);
290: xml.end("component-model-view");
291: out.flush();
292: out.close();
293: }
294:
295: private void printComments() {
296: xml.comment("Cougaar component model view");
297: if (agent != null) {
298: xml.comment("Filtering to only show agent \'" + agent
299: + "\'");
300: }
301: if (hideCV) {
302: xml.comment("Hiding component-view details");
303: } else {
304: if (hideS) {
305: xml
306: .comment("Hiding advertised/obtained Service detail");
307: }
308: if (hideSP) {
309: xml
310: .comment("Hiding ServiceProvider component detail");
311: }
312: if (hideT) {
313: xml.comment("Hiding timestamps");
314: }
315: }
316: }
317:
318: // recursive! print a view, do depth-first print of children
319: private void printView(ComponentView v) {
320: if (v == null) {
321: return;
322: }
323: boolean isContainer = (v instanceof ContainerView);
324: String tag = (isContainer ? "container-view"
325: : hideCV ? null : "component-view");
326: if (tag != null) {
327: xml.begin(tag);
328: int id = (hideCV ? 0 : v.getId());
329: if (id > 0) {
330: xml.attr("id", id);
331: }
332: }
333: long timestamp = (hideT ? 0 : v.getTimestamp());
334: if (timestamp > 0) {
335: xml.value("load-time", timestamp);
336: }
337: ComponentDescription desc = v.getComponentDescription();
338: if (desc == null) {
339: xml
340: .comment("component loaded by \'container.add(Object)\'?");
341: } else {
342: printDesc(desc);
343: if (agent != null && !isAgent(desc)) {
344: xml.comment("skipping this agent, it is not \'"
345: + agent + "\'");
346: xml.end(tag);
347: return;
348: }
349: }
350: //v.getParentView();
351: if (!hideS) {
352: printServices("advertised-services", v
353: .getAdvertisedServices(), true);
354: printServices("obtained-services", v
355: .getObtainedServices(), false);
356: }
357: if (isContainer) {
358: ContainerView contV = (ContainerView) v;
359: List l = contV.getChildViews();
360: int n = (l == null ? 0 : l.size());
361: if (n > 0) {
362: xml.begin("children");
363: for (int i = 0; i < n; i++) {
364: Object o = l.get(i);
365: if (o instanceof ComponentView) {
366: // recurse!
367: printView((ComponentView) o);
368: }
369: }
370: xml.end("children");
371: }
372: }
373: if (tag != null) {
374: xml.end(tag);
375: }
376: }
377:
378: // print a component description
379: private void printDesc(ComponentDescription desc) {
380: String name = desc.getName();
381: String classname = desc.getClassname();
382: int priority = desc.getPriority();
383: String ip = desc.getInsertionPoint();
384: Object param = desc.getParameter();
385: List args = (param instanceof List ? (List) param
386: : ((param instanceof String || param instanceof MessageAddress) ? Collections
387: .singletonList(param)
388: : null));
389: int n = (args == null ? 0 : args.size());
390: // filter out boring defaults:
391: if (isDefaultName(name, classname, args)) {
392: name = null;
393: }
394: String priorityString = (priority == ComponentDescription.PRIORITY_COMPONENT ? (null)
395: : ComponentDescription.priorityToString(priority));
396: if (ip != null
397: && ip
398: .equals("Node.AgentManager.Agent.PluginManager.Plugin")) {
399: ip = null;
400: }
401: // print it:
402: xml.begin("component");
403: if (name != null) {
404: xml.attr("name", name);
405: }
406: if (classname != null) {
407: xml.attr("class", classname);
408: }
409: if (priorityString != null) {
410: xml.attr("priority", priorityString);
411: }
412: if (ip != null) {
413: xml.attr("insertionpoint", ip);
414: }
415: for (int i = 0; i < n; i++) {
416: Object o = args.get(i);
417: if (o instanceof MessageAddress) {
418: o = ((MessageAddress) o).getAddress();
419: }
420: if (o instanceof String) {
421: String s = (String) o;
422: xml.value("argument", s);
423: }
424: }
425: xml.end("component");
426: }
427:
428: // recursive! print advertised/obtained services:
429: private void printServices(String tag, Map m,
430: boolean isAdvertised) {
431: int n = (m == null ? 0 : m.size());
432: if (n <= 0) {
433: return;
434: }
435: xml.begin(tag);
436: Iterator iter = m.entrySet().iterator();
437: for (int i = 0; i < n; i++) {
438: Map.Entry me = (Map.Entry) iter.next();
439: Object key = me.getKey();
440: String classname = (key instanceof Class ? ((Class) key)
441: .getName()
442: : key + "?");
443: Object value = me.getValue();
444: ServiceView sv = (value instanceof ServiceView ? ((ServiceView) value)
445: : null);
446: int id = (sv == null ? 0 : sv.getId());
447: int providerId = (sv == null ? 0 : sv.getProviderId());
448: xml.begin("service");
449: xml.attr("class", classname);
450: if (id > 0) {
451: xml.attr("id", id);
452: }
453: if (hideSP && hideT && providerId > 0) {
454: xml.attr("sp-id", providerId);
455: }
456: if (sv != null) {
457: long timestamp = (hideT ? 0 : sv.getTimestamp());
458: ComponentDescription providerDesc = (hideSP ? (null)
459: : sv.getProviderComponentDescription());
460: Map indirects = sv
461: .getIndirectlyAdvertisedServices();
462: if (timestamp > 0) {
463: String timeTag = (isAdvertised ? "advertise"
464: : "obtain")
465: + "-time";
466: xml.value(timeTag, timestamp);
467: }
468: if (!(hideSP && hideT)
469: && (providerId > 0 || providerDesc != null)) {
470: xml.begin("service-provider");
471: if (providerId > 0) {
472: xml.attr("id", providerId);
473: }
474: if (providerDesc != null) {
475: printDesc(providerDesc);
476: }
477: xml.end("service-provider");
478: }
479: if (indirects != null) {
480: // recurse!
481: printServices("indirectly-advertised-services",
482: indirects, true);
483: }
484: }
485: xml.end("service");
486: }
487: xml.end(tag);
488: }
489:
490: // if this is an AgentImpl, does the first arg match our "agent"?
491: private boolean isAgent(ComponentDescription desc) {
492: if (agent == null
493: || desc == null
494: || !("org.cougaar.core.agent.AgentImpl".equals(desc
495: .getClassname()))) {
496: return true;
497: }
498: String name;
499: Object o = desc.getParameter();
500: if (o instanceof List) {
501: List l = (List) o;
502: Object o2 = (l.isEmpty() ? null : l.get(0));
503: name = (o2 == null ? null
504: : o2 instanceof String ? (String) o2
505: : o2 instanceof MessageAddress ? ((MessageAddress) o2)
506: .getAddress()
507: : null);
508: } else if (o instanceof String) {
509: name = (String) o;
510: } else if (o instanceof MessageAddress) {
511: name = ((MessageAddress) o).getAddress();
512: } else {
513: name = null;
514: }
515: return agent.equals(name);
516: }
517:
518: // compare name to "classname("+comma_separated_args+")"
519: private boolean isDefaultName(String name, String classname,
520: List args) {
521: if (name == null) {
522: return true;
523: }
524: if (!name.startsWith(classname)) {
525: return false;
526: }
527: String tail = name.substring(classname.length());
528: int n = (args == null ? 0 : args.size());
529: if (n <= 0) {
530: return tail.equals("") || tail.equals("()");
531: }
532: StringBuffer buf = new StringBuffer();
533: buf.append('(');
534: for (int i = 0;;) {
535: buf.append(args.get(i));
536: if (++i >= n) {
537: break;
538: }
539: buf.append(',');
540: }
541: buf.append(')');
542: String s = buf.toString();
543: return tail.equals(s);
544: }
545: }
546: }
|