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.glm.plugins.tools;
028:
029: import java.awt.BorderLayout;
030: import java.awt.event.ActionEvent;
031: import java.awt.event.ActionListener;
032: import java.util.Collection;
033: import java.util.Enumeration;
034: import java.util.HashSet;
035: import java.util.Iterator;
036: import java.util.Vector;
037:
038: import javax.swing.JButton;
039: import javax.swing.JFrame;
040: import javax.swing.JLabel;
041: import javax.swing.JPanel;
042: import javax.swing.JTextField;
043:
044: import org.cougaar.glm.callback.GLMOrganizationCallback;
045: import org.cougaar.glm.callback.GLMOrganizationListener;
046: import org.cougaar.glm.ldm.Constants;
047: import org.cougaar.glm.ldm.asset.Organization;
048: import org.cougaar.glm.parser.GLMTaskParser;
049: import org.cougaar.glm.util.AssetUtil;
050: import org.cougaar.lib.filter.UTILPluginAdapter;
051: import org.cougaar.lib.util.UTILAllocate;
052: import org.cougaar.planning.ldm.plan.Allocation;
053: import org.cougaar.planning.ldm.plan.NewTask;
054: import org.cougaar.planning.ldm.plan.PlanElement;
055: import org.cougaar.planning.ldm.plan.Relationship;
056: import org.cougaar.planning.ldm.plan.RelationshipSchedule;
057: import org.cougaar.planning.ldm.plan.Role;
058: import org.cougaar.planning.ldm.plan.Task;
059: import org.cougaar.util.ConfigFinder;
060: import org.cougaar.util.TimeSpan;
061:
062: /**
063: * <pre>
064: * Parses an XML file defining tasks to send to other clusters.
065: * Creates tasks defined in the XML file and sends them
066: * to any subordinate, supporting, or provider clusters.
067: *
068: * Pops up a dialog box which provides a way to specify the
069: * XML file. This defaults to whatever is in the ClusterInputFile
070: * parameter.
071: *
072: * In addition to sending tasks, they can also be rescinded.
073: * Tasks that have been sent are rescinded one at a time, with
074: * each button press.
075: *
076: * Implements the org listener interface so it can get all
077: * reported orgs.
078: *
079: * (This code evolved from a version in the COUGAAR tree.)
080: * </pre>
081: */
082: public class GLMStimulatorPlugin extends UTILPluginAdapter implements
083: GLMOrganizationListener {
084:
085: /** Add the filter for all organizations... */
086: public void setupFilters() {
087: super .setupFilters();
088: addFilter(myOrgCallback = createOrganizationCallback());
089: glmAssetUtil = new AssetUtil(logger);
090: }
091:
092: /** Filter out/listen for all organizations... */
093: protected GLMOrganizationCallback getOrganizationCallback() {
094: return myOrgCallback;
095: }
096:
097: /** Create the filter for all organizations... */
098: protected GLMOrganizationCallback createOrganizationCallback() {
099: return new GLMOrganizationCallback(this , logger);
100: }
101:
102: /** get Organizations */
103: public Collection getOrgs() {
104: return myOrgCallback.getSubscription().getCollection();
105: }
106:
107: /**
108: * <pre>
109: * Are there any subordinates yet?
110: *
111: * This could be false if this plugin's cluster is declared
112: * early in the node .ini file.
113: *
114: * @return true if any subordinates have reported.
115: * </pre>
116: */
117: protected boolean subordinatesHaveReported() {
118: return (!myOrgCallback.getSubscription().isEmpty());
119: }
120:
121: /**
122: * needed to implement the GLMOrganizationListener interface.
123: * Does nothing.
124: *
125: * @see org.cougaar.glm.callback.GLMOrganizationListener
126: */
127: public void handleChangedOrganization(Enumeration e) {
128: }
129:
130: /**
131: * needed to implement the GLMOrganizationListener interface.
132: * Does nothing.
133: *
134: * @see org.cougaar.glm.callback.GLMOrganizationListener
135: */
136: public void handleNewOrganization(Enumeration e) {
137: if (!reportedSeenOrgs && myGLMListener != null) {
138: myGLMListener.indicateReady();
139: reportedSeenOrgs = true;
140: }
141: }
142:
143: boolean reportedSeenOrgs = false;
144:
145: /**
146: * <pre>
147: * Helper function to publish a plan element.
148: *
149: * Expands the direct object and publishes any assets it finds there.
150: *
151: * Publishes the tasks before the allocations, since they
152: * would be automatically removed from the log plan otherwise.
153: *
154: * Also can provide debug output about whether the task holds a passenger.
155: *
156: * </pre>
157: * @param pe the plan element to publish
158: */
159: protected void publishMyPlanElement(PlanElement pe) {
160: if (isInfoEnabled())
161: info(getName() + ".publishMyPlanElement : Publishing " + pe
162: + " of task " + pe.getTask());
163:
164: // Order matters here!
165: publishAdd(pe.getTask());
166: Vector createdObjects;
167: if (!glmAssetUtil.isPassenger(pe.getTask().getDirectObject())) {
168: if (isInfoEnabled())
169: info("not a passenger");
170: createdObjects = glmAssetUtil.ExpandAsset(getLDMService()
171: .getLDM().getFactory(), pe.getTask()
172: .getDirectObject());
173: } else {
174: if (isInfoEnabled())
175: info("is a passenger");
176: createdObjects = new Vector();
177: createdObjects.add(pe.getTask().getDirectObject());
178: }
179:
180: for (Iterator i = createdObjects.iterator(); i.hasNext();)
181: publishAdd(i.next());
182: publishAdd(pe);
183: }
184:
185: /**
186: * Actually create the GUI window. Two buttons, a text input box,
187: * and a status line.
188: *
189: * @param infile - default input file. From param
190: * "ClusterInputFile" defined in <ClusterName>.env.xml.
191: */
192: protected void createGUI(String infile) {
193: frame = new JFrame(getClusterName() + " - GLMStimulatorPlugin");
194: frame.getContentPane().setLayout(new BorderLayout());
195:
196: JPanel panel = new JPanel();
197: JButton button = new JButton("Send Tasks");
198: JButton button2 = new JButton("Rescind Tasks");
199: JTextField text = new JTextField(infile);
200: JLabel label = new JLabel(
201: "No subordinates to task yet. Wait until they report.");
202: myGLMListener = new GLMButtonListener(label, text);
203: button.addActionListener(myGLMListener);
204: button2.addActionListener(myGLMListener);
205:
206: panel.add(button);
207: panel.add(button2);
208: frame.getRootPane().setDefaultButton(button); // hitting return sends the tasks
209: panel.add(text);
210:
211: frame.getContentPane().add("Center", panel);
212: frame.getContentPane().add("South", label);
213: frame.pack();
214: frame.setVisible(true);
215: }
216:
217: /** needed so internal class can find the xml task file */
218: public ConfigFinder getFinder() {
219: return getConfigFinder();
220: }
221:
222: /**
223: * <pre>
224: * An ActionListener that listens to the GLM buttons.
225: *
226: * If the Send Tasks button is clicked, sends the tasks from
227: * the file in the text field of the dialog.
228: *
229: * Otherwise, if the rescind button is clicked, rescinds the
230: * last task sent.
231: *
232: * </pre>
233: */
234: class GLMButtonListener implements ActionListener {
235: GLMButtonListener(JLabel label, JTextField text) {
236: this .label = label;
237: this .text = text;
238: }
239:
240: protected JLabel label;
241: protected JTextField text;
242:
243: public void actionPerformed(ActionEvent e) {
244: String lnfName = e.getActionCommand();
245:
246: if (lnfName.equals("Send Tasks")) {
247: // Get name of XML data file
248: if (getFinder().locateFile(text.getText()) == null) {
249: label
250: .setText("Couldn't find file. Check path, try again.");
251: return;
252: }
253:
254: sendTasks(label, text.getText());
255: } else {
256: rescindTasks(label);
257: }
258: }
259:
260: public void indicateReady() {
261: label.setText("Ready to send tasks.");
262: }
263: }
264:
265: /**
266: * <pre>
267: * Parses the xml file <code>xmlTaskFile</code> and creates a collection of
268: * the tasks that will be allocated to organizations. Then finds all
269: * subordinate, supporting, and provider organizations and allocates a copy
270: * of each task to each asset.
271: *
272: * Sets the label on the dialog to provide feedback about the success
273: * or failure of this process.
274: *
275: * Note that since this is kicked off from the dialog, we must open a
276: * transaction to do the publishing.
277: *
278: * </pre>
279: * @param label - a label from the dialog used to provide feedback
280: * @param xmlTaskFile - the file that is used to create the tasks
281: */
282: protected void sendTasks(JLabel label, String xmlTaskFile) {
283: Collection tasks = null;
284: tasksSent.clear();
285: if (!subordinatesHaveReported()) {
286: label
287: .setText("No subordinates to task yet. Wait until they report.");
288: } else {
289: try {
290: // Have to do this all within transaction boundary
291:
292: // For some reason, every setXXX (e.g. setVerb) on a NewTask
293: // fires off a changeReport, and these must be inside of a transaction,
294: // apparently. There is no way currently to temporarily turn off this feature.
295: // GWFV 10/11/2000
296:
297: blackboard.openTransaction();
298: // Get the tasks out of the XML file
299: tasks = readXmlTasks(xmlTaskFile);
300: // First find the organizations that we will allocate to.
301: Collection supportedOrgs = getSupportedOrgs();
302: if (supportedOrgs.isEmpty()) {
303: label
304: .setText("No task sent, since no subordinates have reported (yet).");
305: } else {
306: allocateTasks(tasks, supportedOrgs);
307: label
308: .setText("Sent task(s) to "
309: + ((Organization) supportedOrgs
310: .iterator().next())
311: .getItemIdentificationPG()
312: .getItemIdentification()
313: + ((supportedOrgs.size() > 1) ? ", etc. clusters"
314: : " cluster"));
315: }
316: } catch (Exception exc) {
317: System.err.println("Could not send tasks.");
318: System.err.println(exc.getMessage());
319: exc.printStackTrace();
320: } finally {
321: blackboard.closeTransactionDontReset();
322: }
323: }
324: }
325:
326: public static final Role SUPPORTING = Role.getRole("Supporting");
327:
328: /**
329: * Find the organizations that we will allocate to.
330: * @return Collection of supporting, subordinate, and provider
331: * organizations
332: */
333:
334: protected Collection getSupportedOrgs() {
335: Collection orgs = getOrgs();
336: Collection returnedOrgs = new HashSet();
337:
338: for (Iterator iter = orgs.iterator(); iter.hasNext();) {
339: Organization org = (Organization) iter.next();
340: //Look in self orgs relationship schedule
341: if (org.isSelf()) {
342: RelationshipSchedule schedule = org
343: .getRelationshipSchedule();
344:
345: Collection subordinates = org.getSubordinates(
346: TimeSpan.MIN_VALUE, TimeSpan.MAX_VALUE);
347: if (!subordinates.isEmpty()) {
348: for (Iterator it = subordinates.iterator(); it
349: .hasNext();) {
350: returnedOrgs.add(schedule
351: .getOther((Relationship) it.next()));
352: }
353: }
354: // returnedOrgs.addAll (subordinates);
355:
356: Collection supporting = schedule
357: .getMatchingRelationships(SUPPORTING);
358: if (!supporting.isEmpty()) {
359: for (Iterator it = supporting.iterator(); it
360: .hasNext();) {
361: returnedOrgs.add(schedule
362: .getOther((Relationship) it.next()));
363: }
364: }
365: // returnedOrgs.addAll (schedule.getOther(supporting);
366:
367: //Assume providers use provider suffix
368: Collection providers = schedule
369: .getMatchingRelationships(
370: Constants.RelationshipType.PROVIDER_SUFFIX,
371: TimeSpan.MIN_VALUE, TimeSpan.MAX_VALUE);
372:
373: if (!providers.isEmpty()) {
374: for (Iterator it = providers.iterator(); it
375: .hasNext();) {
376: returnedOrgs.add(schedule
377: .getOther((Relationship) it.next()));
378: }
379: }
380: // returnedOrgs.addAll (providers);
381: }
382: }
383: return returnedOrgs;
384: }
385:
386: /**
387: * Allocate the tasks to each supportedOrg.
388: *
389: * @param tasks to send
390: * @param supportedOrgs - the orgs to send them to
391: */
392: protected void allocateTasks(Collection tasks,
393: Collection supportedOrgs) {
394: boolean sentOriginals = false;
395:
396: for (Iterator iter = supportedOrgs.iterator(); iter.hasNext();) {
397: Organization supportedOrg = (Organization) iter.next();
398: for (Iterator iter2 = tasks.iterator(); iter2.hasNext();) {
399: Task task = (Task) iter2.next();
400: Task actualTask = (sentOriginals) ? cloneTask(task)
401: : task;
402: allocateToOrg(actualTask, supportedOrg);
403: }
404: sentOriginals = true;
405: }
406: }
407:
408: /**
409: * Clone the input task
410: *
411: * @param toClone task to copy
412: * @return the copy
413: */
414: protected Task cloneTask(Task toClone) {
415: NewTask task = ldmf.newTask();
416:
417: task.setDirectObject(toClone.getDirectObject());
418: task.setPrepositionalPhrases(toClone.getPrepositionalPhrases());
419: task.setVerb(toClone.getVerb());
420: task.setPlan(toClone.getPlan());
421: task.setPreferences(toClone.getPreferences());
422: task.setPriority(toClone.getPriority());
423: task.setSource(toClone.getSource());
424: return task;
425: }
426:
427: /**
428: * Create and publish the allocation.
429: *
430: * @param task to allocate
431: * @param supportedOrg org to allocate to
432: */
433: protected void allocateToOrg(Task task, Organization supportedOrg) {
434: Allocation alloc = (Allocation) allocHelper.makeAllocation(
435: this , ldmf, ldmf.getRealityPlan(), task, supportedOrg,
436: prefHelper.getReadyAt(task), prefHelper
437: .getBestDate(task),
438: allocHelper.HIGHEST_CONFIDENCE,
439: Constants.Role.TRANSPORTER);
440: if (isInfoEnabled())
441: info(getName() + " allocating to " + supportedOrg);
442:
443: publishMyPlanElement(alloc);
444: tasksSent.add(task);
445: }
446:
447: /**
448: * When the rescind task button is pressed, rescind the task.
449: *
450: * @param label provides way to give feedback
451: */
452: protected void rescindTasks(JLabel label) {
453: if (tasksSent.size() == 0) {
454: label.setText("No tasks to Rescind.");
455: } else {
456: try {
457: blackboard.openTransaction();
458: Iterator iter = tasksSent.iterator();
459: Object removed = iter.next();
460: iter.remove();
461:
462: if (isInfoEnabled())
463: info("GLMStimulatorPlugin - Removing " + removed);
464: publishRemove(removed);
465: tasksSent.remove(removed);
466: label.setText("Rescinded last task. "
467: + tasksSent.size() + " left.");
468: } catch (Exception exc) {
469: logger.error("exception ", exc);
470: } finally {
471: blackboard.closeTransactionDontReset();
472: }
473: }
474: }
475:
476: /**
477: * Reads the ClusterInputFile parameter, brings up the GUI.
478: */
479: public void localSetup() {
480: String infile = null;
481: try {
482: infile = getMyParams().getStringParam("ClusterInputFile");
483: } catch (Exception e) {
484: infile = " ";
485: }
486: createGUI(infile);
487: }
488:
489: /**
490: * Parse the xml file and return the COUGAAR tasks.
491: *
492: * @param xmlTaskFile file defining tasks to stimulate cluster with
493: * @return Collection of tasks defined in xml file
494: */
495: protected Collection readXmlTasks(String xmlTaskFile) {
496: Collection tasks = null;
497: try {
498: GLMTaskParser tp = new GLMTaskParser(xmlTaskFile, ldmf,
499: getAgentIdentifier(), getConfigFinder(),
500: getLDMService().getLDM(), logger);
501: tasks = UTILAllocate.enumToList(tp.getTasks());
502: } catch (Exception ex) {
503: logger.error("Got error reading xml file " + xmlTaskFile,
504: ex);
505: }
506: return tasks;
507: }
508:
509: /** frame for 2-button UI */
510: JFrame frame;
511:
512: /** Collection of tasks that have been sent. Needed for later rescinds */
513: protected Collection tasksSent = new HashSet();
514:
515: /** The callback mediating the org subscription */
516: protected GLMOrganizationCallback myOrgCallback;
517: GLMButtonListener myGLMListener;
518:
519: AssetUtil glmAssetUtil;
520: }
|