chiark / gitweb /
debug logging: replace logprogress and if(dtxt!=null)... with debuglog() function
[jarrg-ian.git] / src / net / chiark / yarrg / MarketUploader.java
index 5dc7e6cca0e7429d21c1bff0553510dc62fe6e5f..4f50228a7b77f8131902fb5e19228500322f3725 100644 (file)
@@ -43,6 +43,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
   private JLabel arbitrageResult = null;
   private int unknownPCTBcommods = 0;
   private long startTime = 0;
+  private ProgressMonitor progmon = null;
 
   private final static String PCTB_LIVE_HOST_URL = "http://pctb.crabdance.com/";
   private final static String PCTB_TEST_HOST_URL = "http://pctb.ilk.org/";
@@ -71,26 +72,6 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
   private HashMap<String,Integer> commodMap;
   public PrintStream dtxt = null;
 
-  private PropertyChangeListener changeListener = new PropertyChangeListener() {
-    public void propertyChange(PropertyChangeEvent e) {
-      if(e.getNewValue() != null && 
-        e.getPropertyName().equals
-        (AccessibleContext.ACCESSIBLE_CHILD_PROPERTY)) {
-       Accessible islandInfo = 
-         descendNodes(window,new int[] {0,1,0,0,2,2,0,0,0,0,1,2});;
-       String text = 
-         islandInfo.getAccessibleContext().getAccessibleText()
-         .getAtIndex(AccessibleText.SENTENCE,0);
-       int index = text.indexOf(":");
-       String name = text.substring(0,index);
-       islandName = name;
-       // if (dtxt!=null) dtxt.println(islandName);
-       sidePanel.removePropertyChangeListener(this);
-       latch.countDown();
-      }
-    }
-  };
-
   /*
    * UTILITY METHODS AND SUBCLASSES
    *
@@ -105,7 +86,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
     }
   }
 
-  private void progresslog(String s) {
+  private void debuglog(String s) {
     if (dtxt == null) return;
     long now = new Date().getTime();
     dtxt.println("progress "+(now - startTime)+"ms "+s);
@@ -265,14 +246,14 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
     }
   }
 
-  private void progressNote(ProgressMonitor pm, String s) {
-    String arb = null;
-    new UI() { public void body() {
-      if (arbitrageResult != null)
-       arb = arbitrageResult.getText();
+  private void progressNote(final String s_in) throws Exception {
+    new UIX() { public void body() {
+      String arb = null;
+      arb = arbitrageResult.getText();
+      String s = s_in;
       if (arb != null && arb.length() != 0)
        s = "<html>" + arb + "<br>" + s;
-      pm.setNote(s);
+      progmon.setNote(s);
     }}.exec();
   }
        
@@ -307,8 +288,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
     uploadToPCTB=prefs.getBoolean("uploadToPCTB", true);
     showArbitrage=prefs.getBoolean("showArbitrage", true);
 
-    if (dtxt!=null) dtxt.println("main on dispatch thread: "+
-                                EventQueue.isDispatchThread());
+    debuglog("main on dispatch thread: "+EventQueue.isDispatchThread());
     EventQueue.invokeLater(this);
   }
 
@@ -317,12 +297,12 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
    * every top-level window, and if it
    */
   public void run() {
-    if (dtxt!=null) dtxt.println("MarketUploader run()...");
+    debuglog("MarketUploader run()...");
     if (EventQueueMonitor.isGUIInitialized()) {
-      if (dtxt!=null) dtxt.println("MarketUploader GUI already ready");
+      debuglog("MarketUploader GUI already ready");
       guiInitialized();
     } else {
-      if (dtxt!=null) dtxt.println("MarketUploader waiting for GUI");
+      debuglog("MarketUploader waiting for GUI");
       EventQueueMonitor.addGUIInitializedListener(this);
     }
   }
