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.pizza.servlet;
028:
029: import org.cougaar.core.service.AgentIdentificationService;
030: import org.cougaar.core.service.BlackboardQueryService;
031: import org.cougaar.core.service.LoggingService;
032: import org.cougaar.core.servlet.BaseServletComponent;
033: import org.cougaar.pizza.Constants;
034: import org.cougaar.pizza.plugin.PizzaPreferences;
035: import org.cougaar.planning.ldm.plan.Allocation;
036: import org.cougaar.planning.ldm.plan.AspectType;
037: import org.cougaar.planning.ldm.plan.Expansion;
038: import org.cougaar.planning.ldm.plan.Task;
039: import org.cougaar.util.UnaryPredicate;
040:
041: import javax.servlet.Servlet;
042: import javax.servlet.ServletException;
043: import javax.servlet.http.HttpServlet;
044: import javax.servlet.http.HttpServletRequest;
045: import javax.servlet.http.HttpServletResponse;
046: import java.io.IOException;
047: import java.io.PrintWriter;
048: import java.util.*;
049:
050: /**
051: * The main UI for the application: shows collected RSVPs from invited guests
052: * at "/pizza", and the progress on ordering the pizza.
053: * <p>
054: * Load into the Agent doing the inviting (has the {@link PizzaPreferences} object).
055: * In our case, that is Alice.
056: */
057: public class PizzaPreferenceServlet extends BaseServletComponent {
058: // This is a servlet, so no subscriptions -- instead,
059: // we do one-time queries when needed
060: protected BlackboardQueryService blackboardQueryService;
061: protected LoggingService logger;
062: protected String agentID; // this Agent's name
063:
064: /**
065: * Load services needed by this servlet: BlackboardQueryService, AgentIDService.
066: * Uses the BlackboardQueryService to get a snapshot of the blackboard
067: * status when a user asks, and the AgentIDService to get this agent's name.
068: */
069: public void load() {
070: super .load();
071:
072: // get services
073: blackboardQueryService = (BlackboardQueryService) serviceBroker
074: .getService(this , BlackboardQueryService.class, null);
075: AgentIdentificationService agentIDService = (AgentIdentificationService) serviceBroker
076: .getService(this , AgentIdentificationService.class,
077: null);
078: if (agentIDService != null) {
079: agentID = agentIDService.getMessageAddress().toString();
080:
081: // Release the agentIDService right away, since we don't need it any more
082: serviceBroker.releaseService(this ,
083: AgentIdentificationService.class, agentIDService);
084: agentIDService = null;
085: }
086: }
087:
088: /**
089: * Whenever you have a load() method, you should have an unload, to release services.
090: */
091: public void unload() {
092: if (blackboardQueryService != null) {
093: serviceBroker.releaseService(this ,
094: BlackboardQueryService.class,
095: blackboardQueryService);
096: blackboardQueryService = null;
097: }
098: super .unload();
099: }
100:
101: /**
102: * This servlet listens at "/pizza".
103: */
104: protected String getPath() {
105: return "/pizza";
106: }
107:
108: /**
109: * Using an inner class to implement the Servlet interface
110: * provides a useful design pattern.
111: */
112: protected Servlet createServlet() {
113: return new PizzaWorker();
114: }
115:
116: /**
117: * Inner class that's registered as the servlet.
118: */
119: protected class PizzaWorker extends HttpServlet {
120: // Often we want the servlet to behave identically for Get or Post
121: public void doGet(HttpServletRequest request,
122: HttpServletResponse response) throws IOException,
123: ServletException {
124: doPost(request, response);
125: }
126:
127: public void doPost(HttpServletRequest request,
128: HttpServletResponse response) throws IOException,
129: ServletException {
130: new PizzaFormatter(request, response);
131: }
132: }
133:
134: /**
135: * Worker class that actually produces HTML for the servlet.
136: */
137: protected class PizzaFormatter {
138: public PizzaFormatter(HttpServletRequest request,
139: HttpServletResponse response) throws IOException,
140: ServletException {
141: execute(response);
142: }
143:
144: /**
145: * Write the servlet response into the given response's stream.
146: */
147: public void execute(HttpServletResponse response)
148: throws IOException {
149: response.setContentType("text/html");
150:
151: // This worker only has one call to PrintWriter.print, with the sub-methods
152: // calling StringBuffer.append()...
153: // An alternative approach would be to keep an inner-class member variable
154: // for the PrintWriter, and instead of those buf.append calls, the sub-methods
155: // could write directly to the output stream.
156: PrintWriter out = response.getWriter();
157: out
158: .print("<html><head><title>" + "The "
159: + Constants.PIZZA
160: + " Party"
161: + "</title></head>"
162: + "<body>"
163: + "<p/><center><h1>"
164: + Constants.PIZZA
165: + " Party Planner Notes</h1></center><p/>"
166: + "<center><a href=\"/$"
167: + agentID
168: + "/list\">"
169: + agentID
170: + "</a> is having a "
171: + Constants.PIZZA
172: + " party, and inviting everyone on her \"buddy list\", the people in the <a href=\"/$"
173: + agentID
174: + "/communityViewer?community="
175: + Constants.COMMUNITY
176: + "\">"
177: + Constants.COMMUNITY
178: + "</a> community. She sends them a Relay, with the invitation: `"
179: + Constants.INVITATION_QUERY
180: + "'. After waiting a little while for them to reply, she will find a "
181: + Constants.PIZZA
182: + " parlor, and order them each the kind of "
183: + Constants.PIZZA
184: + " that they prefer - if she can find "
185: + Constants.PIZZA
186: + " parlors to satisfy her guests!"
187: + "<br><br>Parlors each have a Kitchen with specific capabilities. Some serve "
188: + Constants.MEAT_PIZZA
189: + ", some "
190: + Constants.VEGGIE_PIZZA
191: + ", and some both. "
192: + agentID
193: + " wants to order all her "
194: + Constants.PIZZA
195: + " from one parlor. If the parlor can't deliver the "
196: + Constants.PIZZA
197: + " she wants, that order Task fails. "
198: + agentID
199: + " will try to find another "
200: + Constants.PIZZA
201: + " provider, if she knows how to do Service Discovery...."
202: + "</center><br><br>"
203: + "<center><b>RSVP from each invited guest, invited by host "
204: + agentID + "</b>:</center><p/>"
205: + getHtmlForPreferences() + "<br>"
206: + getHtmlForOrders()
207: + "<br><hr><center>Status at: "
208: + new Date(System.currentTimeMillis())
209: + "</center></body>" + "</html>\n");
210: out.flush();
211: }
212:
213: /**
214: * Get HTML to represent the Orders that have been placed.
215: * @return HTML for output
216: */
217: protected String getHtmlForOrders() {
218: // Get the Expansion of the root Task of verb Order.
219: // Note that this is a typical servlet, that has no subscriptions.
220: // Instead, it just wants to display a snapshot of the current
221: // state, so it uses the BlackboardQueryService.
222: Collection pizzaOrders = blackboardQueryService
223: .query(new UnaryPredicate() {
224: public boolean execute(Object o) {
225: if (o instanceof Expansion) {
226: Task task = ((Expansion) o).getTask();
227: return task.getVerb().equals(
228: Constants.Verbs.ORDER);
229: }
230: return false;
231: }
232: });
233:
234: if (pizzaOrders.isEmpty()) {
235: return "<center><b>No Orders placed yet.</b></center>";
236: } else {
237: Iterator iter = pizzaOrders.iterator();
238: StringBuffer buf = new StringBuffer();
239:
240: boolean orderOK = true; // if order sent, did it succeed
241: boolean orderSent = true;
242:
243: String head = ""; // Fill in header once we have the expansion
244: // Loop over the Expansions (expect only one)
245: while (iter.hasNext()) {
246: Expansion exp = (Expansion) iter.next();
247:
248: buf.append("<center>");
249: buf
250: .append("<table border=\"1\"><tr><th>Servings</th><th>Type</th><th>Ordered From</th><th>Status</th></tr>");
251: Enumeration en = exp.getWorkflow().getTasks();
252: // Loop over the sub-tasks
253: while (en.hasMoreElements()) {
254: buf.append("<tr>");
255: Task t = (Task) en.nextElement();
256: buf.append("<td>");
257: // Number of servings of pizzas ordered
258: double qty = t
259: .getPreferredValue(AspectType.QUANTITY);
260: buf.append(String.valueOf(qty));
261: buf.append("</td>");
262: buf.append("<td>");
263: // Kind of pizza ordered
264: buf.append(t.getDirectObject()
265: .getItemIdentificationPG()
266: .getItemIdentification());
267: buf.append("</td>");
268: buf.append("<td>");
269:
270: Allocation pe = (Allocation) t.getPlanElement();
271: if (pe != null) {
272: // Store ordered from
273: String store = ((Allocation) pe).getAsset()
274: .getItemIdentificationPG()
275: .getItemIdentification();
276: // Link store name to that Agent's list of servlets.
277: // FIXME: URLEncode.encode(store, "UTF-8") --- to be safe, should encode these arbitrary strings...
278: buf.append("<a href=\"/$" + store
279: + "/list\">" + store + "</a>");
280: buf.append("</td>");
281:
282: // Order status
283: boolean subSucc = pe.getReportedResult()
284: .isSuccess();
285: buf.append("<td>");
286:
287: // Link the word order to the /tasks servlet for the Order Task ordering this
288: // kind of pizza. Color code text by success result.
289: String orderlink = "<a href=\"/$" + agentID
290: + "/tasks?mode=3&uid=" + t.getUID()
291: + "\">Order</a>";
292: if (subSucc) {
293: buf
294: .append("<font color=green>"
295: + orderlink
296: + " filled!</font>");
297: } else {
298: orderOK = false;
299: // Provider failed this order task. Say so, with link to explanation
300: buf
301: .append("<font color=red>"
302: + orderlink
303: + " FAILed!<a href=\"#why\">*</a></font>");
304: // FIXME: Here, we could indicate if there is
305: // an outstanding FindProviders task, including
306: // the Role / exclusions if any
307: }
308: } else {
309: orderOK = false;
310: orderSent = false;
311: buf
312: .append("[Order not sent yet.]</td><td> ");
313: // FIXME: Here, we could indicate if there is
314: // an outstanding FindProviders task, including
315: // the Role / exclusions if any
316: }
317: buf.append("</td>");
318: buf.append("</tr>");
319: }
320: buf.append("</table>");
321:
322: // If an order failed, include the footnote explaining why
323: if (!orderOK)
324: buf
325: .append("<br><a name=\"why\"/>* This failure may be because the parlor does not have the topping ordered.");
326:
327: buf.append("</center>");
328:
329: // Create the table title: Link the word order to the root Pizza order task in the
330: // PlanViewerServlet (/tasks)
331: // FIXME: encode UID --- to be safe, should encode these arbitrary strings...
332: // FIXME: URLEncode.encode(agentID, "UTF-8")
333: head = "<center><b><a href=\"/$" + agentID
334: + "/tasks?mode=3&uid="
335: + exp.getTask().getUID() + "\">Order</a> ";
336: if (orderSent) {
337: if (orderOK)
338: head += "Placed <font color=green>Successfully</font> - Party is on!</b></center><p/>";
339: else
340: head += "<font color=red>Failed</font> - guests will not be happy!</b></center><p/>";
341: } else {
342: head += "not yet placed.</b></center>";
343: }
344: } // loop over expansions
345:
346: return head + buf.toString();
347: } // had some Pizza Order task expansions
348: } // end of getHtmlForOrders
349:
350: /**
351: * Query the Blackboard for the {@link PizzaPreferences} object,
352: * and display the contents in a Table.
353: */
354: protected String getHtmlForPreferences() {
355: Collection pizzaPreferences = blackboardQueryService
356: .query(new UnaryPredicate() {
357: public boolean execute(Object o) {
358: return (o instanceof PizzaPreferences);
359: }
360: });
361:
362: if (pizzaPreferences.isEmpty()) {
363: // Link the word friends to the Community Viewer servlet showing the membership
364: // in the community. Remember that this membership will be updated as people show up.
365: // FIXME: URLEncode.encode(agentID, "UTF-8") --- to be safe, should encode these arbitrary strings...
366: return "<center><b>Waiting for invitiation RSVPs from <a href=\"/$"
367: + agentID
368: + "/communityViewer?community="
369: + Constants.COMMUNITY
370: + "\">friends</a>....</b></center>";
371: } else {
372: // Our subscription is a Collection, but we only expect one
373: // PizzaPreferences object -- so we just look at the first.
374: PizzaPreferences prefs = (PizzaPreferences) pizzaPreferences
375: .iterator().next();
376: StringBuffer buf = new StringBuffer();
377: buf.append("<table border=1 align=center>");
378: buf.append("<tr>");
379: buf.append("<th>");
380: buf.append("Friend");
381: buf.append("</th>");
382: buf.append("<th>");
383: buf.append("Preference");
384: buf.append("</th>");
385: buf.append("</tr>");
386: // Add one row per friend
387: for (Iterator iter = new TreeSet(prefs.getFriends())
388: .iterator(); iter.hasNext();) {
389: buf.append("<tr>");
390:
391: buf.append("<td>");
392: String friend = (String) iter.next();
393:
394: // Link the name of the friend to that Agent's list of servlets
395: // FIXME: URLEncode.encode(friend, "UTF-8") --- to be safe, should encode these arbitrary strings...
396: buf.append("<a href=\"/$" + friend + "/list\">"
397: + friend + "</a>");
398: buf.append("</td>");
399:
400: buf.append("<td>");
401: String preference = prefs
402: .getPreferenceForFriend(friend);
403: buf.append(preference);
404: buf.append("</td>");
405:
406: buf.append("</tr>");
407: }
408: buf.append("</table>");
409: return buf.toString();
410: }
411: }
412: }
413: }
|