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.core.mobility.servlet;
028:
029: import java.io.ByteArrayOutputStream;
030: import java.io.IOException;
031: import java.io.OutputStream;
032: import java.io.PrintWriter;
033: import java.util.Collection;
034: import java.util.Date;
035: import java.util.Iterator;
036: import javax.servlet.Servlet;
037: import javax.servlet.http.HttpServlet;
038: import javax.servlet.http.HttpServletRequest;
039: import javax.servlet.http.HttpServletResponse;
040: import org.cougaar.core.blackboard.BlackboardClient;
041: import org.cougaar.core.component.ServiceBroker;
042: import org.cougaar.core.domain.Factory;
043: import org.cougaar.core.mobility.AbstractTicket;
044: import org.cougaar.core.mobility.AddTicket;
045: import org.cougaar.core.mobility.MoveTicket;
046: import org.cougaar.core.mobility.RemoveTicket;
047: import org.cougaar.core.mobility.ldm.AgentControl;
048: import org.cougaar.core.mobility.ldm.MobilityFactory;
049: import org.cougaar.core.mts.MessageAddress;
050: import org.cougaar.core.node.NodeIdentificationService;
051: import org.cougaar.core.service.AgentIdentificationService;
052: import org.cougaar.core.service.BlackboardService;
053: import org.cougaar.core.service.DomainService;
054: import org.cougaar.core.servlet.BaseServletComponent;
055: import org.cougaar.core.util.UID;
056: import org.cougaar.util.UnaryPredicate;
057:
058: /**
059: * Servlet that allows the client to add {@link AgentControl} objects
060: * to the blackboard and monitor their progress, such as agent
061: * mobility.
062: * <p>
063: * The path of the servlet is "/move".
064: * <p>
065: * The URL parameters to this servlet are:
066: * <ul><p>
067: * <li><tt>action=STRING</tt><br>
068: * Option action selection, where the default is "Refresh".
069: * <p>
070: * "Refresh" displays the current status.
071: * <p>
072: * "Add" creates a new AgentControl request. Most of the
073: * parameters below are used to support this action.
074: * <p>
075: * "Remove" removes the AgentControl with the UID specified
076: * by the required "removeUID" parameter.
077: * <p>
078: * </li><p>
079: * <li><tt>op=String</tt><br>
080: * Required operation, which may be "Move", "Add", or
081: * "Remove". If none is specified then "Move" is assumed
082: * (for backwards compatibility).
083: * <li><tt>removeUID=String</tt><br>
084: * If the action is "Remove", this is the UID of the script
085: * to be removed. Any running processes are killed.
086: * </li><p>
087: * <li><tt>mobileAgent=STRING</tt><br>
088: * Option agent to move. Defaults to this servlet's agent.
089: * </li><p>
090: * <li><tt>originNode=STRING</tt><br>
091: * Option origin node for the mobile agent. Defaults to
092: * wherever the agent happens to be at the time of the submit.
093: * If set, the move will assert the agent starting node
094: * location.
095: * </li><p>
096: * <li><tt>destNode=STRING</tt><br>
097: * Option destination node for the mobile agent. Defaults
098: * to wherever the agent happens to be at the time of
099: * the submit.
100: * </li><p>
101: * <li><tt>isForceRestart=BOOLEAN</tt><br>
102: * Only applies when the destNode is not specified or
103: * matches the current agent location. If true, the agent
104: * will undergo most of the move work, even though it's
105: * already at the specified destination node.
106: * </li><p>
107: * </ul>
108: * <p>
109: * Note the <b>SECURITY</b> issues of moving agents!
110: */
111: public class MoveAgentServlet extends BaseServletComponent implements
112: BlackboardClient {
113: protected MessageAddress localAgent;
114: protected MessageAddress localNode;
115:
116: protected DomainService domain;
117: protected AgentIdentificationService agentIdService;
118: protected NodeIdentificationService nodeIdService;
119: protected BlackboardService blackboard;
120:
121: protected MobilityFactory mobilityFactory;
122:
123: protected static final UnaryPredicate AGENT_CONTROL_PRED = new UnaryPredicate() {
124: public boolean execute(Object o) {
125: return (o instanceof AgentControl);
126: }
127: };
128:
129: protected String getPath() {
130: return "/move";
131: }
132:
133: protected Servlet createServlet() {
134: // create inner class
135: return new MyServlet();
136: }
137:
138: public void setAgentIdentificationService(
139: AgentIdentificationService agentIdService) {
140: this .agentIdService = agentIdService;
141: if (agentIdService != null) {
142: this .localAgent = agentIdService.getMessageAddress();
143: // } else { // Revocation - nothing more to do
144: }
145: }
146:
147: public void setNodeIdentificationService(
148: NodeIdentificationService nodeIdService) {
149: this .nodeIdService = nodeIdService;
150: if (nodeIdService != null) {
151: this .localNode = nodeIdService.getMessageAddress();
152: // } else { // Revocation - nothing more to do
153: }
154: }
155:
156: public void setBlackboardService(BlackboardService blackboard) {
157: this .blackboard = blackboard;
158: }
159:
160: public void setDomainService(DomainService domain) {
161: this .domain = domain;
162: if (domain == null) {
163: mobilityFactory = null;
164: } else {
165: mobilityFactory = (MobilityFactory) domain
166: .getFactory("mobility");
167: }
168: }
169:
170: // release services:
171: public void unload() {
172: super .unload();
173: if (blackboard != null) {
174: serviceBroker.releaseService(this , BlackboardService.class,
175: blackboard);
176: blackboard = null;
177: }
178: if (domain != null) {
179: serviceBroker.releaseService(this , DomainService.class,
180: domain);
181: domain = null;
182: }
183: if (nodeIdService != null) {
184: serviceBroker.releaseService(this ,
185: NodeIdentificationService.class, nodeIdService);
186: nodeIdService = null;
187: }
188: if (agentIdService != null) {
189: serviceBroker.releaseService(this ,
190: AgentIdentificationService.class, agentIdService);
191: agentIdService = null;
192: }
193: }
194:
195: protected Collection queryAgentControls() {
196: Collection ret = null;
197: try {
198: blackboard.openTransaction();
199: ret = blackboard.query(AGENT_CONTROL_PRED);
200: } finally {
201: blackboard.closeTransactionDontReset();
202: }
203: return ret;
204: }
205:
206: protected AgentControl queryAgentControl(final UID uid) {
207: if (uid == null) {
208: throw new IllegalArgumentException("null uid");
209: }
210: UnaryPredicate pred = new UnaryPredicate() {
211: public boolean execute(Object o) {
212: return ((o instanceof AgentControl) && (uid
213: .equals(((AgentControl) o).getUID())));
214: }
215: };
216: AgentControl ret = null;
217: try {
218: blackboard.openTransaction();
219: Collection c = blackboard.query(pred);
220: if ((c != null) && (c.size() >= 1)) {
221: ret = (AgentControl) c.iterator().next();
222: }
223: } finally {
224: blackboard.closeTransactionDontReset();
225: }
226: return ret;
227: }
228:
229: protected AgentControl createAgentControl(UID ownerUID,
230: MessageAddress target, AbstractTicket ticket) {
231: if (mobilityFactory == null) {
232: throw new RuntimeException(
233: "Mobility factory (and domain) not enabled");
234: }
235: AgentControl ac = mobilityFactory.createAgentControl(ownerUID,
236: target, ticket);
237: return ac;
238: }
239:
240: protected void addAgentControl(AgentControl ac) {
241: try {
242: blackboard.openTransaction();
243: blackboard.publishAdd(ac);
244: } finally {
245: blackboard.closeTransactionDontReset();
246: }
247: }
248:
249: protected void removeAgentControl(AgentControl ac) {
250: try {
251: blackboard.openTransaction();
252: blackboard.publishRemove(ac);
253: } finally {
254: blackboard.closeTransaction();
255: }
256: }
257:
258: /**
259: * Servlet to handle requests.
260: */
261: private class MyServlet extends HttpServlet {
262:
263: public void doGet(HttpServletRequest req,
264: HttpServletResponse res) throws IOException {
265: MyWorker mw = new MyWorker(req, res);
266: mw.execute();
267: }
268:
269: private class MyWorker {
270:
271: // from the "doGet(..)":
272: private HttpServletRequest request;
273: private HttpServletResponse response;
274:
275: // action:
276: public static final String ACTION_PARAM = "action";
277: public static final String ADD_VALUE = "Add";
278: public static final String REMOVE_VALUE = "Remove";
279: public static final String REFRESH_VALUE = "Refresh";
280: public String action;
281:
282: // operation:
283: public static final String OP_PARAM = "op";
284: public static final String ADD_OP_VALUE = "Add";
285: public static final String MOVE_OP_VALUE = "Move";
286: public static final String REMOVE_OP_VALUE = "Remove";
287: public String op;
288:
289: // remove uid:
290: public static final String REMOVE_UID_PARAM = "removeUID";
291: public String removeUID;
292:
293: // ticket options:
294: public static final String MOBILE_AGENT_PARAM = "mobileAgent";
295: public String mobileAgent;
296: public static final String ORIGIN_NODE_PARAM = "originNode";
297: public String originNode;
298: public static final String DEST_NODE_PARAM = "destNode";
299: public String destNode;
300: public static final String IS_FORCE_RESTART_PARAM = "isForceRestart";
301: public boolean isForceRestart;
302:
303: // worker constructor:
304: public MyWorker(HttpServletRequest request,
305: HttpServletResponse response) {
306: this .request = request;
307: this .response = response;
308: }
309:
310: // handle a request:
311: public void execute() throws IOException {
312: parseParams();
313: writeResponse();
314: }
315:
316: private void parseParams() throws IOException {
317:
318: // action:
319: action = request.getParameter(ACTION_PARAM);
320:
321: // operation:
322: op = request.getParameter(OP_PARAM);
323: if ((op != null) && (op.length() == 0)) {
324: op = null;
325: }
326:
327: // remove param:
328: removeUID = request.getParameter(REMOVE_UID_PARAM);
329: if ((removeUID != null) && (removeUID.length() == 0)) {
330: removeUID = null;
331: }
332:
333: // ticket options:
334:
335: mobileAgent = request.getParameter(MOBILE_AGENT_PARAM);
336: if ((mobileAgent != null)
337: && (mobileAgent.length() == 0)) {
338: mobileAgent = null;
339: }
340:
341: destNode = request.getParameter(DEST_NODE_PARAM);
342: if ((destNode != null) && (destNode.length() == 0)) {
343: destNode = null;
344: }
345:
346: originNode = request.getParameter(ORIGIN_NODE_PARAM);
347: if ((originNode != null) && (originNode.length() == 0)) {
348: originNode = null;
349: }
350:
351: isForceRestart = "true".equalsIgnoreCase(request
352: .getParameter(IS_FORCE_RESTART_PARAM));
353: }
354:
355: private AgentControl createAgentControl() {
356: MessageAddress mobileAgentAddr = (mobileAgent != null ? MessageAddress
357: .getMessageAddress(mobileAgent)
358: : localAgent);
359: MessageAddress destNodeAddr = (destNode != null ? (MessageAddress
360: .getMessageAddress(destNode))
361: : null);
362: MessageAddress target;
363: AbstractTicket ticket;
364: if (op == null || op.equalsIgnoreCase(MOVE_OP_VALUE)) {
365: MessageAddress originNodeAddr = (originNode != null ? (MessageAddress
366: .getMessageAddress(originNode))
367: : null);
368: target = (originNodeAddr != null ? (originNodeAddr)
369: : mobileAgentAddr);
370: if (destNodeAddr == null && originNodeAddr != null) {
371: destNodeAddr = originNodeAddr;
372: }
373: ticket = new MoveTicket(null, mobileAgentAddr,
374: originNodeAddr, destNodeAddr,
375: isForceRestart);
376: } else {
377: if (op.equalsIgnoreCase(ADD_OP_VALUE)) {
378: if (destNodeAddr == null) {
379: destNodeAddr = localNode;
380: }
381: target = destNodeAddr;
382: ticket = new AddTicket(null, mobileAgentAddr,
383: destNodeAddr);
384: } else if (op.equalsIgnoreCase(REMOVE_OP_VALUE)) {
385: target = (destNodeAddr != null ? destNodeAddr
386: : mobileAgentAddr);
387: ticket = new RemoveTicket(null,
388: mobileAgentAddr, destNodeAddr);
389: } else {
390: throw new IllegalArgumentException(
391: "Invalid url-parameter: \"" + OP_PARAM
392: + "\"=" + op);
393: }
394: }
395: AgentControl ac = MoveAgentServlet.this
396: .createAgentControl(null, target, ticket);
397: return ac;
398: }
399:
400: private void writeResponse() throws IOException {
401: if (ADD_VALUE.equals(action)) {
402: try {
403: AgentControl ac = createAgentControl();
404: addAgentControl(ac);
405: } catch (Exception e) {
406: writeFailure(e);
407: return;
408: }
409: writeSuccess();
410: } else if (REMOVE_VALUE.equals(action)) {
411: try {
412: UID uid = UID.toUID(removeUID);
413: AgentControl ac = queryAgentControl(uid);
414: if (ac != null) {
415: removeAgentControl(ac);
416: }
417: } catch (Exception e) {
418: writeFailure(e);
419: return;
420: }
421: writeSuccess();
422: } else {
423: writeUsage();
424: }
425: }
426:
427: private void writeUsage() throws IOException {
428: response.setContentType("text/html");
429: PrintWriter out = response.getWriter();
430: writeHeader(out);
431: out.print("<center>" + "<h2>Agent Control (usage)</h2>"
432: + "</center>\n");
433: writeForm(out);
434: out.print("</body></html>\n");
435: out.close();
436: }
437:
438: private void writeFailure(Exception e) throws IOException {
439: // select response message
440: response.setContentType("text/html");
441: // build up response
442: ByteArrayOutputStream baos = new ByteArrayOutputStream();
443: PrintWriter out = new PrintWriter(baos);
444: writeHeader(out);
445: out.print("<center>"
446: + "<h2>Agent Control (failed)</h2>"
447: + "</center>" + "<p><pre>\n");
448: e.printStackTrace(out);
449: out
450: .print("\n</pre><p>"
451: + "<h3>Please double-check these parameters:</h3>\n");
452: writeForm(out);
453: out.print("</body></html>\n");
454: out.close();
455: // send error code
456: response.sendError(HttpServletResponse.SC_BAD_REQUEST,
457: new String(baos.toByteArray()));
458: }
459:
460: private void writeSuccess() throws IOException {
461: // write response
462: response.setContentType("text/html");
463: PrintWriter out = response.getWriter();
464: writeHeader(out);
465: out.print("<center><h2>" + "Agent control (success)"
466: + "</h2></center><p>\n");
467: writeForm(out);
468: out.print("</body></html>\n");
469: out.close();
470: }
471:
472: private void writeHeader(PrintWriter out)
473: throws IOException {
474: out.print("<html><head><title>");
475: out.print(localAgent);
476: out
477: .print(" agent control</title>\n"
478: + "<script language=\"JavaScript\">\n"
479: + "<!--\n"
480: + "function selectOp() {\n"
481: + " var iop = document.f.op.selectedIndex;\n"
482: + " var sop = document.f.op.options[iop].text;\n"
483: + " enableOp(sop != \""
484: + MOVE_OP_VALUE
485: + "\");\n"
486: + "}\n"
487: + "function enableOp(b) {\n"
488: + " document.f.originNode.disabled=b;\n"
489: + " document.f.isForceRestart.disabled=b;\n"
490: + "}\n" + "// -->\n" + "</script>\n"
491: + "</head>"
492: + "<body onLoad=\"selectOp();\">\n");
493: }
494:
495: private void writeForm(PrintWriter out) throws IOException {
496: // begin form
497: out.print("<form name=\"f\" method=\"GET\" action=\"");
498: out.print(request.getRequestURI());
499: out.print("\">\n" + "<i>Agent:</i> ");
500: out.print(localAgent);
501: out.print("<br>" + "<i>Node:</i> ");
502: out.print(localNode);
503: out.print("<br>" + "<i>Time:</i> ");
504: Date d = new Date();
505: out.print(d);
506: out.print(" (");
507: out.print(d.getTime());
508: out.print(")" + "<p>" + "<h3>Local requests:</h3><p>");
509: // show current AgentControl objects
510: Collection c = queryAgentControls();
511: int n = ((c != null) ? c.size() : 0);
512: if (n == 0) {
513: out.print("<i>none</i>");
514: } else {
515: out
516: .print("<table border=1 cellpadding=1\n"
517: + " cellspacing=1 width=95%\n"
518: + " bordercolordark=#660000 bordercolorlight=#cc9966>\n"
519: + "<tr>"
520: + "<td>\n"
521: + "<font size=+1 color=mediumblue><b>UID</b></font>"
522: + "</td>"
523: + "<td>"
524: + "<font size=+1 color=mediumblue><b>Ticket</b></font>"
525: + "</td>"
526: + "<td>"
527: + "<font size=+1 color=mediumblue><b>Status</b></font>"
528: + "</td>" + "</tr>\n");
529: Iterator iter = c.iterator();
530: for (int i = 0; i < n; i++) {
531: AgentControl ac = (AgentControl) iter.next();
532: out.print("<tr><td>");
533: out.print(ac.getUID());
534: out.print("</td><td>");
535: out.print(ac.getAbstractTicket());
536: out.print("</td><td bgcolor=\"");
537: int status = ac.getStatusCode();
538: if (status == AgentControl.NONE) {
539: out.print("#FFFFBB\">" + // yellow
540: "In progress");
541: } else {
542: out
543: .print((status != AgentControl.FAILURE) ? "#BBFFBB\">"
544: : // green
545: "#FFBBBB\">"); // red
546: out.print(ac.getStatusCodeAsString());
547: }
548: out.print("</td></tr>\n");
549: }
550: out.print("</table>\n");
551: }
552: out.print("<p><input type=\"submit\" name=\""
553: + ACTION_PARAM + "\" value=\"" + REFRESH_VALUE
554: + "\">\n");
555:
556: // allow user to remove an existing AgentControl
557: out.print("<p>"
558: + "<h3>Remove an existing request:</h3>\n");
559: if (n > 0) {
560: out.print("<select name=\"" + REMOVE_UID_PARAM
561: + "\">");
562: Iterator iter = c.iterator();
563: for (int i = 0; i < n; i++) {
564: AgentControl ac = (AgentControl) iter.next();
565: UID uid = ac.getUID();
566: out.print("<option value=\"");
567: out.print(uid);
568: out.print("\">");
569: out.print(uid);
570: out.print("</option>");
571: }
572: out.print("</select>"
573: + "<input type=\"submit\" name=\""
574: + ACTION_PARAM + "\" value=\""
575: + REMOVE_VALUE + "\">\n");
576: } else {
577: out.print("<i>none</i>");
578: }
579:
580: // allow user to submit a new AgentControl request
581: out.print("<p>" + "<h3>Create a new request:</h3>\n");
582: out.print("<table>\n" + "<tr><td>" + "Operation"
583: + "</td><td>\n" + "<select name=\"" + OP_PARAM
584: + "\" onChange=\"selectOp();\">\n" + "<option");
585: if (ADD_OP_VALUE.equalsIgnoreCase(op)) {
586: out.print(" selected");
587: }
588: out.print(">" + ADD_OP_VALUE + "</option>\n"
589: + "<option");
590: if (op == null || MOVE_OP_VALUE.equalsIgnoreCase(op)) {
591: out.print(" selected");
592: }
593: out.print(">" + MOVE_OP_VALUE + "</option>\n"
594: + "<option");
595: if (REMOVE_OP_VALUE.equalsIgnoreCase(op)) {
596: out.print(" selected");
597: }
598: out.print(">" + REMOVE_OP_VALUE + "</option>\n"
599: + "</select>\n" + "</td></tr>\n" + "<tr><td>"
600: + "Mobile Agent" + "</td><td>\n"
601: + "<input type=\"text\" name=\""
602: + MOBILE_AGENT_PARAM + "\" size=\"30\"");
603: if (mobileAgent != null) {
604: out.print(" value=\"");
605: out.print(mobileAgent);
606: out.print("\"");
607: }
608: out.print(">" + "</td></tr>\n" + "<tr><td>"
609: + "Origin Node" + "</td><td>"
610: + "<input type=\"text\" name=\""
611: + ORIGIN_NODE_PARAM + "\" size=\"30\"");
612: if (originNode != null) {
613: out.print(" value=\"");
614: out.print(originNode);
615: out.print("\"");
616: }
617: out.print(">" + "</td></tr>\n" + "<tr><td>"
618: + "Destination Node" + "</td><td>"
619: + "<input type=\"text\" name=\""
620: + DEST_NODE_PARAM + "\" size=\"30\"");
621: if (destNode != null) {
622: out.print(" value=\"");
623: out.print(destNode);
624: out.print("\"");
625: }
626: out.print(">" + "</td></tr>\n" + "<tr><td>"
627: + "Force Restart" + "</td><td>"
628: + "<select name=\"" + IS_FORCE_RESTART_PARAM
629: + "\">" + "<option value=\"true\"");
630: if (isForceRestart) {
631: out.print(" selected");
632: }
633: out.print(">true</option>" + "<option value=\"false\"");
634: if (!(isForceRestart)) {
635: out.print(" selected");
636: }
637: out.print(">false</option>" + "</select>\n"
638: + "</td></tr>\n" + "<tr><td colwidth=2>"
639: + "<input type=\"submit\" name=\""
640: + ACTION_PARAM + "\" value=\"" + ADD_VALUE
641: + "\">" + "<input type=\"reset\">"
642: + "</td></tr>\n" + "</table>\n" + "</form>\n");
643: }
644: }
645: }
646:
647: // odd BlackboardClient method:
648: public String getBlackboardClientName() {
649: return getPath();
650: }
651:
652: // odd BlackboardClient method:
653: public long currentTimeMillis() {
654: throw new UnsupportedOperationException(this
655: + " asked for the current time???");
656: }
657:
658: // unused BlackboardClient method:
659: public boolean triggerEvent(Object event) {
660: // if we had Subscriptions we'd need to implement this.
661: //
662: // see "ComponentPlugin" for details.
663: throw new UnsupportedOperationException(this
664: + " only supports Blackboard queries, but received "
665: + "a \"trigger\" event: " + event);
666: }
667:
668: public String toString() {
669: return "\"" + getPath() + "\" servlet";
670: }
671: }
|