Path: news.mathworks.com!not-for-mail
From: "Michael Bushe" <michael.bushe-removeme@mathworks.com>
Newsgroups: comp.soft-sys.matlab
Subject: Re: Profiling Java in Matlab
Date: Wed, 31 Oct 2007 18:54:40 +0000 (UTC)
Organization: The MathWorks Inc
Lines: 179
Message-ID: <fgaj1g$imo$1@fred.mathworks.com>
References: <fcqhf3$fcj$1@fred.mathworks.com> <feg3vs$6ha$1@fred.mathworks.com>
Reply-To: "Michael Bushe" <michael.bushe-removeme@mathworks.com>
NNTP-Posting-Host: webapp-05-blr.mathworks.com
Content-Type: text/plain; charset="ISO-8859-1"
Content-Transfer-Encoding: 8bit
X-Trace: fred.mathworks.com 1193856880 19160 172.30.248.35 (31 Oct 2007 18:54:40 GMT)
X-Complaints-To: news@mathworks.com
NNTP-Posting-Date: Wed, 31 Oct 2007 18:54:40 +0000 (UTC)
X-Newsreader: MATLAB Central Newsreader 887095
Xref: news.mathworks.com comp.soft-sys.matlab:435487



"Wolfgang Ulmer" <spam@wulmer.de> wrote in message
<feg3vs$6ha$1@fred.mathworks.com>...
> Hi Michael,
> 
> while using Swing with Java, I discovered the same problem
> which is due to the fact, that Matlab uses the AWT/Swing
> Event  Handling Thread to do some computation.
> 
> You can even easily create a deadlock if you enter the
> following commands on the prompt:
> >> import com.mathworks.jmi.*;
> >> matlab = Matlab;
> >> matlab.eval('1+1'); 
> 
> I think, that one solution is not to use the java event
> mechanisms but the Matlab methods for defining callback
> routines.
> 
> Example:
> 
> button = JButton('Close');
> set(button,'ActionPerformedCallback',@buttonClosePressed);
> 
> function buttonClosePressed(handle,event)
>    doSomething();
> end
> 
> -------------
> 
> See http://xtargets.com/snippets/tag/java for details and
> further examples.
> 
> Don't expect Mathworks to answer your question... they do
> not support Java GUI-Matlab interaction.
> 
> Wolfgang


Wolfgang's example will do most of what you want, and he is
right - you need to use MATLAB-provided APIs for accessing
Swing Events.  It is not correct, however, that MATLAB does
computation on the AWT/Swing thread, this would lock up
GUIs.  Also, using jmi classes is not a good idea.  They are
not public or supported and it's easy to use them improperly
(even for developers inside the MathWorks).  Lastly, there
is a memory leak in the example, the workaround is detailed
below, but first, let's discuss Swing threading.

The Swing threading problem is not specific to MATLAB.  In
pure Java if you write the following innocent looking code,
it is erroneous:

package example;

import javax.swing.*;

public class SwingApp {
    JFrame frame = new JFrame();
    JButton button = new JButton("Do Something");

    public void showFrame() {
      frame.getContentPane().add(getButton());
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }

    public JButton getButton() {
        return button;
    }

    public static void main(String args[]) {
        //Wrong! Swing object construction not on the
EventDispatchThread
        SwingApp app = new SwingApp();
        //Wrong! Swing component access not on the
EventDispatchThread
        app.showFrame();
    }
}

The problem is that the class is making Swing calls on
Java's main thread.  Sun asks developers to follow the rule
that all access to Swing components must occur on Swing's
EventDispatchThread (there may be some leeway, but it's best
to always follow the rule).  This may look like it works
fine, but every so often you'll encounter lock ups and
strange behavior like listeners not firing.

This rule is broken when calling Swing components from the
MATLAB command line in the same manner since Swing calls are
being made from the MATLAB thread:

>> javaaddpath c:\myapp\classes
>> app = example.SwingApp;  % Wrong! Swing object
construction not on the EventDispatchThread
>> app.showFrame(); % Wrong! Swing component access not on
the EventDispatchThread

In pure Java, the right way to write this code is to wrap
all Swing calls in a Runnable that is later called on the
EventDispatchThread:

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                SwingApp app = new SwingApp();
                app.showFrame();
            }
        });
    }

How can you do the same from MATLAB?  There are additional
undocumented APIs that you can use to avoid Java Swing
threading issues, specifically awtcreate, awtinvoke and
javacomponent.  These APIs are not yet final and are
therefore undocumented and can change in future releases. 
Please don&#8217;t expect this to be the best way to work with
Swing Components going forward as we are planning to change
these APIs.  We appreciate any feedback you can give us to
help us deliver APIs that meet your needs.  

The functions awtcreate and awtinvoke both ship undocumented
&#8211; open the M-file for example usage.  They both ensure that
the work for the M command that interacts with the Java
component happens on Java's EventDispathThread and not
MATLAB's main thread.  

For the SwingApp, you can ensure the calls are pushed onto
the EventDispatchThread by using awtcreate and awtinvoke
like so:

>> app = awtcreate('example.SwingApp');
>> awtinvoke(app, 'showFrame');

The undocumented awtcreate and awtinvoke commands are like
wrapping each M line in a SwingUtiities.invokeLater call. 
Though unsupported now, it works well.  

If you are writing your own Java classes, you may want to
consider not using awtcreate and awtinvoke and push
Swing-related Java code to your own classes.  This will make
for better performance since it is one push to Swing's
EventDispatchThread, not many.  

For example, you could run the example SwingApp above with
the main() corrected to use SwingUtilities.invokeLater,
directly from M:

>> example.SwingApp.main('')

Since the class's main() method calls invokeLater, this is
safe.  

Rewriting your original example using awtcreate and
awtinvoke would look like this:

button = awtcreate('javax.swing.JButton');
awtinvoke(button,'setLabel','Close');

% make the button a handle object before calling set
button = handle(button,'callbackproperties');
set(button,'ActionPerformedCallback',@buttonClosePressed);
javacomponent(button)

In addition to the awtcreate and awtinvoke usage, there are
two other things to notice about the rewritten example:
1) It's better to use the "handle" command before calling
"set" (this will prevent a memory leak).
2) The javacomponent command, also undocumented, puts any
Java Swing component in a figure window &#8211; open the M-file
for example usage.

This is tricky stuff especially since the threading issues
are not immediately apparent.  We are interested in
providing the best Java GUI experience we can in MATLAB. 
Any feedback is greatly appreciated.