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.tools.csmart.ui.console;
028:
029: import org.cougaar.tools.csmart.experiment.Experiment;
030: import org.cougaar.tools.csmart.experiment.HostComponent;
031: import org.cougaar.tools.csmart.experiment.NodeComponent;
032: import org.cougaar.tools.csmart.society.AgentComponent;
033: import org.cougaar.tools.csmart.ui.viewer.CSMART;
034: import org.cougaar.tools.server.ProcessDescription;
035: import org.cougaar.tools.server.RemoteHost;
036: import org.cougaar.util.log.Logger;
037:
038: import javax.swing.*;
039: import java.util.*;
040: import java.io.File;
041:
042: /**
043: * org.cougaar.tools.csmart.ui.console
044: *
045: */
046: public class CSMARTConsoleModel extends Observable implements Observer {
047:
048: /** Used by listeners to determine which event was fired. **/
049: public static final String ADD_GLS_WINDOW = "Add GLS";
050: public static final String APP_SERVERS_REFRESH = "App Servers Refresh";
051: public static final String APP_SERVER_ADDED = "App Server Added";
052: public static final String APP_SERVER_DELETED = "App Server Deleted";
053: public static final String NODE_ADDED = "Node Added";
054: public static final String NODE_REMOVED = "Node Removed";
055: public static final String STOP_EXPERIMENT_TIMER = "Stop Timer";
056: public static final String START_EXPERIMENT_TIMER = "Start Timer";
057: public static final String NEW_EXPERIMENT = "New Experiment";
058:
059: public static final String COMMAND_ARGUMENTS = "Command$Arguments";
060:
061: public static final int DEFAULT_VIEW_SIZE = 300000; // 60 pages of text or 300K
062:
063: private Experiment experiment = null;
064: private transient Logger log;
065: private AppServerSupport appServerSupport;
066: private static Date runStart = null;
067: // set this flag when you first run an experiment
068: // it's purpose is to ignore a non-null experiment if you're only attaching to nodes
069: private boolean usingExperiment = false;
070: private CSMART csmart;
071:
072: // Time in milliseconds between looking for new Nodes to attach to.
073: private static int asPollInterval = 30000;
074: private transient volatile java.util.Timer asPollTimer = null;
075: private TimerTask monitorAppServerTask;
076: private Hashtable nodeModels; // Hashtable of all node name to node models mappings.
077: private Hashtable nodeViews;
078: private Object appServerLock = new Object(); // used to lock nodeToAppServer and appServers
079: private Hashtable nodeToAppServer; // maps node name to AppServerDesc
080: private AppServerList appServers;// known app servers; array of AppServerDesc
081: public int viewSize = DEFAULT_VIEW_SIZE; // default view size for nodes
082: private String selectedNodeName = null; // name of node whose status button is selected
083: private String notifyCondition = "exception";
084: private boolean notifyOnStdErr;
085: private File XMLFile;
086:
087: // Default contact info for the GLSInit UI
088: private String[] glsContactInfo = { "http", "localhost", "8800",
089: "NCA" };
090:
091: public CSMARTConsoleModel(Experiment experiment, CSMART csmart) {
092: this .csmart = csmart;
093: this .runStart = new Date();
094: this .experiment = experiment;
095: this .appServerSupport = new AppServerSupport(this );
096: this .nodeModels = new Hashtable(5);
097: this .nodeViews = new Hashtable(5);
098: this .nodeToAppServer = new Hashtable(5);
099: this .appServers = new AppServerList();
100:
101: if (experiment != null)
102: getAppServersFromExperiment();
103: createLogger();
104: resetASPoller(asPollInterval); // start polling app servers
105: }
106:
107: private void createLogger() {
108: log = CSMART.createLogger(this .getClass().getName());
109: }
110:
111: /**
112: * Add any app servers on the hosts and ports that the experiment
113: * will use to the list of app servers we're monitoring.
114: */
115: private void getAppServersFromExperiment() {
116: HostComponent[] hosts = experiment.getHostComponents();
117: for (int i = 0; i < hosts.length; i++) {
118: String hostName = hosts[i].getShortName();
119: NodeComponent[] nodes = hosts[i].getNodes();
120: for (int j = 0; j < nodes.length; j++) {
121: NodeComponent nodeComponent = nodes[j];
122: Properties properties = getNodeMinusD(nodeComponent,
123: hostName);
124: int port = Experiment.APP_SERVER_DEFAULT_PORT;
125: if (properties != null) {
126: try {
127: String tmp = properties
128: .getProperty(Experiment.CONTROL_PORT);
129: if (tmp != null)
130: port = Integer.parseInt(tmp);
131: } catch (NumberFormatException nfe) {
132: // use default port
133: }
134: if (port < 1)
135: port = Experiment.APP_SERVER_DEFAULT_PORT;
136: }
137: appServerSupport.add(hostName, port);
138: }
139: }
140: }
141:
142: // Find the Node on which the GLSInitServlet is running, if any
143: private String findGLSNode() {
144: // Will have no experiment if just attached to Nodes
145: // Return empty String to avoid null pointer
146: if (experiment == null)
147: return "";
148:
149: HostComponent[] hosts = experiment.getHostComponents();
150: for (int i = 0; i < hosts.length; i++) {
151: NodeComponent[] nodes = hosts[i].getNodes();
152: for (int j = 0; j < nodes.length; j++) {
153: AgentComponent[] agents = nodes[j].getAgents();
154: for (int k = 0; k < agents.length; k++) {
155: AgentComponent agent = agents[k];
156: // FIXME: This assumes that if this agent exists, the servlet exists.
157: // Mostly this is a reasonable shortcut, but not always.
158: // Alternative is to do experiment.getSocietyComponentData
159: // and then walk the ComponentData tree down to AgentsComponentData,
160: // and then do getPluginNames, and look for one that ends with GLSInitServlet
161: if (agent.getShortName().equalsIgnoreCase("NCA")
162: || agent.getShortName().equalsIgnoreCase(
163: "OSD.GOV")) {
164: glsContactInfo[3] = agent.getShortName();
165: glsContactInfo[1] = hosts[i].getShortName();
166:
167: // Pull out the gls servlet protocol and port here too
168: getGLSPort(nodes[j].getArguments());
169:
170: return nodes[j].getShortName();
171: }
172: }
173: }
174: }
175: return "";
176: }
177:
178: public boolean haveAttached() {
179: Collection models = nodeModels.values();
180: Iterator i = models.iterator();
181: while (i.hasNext()) {
182: NodeModel nodeModel = (NodeModel) i.next();
183: String state = nodeModel.getState();
184: if (!state.equals(NodeModel.STATE_INITTED)
185: && !state.equals(NodeModel.STATE_STOPPED))
186: return true;
187: }
188: return false;
189: }
190:
191: public void detachFromSociety() {
192: // This flushes the output and disconnects it
193: appServerSupport.killListeners();
194:
195: setChanged();
196: notifyObservers(STOP_EXPERIMENT_TIMER);
197:
198: updateExperimentStatus(false);
199: }
200:
201: /**
202: * Start or stop the nodes.
203: * Called when the user selects the Run or Stop buttons.
204: *
205: * @param doRun -- The new running state.
206: */
207: public void setRunning(boolean doRun) {
208: if (doRun) {
209: destroyOldNodes(); // remove any GUIs from previous runs
210: if (experiment == null)
211: return; // can't run if no experiment
212: createNodeModels();// create node models if they don't exist
213: startNodes(); // start nodes
214: } else { // stopping
215: stopNodes();
216: }
217: }
218:
219: /**
220: * Start nodes that are not in the Running state.
221: */
222: private void startNodes() {
223: Enumeration en = this .nodeModels.keys();
224: while (en.hasMoreElements()) {
225: Object key = en.nextElement();
226: NodeModel model = (NodeModel) nodeModels.get(key);
227: if (model.getState().equals(NodeModel.STATE_RUNNING))
228: continue;
229: model.start();
230: }
231: setChanged();
232: notifyObservers(START_EXPERIMENT_TIMER); // start Experiment timer
233: }
234:
235: /**
236: * Tell the node model to stop the node, but the GUIs are not updated
237: * until the ConsoleNodeListener gets a node destroyed message.
238: * Only act on nodes that are in the "Running" state.
239: */
240: public void stopNodes() {
241: // FIXME: Really should call glsClient.stop() if we have a glsClient
242: Enumeration en = this .nodeModels.elements();
243: while (en.hasMoreElements()) {
244: NodeModel nodeModel = (NodeModel) en.nextElement();
245: if (nodeModel.getState().equals(NodeModel.STATE_RUNNING)) {
246: if (log.isDebugEnabled()) {
247: log.debug("Stopping Node: "
248: + nodeModel.getNodeName());
249: }
250: nodeModel.stop();
251: }
252: }
253: setChanged();
254: notifyObservers(STOP_EXPERIMENT_TIMER); // stop experiment timer
255: }
256:
257: /**
258: * This is called from the node start thread, when a node
259: * is successfully started.
260: * The node view and node status button are created if necessary;
261: * and CSMARTConsoleView is notified, and adds them to the display.
262: */
263: public void createGUI(String nodeName) {
264: SwingUtilities.invokeLater(new CreateNodeViewThread(nodeName,
265: (NodeModel) nodeModels.get(nodeName), nodeViews));
266: }
267:
268: class CreateNodeViewThread implements Runnable {
269: String nodeName;
270: NodeModel nodeModel;
271: Hashtable nodeViews;
272:
273: public CreateNodeViewThread(String nodeName,
274: NodeModel nodeModel, Hashtable nodeViews) {
275: this .nodeName = nodeName;
276: this .nodeModel = nodeModel;
277: this .nodeViews = nodeViews;
278: }
279:
280: public void run() {
281: if (nodeViews.get(nodeName) == null) {
282: NodeView view = new NodeView(nodeModel);
283: nodeViews.put(nodeName, view);
284: setChanged();
285: notifyObservers(view);
286: }
287: String glsNode = findGLSNode();
288: // if(glsNode != null) {
289: // if(runningNodes.containsKey(glsNode)) {
290: // setChanged();
291: // notifyObservers(ADD_GLS_WINDOW);
292: // }
293: // }
294:
295: // notify observers if added GLS window
296: // TODO: is this right?
297: NodeModel model = (NodeModel) nodeModels.get(glsNode);
298: if (model != null) {
299: // record info from the Node model on the protocol, host, port, and agent for the GLS servlet
300: recordGLSContactInfo(model);
301: setChanged();
302: notifyObservers(ADD_GLS_WINDOW);
303: }
304: }
305: }
306:
307: // create node models for nodes that do not already exist
308: private void createNodeModels() {
309: // Create all Node Models, either from the Experiment or from the XML file?
310: usingExperiment = true;
311:
312: HostComponent[] hostsToRunOn = experiment.getHostComponents();
313: for (int i = 0; i < hostsToRunOn.length; i++) {
314: String hostName = hostsToRunOn[i].getShortName();
315: NodeComponent[] nodesToRun = hostsToRunOn[i].getNodes();
316: for (int j = 0; j < nodesToRun.length; j++) {
317: NodeComponent nodeComponent = nodesToRun[j];
318: String nodeName = nodeComponent.getShortName();
319: if (nodeModels.get(nodeName) != null)
320: continue; // don't create node model if one already exists
321:
322: // get arguments from NodeComponent and pass them to ApplicationServer
323: // note that these properties augment any properties that
324: // are passed to the server in a properties file on startup
325: Properties properties = getNodeMinusD(nodeComponent,
326: hostName);
327: List args = getNodeArguments(nodeComponent);
328:
329: if (experiment.getTrialID() != null) {
330: properties.setProperty(Experiment.EXPERIMENT_ID,
331: experiment.getTrialID());
332: } else {
333: log
334: .warn("Null trial ID for experiment. This is fine if the experiment is running from XML.");
335: }
336: // get the app server to use
337: int port = getAppServerPort(properties);
338: requestAppServerAdd(hostName, port);
339: AppServerDesc appServerDesc = getAppServer(hostName,
340: port);
341: if (appServerDesc == null) {
342: continue;
343: }
344: NodeInfo info = new NodeInfo(appServerDesc.appServer,
345: nodeName, hostName, properties, args);
346: NodeModel nodeModel = new NodeModel(info, this );
347: nodeModel.addObserver(this );
348: this .nodeModels.put(nodeName, nodeModel);
349: }
350: }
351: }
352:
353: public static int getAppServerPort(Properties properties) {
354: int port = Experiment.APP_SERVER_DEFAULT_PORT;
355: if (properties == null)
356: return port;
357: try {
358: String tmp = properties
359: .getProperty(Experiment.CONTROL_PORT);
360: if (tmp != null)
361: port = Integer.parseInt(tmp);
362: } catch (NumberFormatException nfe) {
363: // use default port
364: }
365: if (port < 1)
366: port = Experiment.APP_SERVER_DEFAULT_PORT;
367: return port;
368: }
369:
370: /**
371: * Sets an experiment for this model. This is used when an experiment is
372: * loaded from the Console.
373: * @param experiment
374: */
375:
376: public void setExperiment(Experiment experiment) {
377: this .experiment = experiment;
378: getAppServersFromExperiment();
379: destroyOldNodes(); // remove any GUIs from previous runs
380: setChanged();
381: notifyObservers(NEW_EXPERIMENT);
382: }
383:
384: /**
385: * Update the gui controls when experiments (or attached nodes)
386: * are started or stopped.
387: */
388: private void updateExperimentStatus(boolean isRunning) {
389: if (experiment != null && usingExperiment) {
390: if (!isRunning) {
391: experiment.experimentStopped();
392: if (csmart != null)
393: csmart.removeRunningExperiment(experiment);
394: } else {
395: if (csmart != null) {
396: csmart.addRunningExperiment(experiment);
397: }
398: }
399: experiment.getSocietyComponent().setRunning(isRunning);
400: }
401: }
402:
403: /**
404: * Destroy any previous node GUIs and models before running again.
405: * Destroys nodes whose state is STOPPED.
406: */
407: private void destroyOldNodes() {
408: Enumeration en = nodeModels.keys();
409: while (en.hasMoreElements()) {
410: String nodeName = (String) en.nextElement();
411: NodeModel model = (NodeModel) nodeModels.get(nodeName);
412: if (model.getState().equals(NodeModel.STATE_STOPPED)) {
413: setChanged();
414: notifyObservers(new NodeChange(nodeName, NODE_REMOVED));
415: nodeModels.remove(nodeName);
416: nodeViews.remove(nodeName);
417: }
418: }
419: }
420:
421: public int getASPollInterval() {
422: return asPollInterval;
423: }
424:
425: /**
426: * Cancel old AppServer poller if any.
427: * If new interval is greater than 0,
428: * start a new timer to poll every interval milliseconds.
429: */
430: public void resetASPoller(int newInterval) {
431: if (asPollTimer != null) {
432: if (log.isDebugEnabled()) {
433: log.debug("Canceling old ASPoller timer");
434: }
435: asPollTimer.cancel();
436: monitorAppServerTask.cancel();
437: }
438:
439: if (newInterval != 0) {
440: asPollInterval = newInterval;
441: if (log.isDebugEnabled()) {
442: log.debug("creating new ASPoller with interval "
443: + asPollInterval);
444: }
445:
446: // contact known app servers periodically to get lists of their nodes
447: // and update controls when new nodes detected
448: monitorAppServerTask = new TimerTask() {
449: public void run() {
450: appServerSupport.refreshAppServers();
451: }
452: };
453:
454: asPollTimer = new java.util.Timer();
455: asPollTimer.schedule(monitorAppServerTask, new Date(),
456: asPollInterval);
457: }
458: }
459:
460: /**
461: * Get node -d arguments.
462: * Substitute host name for $HOST value if it occurs.
463: * @param nc for which to get the -d arguments
464: * @return properties the -d arguments
465: */
466: public Properties getNodeMinusD(NodeComponent nc, String hostName) {
467: Properties result = new Properties();
468: Properties props = nc.getArguments();
469: boolean foundclass = false;
470: for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
471: String pname = (String) e.nextElement();
472: if (pname.equals(COMMAND_ARGUMENTS))
473: continue;
474: if (pname.equals(Experiment.BOOTSTRAP_CLASS))
475: foundclass = true;
476: String value = props.getProperty(pname);
477:
478: // Allow $HOST to stand for hostName
479: int index = value.indexOf("$HOST");
480: if (index != -1)
481: value = value.substring(0, index) + hostName
482: + value.substring(index + 5);
483:
484: // Could add other substitutions here - for the node name, for example
485:
486: result.put(pname, value);
487: }
488: // make sure that the classname is "Node"
489: //
490: // this can be removed once the CMT and all "node.props"
491: // are sure to have this property.
492:
493: if (foundclass == false)
494: result.put(Experiment.BOOTSTRAP_CLASS,
495: Experiment.DEFAULT_BOOTSTRAP_CLASS);
496: return result;
497: }
498:
499: private List getNodeArguments(NodeComponent nc) {
500: Properties props = nc.getArguments();
501: String commandArguments = props.getProperty(COMMAND_ARGUMENTS);
502: if (commandArguments == null
503: || commandArguments.trim().equals("")) {
504: // Warning: If you are running the bootstrapper and supply
505: // nothing here, nothing will run!
506: // So if were using the default bootstrapper and have no arguments
507: // Give it an argument with the default node class
508: if (props.getProperty(Experiment.BOOTSTRAP_CLASS) == null
509: || props.getProperty(Experiment.BOOTSTRAP_CLASS)
510: .equals(Experiment.DEFAULT_BOOTSTRAP_CLASS))
511: return Collections
512: .singletonList(Experiment.DEFAULT_NODE_CLASS);
513: return Collections.EMPTY_LIST;
514: }
515: StringTokenizer tokens = new StringTokenizer(commandArguments
516: .trim(), "\n");
517: String[] result = new String[tokens.countTokens()];
518: for (int i = 0; i < result.length; i++) {
519: result[i] = tokens.nextToken();
520: }
521:
522: List l = Arrays.asList(result);
523: return l;
524: }
525:
526: public NodeModel getNodeModel(String name) {
527: return (NodeModel) this .nodeModels.get(name);
528: }
529:
530: public ArrayList getNodeModels() {
531: return new ArrayList(nodeModels.values());
532: }
533:
534: // New Application Server Support
535: // We maintain a list of known app servers
536: // and a hashtable of node-to-appServer mappings.
537: // The separate list of app Servers is needed
538: // because we may have app Servers that do not have nodes.
539:
540: /**
541: * User selected "add app server" menu item or
542: * we're creating a node that specified this app server.
543: * Notify AppServerSupport.
544: */
545: public void requestAppServerAdd(String hostName, int port) {
546: setChanged();
547: notifyObservers(new AppServerRequest(hostName, port,
548: AppServerRequest.ADD));
549: }
550:
551: /**
552: * Called by AppServerSupport to add an app server.
553: */
554: public void addAppServer(AppServerDesc desc) {
555: synchronized (appServerLock) {
556: appServers.add(desc);
557: setChanged();
558: notifyObservers(APP_SERVER_ADDED);
559: }
560: }
561:
562: /**
563: * Add node to node-to-appServer mapping.
564: */
565: public void addNodeToAppServerMapping(String name,
566: AppServerDesc appServerDesc) {
567: // first ensure that app server is in list of app servers
568: addAppServer(appServerDesc);
569: boolean nodeAdded = false;
570: synchronized (appServerLock) {
571: if (nodeToAppServer.get(name) == null)
572: nodeAdded = true;
573: nodeToAppServer.put(name, appServerDesc);
574: }
575: if (nodeAdded) {
576: setChanged();
577: notifyObservers(NODE_ADDED);
578: }
579: }
580:
581: /**
582: * Return array list of AppServerDesc of known app servers.
583: */
584: public ArrayList getAppServers() {
585: synchronized (appServerLock) {
586: return appServers;
587: }
588: }
589:
590: /**
591: * Get app server description for a node.
592: */
593: public AppServerDesc getAppServer(String name) {
594: AppServerDesc server = null;
595: synchronized (appServerLock) {
596: server = (AppServerDesc) nodeToAppServer.get(name);
597: }
598: return server;
599: }
600:
601: /**
602: * Get app server on this host and port.
603: */
604: public AppServerDesc getAppServer(String hostName, int port) {
605: AppServerDesc foundDesc = null;
606: synchronized (appServerLock) {
607: for (int i = 0; i < appServers.size(); i++) {
608: AppServerDesc desc = (AppServerDesc) appServers.get(i);
609: if (desc.hostName.equals(hostName) && desc.port == port) {
610: foundDesc = desc;
611: break;
612: }
613: }
614: }
615: return foundDesc;
616: }
617:
618: /**
619: * Delete app server; just remove entries for this app server.
620: */
621: public void appServerDelete(AppServerDesc appServerDesc) {
622: synchronized (appServerLock) {
623: Enumeration nodes = nodeToAppServer.keys();
624: while (nodes.hasMoreElements()) {
625: String nodeName = (String) nodes.nextElement();
626: AppServerDesc desc = getAppServer(nodeName);
627: if (desc.hostName.equals(appServerDesc.hostName)
628: && desc.port == appServerDesc.port) {
629: nodeToAppServer.remove(nodeName);
630: // if app server had any running nodes, then treat them as stopped
631: NodeModel nodeModel = (NodeModel) nodeModels
632: .get(nodeName);
633: if (nodeModel != null
634: && nodeModel.getState().equals(
635: NodeModel.STATE_RUNNING)) {
636: nodeModel.stop();
637: }
638: }
639: }
640: appServers.remove(appServerDesc);
641: }
642: setChanged();
643: notifyObservers(APP_SERVER_DELETED);
644: }
645:
646: /**
647: * Refresh app servers; notify AppServerSupport.
648: */
649: public void refreshAppServers() {
650: setChanged();
651: notifyObservers(APP_SERVERS_REFRESH);
652: }
653:
654: /**
655: * Kill all processes known by all known AppServers,
656: * whether or not we're attached to them.
657: */
658: public void killAllProcesses() {
659: appServerSupport.killAllProcesses();
660: }
661:
662: /**
663: * Attach to a running node.
664: * Create a node model and node view.
665: */
666: public void attachToNode(String name) {
667: // get the app server for the node
668: RemoteHost appServer = getAppServer(name).appServer;
669: // get information about the node from the app server
670: ProcessDescription pd = null;
671: try {
672: pd = appServer.getProcessDescription(name);
673: } catch (Exception e) {
674: if (log.isErrorEnabled()) {
675: log.error("Exception attaching to: " + name, e);
676: }
677: // Remove this node from nodeToAppServer since we can't access it
678: nodeToAppServer.remove(name);
679: }
680: if (pd == null)
681: return;
682: Properties properties = (Properties) pd.getJavaProperties();
683: String hostName = (String) properties
684: .get(Experiment.NAME_SERVER);
685: // Must remove the port numbers...
686: int pStart = hostName.indexOf(':');
687: if (pStart != -1) {
688: hostName = hostName.substring(0, pStart).trim();
689: }
690: java.util.List args = pd.getCommandLineArguments();
691: NodeInfo ni = new NodeInfo(appServer, name, hostName,
692: properties, args);
693: NodeModel nodeModel = new NodeModel(ni, this );
694: nodeModel.addObserver(this );
695: nodeModels.put(name, nodeModel);
696: // attach to running node
697: // node view is created and added when attach succeeds
698: nodeModel.attach();
699: }
700:
701: /**
702: * This is called from the node start thread, when attaching to a node fails.
703: * It uses a Swing thread to remove the node model, so that we don't
704: * have to synchronize the nodeModels hashtable.
705: * @param nodeName name of node to remove
706: */
707: public void removeNodeModel(String nodeName) {
708: SwingUtilities.invokeLater(new NodeModelRemover(nodeName,
709: nodeModels));
710: }
711:
712: class NodeModelRemover implements Runnable {
713: String nodeName;
714: Hashtable nodeModels;
715:
716: public NodeModelRemover(String nodeName, Hashtable nodeModels) {
717: this .nodeName = nodeName;
718: this .nodeModels = nodeModels;
719: }
720:
721: public void run() {
722: nodeModels.remove(nodeName);
723: }
724: }
725:
726: /**
727: * Get list of nodes to which CSMART is not attached.
728: * Returns an array of Strings (node names).
729: */
730: public ArrayList getUnattachedNodes() {
731: return appServerSupport.getUnattachedNodes();
732: }
733:
734: /**
735: * Get list of nodes to which CSMART is attached.
736: * Returns an array of Strings (node names).
737: */
738: public ArrayList getAttachedNodes() {
739: ArrayList nodes = null;
740: synchronized (appServerLock) {
741: nodes = new ArrayList(nodeToAppServer.keySet());
742: }
743: return nodes;
744: }
745:
746: /**
747: * Add an unique app server to the list of known app servers.
748: * This assumes that there is one app server per host & port.
749: */
750: class AppServerList extends ArrayList {
751: public void add(AppServerDesc descToAdd) {
752: if (descToAdd == null)
753: return;
754: for (int i = 0; i < size(); i++) {
755: AppServerDesc desc = (AppServerDesc) get(i);
756: if (descToAdd.hostName.equals(desc.hostName)
757: && descToAdd.port == desc.port) {
758: return;
759: }
760: }
761: super .add(descToAdd);
762: }
763: }
764:
765: public Experiment getExperiment() {
766: return experiment;
767: }
768:
769: /**
770: * Set the view size for all the nodes.
771: */
772: public void setViewSize(int size) {
773: Enumeration en = this .nodeViews.elements();
774: while (en.hasMoreElements()) {
775: NodeView nodeView = (NodeView) en.nextElement();
776: nodeView.setViewSize(size);
777: }
778: viewSize = size;
779: }
780:
781: /**
782: * Return the view size used for all nodes.
783: * This value may be overwritten for individual nodes.
784: */
785: public int getViewSize() {
786: return viewSize;
787: }
788:
789: /**
790: * Set the filter for all the nodes.
791: */
792: public void setFilter(ConsoleNodeOutputFilter filter) {
793: Enumeration en = this .nodeModels.elements();
794: while (en.hasMoreElements()) {
795: NodeModel nodeModel = (NodeModel) en.nextElement();
796: nodeModel.setFilter(filter);
797: }
798: }
799:
800: /**
801: * Set the name of the node whose status button is selected.
802: */
803: public void setSelectedNodeName(String name) {
804: selectedNodeName = name;
805: }
806:
807: /**
808: * Get the name of the node whose status button is selected.
809: */
810: public String getSelectedNodeName() {
811: return selectedNodeName;
812: }
813:
814: public void addGLSWindow() {
815: // Try to get reasonable contact info for the GLS servlet
816: NodeModel glsNode = getNodeModel(findGLSNode());
817: if (glsNode == null) {
818: ArrayList models = getNodeModels();
819: if (!models.isEmpty())
820: glsNode = (NodeModel) models.get(0);
821: }
822: recordGLSContactInfo(glsNode);
823:
824: setChanged();
825: notifyObservers(ADD_GLS_WINDOW);
826: }
827:
828: public static Date getRunStart() {
829: return runStart;
830: }
831:
832: public void setNotification(String notifyCondition,
833: boolean notifyOnStdErr) {
834: Enumeration en = this .nodeModels.elements();
835: while (en.hasMoreElements()) {
836: NodeModel nodeModel = (NodeModel) en.nextElement();
837: nodeModel.setNotification(notifyCondition, notifyOnStdErr);
838: }
839: this .notifyCondition = notifyCondition;
840: this .notifyOnStdErr = notifyOnStdErr;
841: }
842:
843: public String getNotifyCondition() {
844: return notifyCondition;
845: }
846:
847: public boolean getNotifyOnStandardError() {
848: return notifyOnStdErr;
849: }
850:
851: public boolean isRunnable() {
852: if (experiment != null) {
853: HostComponent[] hosts = experiment.getHostComponents();
854: for (int i = 0; i < hosts.length; i++) {
855: NodeComponent[] nodes = hosts[i].getNodes();
856: if (nodes != null && nodes.length > 0) {
857: // Bug 1763: Perhaps allow running a society with just Nodes, no Agents?
858: for (int j = 0; j < nodes.length; j++) {
859: AgentComponent[] agents = nodes[j].getAgents();
860: if (agents != null && agents.length > 0) {
861: return true;
862: }
863: }
864: }
865: }
866: }
867: return false;
868: }
869:
870: public void resetNotifyStatus() {
871: Enumeration en = this .nodeViews.elements();
872: while (en.hasMoreElements()) {
873: NodeView nodeView = (NodeView) en.nextElement();
874: nodeView.resetNotify();
875: }
876: }
877:
878: /**
879: * This is an observer on all the node models.
880: * It notifies its observers when node states change.
881: * It updates the experiment status --
882: * if any nodes is running, then the experiment is running
883: * if all nodes are stopped, then the experiment is stopped
884: */
885: public void update(Observable o, Object arg) {
886: if (((String) arg).startsWith("NODE_STATE")) {
887: // notify our observers, which includes CSMARTConsoleView
888: setChanged();
889: notifyObservers(arg);
890: Collection models = nodeModels.values();
891: Iterator i = models.iterator();
892: while (i.hasNext()) {
893: NodeModel nodeModel = (NodeModel) i.next();
894: String state = nodeModel.getState();
895: if (state.equals(NodeModel.STATE_RUNNING)) {
896: updateExperimentStatus(true);
897: refreshAppServers(); // ensure that app server reports new node
898: return;
899: } else if (!state.equals(NodeModel.STATE_STOPPED))
900: return;
901: }
902: // all nodes are stopped
903: updateExperimentStatus(false);
904: }
905: }
906:
907: // Given the node that has the GLS servlet, record the contact info for it
908: private void recordGLSContactInfo(NodeModel glsNode) {
909: if (glsNode == null)
910: return;
911:
912: // find the correct protocol, host, port, and agent name for the GLS servlet
913: // and set in the glsContactInfo array
914:
915: String host = glsNode.getInfo().getHostName();
916: glsContactInfo[1] = host;
917:
918: // Pull out the gls servlet protocol and port here too
919: getGLSPort(glsNode.getInfo().getProperties());
920: }
921:
922: // Called from the ConsoleView to get the correct contact info for the GLS servlet
923: public String[] getGLSContactInfo() {
924: return glsContactInfo;
925: }
926:
927: // Helper function that takes Nodes arguments
928: // and tries to find non-default protocol and port for servlets
929: private void getGLSPort(Properties arguments) {
930: if (arguments != null) {
931: String s = arguments
932: .getProperty("org.cougaar.lib.web.https.port");
933: if (s != null) {
934: glsContactInfo[2] = s;
935: glsContactInfo[0] = "https";
936: } else {
937: s = arguments
938: .getProperty("org.cougaar.lib.web.http.port");
939: if (s != null)
940: glsContactInfo[2] = s;
941: }
942: }
943: }
944:
945: public boolean isCSMARTNull() {
946: return (csmart == null) ? true : false;
947: }
948:
949: public void setXMLFile(File selectedFile) {
950: this .XMLFile = selectedFile;
951: }
952:
953: public File getXMLFile() {
954: return this.XMLFile;
955: }
956: }
|