@@ -331,13 +311,13 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
     Window ws[]= EventQueueMonitor.getTopLevelWindows();
     EventQueueMonitor.addTopLevelWindowListener(this);
     for (int i=0; i<ws.length; i++) {
-      if (dtxt!=null) dtxt.println("MarketUploader existing toplevel "+i);
+      debuglog("MarketUploader existing toplevel "+i);
       topLevelWindowCreated(ws[i]);
     }
   }
 
   public void topLevelWindowDestroyed(Window w) {
-    if (dtxt!=null) dtxt.println("MarketUploader destroyed toplevel");
+    debuglog("MarketUploader destroyed toplevel");
   }
        
   public void topLevelWindowCreated(Window w) {
@@ -345,10 +325,10 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
       // already got it
       return;
     String name = w.getAccessibleContext().getAccessibleName();
-    if (dtxt!=null) dtxt.println("MarketUploader new toplevel "+name);
+    debuglog("MarketUploader new toplevel "+name);
     if (!name.equals("Puzzle Pirates"))
       return;
-    if (dtxt!=null) dtxt.println("MarketUploader found toplevel, creating gui");
+    debuglog("MarketUploader found toplevel, creating gui");
     window = w;
     createGUI();
     frame.setVisible(true);
@@ -360,7 +340,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
    *   a Window named "Puzzle Pirates" though.
    */
   private void createGUI() {
-    // on event thread
+    on_ui_thread();
     frame = new JFrame("Jarrg Uploader");
     frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
     GridLayout layout = new GridLayout(2,1);
@@ -370,6 +350,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
     findMarket = new JButton("Upload Market Data");
     findMarket.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
+         on_ui_thread();
          findMarket.setEnabled(false);
          resultSummary.setText("");
          arbitrageResult.setText("");
@@ -383,12 +364,21 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
                error(e.toString());
                e.printStackTrace();
              }
-             EventQueue.invokeAndWait(new Runnable() { public void run() { 
-               if(sidePanel != null) {
-                 sidePanel.removePropertyChangeListener(changeListener);
-               }
-               findMarket.setEnabled(true);
-             }});
+             try {
+               new UIX() { public void body() { 
+                 if(sidePanel != null) {
+                   sidePanel.removePropertyChangeListener(changeListener);
+                 }
+                 if (progmon != null) {
+                   progmon.close();
+                   progmon = null;
+                 }
+                 findMarket.setEnabled(true);
+               }}.exec();
+             } catch (Exception e) {
+               System.err.println("exception tidying on UI thread:");
+               e.printStackTrace();
+             }
            }
          }.start();
        }
@@ -415,28 +405,41 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
    * error and error_html may be called from any thread
    */ 
 
