Menu

Using SwingUtilities.invokeAndWait()

Help
eyemme
2008-03-06
2013-04-16
  • eyemme

    eyemme - 2008-03-06

    I'm working with an applet where tables and list selections are changed (as in changing the row that is currently selected) by calling JavaScript functions which, in turn, call public methods off of the applet.  To ensure that the UI gets redrawn correctly, I've been running the relevant code through the SwingUtilties.invokeAndWait() method.  This way of doing things works in IE and Firefox on Windows and in Safari on Mac OS X, but I'm getting the following exception when I run the applet in Firefox, Camino, and SeaMonkey on the Mac.

    java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread
         at java.awt.EventQueue.invokeAndWait(EventQueue.java:834)
         at javax.swing.SwingUtilities.invokeAndWait(SwingUtilities.java:1257)
         at Client.Main.ShowNextDocument(Main.java:291)
         at netscape.oji.JNIRunnable.run(Native Method)
         at netscape.oji.LiveConnectProxy.run(LiveConnectProxy.java:48)
         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
         at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
         at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
         at java.lang.reflect.Method.invoke(Method.java:585)
         at jep.LiveConnect$DoProxy.run(LiveConnect.java:125)
         at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
         at java.awt.EventQueue.dispatchEvent(EventQueue.java:461)
         at jep.MySimpleEventQueue.dispatchEvent(MySimpleEventQueue.java:59)
         at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:269)
         at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:190)
         at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:184)
         at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:176)
         at java.awt.EventDispatchThread.run(EventDispatchThread.java:110)

    Since invokeAndWait() blocks the calling thread, this method cannot be called from the event dispatch thread, which is what seems to be happening here.  Is this a side effect of how the JEP does things and if so, is there a way to use invokeAndWait()?

    As a side note, SwingUtilities.invokeLater() is allowed in the event dispatch thread since it doesn't block the calling thread, but I need to do some further processing based on the newly selected row, so invokeAndWait() seems to be the simplest way to wait for the UI to update.

    I can post a more complete example if neccessary.  Any help would be appreciated.

    Javadoc explanation of the invokeAndWait() method:
    http://java.sun.com/javase/6/docs/api/javax/swing/SwingUtilities.html#invokeAndWait\(java.lang.Runnable)

    Java code snippet:
        public void SelectNextRow()
        {
        Runnable runnableCode = new Runnable()
        {
            public void run()
            {
            InternalSelectNextRow();
            }
        };
       
        try
        {
            SwingUtilities.invokeAndWait(runnableCode);
        }
        catch(Exception ex)
        {
            System.out.println("Problem selecting next row");
            ex.printStackTrace();
        }
        }
       
        private void InternalSelectNextRow()
        {
        int row = _table.getSelectedRow();
       
        if(row < _table.getRowCount() - 1)
        {
            row++;
        }
        else
        {
            row = 0;
        }

        _table.changeSelection(row, 0, false, false);
        }

    JavaScript code snippet:
        function SelectNextRow()
        {
            testApplet.SelectNextRow();
        }

     
    • Steven Michaud

      Steven Michaud - 2008-03-08

      > I can post a more complete example if neccessary.

      Please do (please post the source code for a complete test applet).

      Moreover (as you say) your code is running via a call from
      JavaScript-to-Java LiveConnect.  (This is also apparent from your
      exception's stack trace.)

      Does the exception still happen when the code is called from Java?

       
    • eyemme

      eyemme - 2008-03-13

      // This is the source code for the test applet.
      package Main;

      import javax.swing.SwingUtilities;

      public class TableApplet extends javax.swing.JApplet
      {
          private javax.swing.JButton _normalNext;
          private javax.swing.JButton _normalPrevious;
          private javax.swing.JScrollPane _scroll;
          private javax.swing.JTable _table;
          private javax.swing.JButton _threadedNext;
          private javax.swing.JButton _threadedPrevious;
          private javax.swing.JPanel jPanel1;

          /** Initializes the applet TableApplet */
          public void init()
          {
          try
          {
              java.awt.EventQueue.invokeAndWait(new Runnable()
              {
              public void run()
              {
                  initComponents();
              }
              });
          }
          catch (Exception ex)
          {
              ex.printStackTrace();
          }
          }
         
          // Called by JavaScript to select the next row in the table through the event dispatch thread.
          // This method should not throw an exception when called from JavaScript.
          public void SelectNextRow()
          {
          Runnable runnableCode = new Runnable()
          {
              public void run()
              {
              InternalSelectNextRow();
              }
          };
         
          try
          {
              SwingUtilities.invokeAndWait(runnableCode);
          }
          catch(Exception ex)
          {
              System.out.println("Problem selecting next row");
              ex.printStackTrace();
          }
          }
         
          // Selects the next row in the table.
          private void InternalSelectNextRow()
          {
          int row = _table.getSelectedRow();
         
          if(row < _table.getRowCount() - 1)
          {
              row++;
          }
          else
          {
              row = 0;
          }

          _table.changeSelection(row, 0, false, false);
          }
         
          // Called by JavaScript to select the previous row in the table through the event dispatch thread.
          // This method should not throw an exception when called from JavaScript.
          public void SelectPreviousRow()
          {
          Runnable runnableCode = new Runnable()
          {
              public void run()
              {
              InternalSelectPreviousRow();
              }
          };
         
          try
          {
              SwingUtilities.invokeAndWait(runnableCode);
          }
          catch(Exception ex)
          {
              System.out.println("Problem selecting previous row");
              ex.printStackTrace();
          }
          }
         
          // Selects the previous row in the table.
          private void InternalSelectPreviousRow()
          {
          int row = _table.getSelectedRow();
         
          if(row > 0)
          {
              row--;
          }
          else
          {
              row = _table.getRowCount() - 1;
          }

          _table.changeSelection(row, 0, false, false);
          }
         
          private void initComponents()
          {
              _scroll = new javax.swing.JScrollPane();
              _table = new javax.swing.JTable();
              jPanel1 = new javax.swing.JPanel();
              _normalPrevious = new javax.swing.JButton();
              _normalNext = new javax.swing.JButton();
              _threadedPrevious = new javax.swing.JButton();
              _threadedNext = new javax.swing.JButton();

              _table.setModel(new javax.swing.table.DefaultTableModel(
                  new Object [][]
                  {
                      {"1", "a"},
                      {"2", "b"},
                      {"3", "c"},
                      {"4", "d"},
                      {"5", "e"},
                      {"6", "f"},
                      {"7", "g"},
                      {"8", "h"},
                      {"9", "i"},
                      {"10", "j"},
                      {"11", "k"},
                      {"12", "l"},
                      {"13", "m"},
                      {"14", "n"},
                      {"15", "o"},
                      {"16", "p"},
                      {"17", "q"},
                      {"18", "r"},
                      {"19", "s"},
                      {"20", "t"},
                      {"21", "u"},
                      {"22", "v"},
                      {"23", "w"},
                      {"24", "x"},
                      {"25", "y"},
                      {"26", "z"}
                  },
                  new String []
                  {
                      "Title 1", "Title 2"
                  }
              ));
              _scroll.setViewportView(_table);

              getContentPane().add(_scroll, java.awt.BorderLayout.WEST);

              _normalPrevious.setText("Previous (normal)");
              _normalPrevious.addActionListener(new java.awt.event.ActionListener()
              {
                  public void actionPerformed(java.awt.event.ActionEvent evt)
                  {
                      _normalPreviousActionPerformed(evt);
                  }
              });

              jPanel1.add(_normalPrevious);

              _normalNext.setText("Next (normal)");
              _normalNext.addActionListener(new java.awt.event.ActionListener()
              {
                  public void actionPerformed(java.awt.event.ActionEvent evt)
                  {
                      _normalNextActionPerformed(evt);
                  }
              });

              jPanel1.add(_normalNext);

              _threadedPrevious.setText("Previous (threaded)");
              _threadedPrevious.addActionListener(new java.awt.event.ActionListener()
              {
                  public void actionPerformed(java.awt.event.ActionEvent evt)
                  {
                      _threadedPreviousActionPerformed(evt);
                  }
              });

              jPanel1.add(_threadedPrevious);

              _threadedNext.setText("Next (threaded)");
              _threadedNext.addActionListener(new java.awt.event.ActionListener()
              {
                  public void actionPerformed(java.awt.event.ActionEvent evt)
                  {
                      _threadedNextActionPerformed(evt);
                  }
              });

              jPanel1.add(_threadedNext);

              getContentPane().add(jPanel1, java.awt.BorderLayout.NORTH);

          }

          // Next (threaded) button action.
          // Attempts to select the next row in the table through the AWT event dispatch thread.
          // This method should throw an exception.
          private void _threadedNextActionPerformed(java.awt.event.ActionEvent evt)                                             
          {                                                 
          Runnable runnableCode = new Runnable()
          {
              public void run()
              {
              InternalSelectNextRow();
              }
          };
         
          try
          {
              SwingUtilities.invokeAndWait(runnableCode);
          }
          catch(Exception ex)
          {
              System.out.println("Problem selecting next row");
              ex.printStackTrace();
          }
          }                                            

          // Previous (threaded) button action.
          // Attempts to select the previous row in the table through the AWT event dispatch thread.
          // This method should throw an exception.
          private void _threadedPreviousActionPerformed(java.awt.event.ActionEvent evt)                                                 
          {                                                     
          Runnable runnableCode = new Runnable()
          {
              public void run()
              {
              InternalSelectPreviousRow();
              }
          };
         
          try
          {
              SwingUtilities.invokeAndWait(runnableCode);
          }
          catch(Exception ex)
          {
              System.out.println("Problem selecting previous row");
              ex.printStackTrace();
          }
          }                                                

          // Next (normal) button action.
          // Selects the next row in the document without doing anything fancy.
          private void _normalNextActionPerformed(java.awt.event.ActionEvent evt)                                           
          {                                               
          InternalSelectNextRow();
          }                                          

          // Previous (normal) button action.
          // Selects the previous row in the document without doing anything fancy.
          private void _normalPreviousActionPerformed(java.awt.event.ActionEvent evt)                                               
          {                                                   
          InternalSelectPreviousRow();
          }                                              
      }

       
    • eyemme

      eyemme - 2008-03-13

      This is the HTML for the web page that's using the above applet.  Let me know if you need more information or want this code packaged differently.  Thanks for your help.

      <html>
          <head>
              <title>Table Test</title>
              <script language="javascript" type="text/javascript">
                  function SelectNextRow()
                  {
                      testApplet.SelectNextRow();
                  }
                 
                  function SelectPreviousRow()
                  {
                      testApplet.SelectPreviousRow();
                  }
              </script>
          </head>
          <body>
              <p>
                  <input type="button" value="Previous Row" onclick="SelectPreviousRow();" />
              </p>
              <p>
                  <input type="button" value="Next Row" onclick="SelectNextRow();" />
              </p>
              <p>
                  <applet code="Main.TableApplet.class" name="testApplet" archive="ThrowAway.jar" height="400px" width="600px"></applet>
              </p>
          </body>
      </html>

       
    • Steven Michaud

      Steven Michaud - 2008-03-16

      Thanks for the very thorough testcase.

      I can reproduce the problem you report with the top two buttons in
      your HTML page ("Previous Row" and "Next Row"), which call the
      SelectNextRow() and SelectPreviousRow() functions in your applet.

      This happens on Tiger (OS X 10.4.X) and Leopard (OS X 10.5.X), though
      not on earlier versions of OS X (10.3.X or 10.2.8).  I've tracked this
      down to a design flaw in the way the Java Embedding Plugin handles
      JavaScript-to-Java LiveConnect on Tiger and Leopard -- the JEP always
      runs such calls on an event dispatch thread (preferrably the applet's
      event dispatch thread).

      This won't be easy to change, and I currently don't have much time to
      spend on the Java Embedding Plugin.  But I do hope to address this
      problem at some point in the next few months.

      Interestingly, I can also reproduce the same problem in Firefox on all
      platforms (and in Safari) with your applet's "Previous (threaded)" and
      "Next (threaded)" buttons.  The problem seems to be that (on all JVMs)
      "action listeners" always run their commands on an event dispatch
      thread.  Besides OS X (Tiger and Leopard), I tested on Windows XP and
      Kubuntu Linux.

       
    • eyemme

      eyemme - 2008-03-17

      The "Previous (threaded)" and "Next (threaded)" buttons are supposed to throw exceptions on all platforms and browsers since, as you pointed out, button presses are run in the event dispatch thread.  I included it to show the similarity between that case and the HTML buttons throwing exceptions in Firefox.  I guess I should have made it clearer in the comments that this isn't a bug, just another case to compare and contrast against.  Sorry for the confusion.

      At any rate, thanks for taking a look at the issue.  I understand that your work on this plugin is essentially a free service to us, so any time that you do put into it is always appreciated.  Now that I have a better understanding of what's going on, I can start working on a workaround for what I'm trying to do.

      Finally, did you want me to write up a bug report for this issue?  Otherwise, I'm fine with leaving it in the forums.

      Thanks again for your help.

       

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.