001: /*
002: * <copyright>
003: *
004: * Copyright 2000-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.lib.web.service;
028:
029: import java.io.IOException;
030: import java.io.PrintWriter;
031: import java.util.ArrayList;
032: import java.util.Collections;
033: import java.util.Iterator;
034: import java.util.List;
035: import java.util.Set;
036:
037: import javax.servlet.Servlet;
038: import javax.servlet.ServletConfig;
039: import javax.servlet.ServletException;
040: import javax.servlet.ServletRequest;
041: import javax.servlet.ServletResponse;
042: import javax.servlet.http.HttpServletRequest;
043: import javax.servlet.http.HttpServletResponse;
044:
045: import org.cougaar.bootstrap.SystemProperties;
046: import org.cougaar.lib.web.arch.ServletRegistry;
047: import org.cougaar.lib.web.arch.root.GlobalRegistry;
048:
049: /**
050: * A servlet that handles all "/agents" requests by optionally
051: * displaying locally-registered names or globally-registered names
052: * (in the nameserver).
053: * <p>
054: * The supported URL-parameter arguments are:<ul>
055: * <li>"?suffix=" -- list the local agent names (default)</li>
056: * <li>"?suffix=<i>text</i>" -- list the agent names in the
057: * global white pages with the matching suffix, where "."
058: * is the root. If the suffix doesn't start with ".",
059: * the substring starting at the first "." is used, and
060: * if there is no "." then the root (".") is used.</li>
061: * <li>"?format=<i>text</i>" -- specify the output format,
062: * where the options are:
063: * <ul>
064: * <li>"html" (default)</li>
065: * <li>"text" for plain text, one agent name per line</li>
066: * <li>"input" for HTML text form for typing in an agent
067: * name, plus a link to the "?format=select" page.
068: * See the javascript notes below.</li>
069: * <li>"select" for an interactive html form that uses
070: * javascript reloading. This can be embedded in an
071: * html frame or popup window. The selected name
072: * can be accessed by using javascript:<pre>
073: * var name = top.<i>frame</i>.document.agent.name.value;
074: * </pre> This only works if both frames were generated
075: * by the same <i>host:port</i>, otherwise javascript will
076: * throw a "permission denied" error.</li>
077: * </ul></li>
078: * <li>"?depth=<i>int</i> -- if the suffix is specified, this
079: * limits the recursion depth, where -1 is no limit (default
080: * is specified by the "-Dorg.cougaar.lib.web.list.depth"
081: * system property, which defaults to 1)</li>
082: * <li>"?size=<i>int</i> -- limit the list length, where -1 is
083: * no limit (default is specified by the
084: * "-Dorg.cougaar.lib.web.list.size" system property,
085: * which defaults to -1)</li>
086: * <li>"?time=<i>long</i> -- if the suffix is specified, this
087: * limits any single lookup time in milliseconds, where 0
088: * is no limit and -1 is cache-only (default is specified by
089: * the "-Dorg.cougaar.lib.web.list.timeout" system property,
090: * which defaults to 0)</li>
091: * <li>"?sorted=<i>boolean</i> -- sort the names in alphabetical
092: * order (default is "true")</li>
093: * <li>"?split=<i>boolean</i> -- for "?format=html", should links
094: * be split along hierarchy levels (default is "true")</li>
095: * <li>"?scope=all" -- backwards compatibility for listing
096: * agents, equivalent to "?suffix=."</li>
097: * </ul>
098: * <p>
099: * For example: "/agents?suffix=.&format=text"
100: * <p>
101: * Note this nice feature: With the normal "/$name" redirects and this
102: * servlet, the client can request "/$name/agents" to list all
103: * co-located agents, regardless of where the named agent happens to
104: * be located.
105: *
106: * @property org.cougaar.lib.web.list.split=true
107: * "/agents" servlet boolean to split HTML links by "." separator
108: * for per-level "?suffix=" links. Defaults to "true".
109: * @property org.cougaar.lib.web.list.depth=5
110: * "/agents" servlet recursion depth for white pages listings,
111: * where -1 indicates no limit. Defaults to 5.
112: * @property org.cougaar.lib.web.list.size=-1
113: * "/agents" servlet size limit for white pages listings,
114: * where -1 indicates no limit. Defaults to -1.
115: * @property org.cougaar.lib.web.list.timeout=-1
116: * "/agents" servlet timeout in millseconds for white pages
117: * listings, where -1 indicates block forever. Defaults to -1.
118: */
119: public class AgentsServlet implements Servlet {
120:
121: private static final boolean SPLIT = SystemProperties.getBoolean(
122: "org.cougaar.lib.web.list.split", true);
123: private static final int DEPTH = SystemProperties.getInt(
124: "org.cougaar.lib.web.list.depth", 5);
125: private static final int SIZE = SystemProperties.getInt(
126: "org.cougaar.lib.web.list.size", -1);
127: private static final long TIME = SystemProperties.getLong(
128: "org.cougaar.lib.web.list.timeout", -1);
129:
130: private static final String path = "/agents";
131:
132: private final String localNode;
133:
134: // read-only registries:
135: private final ServletRegistry localReg;
136: private final GlobalRegistry globReg;
137:
138: public AgentsServlet(String localNode, ServletRegistry localReg,
139: GlobalRegistry globReg) {
140: this .localNode = localNode;
141: this .localReg = localReg;
142: this .globReg = globReg;
143:
144: String s = (localNode == null ? "localNode"
145: : localReg == null ? "localReg"
146: : globReg == null ? "globReg" : null);
147: if (s != null) {
148: throw new IllegalArgumentException("null " + s);
149: }
150: }
151:
152: public void service(ServletRequest sreq, ServletResponse sres)
153: throws ServletException, IOException {
154:
155: HttpServletRequest req = (HttpServletRequest) sreq;
156: HttpServletResponse res = (HttpServletResponse) sres;
157:
158: MyHandler h = new MyHandler(localNode, localReg, globReg);
159: h.execute(req, res);
160: }
161:
162: private static class MyHandler {
163:
164: private final String localNode;
165: private final ServletRegistry localReg;
166: private final GlobalRegistry globReg;
167:
168: private String encSuffix;
169: private String encName;
170: private boolean isLocal;
171:
172: private boolean useHtml;
173: private boolean useInput;
174: private boolean useSelect;
175:
176: private int depthLimit;
177: private int sizeLimit;
178: private long timeLimit;
179:
180: private boolean sorted;
181: private boolean split;
182:
183: private String serverName;
184: private int serverPort;
185:
186: public MyHandler(String localNode, ServletRegistry localReg,
187: GlobalRegistry globReg) {
188: this .localNode = localNode;
189: this .localReg = localReg;
190: this .globReg = globReg;
191: }
192:
193: public void execute(HttpServletRequest req,
194: HttpServletResponse res) throws IOException {
195: parseParams(req);
196: List names = new ArrayList();
197: Limit lim = listNames(names);
198: showNames(res, names, lim);
199: }
200:
201: private void parseParams(HttpServletRequest req) {
202: // global url-encoded suffix (default is "")
203: encSuffix = req.getParameter("suffix");
204: encName = encSuffix;
205: isLocal = (encSuffix == null || encSuffix.length() == 0);
206: if (isLocal) {
207: encSuffix = ".";
208: } else {
209: int j = encSuffix.indexOf('.');
210: if (j < 0) {
211: encSuffix = ".";
212: } else if (j == 0) {
213: encName = null;
214: } else {
215: encSuffix = encSuffix.substring(j);
216: }
217: }
218:
219: // html v.s. plain-text response
220: String format = req.getParameter("format");
221: useHtml = (!("text".equals(format)));
222: useInput = "input".equals(format);
223: useSelect = "select".equals(format);
224:
225: // limits
226: String s_depthLimit = req.getParameter("depth");
227: depthLimit = (s_depthLimit == null ? (DEPTH) : Integer
228: .parseInt(s_depthLimit));
229: String s_sizeLimit = req.getParameter("size");
230: sizeLimit = (s_sizeLimit == null ? (SIZE) : Integer
231: .parseInt(s_sizeLimit));
232: String s_timeLimit = req.getParameter("time");
233: timeLimit = (s_timeLimit == null ? (TIME) : Long
234: .parseLong(s_timeLimit));
235:
236: // sorted v.s. unsorted response
237: //
238: // TODO support option to sort by suffix, e.g.:
239: // [ "x.a", "z.a", "y.b" ]
240: // instead of alphabetical, which in this case would split ".a":
241: // [ "x.a", "y.b", "z.a" ]
242: sorted = (!("false".equals(req.getParameter("sorted"))));
243:
244: // split HTML links
245: String s_split = req.getParameter("split");
246: split = (s_split == null ? SPLIT : "true".equals(s_split));
247:
248: // backwards compatibility:
249: if ("all".equals(req.getParameter("scope"))) {
250: isLocal = false;
251: }
252:
253: // server name & port
254: serverName = req.getServerName();
255: serverPort = req.getServerPort();
256: }
257:
258: private Limit listNames(List toList) {
259: toList.clear();
260: // get the listing
261: if (useInput || sizeLimit == 0) {
262: // none
263: return null;
264: }
265: Limit lim = null;
266: if (isLocal) {
267: // local names
268: toList.addAll(localReg.listNames());
269: } else {
270: long deadline;
271: if (timeLimit < 0) {
272: // no limit
273: deadline = -1;
274: } else if (timeLimit == 0) {
275: // cache-only
276: deadline = 0;
277: } else {
278: deadline = System.currentTimeMillis() + timeLimit;
279: if (deadline <= 0) {
280: // fix wrap-around
281: deadline = -1;
282: }
283: }
284: lim = listRecurse(toList, encSuffix, 0, deadline);
285: }
286: if (sizeLimit > 0) {
287: int i = toList.size();
288: if (i > sizeLimit) {
289: Collections.sort(toList);
290: while (--i >= sizeLimit) {
291: toList.remove(i);
292: }
293: if (lim == null) {
294: lim = Limit.SIZE;
295: }
296: }
297: } else if (sorted) {
298: Collections.sort(toList);
299: }
300: return lim;
301: }
302:
303: // recursive!
304: private Limit listRecurse(List toList, String encS, int depth,
305: long deadline) {
306: int size = toList.size();
307: if (depthLimit >= 0 && depth >= depthLimit) {
308: // reached max depth, add suffix
309: if (sizeLimit >= 0 && size >= sizeLimit) {
310: return Limit.SIZE;
311: }
312: // obvious depth limit if any entry starts with "."
313: toList.add(encS);
314: return Limit.DEPTH;
315: }
316: // list names at this depth level
317: long t;
318: if (deadline < 0) {
319: // no deadline
320: t = 0;
321: } else if (deadline == 0) {
322: // cache-only
323: t = -1;
324: } else {
325: t = deadline - System.currentTimeMillis();
326: if (t < 0) {
327: // ran out of time, don't switch to cache-only
328: return new Limit.Failed(null, encS, t, timeLimit);
329: }
330: }
331: Set encNames;
332: try {
333: encNames = globReg.list(encS, t);
334: } catch (Exception e) {
335: return new Limit.Failed(e, encS, t, timeLimit);
336: }
337: // sort, to preserve sizeLimit order
338: //
339: // note that this sort controls the recursion order, which will
340: // sort by suffix. If "&sort=true" is specified then the full
341: // result will be further sorted by prefix.
342: List l = new ArrayList(encNames);
343: Collections.sort(l);
344: Limit lim = null;
345: for (int i = 0, n = l.size(); i < n; i++) {
346: String s = (String) l.get(i);
347: if (s == null) {
348: continue;
349: }
350: if (sizeLimit >= 0 && size >= sizeLimit) {
351: // reached max count
352: return Limit.SIZE;
353: }
354: if (s.length() > 0 && s.charAt(0) == '.') {
355: // recurse!
356: Limit lim2 = listRecurse(toList, s, (depth + 1),
357: deadline);
358: if (lim2 != null && lim2 != Limit.DEPTH) {
359: return lim2;
360: }
361: if (lim == null) {
362: lim = lim2;
363: }
364: size = toList.size();
365: } else {
366: toList.add(s);
367: }
368: }
369: return lim;
370: }
371:
372: private void showNames(HttpServletResponse res, List names,
373: Limit lim) throws IOException {
374: if (lim == Limit.DEPTH) {
375: // ignore depth limit, since it's obvious if any entries
376: // start with a "."
377: lim = null;
378: } else if ((lim instanceof Limit.Failed) && useSelect) {
379: // discard partial results (!)
380: // use input field instead of drop-down list
381: useSelect = false;
382: useInput = true;
383: }
384:
385: // write response
386: res.setContentType((useHtml ? "text/html" : "text/plain"));
387: PrintWriter out = res.getWriter();
388: if (!(useHtml)) {
389: listPlain(out, names);
390: } else if (useInput) {
391: listInput(out);
392: } else if (useSelect) {
393: if (isLocal) {
394: listSelectLocal(out, names);
395: } else {
396: listSelectAll(out, names);
397: }
398: } else {
399: listHTML(out, names, lim);
400: }
401: out.close();
402: }
403:
404: private void listPlain(PrintWriter out, List names) {
405: // simple line-by-line output
406: int n = names.size();
407: if (n > 0) {
408: Iterator iter = names.iterator();
409: for (int i = 0; i < n; i++) {
410: String ni = (String) iter.next();
411: out.println(ni);
412: }
413: }
414: }
415:
416: private void listInput(PrintWriter out) {
417: // text box
418: boolean isName = (encName != null);
419: out.print("<html><head>\n"
420: + "<script language=\"JavaScript\">\n" + "<!--\n"
421: + "function toSelect() {\n"
422: + " var val = document.agent.name.value;\n"
423: + " location.href="
424: + getLink("\"+val+\"", "select") + ";\n" + "}\n"
425: + "// this works on some browsers:\n"
426: + "function noenter() {\n" + " var key = 0;\n"
427: + " if (window.event) {\n"
428: + " if (navigator.appName == 'Netscape') {\n"
429: + " key = window.event.which;\n"
430: + " } else {\n"
431: + " key = window.event.keyCode;\n" + " }\n"
432: + " }\n" + " return (key != 13);\n" + "}\n"
433: + "// -->\n" + "</script>\n" + "</head>\n"
434: + "<body>\n" + "<form name=\"agent\"");
435: // We don't want this target, but some browsers will accept
436: // an ENTER in the text field as a submit.
437: out.print(" target="
438: + getLink((isName ? encName : encSuffix), "input"));
439: out
440: .print(">\n"
441: + "<input type=\"text\" size=\"20\" name=\"name\" value=\""
442: + (isName ? encName : encSuffix)
443: + "\" onKeypress=\"noenter()\"> "
444: + "<input type=\"button\" value=\"list\""
445: + " onClick=\"toSelect()\">"
446: + "</form></body></html>");
447: }
448:
449: private void listSelectLocal(PrintWriter out, List names) {
450: // local names
451: out
452: .print("<html><head>\n"
453: + "<script language=\"JavaScript\">\n"
454: + "<!--\n"
455: + "function selectAgent() {\n"
456: + " var idx = document.agent.select.selectedIndex;\n"
457: + " var val = document.agent.select.options[idx].text;\n"
458: + " document.agent.name.value = val;\n"
459: + "}\n"
460: + "function toText() {\n"
461: + " var val = document.agent.name.value;\n"
462: + " location.href="
463: + getLink("\"+val+\"", "input")
464: + ";\n"
465: + "}\n"
466: + "// -->\n"
467: + "</script>\n"
468: + "</head>\n"
469: + "<body>\n"
470: + "<form name=\"agent\" onSubmit=\";\">\n"
471: + "<input type=\"hidden\" name=\"name\" value=\""
472: + encSuffix + "\">\n"
473: + "<select name=\"select\""
474: + " onChange=\"selectAgent()\"" + ">\n");
475: // print names
476: int n = names.size();
477: if (n > 0) {
478: Iterator iter = names.iterator();
479: for (int i = 0; i < n; i++) {
480: String ni = (String) iter.next();
481: out.print("<option>" + ni + "</option>\n");
482: }
483: }
484: out.print("</select> \n"
485: + "<input type=\"button\" value=\"text\""
486: + " onClick=\"toText()\">" + "</form>\n"
487: + "</body></html>\n");
488: }
489:
490: private void listSelectAll(PrintWriter out, List names) {
491: // interactive javascript form
492: boolean isName = (encName != null);
493: boolean isRoot = ".".equals(encSuffix);
494: out
495: .print("<html><head>\n"
496: + "<script language=\"JavaScript\">\n"
497: + "<!--\n"
498: + "function selectAgent() {\n"
499: + " var idx = document.agent.select.selectedIndex;\n"
500: + " var val = document.agent.select.options[idx].text;\n"
501: + " document.agent.name.value = val;\n"
502: + " if (val.length > 0 &&\n"
503: + " val.charAt(0) == '.' &&\n"
504: + " val != \""
505: + encSuffix
506: + "\") {\n"
507: + " location.href="
508: + getLink("\"+val+\"", "select")
509: + ";\n"
510: + " }\n"
511: + "}\n"
512: + "function toText() {\n"
513: + " var val = document.agent.name.value;\n"
514: + " location.href="
515: + getLink("\"+val+\"", "input")
516: + ";\n"
517: + "}\n"
518: + "// -->\n"
519: + "</script>\n"
520: + "</head>\n"
521: + "<body>\n"
522: + "<form name=\"agent\" onSubmit=\";\">\n"
523: + "<input type=\"hidden\" name=\"name\" value=\""
524: + (isName ? encName : encSuffix)
525: + "\">"
526: + "<select name=\"select\""
527: + " onChange=\"selectAgent()\"" + ">\n");
528: // print parents back to root
529: out.print("<option"
530: + ((isRoot && !isName) ? " selected" : "")
531: + ">.</option>\n");
532: if (!isRoot) {
533: // assert (encSuffix.startsWith("."));
534: for (int j = encSuffix.length(); j > 0;) {
535: j = encSuffix.lastIndexOf('.', j - 1);
536: String s = encSuffix.substring(j);
537: out.print("<option"
538: + ((j == 0 && !isName) ? " selected" : "")
539: + ">" + s + "</option>\n");
540: }
541: }
542: // print names
543: int n = names.size();
544: if (n > 0) {
545: Iterator iter = names.iterator();
546: for (int i = 0; i < n; i++) {
547: String ni = (String) iter.next();
548: out
549: .print("<option"
550: + ((isName && encName.equals(ni)) ? " selected"
551: : "") + ">" + ni
552: + "</option>\n");
553: }
554: }
555: out.print("</select>\n"
556: + "<input type=\"button\" value=\"text\""
557: + " onClick=\"toText()\">"
558: + "</form></body></html>\n");
559: }
560:
561: private void listHTML(PrintWriter out, List names, Limit lim) {
562: // pretty HTML
563: int n = names.size();
564: boolean isAll = (!isLocal && lim == null && "."
565: .equals(encSuffix));
566: if (isAll) {
567: for (int i = 0; i < n; i++) {
568: String ni = (String) names.get(i);
569: if (ni.length() > 0 && ni.charAt(0) == '.') {
570: isAll = false;
571: break;
572: }
573: }
574: }
575: String title = (isLocal ? ("Local agents on node " + localNode)
576: : isAll ? ("All agents in the society")
577: : ("All agents "
578: + (".".equals(encSuffix) ? ("at the Root (\"")
579: : ("with Suffix (\"" + createSuffixLinks(encSuffix)))
580: + "<a href=" + getLink(".", "html") + ">.</a>\")"));
581: out.print("<html><head><title>");
582: out.print(title);
583: out.print("</title></head>\n" + "<body><p><h1>");
584: out.print(title);
585: out.print("</h1>\n");
586: if (n > 0) {
587: out.print("<table border=\"0\">\n");
588: for (int i = 0; i < n; i++) {
589: String ni = (String) names.get(i);
590: int j = ni.indexOf('.');
591: out.print(((i > 0) ? "</td></tr>\n" : "")
592: + "<tr><td align=\"right\"> "
593: + (j == 0 ? "<b>" : "") + (i + 1)
594: + (j == 0 ? "</b>" : "")
595: + ". </td><td align=\"right\">");
596: if (split) {
597: if (j != 0) {
598: // print head(\.tail)?
599: out.print("<a href=\"/$" + ni + "/list\">"
600: + (j < 0 ? ni : ni.substring(0, j))
601: + "</a>");
602: }
603: if (j >= 0) {
604: // print \.tail
605: out
606: .print(createSuffixLinks(ni
607: .substring(j)));
608: }
609: } else {
610: // print complete head(\.tail)?
611: out.print("<a href="
612: + ((j == 0) ? (getLink(ni, "html"))
613: : ("\"/$" + ni + "/list\""))
614: + ">" + ni + "</a>");
615: }
616: }
617: if (lim != null) {
618: out.print("<tr><td> </td><td align=\"left\">"
619: + "<font color=\"red\">" + lim
620: + "</font></td></tr>");
621: }
622: out.print("</table>\n");
623: } else {
624: out.print("<font color=\"red\">zero agents found"
625: + (lim == null ? "" : ("<br>" + lim))
626: + "</font>");
627: }
628: out.print("<p>\n" + "<a href=" + getLink(null, "html")
629: + "><b>Local</b> agents on node " + localNode
630: + "</a><br>\n" + "<a href=" + getLink(".", "html")
631: + "><b>All</b> agents in the society</a><br>"
632: + "</body></html>\n");
633: }
634:
635: /** Create URI back to this servlet */
636: private String getLink(String suffix, String format) {
637: return "\"" + "/$" + localNode + "/agents" + "?suffix="
638: + (suffix == null ? "" : suffix) + "&format="
639: + format + "&depth=" + depthLimit + "&size="
640: + sizeLimit + "&time=" + timeLimit + "&sorted="
641: + sorted + "&split=" + split + "\"";
642: }
643:
644: /**
645: * Given a suffix generate an HTML href list.
646: * <p>
647: * For example, given:<pre>
648: * .a.b.c
649: * </pre>Generate:<pre>
650: * <a href="?suffix=.a.b.c">.a</a> <i>+</i>
651: * <a href="?suffix=.b.c">.b</a> <i>+</i>
652: * <a href="?suffix=.c">.c</a>
653: * </pre>
654: */
655: private String createSuffixLinks(String encS) {
656: // assert (encS.charAt(0) == '.');
657: StringBuffer buf = new StringBuffer();
658: int len = encS.length();
659: for (int j = 0; j < len;) {
660: int k = encS.indexOf('.', j + 1);
661: if (k < 0) {
662: if (j >= len) {
663: break;
664: }
665: k = len;
666: }
667: buf.append("<a href=");
668: buf.append(getLink(encS.substring(j), "html"));
669: buf.append(">");
670: buf.append(encS.substring(j, k));
671: buf.append("</a>");
672: j = k;
673: }
674: return buf.toString();
675: }
676: }
677:
678: //
679: // other Servlet methods
680: //
681:
682: private ServletConfig config;
683:
684: public void init(ServletConfig config) {
685: this .config = config;
686: }
687:
688: public ServletConfig getServletConfig() {
689: return config;
690: }
691:
692: public String getServletInfo() {
693: return "agents-servlet";
694: }
695:
696: public void destroy() {
697: // ignore
698: }
699:
700: private static abstract class Limit {
701: public static final Limit SIZE = new Limit() {
702: public String toString() {
703: return "Reached size limit";
704: }
705: };
706: public static final Limit DEPTH = new Limit() {
707: public String toString() {
708: return "Reached depth limit";
709: }
710: };
711:
712: public static class Failed extends Limit {
713: public final Exception e;
714: public final String encS;
715: public long timeout;
716: public long timeLimit;
717:
718: public Failed(Exception e, String encS, long timeout,
719: long timeLimit) {
720: this .e = e;
721: this .encS = encS;
722: this .timeout = timeout;
723: this .timeLimit = timeLimit;
724: }
725:
726: public String toString() {
727: return "Failed list (suffix="
728: + encS
729: + ", timeout="
730: + timeout
731: + ", timeLimit="
732: + timeLimit
733: + (e == null ? "" : ", exception="
734: + e.getMessage()) + ")";
735: }
736: }
737: }
738: }
|