       1 /*
       2  * $Id$
       3  *
       4  * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
       5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       6  *
       7  * This code is free software; you can redistribute it and/or modify it
       8  * under the terms of the GNU General Public License version 2 only, as
       9  * published by the Free Software Foundation.  Oracle designates this
      10  * particular file as subject to the "Classpath" exception as provided
      11  * by Oracle in the LICENSE file that accompanied this code.
      12  *
      13  * This code is distributed in the hope that it will be useful, but WITHOUT
      14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      16  * version 2 for more details (a copy is included in the LICENSE file that
      17  * accompanied this code).
      18  *
      19  * You should have received a copy of the GNU General Public License version
      20  * 2 along with this work; if not, write to the Free Software Foundation,
      21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      22  *
      23  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      24  * or visit www.oracle.com if you need additional information or have any
      25  * questions.
      26  */
      27 package com.sun.javatest.agent;
      28
      29 import java.awt.Button;
      30 import java.awt.Choice;
      31 import java.awt.Color;
      32 import java.awt.Component;
      33 import java.awt.Dimension;
      34 import java.awt.Event;
      35 import java.awt.Font;
      36 import java.awt.GridBagConstraints;
      37 import java.awt.GridBagLayout;
      38 import java.awt.Insets;
      39 import java.awt.Label;
      40 import java.awt.List;
      41 import java.awt.Panel;
      42 import java.awt.ScrollPane;
      43 import java.awt.TextArea;
      44 import java.awt.TextField;
      45 import java.awt.event.ActionEvent;
      46 import java.awt.event.ActionListener;
      47 import java.awt.event.ItemEvent;
      48 import java.awt.event.ItemListener;
      49 import java.io.IOException;
      50 import java.net.URL;
      51 import java.util.Vector;
      52
      53 import com.sun.javatest.ProductInfo;
      54 import com.sun.javatest.Status;
      55 import java.io.PrintStream;
      56
      57 /**
      58  * A component to monitor the status of a Agent. This is normally displayed
      59  * in either an {@link AgentFrame} or an {@link AgentApplet}.
      60  */
      61 class AgentPanel extends ScrollPane
      62 {
      63     public interface MapReader {
      64         public abstract Map read(String name) throws IOException;
      65     };
      66
      67     /**
      68      * Create a standard AgentPanel to control and monitor a Agent.
      69      *
      70      * @see Agent
      71      */
      72     public AgentPanel(ModeOptions[] modeOptions, MapReader mr) {
      73         mapReader = mr;
      74         initGUI(modeOptions);
      75     }
      76
      77     /**
      78      * Set the mode of the agent.
      79      *
      80      * @param mode      "active" or "passive"
      81      * @see Agent
      82      */
      83     public synchronized void setMode(String mode) {
      84         if (currAgent != null)
      85             throw new IllegalStateException();
      86
      87         paramPanel.setMode(mode);
      88     }
      89
      90     /**
      91      * Set the maximum number of requests that will be serviced at any
      92      * one time.
      93      *
      94      * @param conc      the maximum number of requests
      95      * @see Agent
      96      */
      97     public void setConcurrency(int conc) {
      98         if (currAgent != null)
      99             throw new IllegalStateException();
     100
     101         paramPanel.setConcurrency(conc);
     102     }
     103
     104     /**
     105      * Set the map file used to translate the arguments of incoming requests.
     106      *
     107      * @param mapFile   the map file
     108      * @see Agent#setMap
     109      */
     110     public void setMapFile(String mapFile) {
     111         if (currAgent != null)
     112             throw new IllegalStateException();
     113
     114         paramPanel.setMapFile(mapFile);
     115     }
     116
     117     /**
     118      * Set the retry delay for active agents.
     119      *
     120      * @param delay     the interval (in seconds) to wait after
     121      *                  a connection has been refused
     122      * @see Agent
     123      */
     124     public void setRetryDelay(int delay) {
     125         if (currAgent != null)
     126             throw new IllegalStateException();
     127
     128         paramPanel.setRetryDelay(delay);
     129     }
     130
     131     /**
     132      * Enable or disable tracing output from the agent.
     133      * Must be called before #start().
     134      * @param state True to enable tracing output.
     135      */
     136     public void setTracing(boolean state, PrintStream out) {
     137         tracing = state;
     138         if (!state) {
     139             traceOut = null;
     140         }
     141         else {
     142             traceOut = out;
     143         }
     144     }
     145
     146     /**
     147      * Set the number of requests remembered in the history.
     148      *
     149      * @param limit     the maximum number of requests to be remembered
     150      * @see Agent
     151      */
     152     public void setHistoryLimit(int limit) {
     153         if (currAgent != null)
     154             throw new IllegalStateException();
     155
     156         historyList.setMaxTasks(limit);
     157     }
     158
     159     /**
     160      * Add a user observer to the agent when it is created.
     161      * @param userObs an observer to monitor the agent
     162      */
     163     public void addObserver(Agent.Observer userObs) {
     164         if (userObs == null)
     165             throw new NullPointerException();
     166         if (this.userObs != null)
     167             throw new IllegalStateException("Only one observer can be registered.");
     168         this.userObs = userObs;
     169     }
     170
     171     /**
     172      * Start a agent running, based on the parameters in the panel.
     173      */
     174     public synchronized void start() {
     175         try {
     176             currAgent = paramPanel.createAgent();
     177             currAgent.setTracing(tracing);
     178             currAgent.addObserver(agentObs);
     179             if (userObs != null)
     180                 currAgent.addObserver(userObs);
     181
     182             Thread t = new Thread(currAgent, "AgentPanel worker thread");
     183             t.start();
     184
     185             paramPanel.setEnabled(false);
     186             buttonPanel.setStartEnabled(false);
     187             buttonPanel.setStopEnabled(true);
     188         }
     189         catch (BadValue e) {
     190             error(e.getMessages());
     191         }
     192         catch (SecurityException e) {
     193             String[] msgs = {"Security Exception", e.getMessage()};
     194             error(msgs);
     195         }
     196         catch (ConnectionFactory.Fault e) {
     197             String[] msgs = {"Cannot create connection", e.getMessage()};
     198             error(msgs);
     199         }
     200         catch (IOException e) {
     201             String[] msgs = {"Problem reading map file", e.getMessage()};
     202             error(msgs);
     203         }
     204     }
     205
     206     /**
     207      * Stop a agent running. Initially, the thread running the agent is
     208      * interrupted. If that doesn't work, after a short timeout, the thread
     209      * is stopped.  If that doesn't work, after another short timeout,
     210      * the thread is abandoned.
     211      */
     212     public synchronized void stop() {
     213         buttonPanel.setStopEnabled(false);
     214         if (currAgent != null)
     215             currAgent.interrupt();
     216         // the panel will be set idle when the agent reports (via the AgentObserver)
     217         // that it is finished
     218     }
     219
     220
     221
     222     //-------------------------------------------------------------------
     223
     224     public Dimension getPreferredSize() {
     225         Insets i = getInsets();
     226         Dimension d = deck.getPreferredSize();
     227         return new Dimension(i.left + d.width + i.right, i.top + d.height + i.bottom);
     228     }
     229
     230     //-------------------------------------------------------------------
     231
     232     void showTask(TaskState task) {
     233         mainFolder.show(taskPanel);
     234         taskPanel.setTask(task);
     235     }
     236
     237     //-------------------------------------------------------------------
     238     //
     239     // The following are private methods for the GUI
     240
     241     private void initGUI(ModeOptions[] modeOptions) {
     242         agentObs = new AgentObserver();
     243         deck = new Deck();
     244         initMainPanel(modeOptions);
     245         deck.add(mainPanel);
     246         errorPanel = new ErrorPanel();
     247         deck.add(errorPanel);
     248         helpPanel = new HelpPanel();
     249         deck.add(helpPanel);
     250         add(deck);
     251     }
     252
     253     Component initMainPanel(ModeOptions[] modeOptions) {
     254         mainPanel = new Panel(new GridBagLayout());
     255
     256         GridBagConstraints c = new GridBagConstraints();
     257         c.fill = GridBagConstraints.BOTH;
     258         c.gridwidth = GridBagConstraints.REMAINDER;
     259         c.weightx = 1;
     260         c.weighty = 1;
     261
     262         mainFolder = new Folder();
     263
     264         paramPanel = new ParamPanel(modeOptions);
     265         mainFolder.add("parameters", paramPanel);
     266
     267         statsPanel = new StatsPanel();
     268         mainFolder.add("statistics", statsPanel);
     269
     270         historyList = new HistoryList();
     271         mainFolder.add("history", historyList);
     272
     273         taskPanel = new TaskPanel();
     274         mainFolder.add("selected task", taskPanel);
     275
     276         c.insets.top = c.insets.left = c.insets.right = 10;
     277         mainPanel.add(mainFolder, c);
     278
     279         buttonPanel = new ButtonPanel();
     280         c.weighty= 0;
     281         c.insets.bottom = 20;
     282         mainPanel.add(buttonPanel, c);
     283
     284         return mainPanel;
     285     }
     286
     287     private void setIdle() {
     288         paramPanel.setEnabled(true);
     289         buttonPanel.setStartEnabled(true);
     290         buttonPanel.setStopEnabled(false);
     291         currAgent = null;
     292         notifyAll();
     293     }
     294
     295     private void showHelp() {
     296         deck.show(helpPanel);
     297     }
     298
     299     private void error(String[] msgs) {
     300         for (int i = 0; i < msgs.length; i++)
     301             System.out.println(msgs[i]);
     302         errorPanel.show(msgs);
     303         deck.show(errorPanel);
     304     }
     305
     306     private void ackError() {
     307         deck.show(mainPanel);
     308     }
     309
     310     private static int getInt(String label, TextField field) throws BadValue {
     311         try {
     312             return Integer.parseInt(field.getText(), 10);
     313         }
     314         catch (NumberFormatException e) {
     315             throw new BadValue("Bad value in `" + label + "' field");
     316         }
     317     }
     318
     319
     320     private MapReader mapReader;
     321
     322     private Deck deck;
     323     private ErrorPanel errorPanel;
     324     private HelpPanel helpPanel;
     325     private Panel mainPanel;
     326     private Folder mainFolder;
     327     private ButtonPanel buttonPanel;
     328     private ParamPanel paramPanel;
     329     private StatsPanel statsPanel;
     330     private TaskPanel taskPanel;
     331     private HistoryList historyList;
     332     private AgentObserver agentObs;
     333     private Agent.Observer userObs;
     334
     335     private boolean tracing;
     336     private PrintStream traceOut;
     337
     338     private Agent currAgent;
     339
     340     private static final String[] statusCodes =
     341         {"PASS", "FAIL", "CHCK", "ERR ", "!RUN"};
     342
     343
     344
     345     //-------------------------------------------------------------------
     346
     347
     348
     349     //-------------------------------------------------------------------
     350
     351     private static class TaskState
     352     {
     353         TaskState(Connection c) {
     354             connection = c;
     355         }
     356
     357         Connection connection;
     358         int mode;
     359         String tag;
     360         String className;
     361         String[] args;
     362         Status status;
     363
     364         static final int TEST = 0, COMMAND = 1, MAIN=2;
     365     }
     366
     367
     368     //-------------------------------------------------------------------
     369
     370     private class AgentObserver implements Agent.Observer {
     371         public void started(Agent sl) {
     372             synchronized (AgentPanel.this) {
     373                 if (sl != currAgent)
     374                     return;
     375
     376                 historyList.removeAll();
     377                 taskPanel.setTask(null);
     378                 statsPanel.reset();
     379             }
     380         }
     381
     382         public void errorOpeningConnection(Agent sl, Exception e) {
     383             synchronized (AgentPanel.this) {
     384                 if (sl != currAgent)
     385                     return;
     386
     387                 String[] msgs = {"error opening connection", e.getMessage()};
     388                 error(msgs);
     389             }
     390         }
     391
     392         public void finished(Agent sl) {
     393             synchronized (AgentPanel.this) {
     394                 if (sl != currAgent)
     395                     return;
     396
     397                 setIdle();
     398             }
     399         }
     400
     401         public void openedConnection(Agent sl, Connection c) {
     402             synchronized (AgentPanel.this) {
     403                 if (sl != currAgent)
     404                     return;
     405
     406                 TaskState ts = new TaskState(c);
     407                 historyList.addTask(ts);
     408                 if (taskPanel.getTask() == null)
     409                     taskPanel.setTask(ts);
     410                 statsPanel.startedTask(ts);
     411             }
     412         }
     413
     414         public void execTest(Agent sl, Connection c, String tag, String className, String[] args) {
     415             synchronized (AgentPanel.this) {
     416                 if (sl != currAgent)
     417                     return;
     418
     419                 TaskState ts = historyList.getTask(c);
     420                 ts.mode = TaskState.TEST;
     421                 ts.tag = tag;
     422                 ts.className = className;
     423                 ts.args = args;
     424                 historyList.update(ts, "EXEC", tag);
     425                 if (ts == taskPanel.getTask())
     426                     taskPanel.update();
     427             }
     428         }
     429
     430         public void execCommand(Agent sl, Connection c, String tag, String className, String[] args) {
     431             synchronized (AgentPanel.this) {
     432                 if (sl != currAgent)
     433                     return;
     434
     435                 TaskState ts = historyList.getTask(c);
     436                 ts.mode = TaskState.COMMAND;
     437                 ts.tag = tag;
     438                 ts.className = className;
     439                 ts.args = args;
     440                 historyList.update(ts, "EXEC", tag);
     441                 if (ts == taskPanel.getTask())
     442                     taskPanel.update();
     443             }
     444         }
     445
     446         public void execMain(Agent sl, Connection c, String tag, String className, String[] args) {
     447             synchronized (AgentPanel.this) {
     448                 if (sl != currAgent)
     449                     return;
     450
     451                 TaskState ts = historyList.getTask(c);
     452                 ts.mode = TaskState.MAIN;
     453                 ts.tag = tag;
     454                 ts.className = className;
     455                 ts.args = args;
     456                 historyList.update(ts, "EXEC", tag);
     457                 if (ts == taskPanel.getTask())
     458                     taskPanel.update();
     459             }
     460         }
     461
     462         public void result(Agent sl, Connection c, Status r) {
     463             synchronized (AgentPanel.this) {
     464                 if (sl != currAgent)
     465                     return;
     466
     467                 TaskState ts = historyList.getTask(c);
     468                 // don't update history list until data passed back to AgentClient OK
     469                 // (i.e. in completed(...)
     470                 ts.status = r;
     471                 if (ts == taskPanel.getTask())
     472                     taskPanel.update();
     473             }
     474         }
     475
     476         public void exception(Agent sl, Connection c, Throwable t) {
     477             synchronized (AgentPanel.this) {
     478                 if (sl != currAgent)
     479                     return;
     480
     481                 TaskState ts = historyList.getTask(c);
     482                 if (ts.tag == null)
     483                     historyList.removeTask(ts);
     484                 else
     485                     historyList.update(ts, "*IO*", ts.tag);
     486                 statsPanel.finishedTask(ts, false);
     487             }
     488         }
     489
     490         public void completed(Agent sl, Connection c) {
     491             synchronized (AgentPanel.this) {
     492                 if (sl != currAgent)
     493                     return;
     494
     495                 TaskState ts = historyList.getTask(c);
     496                 historyList.update(ts, statusCodes[ts.status.getType()], ts.tag);
     497                 statsPanel.finishedTask(ts, true);
     498             }
     499         }
     500     }
     501
     502     //-------------------------------------------------------------------
     503
     504     private class ButtonPanel extends Panel implements ActionListener {
     505         ButtonPanel() {
     506             setLayout(new GridBagLayout());
     507             GridBagConstraints c = new GridBagConstraints();
     508             c.weightx = 1.0;
     509
     510             startButton = new Button("Start");
     511             startButton.addActionListener(this);
     512             add(startButton, c);
     513
     514             stopButton = new Button("Stop");
     515             stopButton.addActionListener(this);
     516             stopButton.setEnabled(false);
     517             add(stopButton, c);
     518
     519             helpButton = new Button("Help");
     520             helpButton.addActionListener(this);
     521             add(helpButton, c);
     522         }
     523
     524         public void actionPerformed(ActionEvent e) {
     525             Object src = e.getSource();
     526             if (src == startButton) {
     527                 start();
     528             }
     529             else if (src == stopButton) {
     530                 stop();
     531             }
     532             else if (src == helpButton) {
     533                 showHelp();
     534             }
     535         }
     536
     537         void setStartEnabled(boolean b) {
     538             startButton.setEnabled(b);
     539         }
     540
     541         void setStopEnabled(boolean b) {
     542             stopButton.setEnabled(b);
     543         }
     544
     545         private Button startButton;
     546         private Button stopButton;
     547         private Button helpButton;
     548     }
     549
     550     //-------------------------------------------------------------------
     551
     552     private class ErrorPanel extends Panel implements ActionListener {
     553         /**
     554          * Display a multi-line message in the error panel.
     555          *
     556          * @param msgs  The lines of the message to display
     557          */
     558         public synchronized void show(String[] msgs) {
     559             removeAll();
     560
     561             setLayout(new GridBagLayout());
     562
     563             GridBagConstraints c = new GridBagConstraints();
     564             c.gridwidth = GridBagConstraints.REMAINDER;
     565             c.insets = new Insets(30, 30, 0, 30); // top, left, bottom, right
     566
     567             for (int i = 0; i < msgs.length; i++) {
     568                 Label msg = new Label(msgs[i]);
     569                 add(msg, c);
     570                 c.insets.top = 0;
     571             }
     572
     573             c.insets.top = 30;
     574             c.insets.bottom = 30;
     575             Button okBtn = new Button("OK");
     576             okBtn.addActionListener(this);
     577             add(okBtn, c);
     578             validate();
     579         }
     580
     581         public void actionPerformed(ActionEvent e) {
     582             ackError();
     583         }
     584     }
     585
     586     //-------------------------------------------------------------------
     587
     588     private class HelpPanel extends Panel implements ActionListener {
     589         HelpPanel() {
     590             setLayout(new GridBagLayout());
     591             setBackground(Color.white);
     592
     593             Panel logoPanel = new Panel(new GridBagLayout());
     594             GridBagConstraints lpc = new GridBagConstraints();
     595             lpc.gridwidth = GridBagConstraints.REMAINDER;
     596             lpc.weighty = 1;
     597             lpc.insets = new Insets(20, 20, 20, 20);
     598
     599             Icon logoJava = createIcon("jticon.gif");
     600             if (logoJava != null) {
     601                 lpc.anchor = GridBagConstraints.NORTH;
     602                 logoPanel.add(logoJava, lpc);
     603             }
     604
     605             /*
     606             Icon logo100pc = createIcon("100percent.gif");
     607             if (logo100pc != null) {
     608                 lpc.anchor = GridBagConstraints.SOUTH;
     609                 logoPanel.add(logo100pc, lpc);
     610             }
     611             */
     612
     613             GridBagConstraints lrc = new GridBagConstraints();
     614             lrc.weighty = 1;
     615             lrc.fill = GridBagConstraints.BOTH;
     616             add(logoPanel, lrc);
     617
     618             GridBagConstraints c = new GridBagConstraints();
     619             c.gridwidth = GridBagConstraints.REMAINDER;
     620             Panel infoPanel = new Panel(new GridBagLayout());
     621             Label nameLabel = new Label("JT Harness Agent");
     622             nameLabel.setFont(new Font("SansSerif", Font.BOLD, 32));
     623             infoPanel.add(nameLabel, c);
     624
     625             Label versionLabel = new Label(ProductInfo.getVersion());
     626             versionLabel.setFont(new Font("SansSerif", Font.BOLD, 24));
     627             infoPanel.add(versionLabel, c);
     628
     629             Label copyLabel1 = new Label("Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.");
     630             infoPanel.add(copyLabel1, c);
     631
     632             Label copyLabel2 = new Label("Use is subject to license terms.");
     633             infoPanel.add(copyLabel2, c);
     634
     635             String helpText =
     636                 "  For full information on using the\n" +
     637                 "  JT Harness Agent, please refer to the\n" +
     638                 "     JT Harness User's Guide\n" +
     639                 "  and to the documentation for the\n" +
     640                 "  test suite you are using.\n";
     641
     642             TextArea helpArea = new TextArea(helpText, 5, 35, TextArea.SCROLLBARS_NONE);
     643             helpArea.setEditable(false);
     644             c.weightx = 1;
     645             c.weighty = 1;
     646             infoPanel.add(helpArea, c);
     647
     648             Button okBtn = new Button("OK");
     649             okBtn.addActionListener(this);
     650             c.weightx = 0;
     651             c.weighty = 0;
     652             c.insets.bottom = 10;
     653             infoPanel.add(okBtn, c);
     654
     655             lrc.weightx = 1;
     656             add(infoPanel, lrc);
     657         }
     658
     659         private Icon createIcon(String name)  {
     660             try {
     661                 URL url = getClass().getResource(name);
     662                 if (url == null)
     663                     return null;
     664                 Icon icon = new Icon(url);
     665                 return icon;
     666             }
     667             catch (RuntimeException e) {
     668                 return null;
     669             }
     670             catch (Error e) {
     671                 return null;
     672             }
     673
     674
     675         }
     676
     677         public void actionPerformed(ActionEvent e) {
     678             ackError(); // hmm, not the most obvious name, but it will work
     679         }
     680     }
     681
     682     //-------------------------------------------------------------------
     683
     684     private class HistoryList extends List implements ItemListener {
     685         HistoryList() {
     686             super(5, false);
     687             super.addItemListener(this);
     688         }
     689
     690         public synchronized void addTask(TaskState task) {
     691             if (tasks.size() >= maxTasks) {
     692                 for (int i = 0; i < tasks.size(); i++) {
     693                     String s = getItem(i);
     694                     // skip over active tasks that will be updated later
     695                     if (s.startsWith("CONN") || s.startsWith("EXEC"))
     696                         continue;
     697
     698                     tasks.removeElementAt(i);
     699                     super.remove(i);
     700                 }
     701             }
     702
     703             tasks.addElement(task);
     704             super.add("CONN " + task.connection.getName());
     705         }
     706
     707         public synchronized TaskState getTask(Connection c) {
     708             for (int i = 0; i < tasks.size(); i++) {
     709                 TaskState ts = tasks.elementAt(i);
     710                 if (ts.connection == c)
     711                     return ts;
     712             }
     713             throw new Error("AgentPanel.HistoryList.getTask: can't find socket");
     714         }
     715
     716         public synchronized TaskState getSelectedTask() {
     717             return tasks.elementAt(super.getSelectedIndex());
     718         }
     719
     720         public synchronized void removeAll() {
     721             tasks.setSize(0);
     722             if (getItemCount() > 0)
     723                 // calling List.removeAll on empty list generates motif
     724                 // warning message
     725                 super.removeAll();
     726         }
     727
     728         public synchronized void removeTask(TaskState task) {
     729             int index = tasks.indexOf(task);
     730             tasks.removeElementAt(index);
     731             super.remove(index);
     732         }
     733
     734         public synchronized void setMaxTasks(int n) {
     735             maxTasks = n;
     736         }
     737
     738         public synchronized void update(TaskState task, String state, String detail) {
     739             super.replaceItem(getKey(state, detail), tasks.indexOf(task));
     740         }
     741
     742         public void itemStateChanged(ItemEvent e) {
     743             Object src = e.getItemSelectable();
     744             if (src == this) {
     745                 switch (e.getStateChange()) {
     746                 case ItemEvent.SELECTED:
     747                     showTask(getSelectedTask());
     748                     break;
     749                 case Event.LIST_DESELECT:
     750                     showTask(null);
     751                     break;
     752                 }
     753             }
     754         }
     755
     756         private String getKey(String state, String detail) {
     757             return (state + " " + detail);
     758         }
     759
     760         private int maxTasks = 10;
     761         private Vector<TaskState> tasks = new Vector<>();
     762     }
     763
     764
     765     //-------------------------------------------------------------------
     766
     767     private class ParamPanel extends Panel  implements ItemListener {
     768         ParamPanel(ModeOptions[] modeOptions) {
     769             setLayout(new GridBagLayout());
     770
     771             GridBagConstraints lc = new GridBagConstraints();
     772             lc.anchor = GridBagConstraints.EAST;
     773
     774             GridBagConstraints fc = new GridBagConstraints();
     775             fc.fill = GridBagConstraints.HORIZONTAL;
     776             fc.gridwidth = GridBagConstraints.REMAINDER;
     777             fc.weightx = 1;
     778             fc.weighty = 0;
     779
     780
     781             // First row: mode choice, and the mode options deck
     782             add(new Label("mode:"), lc);
     783
     784             modeChoice = new Choice();
     785             modeDeck = new Deck();
     786
     787             for (int i = 0; i < modeOptions.length; i++) {
     788                 ModeOptions m = modeOptions[i];
     789                 if (m != null) {
     790                     modeChoice.addItem(m.getModeName());
     791                     modeDeck.add(m);
     792                 }
     793             }
     794
     795             // choices added later by modeOption objects
     796             modeChoice.addItemListener(this);
     797             fc.weightx = 0;
     798             fc.gridwidth = 1;
     799             add(modeChoice, fc);
     800
     801             fc.insets.left = 20;
     802             fc.fill = GridBagConstraints.HORIZONTAL;
     803             fc.gridwidth = GridBagConstraints.REMAINDER;
     804             fc.weightx = 1;
     805             add(modeDeck, fc);
     806             fc.insets.left = 0;
     807
     808             // Second row: the optional map file field
     809             add(new Label("map:"), lc);
     810
     811             mapFileField = new TextField();
     812             add(mapFileField, fc);
     813
     814             // Third row: the concurrency field
     815             Label concurrencyLabel = new Label("concurrency:");
     816             add(concurrencyLabel, lc);
     817
     818             concurrencyField = new TextField("1", 3);
     819             fc.gridwidth = 1;
     820             fc.weightx = 0;
     821             add(concurrencyField, fc);
     822
     823             fc.gridwidth = GridBagConstraints.REMAINDER;
     824             fc.weightx = 1;
     825             add(new Label(), fc); // dummy filler
     826
     827         }
     828
     829         public void setEnabled(boolean b) {
     830             modeChoice.setEnabled(b);
     831             for (int i = 0; i < modeDeck.getComponentCount(); i++) {
     832                 ModeOptions o = (ModeOptions)(modeDeck.getComponent(i));
     833                 o.setEnabled(b);
     834             }
     835             mapFileField.setEnabled(b);
     836             concurrencyField.setEnabled(b);
     837         }
     838
     839         public void setMode(String modeName) {
     840             modeChoice.select(modeName);
     841             for (int i = 0; i < modeDeck.getComponentCount(); i++) {
     842                 ModeOptions m = (ModeOptions)(modeDeck.getComponent(i));
     843                 if (modeName.equals(m.getModeName())) {
     844                     modeDeck.show(m);
     845                     return;
     846                 }
     847             }
     848         }
     849
     850         public void setConcurrency(int conc) {
     851             concurrencyField.setText(Integer.toString(conc));
     852         }
     853
     854         public void setMapFile(String mapFile) {
     855             mapFileField.setText(mapFile);
     856         }
     857
     858         public void setRetryDelay(int delay) {
     859             retryDelay = delay; // not available in GUI
     860         }
     861
     862         public Agent createAgent() throws BadValue, ConnectionFactory.Fault, IOException {
     863             // should consider catching IllegalArgumentException in here
     864             // and IllegalState?
     865             Map map = mapReader.read(mapFileField.getText());
     866             int concurrency = getInt("concurrency", concurrencyField);
     867             if (!Agent.isValidConcurrency(concurrency)) {
     868                 throw new BadValue("Bad value for `concurrency field': " + concurrency);
     869             }
     870
     871             ModeOptions mo = (ModeOptions)(modeDeck.getCurrentCard());
     872
     873             try {
     874                 ConnectionFactory cf = mo.createConnectionFactory(concurrency);
     875                 Agent agent = new Agent(cf, concurrency);
     876
     877                 if (retryDelay > 0)
     878                     agent.setRetryDelay(retryDelay);
     879
     880                 agent.setMap(map);
     881
     882                 return agent;
     883             }
     884             catch (IllegalArgumentException e) {
     885                 throw new BadValue(e.getLocalizedMessage());
     886             }
     887         }
     888
     889         private Deck createModeDeck(Choice modeChoice) {
     890             return deck;
     891         }
     892
     893         public void itemStateChanged(ItemEvent e) {
     894             Object src = e.getItemSelectable();
     895             if (src == modeChoice) {
     896                 String modeName = ((Choice)src).getSelectedItem();
     897                 for (int i = 0; i < modeDeck.getComponentCount(); i++) {
     898                     ModeOptions m = (ModeOptions)(modeDeck.getComponent(i));
     899                     if (modeName.equals(m.getModeName())) {
     900                         modeDeck.show(m);
     901                         return;
     902                     }
     903                 }
     904             }
     905         }
     906
     907         private Choice modeChoice;
     908         private Deck modeDeck;
     909         private TextField mapFileField;
     910         private Label concurrencyLabel;
     911         private TextField concurrencyField;
     912         private int retryDelay; // not currently in GUI
     913     }
     914
     915
     916     //-------------------------------------------------------------------
     917
     918     private class StatsPanel extends Panel {
     919         StatsPanel() {
     920             setLayout(new GridBagLayout());
     921
     922             GridBagConstraints lc = new GridBagConstraints();
     923             lc.anchor = GridBagConstraints.EAST;
     924             GridBagConstraints fc = new GridBagConstraints();
     925             fc.weightx = 1;
     926             fc.anchor = GridBagConstraints.WEST;
     927             fc.gridwidth = GridBagConstraints.REMAINDER;
     928
     929             activeField = addField("currently active:", lc, fc);
     930             statusFields[Status.PASSED] = addField("passed:", lc, fc);
     931             statusFields[Status.FAILED] = addField("failed:", lc, fc);
     932             statusFields[Status.ERROR] = addField("error:", lc, fc);
     933             statusFields[Status.NOT_RUN] = addField("not run:", lc, fc);
     934             exceptionsField = addField("exceptions:", lc, fc);
     935         }
     936
     937         void reset() {
     938             tasks.setSize(0);
     939             for (int i = 0; i < statusCounts.length; i++)
     940                 statusCounts[i] = 0;
     941             exceptionsCount = 0;
     942             activeField.setText("0");
     943             for (int i = 0; i < statusCounts.length; i++)
     944                 statusFields[i].setText("0");
     945             exceptionsField.setText("0");
     946         }
     947
     948         void startedTask(TaskState task) {
     949             tasks.addElement(task);
     950             activeField.setText(String.valueOf(tasks.size()));
     951         }
     952
     953         void finishedTask(TaskState task, boolean completedNormally) {
     954             int index = tasks.indexOf(task);
     955             if (index == -1)
     956                 return;
     957             tasks.removeElementAt(index);
     958             activeField.setText(String.valueOf(tasks.size()));
     959             if (!completedNormally)
     960                 exceptionsField.setText(String.valueOf(++exceptionsCount));
     961             if (task.status != null) {
     962                 int t = task.status.getType();
     963                 statusFields[t].setText(String.valueOf(++statusCounts[t]));
     964             }
     965         }
     966
     967         private TextField addField(String name, GridBagConstraints lc, GridBagConstraints fc) {
     968             add(new Label(name), lc);
     969             TextField f = new TextField("0", 10);
     970             f.setEditable(false);
     971             add(f, fc);
     972             return f;
     973         }
     974
     975
     976         private Vector<TaskState> tasks = new Vector<>();
     977
     978         private int[] statusCounts = new int[Status.NUM_STATES];
     979         private int exceptionsCount;
     980
     981         private TextField activeField;
     982         private TextField[] statusFields = new TextField[Status.NUM_STATES];
     983         private TextField exceptionsField;
     984     }
     985
     986     //-------------------------------------------------------------------
     987
     988     private class TaskPanel extends Panel {
     989         TaskPanel() {
     990             setLayout(new GridBagLayout());
     991
     992             GridBagConstraints lc = new GridBagConstraints();
     993             lc.anchor = GridBagConstraints.EAST;
     994             GridBagConstraints fc = new GridBagConstraints();
     995             fc.weightx = 1;
     996             fc.fill = GridBagConstraints.HORIZONTAL;
     997             fc.gridwidth = GridBagConstraints.REMAINDER;
     998
     999             Label clientLabel = new Label("client:");
    1000             add(clientLabel, lc);
    1001
    1002             clientField = new Label("");
    1003             add(clientField, fc);
    1004
    1005             Label tagLabel = new Label("request:");
    1006             add(tagLabel, lc);
    1007
    1008             tagField = new Label("");
    1009             add(tagField, fc);
    1010
    1011             Label classLabel = new Label("class:");
    1012             add(classLabel, lc);
    1013
    1014             classField = new Label("");
    1015             add(classField, fc);
    1016
    1017             Label argsLabel = new Label("args:");
    1018             add(argsLabel, lc);
    1019
    1020             argsField = new TextField("");
    1021             // make it a text field to support scrolling of very long fields
    1022             argsField.setEditable(false);
    1023             add(argsField, fc);
    1024
    1025             Label resultLabel = new Label("result:");
    1026             add(resultLabel, lc);
    1027
    1028             resultField = new Label("");
    1029             add(resultField, fc);
    1030         }
    1031
    1032         TaskState getTask() {
    1033             return task;
    1034         }
    1035
    1036         void setTask(TaskState ts) {
    1037             task = ts;
    1038             update();
    1039         }
    1040
    1041         void update() {
    1042             if (task == null) {
    1043                 clientField.setText("");
    1044                 tagField.setText("");
    1045                 classField.setText("");
    1046                 argsField.setText("");
    1047                 resultField.setText("");
    1048             } else {
    1049                 clientField.setText(task.connection.getName());
    1050                 tagField.setText(task.tag);
    1051                 classField.setText(task.className);
    1052                 String a = "";
    1053                 if (task.args != null) {
    1054                     for (int i = 0; i < task.args.length; i++)
    1055                         a += task.args[i] + " ";
    1056                 }
    1057                 argsField.setText(a);
    1058                 if (task.status == null)
    1059                     resultField.setText("");
    1060                 else
    1061                     resultField.setText(task.status.toString());
    1062             }
    1063         }
    1064
    1065         private TaskState task;
    1066
    1067         private Label clientField;
    1068         private Label tagField;
    1069         private Label classField;
    1070         private TextField argsField;
    1071         private Label resultField;
    1072     }
    1073 }