-  private Class UI implements Runnable {
-    public virtual void body();
-    public void run() { body(); };
-    public void exec() {
+  private abstract class UIXR<ReturnType> implements Runnable {
+    public abstract ReturnType bodyr();
+    public ReturnType return_value;
+    public void run() { return_value = bodyr(); };
+    public ReturnType exec() throws Exception {
       if (EventQueue.isDispatchThread()) {
-       r.run();
+       this.run();
       } else {
-       invokeAndWait(r);
+       EventQueue.invokeAndWait(this);
       }
+      return return_value;
     };
   };
+  private abstract class UIX extends UIXR<Object> implements Runnable {
+    public abstract void body();
+    public Object bodyr() { body(); return null; }
+  };
 
-  private void error(String msg) {
-    new UI() { public booolean body() {
-      resultSummary.setText("failed");
-      JOptionPane.showMessageDialog(frame,msg,"Error",
-                                   JOptionPane.ERROR_MESSAGE);
-      return true;
-    }}.exec();
+  private void on_ui_thread() { assert(EventQueue.isDispatchThread()); }
+  private void on_our_thread() { assert(!EventQueue.isDispatchThread()); }
+
+  private void error(final String msg) {
+    try {
+      new UIX() { public void body() {
+       resultSummary.setText("failed");
+       JOptionPane.showMessageDialog(frame,msg,"Error",
+                                     JOptionPane.ERROR_MESSAGE);
+      }}.exec();
+    } catch (Exception e) {
+      System.err.println("exception reporting to UI thread:");
+      e.printStackTrace();
+    }
   }
        
-  private void error_html(String msg, String html) {
+  private void error_html(final String msg, String html) {
     Pattern body = Pattern.compile("<body>(.*)</body>",
                                   Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
     Matcher m = body.matcher(html);
@@ -450,10 +453,21 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
     }
     String whole_msg = "<html><h1>Error</h1>"+msg
       +"<h1>PCTB Server said:</h1><blockquote>"+html+"</blockquote>";
-    if (dtxt!=null) dtxt.println("###" + whole_msg + "###");
+    debuglog("###" + whole_msg + "###");
   
     error(whole_msg);
   }
+
+  private void setProgress(final int nv) throws Exception {
+    new UIX() { public void body() {
+      progmon.setProgress(nv);
+    }}.exec();
+  }
+  private boolean isCanceled() throws Exception {
+    return new UIXR<Boolean>() { public Boolean bodyr() {
+      return new Boolean(progmon.isCanceled());
+    }}.exec().booleanValue();
+  }
        
   /*
    * GUI MANIPULATION CALLBACKS
@@ -461,6 +475,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
 
   private PropertyChangeListener changeListener = new PropertyChangeListener() {
     public void propertyChange(PropertyChangeEvent e) {
+      on_ui_thread();
       if(e.getNewValue() != null && 
         e.getPropertyName().equals
         (AccessibleContext.ACCESSIBLE_CHILD_PROPERTY)) {
@@ -471,7 +486,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
        int index = text.indexOf(":");
        String name = text.substring(0,index);
        islandName = name;
-       // if (dtxt!=null) dtxt.println(islandName);
+       // debuglog(islandName);
        sidePanel.removePropertyChangeListener(this);
        latch.countDown();
       }
@@ -479,7 +494,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
   };
 
   private void getIsland() {
-    // runs on event thread
+    on_ui_thread();
 
     // If the league tracker is there, we can skip the faff
     // and ask for its tooltip, since we're on a boat
@@ -531,7 +546,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
    *      oceanName variable
    */
   private void getOcean() {
-               // runs on event thread
+    on_ui_thread();
     oceanName = null;
     AccessibleContext topwindow = window.getAccessibleContext();
     oceanName = topwindow.getAccessibleName()
@@ -553,7 +568,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
     public void run() {
       try {
        ts = getYarrgTimestamp();
-       progresslog("(async) yarrg timestamp ready.");
+       debuglog("(async) yarrg timestamp ready.");
       } catch(Exception e) {
        error("Error getting YARRG timestamp: "+e);
       }
@@ -561,57 +576,61 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
   };
 
   private void runUpload() throws Exception {
+    on_our_thread();
+
     boolean doneyarrg = false, donepctb = false;
     YarrgTimestampFetcher yarrgts_thread = null;
 
-    progresslog("starting");
+    debuglog("starting");
 
     if (uploadToYarrg) {
-      progresslog("(async) yarrg timestamp...");
+      debuglog("(async) yarrg timestamp...");
       yarrgts_thread = new YarrgTimestampFetcher();
       yarrgts_thread.start();
     }
 
-    AccessibleTable accesstable = null;
-    new UI() { public boolean body() {
-      ProgressMonitor pm = new ProgressMonitor
+    final AccessibleTable accesstable = 
+    new UIXR<AccessibleTable>() { public AccessibleTable bodyr() {
+      progmon = new ProgressMonitor
        (frame,"Processing Market Data","Getting table data",0,100);
-      pm.setMillisToDecideToPopup(0);
-      pm.setMillisToPopup(0);
+      progmon.setMillisToDecideToPopup(0);
+      progmon.setMillisToPopup(0);
 
-      accesstable = findMarketTable();
-      if(accesstable == null) {
+      AccessibleTable at = findMarketTable();
+      if(at == null) {
        error("Market table not found!"+
              " Please open the Buy/Sell Commodities interface.");
-       return;
+       return null;
       }
-      if(accesstable.getAccessibleRowCount() == 0) {
+      if(at.getAccessibleRowCount() == 0) {
        error("No data found, please wait for the table to have data first!");
-       return;
+       return null;
       }
       if(!isDisplayAll()) {
        error("Please select \"All\" from the Display: popup menu.");
-       return;
+       return null;
       }
 
-      progresslog("(async) getisland...");
+      debuglog("(async) getisland...");
       getIsland();
-      progresslog("getocean...");
+      debuglog("getocean...");
       getOcean();
-      progresslog("getocean done");
+      debuglog("getocean done");
+
+      return at;
     }}.exec();
     if (accesstable == null) return;
 
     if (latch != null) {
       latch.await(2, java.util.concurrent.TimeUnit.SECONDS);
     }
-    progresslog("(async) getisland done");
+    debuglog("(async) getisland done");
 
     String yarrgts = null;
     if (yarrgts_thread != null) {
-      progresslog("(async) yarrg timestamp join...");
+      debuglog("(async) yarrg timestamp join...");
       yarrgts_thread.join();
-      progresslog("(async) yarrg timestamp joined.");
+      debuglog("(async) yarrg timestamp joined.");
       yarrgts = yarrgts_thread.ts;
     }
 
@@ -620,24 +639,25 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
       return;
     }
 
-    progresslog("table check...");
+    debuglog("table check...");
 
-    ArrayList<ArrayList<String>> data = null;
-    String headings_expected[] = new String[]
-      { "Commodity", "Trading outlet", "Buy price",
-       "Will buy", "Sell price", "Will sell" };
+    final ArrayList<ArrayList<String>> data =
+    new UIXR<ArrayList<ArrayList<String>>>
+          () { public ArrayList<ArrayList<String>> bodyr() {
+      String headings_expected[] = new String[]
+       { "Commodity", "Trading outlet", "Buy price",
+         "Will buy", "Sell price", "Will sell" };
 
-    new UI() { public void body() {
       ArrayList<ArrayList<String>> headers =
        getData(accesstable.getAccessibleColumnHeader());
       if (headers.size() != 1) {
        error("Table headings not one row! " + headers.toString());
-       return;
+       return null;
       }
       if (headers.get(0).size() < 6 ||
          headers.get(0).size() > 7) {
        error("Table headings not six or seven columns! " + headers.toString());
-       return;
+       return null;
       }
       for (int col=0; col<headings_expected.length; col++) {
        String expd = headings_expected[col];
@@ -646,26 +666,26 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
          error("Table heading for column "+col
                +" is not \""+expd+"\" but \""+got+"\".\n\n"
                +"Please do not reorder the table when using this tool.");
-         return;
+         return null;
        }
       }
 
-      progresslog("table read...");
+      debuglog("table read...");
 
-      ArrayList<ArrayList<String>> data = getData(accesstable);
+      return getData(accesstable);
     }}.exec();
-    if (!data) return;
+    if (data == null) return;
 
     if (showArbitrage) {
-      progresslog("arbitrage...");
+      debuglog("arbitrage...");
       calculateArbitrage(data);
-      progresslog("arbitrage done.");
+      debuglog("arbitrage done.");
     }
 
     if (uploadToYarrg && yarrgts != null) {
-      progresslog("yarrg prepare...");
-      progressNote(pm, "Yarrg: Preparing data");
-      pm.setProgress(10);
+      debuglog("yarrg prepare...");
+      progressNote("Yarrg: Preparing data");
+      setProgress(10);
 
       StringBuilder yarrgsb = new StringBuilder();
       String yarrgdata; // string containing what we'll feed to yarrg
@@ -684,44 +704,44 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
 
       yarrgdata = yarrgsb.toString();
 
-      progressNote(pm, "Yarrg: Uploading");
-      progresslog("yarrg upload...");
+      progressNote("Yarrg: Uploading");
+      debuglog("yarrg upload...");
 
       doneyarrg = runYarrg(yarrgts, oceanName, islandName, yarrgdata);
-      progresslog("yarrg done.");
+      debuglog("yarrg done.");
     }
 
     if (uploadToPCTB) {
-      progresslog("pctb prepare...");
-      progressNote(pm, "PCTB: Getting stall names");
-      pm.setProgress(20);
-      if(pm.isCanceled()) {
+      debuglog("pctb prepare...");
+      progressNote("PCTB: Getting stall names");
+      setProgress(20);
+      if(isCanceled()) {
        return;
       }
       TreeSet<Offer> buys = new TreeSet<Offer>();
       TreeSet<Offer> sells = new TreeSet<Offer>();
       LinkedHashMap<String,Integer> stallMap = getStallMap(data);
-      pm.setProgress(40);
-      progressNote(pm, "PCTB: Sorting offers");
-      if(pm.isCanceled()) {
+      setProgress(40);
+      progressNote("PCTB: Sorting offers");
+      if(isCanceled()) {
        return;
       }
       // get commod map
                
-      progresslog("pctb commodmap...");
+      debuglog("pctb commodmap...");
       HashMap<String,Integer> commodMap = getCommodMap();
       if(commodMap == null) {
        return;
       }
-      progresslog("pctb commodmap done.");
+      debuglog("pctb commodmap done.");
       int[] offerCount = getBuySellMaps(data,buys,sells,stallMap,commodMap);
-      // if (dtxt!=null) dtxt.println(sells);
-      // if (dtxt!=null) dtxt.println("\n\n\n"+buys);
+      // debuglog(sells);
+      // debuglog("\n\n\n"+buys);
 
       ByteArrayOutputStream outStream = new ByteArrayOutputStream();
-      pm.setProgress(60);
-      progressNote(pm, "PCTB: Sending data");
-      if(pm.isCanceled()) {
+      setProgress(60);
+      progressNote("PCTB: Sending data");
+      if(isCanceled()) {
        return;
       }
       GZIPOutputStream out = new GZIPOutputStream(outStream);
@@ -731,36 +751,42 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
       dos.writeBytes(getAbbrevStallList(stallMap));
       writeBuySellOffers(buys,sells,offerCount,out);
       out.finish();
-      progresslog("pctb send...");
+      debuglog("pctb send...");
 
       byte[] ba = outStream.toByteArray();
       debug_write_bytes("pctb-marketdata.gz", ba);
 
       InputStream in = sendInitialData(new ByteArrayInputStream(ba));
-      progresslog("pctb sent.");
+      debuglog("pctb sent.");
       if (in == null) return;
-      pm.setProgress(80);
-      if(pm.isCanceled()) {
+      setProgress(80);
+      if(isCanceled()) {
        return;
       }
-      progressNote(pm, "PCTB: Waiting ...");
-      progresslog("pctb finish...");
+      progressNote("PCTB: Waiting ...");
+      debuglog("pctb finish...");
       donepctb = finishUpload(in);
-      progresslog("pctb done.");
+      debuglog("pctb done.");
     }
-    pm.setProgress(100);
+    setProgress(99);
 
+    String summary;
     if ((uploadToPCTB && !donepctb) ||
        (uploadToYarrg && !doneyarrg)) {
-      resultSummary.setText("trouble");
+      summary= "trouble";
     } else if (unknownPCTBcommods != 0) {
-      resultSummary.setText("PCTB lacks "+unknownPCTBcommods+" commod");
+      summary= "PCTB lacks "+unknownPCTBcommods+" commod(s)";
     } else if (donepctb || doneyarrg) {
-      resultSummary.setText("Done " + islandName);
+      summary= "Done " + islandName;
     } else {
-      resultSummary.setText("uploaded nowhere!");
+      summary= "uploaded nowhere!";
     }
-    progresslog("done.");
+    final String summary_final = summary;
+    new UIX() { public void body() {
+      resultSummary.setText(summary_final);
+    }}.exec();
+
+    debuglog("done.");
   }
        
   /**
@@ -771,6 +797,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
    *   @return an array of record arrays, each representing a row of the table
    */
   private ArrayList<ArrayList<String>> getData(AccessibleTable table) {
+    on_ui_thread();
     ArrayList<ArrayList<String>> data = new ArrayList<ArrayList<String>>();
     for (int i = 0; i < table.getAccessibleRowCount(); i++) {
       ArrayList<String> row = new ArrayList<String>();
@@ -788,18 +815,19 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
    *   otherwise <code>null</code>
    */
   public AccessibleTable findMarketTable() {
+    on_ui_thread();
     Accessible node1 = window;
     Accessible node = descendNodes(node1,new int[] {0,1,0,0,0,0,1,0,0,1,0,0}); 
       // commod market
     // commod market: {0,1,0,0,0,0,1,0,0,1,0}  {0,1,0,0,0,0,1,0,1,0,0,1,0,0})
-    // if (dtxt!=null) dtxt.println(node);
+    // debuglog(node);
     if (!(node instanceof JTable)) {
       node = descendNodes(node1,new int[] {0,1,0,0,0,0,1,0,1,0,0,1,0,0});
         // commod market
     }
     if (!(node instanceof JTable)) return null;
     AccessibleTable table = node.getAccessibleContext().getAccessibleTable();
-    // if (dtxt!=null) dtxt.println(table);
+    // debuglog(table);
     return table;
   }
        
@@ -815,6 +843,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
    *   invalid.
    */
   private Accessible descendNodes(Accessible parent, int[] path) {
+    on_ui_thread();
     for(int i=0;i<path.length;i++) {
       if (null == (parent = descend(parent, path[i]))) return null;
     }
@@ -831,17 +860,16 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
    *   or <code>null</code> if the child is not found.
    */
   private Accessible descend(Accessible parent, int childNum) {
+    on_ui_thread();
     if (parent == null) return null;
     int children = parent.getAccessibleContext().getAccessibleChildrenCount();
     if (childNum >= children) {
-      if (dtxt!=null) dtxt.println("DESCEND "+childNum+" > "
-                                  +children+" NOT FOUND");
+      debuglog("DESCEND "+childNum+" > "+children+" NOT FOUND");
       return null;
     }
     Accessible child = parent.getAccessibleContext()
       .getAccessibleChild(childNum);
-    if (dtxt!=null) dtxt.println("DESCEND "+childNum+" "
-                                +child.getClass().getName()+" OK");
+    debuglog("DESCEND "+childNum+" "+child.getClass().getName()+" OK");
     return child;
   }
 
@@ -853,17 +881,18 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
    *   @return the child or <code>null</code> if the child is not found.
    */
   private Accessible descendByClass(Accessible parent, String classname) {
+    on_ui_thread();
     if (parent == null) return null;
     AccessibleContext ac = parent.getAccessibleContext();
     int children = ac.getAccessibleChildrenCount();
     for (int i=0; i<children; i++) {
       Accessible child = ac.getAccessibleChild(i);
       if (child.getClass().getName() == classname) {
-       if (dtxt!=null) dtxt.println("DESCEND CLASS "+classname+" OK");
+       debuglog("DESCEND CLASS "+classname+" OK");
        return child;
       }
     }
-    if (dtxt!=null) dtxt.println("DESCEND CLASS "+classname+" NOT FOUND");
+    debuglog("DESCEND CLASS "+classname+" NOT FOUND");
     return null;
   }
 
@@ -879,6 +908,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
    *   otherwise <code>false</code>
    */
   private boolean isDisplayAll() {
+    on_ui_thread();
     Accessible button = descendNodes(window,new int[] {0,1,0,0,0,0,1,0,0,0,1});
     if(!(button instanceof JButton)) {
       button = descendNodes(window,new int[] {0,1,0,0,0,0,1,0,1,0,0,0,1});
@@ -897,6 +927,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
    *   the commodity id.
    */
   private HashMap<String,Integer> getCommodMap() {
+    on_our_thread();
     if(commodMap != null) {
       return commodMap;
     }
@@ -996,8 +1027,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
        }
       } catch(IllegalArgumentException e) {
        unknownPCTBcommods++;
-       if (dtxt!=null) dtxt.println("Error: Unsupported Commodity \""
-                                    + offer.get(0) + "\"");
+       debuglog("Error: Unsupported Commodity \"" + offer.get(0) + "\"");
       }
     }
     if (buySellCount[0]==0 && buySellCount[1]==0) {
@@ -1144,6 +1174,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
    *   @param file an InputStream open to the gzipped data we want to send
    */
   private InputStream sendInitialData(InputStream file) throws IOException {
+    on_our_thread();
     ClientHttpRequest http =
       new ClientHttpRequest(PCTB_HOST_URL + "upload.php");
     http.setParameter("marketdata","marketdata.gz",file,"application/gzip");
@@ -1177,6 +1208,8 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
    *   @param in stream of data from the server to read
    */
   private boolean finishUpload(InputStream in) throws IOException {
+    on_our_thread();
+
     String html = readstreamstring(in);
     debug_write_stringdata("pctb-initial.html", html);
     Matcher m;
@@ -1238,6 +1271,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
 
   private InputStream post_for_yarrg(ClientHttpRequest http)
   throws IOException {
+    on_our_thread();
     if (!http.post()) {
       String err = readstreamstring(http.resultstream());
       error("<html><h1>Error reported by YARRG server</h1>\n" + err);
@@ -1291,7 +1325,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
   }
 
   private int calculateArbitrageCommodity(ArrayList<SortedSet<int[]>> arb_bs) {
-    // if (dtxt!=null) dtxt.println("ARBITRAGE?");
+    // debuglog("ARBITRAGE?");
     int profit = 0;
     SortedSet<int[]> buys = arb_bs.get(0);
     SortedSet<int[]> sells = arb_bs.get(1);
@@ -1307,9 +1341,9 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
 
       int unitprofit = buy[0] - sell[0];
       int count = buy[1] < sell[1] ? buy[1] : sell[1];
-      // if (dtxt!=null) dtxt.println(" sell @"+sell[0]+" x"+sell[1]
-      //                       +" buy @"+buy[0]+" x"+buy[1]
-      //                      +" => x"+count+" @"+unitprofit);
+      // debuglog(" sell @"+sell[0]+" x"+sell[1]
+      //          +" buy @"+buy[0]+" x"+buy[1]
+      //         +" => x"+count+" @"+unitprofit);
 
       if (unitprofit <= 0)
        break;
@@ -1320,7 +1354,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
       if (buy[1]==0) buys.remove(buy);
       if (sell[1]==0) sells.remove(sell);
     }
-    // if (dtxt!=null) dtxt.println(" PROFIT "+profit);
+    // debuglog(" PROFIT "+profit);
     return profit;
   }
 
@@ -1333,7 +1367,8 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
   }
 
   private @SuppressWarnings("unchecked")
-    void calculateArbitrage(ArrayList<ArrayList<String>> data) {
+  void calculateArbitrage(ArrayList<ArrayList<String>> data) 
+  throws InterruptedException {
     int arbitrage = 0;
     ArrayList<SortedSet<int[]>> arb_bs = null;
     String lastcommod = null;
@@ -1341,15 +1376,15 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
 
     for (ArrayList<String> row : data) {
       String thiscommod = row.get(0);
-      // if (dtxt!=null) dtxt.println("ROW "+row.toString());
+      // debuglog("ROW "+row.toString());
       if (lastcommod == null || !thiscommod.equals(lastcommod)) {
        if (lastcommod != null)
          arbitrage += calculateArbitrageCommodity(arb_bs);
-       // if (dtxt!=null) dtxt.println("ROW rdy");
+       // debuglog("ROW rdy");
        arb_bs = new ArrayList<SortedSet<int[]>>(2);
        arb_bs.add(0, new TreeSet<int[]>(compar));
        arb_bs.add(1, new TreeSet<int[]>(compar));
-       // if (dtxt!=null) dtxt.println("ROW init");
+       // debuglog("ROW init");
        lastcommod = thiscommod;
       }
       for (int bs = 0; bs < 2; bs++) {
@@ -1357,19 +1392,23 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener {
        if (pricestr == null)
          continue;
        int[] entry = new int[2];
-       // if (dtxt!=null) dtxt.println("ROW BS "+bs);
+       // debuglog("ROW BS "+bs);
        entry[0] = parseQty(pricestr);
        entry[1] = parseQty(row.get(bs*2 + 3));
        arb_bs.get(bs).add(entry);
       }
     }
     arbitrage += calculateArbitrageCommodity(arb_bs);
+    String arb;
     if (arbitrage != 0) {
-      arbitrageResult.setText("<html><strong>arbitrage: "+arbitrage
-                             +" poe</strong>");
+      arb = "<html><strong>arbitrage: "+arbitrage+" poe</strong>";
     } else {
-      arbitrageResult.setText("no arbitrage");
+      arb = "no arbitrage";
     }
+    final String arb_final = arb;
+    EventQueue.invokeLater(new Runnable() { public void run() {
+      arbitrageResult.setText(arb_final);
+    }});
   }
     
 }