How to Replace the Awt Eventqueue with Own Implementation

How to replace the AWT EventQueue with own implementation

EventQueue has a method called push() that will do exactly what you want. Here is a little demo:

public class QueueTest {
public static void main(String[] args) throws InterruptedException, InvocationTargetException {
EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
eventQueue.push(new MyEventQueue());

EventQueue.invokeAndWait(new Runnable() {
public void run() {
System.out.println("Run");
}
});
}

private static class MyEventQueue extends EventQueue {
public void postEvent(AWTEvent theEvent) {
System.out.println("Event Posted");
super.postEvent(theEvent);
}
}
}

Prevent AWT Event Queue from overloading

You cannot reliably do this from the outside. It is not thread-safe to manipulate the event queue from outside the event-dispatch thread, and you cannot execute a task on the event dispatch thread that that mucks with events posted prior to that task itself being processed. You could maybe push a cleanup task onto the queue every second or two that would handle yet-to-be-processed events, but ... just don't.

If you want to filter AWT / Swing events then you need to install your own event queue, which is easier than it may sound. You may find this article to be instructive.

How to choose an AWT-EventQueue thread, when there are several of them

After a lot of experimentation and google searches with keywords like EventQueue and ThreadGroup I have finally found a solution (in the Works For Me category, mind you).

I use the sun.awt.AppContext class. Some documentation and sources here (grepcode.com)

  1. Get a Collection of the running AppContext's using the getAppContexts method.
  2. For each retrieved AppContext, get his ThreadGroup using the getThreadGroup method.
  3. With the ThreadGroup object, Use the getName method.
  4. When the name of the Thread Group starts with the http: address of your Forms Application, retrieve the Object property with key name sun.awt.AppContext.EVENT_QUEUE_KEY, using the get method of AppContext.
  5. The retrieved object is an EventQueue. Create an java.awt.event.InvocationEvent object, passing your Runnable to the CTOR, and use the postEvent method of EventQueue.
  6. Your run method will be executed in the right thread.

Remarks:

  • This answer is a specific, works for me, solution for an Oracle Forms Application launched via an Internet Explorer link, and running in a java.exe process. In that situation, the 3 Thread Groups are as shown in the question: main, Plugin Thread Group, and http://xxxx.xxxx.xxxxx.xx:8001/OA_JAVA/-threadGroup Your mileage may vary.
  • If you don't use full reflection, but instead do import sun.awt.AppContext, the compiler may emit warnings in the form warning: sun.awt.AppContext is Sun proprietary API and may be removed in a future release That's not very cool, but I will live with that, for the time being.
  • In the run method, I tested OK with the simulatePush method of oracle.ewt.lwAWT.AbstractButton.
  • The method emulated here is invokeLater. For invokeAndWait, more code is needed around the postEvent call. See some sources for the EventQueue class, as a starting point.

AWTEvent and EventQueue

I can only encourage people who try to take care of the Swing threading rules. However, despite your efforts to create events and push them onto the EventQueue you still access the Swing component on the background Thread with the calls for the length of the document and the altering of the caret position.

Extending a text component in order to set text on it looks like overkill to me, and certainly not the best way to approach this problem. Personally I would let the background Thread fill a buffer and flush that buffer to the text document once in a while (e.g. at each new line, twice a second using a Timer, each time I reach 1000 characters, ... ). For the update, I would simply use SwingUtilities.invokeLater.

Some example code to illustrate this, retrieved from an old project of mine. It won't compile but it illustrates my point. The class appends Strings to an ISwingLogger which should be accessed on the EDT. Note the usage of the javax.swing.Timer to have periodical updates on the EDT.

import javax.swing.Timer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class SwingOutputStream extends ByteArrayOutputStream{
private final ISwingLogger fSwingLogger;

private Timer fTimer;
private final StringBuilder fBuffer = new StringBuilder( 1000 );

public SwingOutputStream( ISwingLogger aSwingLogger ) {
fSwingLogger = aSwingLogger;

fTimer = new Timer( 200, new ActionListener() {
public void actionPerformed( ActionEvent aActionEvent ) {
flushBuffer();
}
} );
fTimer.setRepeats( false );
}

@Override
public void flush() throws IOException {
synchronized( fBuffer ){
fBuffer.append( toString( "UTF-8") );
}
if ( fTimer.isRunning() ){
fTimer.restart();
} else {
fTimer.start();
}

super.flush();
reset();
}

private void flushBuffer(){
synchronized ( fBuffer ){
final String output = fBuffer.toString();
fSwingLogger.appendString( output );
fBuffer.setLength( 0 );
}
}
}

java swing clear the event queue

OK, I finally got everything to work. I am posting the SSCCE for a correctly working example. The trick is to hide the glasspane using "javax.swing.SwingUtilities.invokeLater()" method. Wrap the necessary code in a Runnable and then invoke it using invokeLater. In such a case, Swing processes all the mouse events (nothing happens since a glasspane intercepts them), and then hides the glasspane. Here is the SSCCE.


public class BusyCursorTest2 extends javax.swing.JFrame {

public BusyCursorTest2() {

javax.swing.JButton wait = new javax.swing.JButton("Wait 3 seconds");
getContentPane().setLayout(new java.awt.GridLayout(2, 1, 0, 0));
getContentPane().add(wait);
getContentPane().add(new javax.swing.JToggleButton("Click me"));
setTitle("Busy Cursor");
setSize(300, 200);
setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
setVisible(true);

wait.addActionListener(new java.awt.event.ActionListener() {

public void actionPerformed(java.awt.event.ActionEvent event) {

final java.util.Timer timer = switchToBusyCursor(BusyCursorTest2.this);

try {
//do something expensive in EDT or otherwise
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
//do nothing
}
} finally {
switchToNormalCursorEventThread(BusyCursorTest2.this, timer);
}

}

});
}

public static java.util.Timer switchToBusyCursor(final javax.swing.JFrame frame) {
startEventTrap(frame);
java.util.TimerTask timerTask = new java.util.TimerTask() {

public void run() {
startWaitCursor(frame);
}

};
final java.util.Timer timer = new java.util.Timer();
timer.schedule(timerTask, DELAY_MS);
return timer;
}

public static void switchToNormalCursorEventThread(final javax.swing.JFrame frame, final java.util.Timer timer) {

Runnable r = new Runnable() {

public void run() {
switchToNormalCursor(frame, timer);
}

};

javax.swing.SwingUtilities.invokeLater(r);

}

public static void switchToNormalCursor(final javax.swing.JFrame frame, final java.util.Timer timer) {
timer.cancel();
stopWaitCursor(frame);
stopEventTrap(frame);
}

private static void startWaitCursor(javax.swing.JFrame frame) {
frame.getGlassPane().setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.WAIT_CURSOR));
frame.getGlassPane().addMouseListener(mouseAdapter);
frame.getGlassPane().setVisible(true);
}

private static void stopWaitCursor(javax.swing.JFrame frame) {
frame.getGlassPane().setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.DEFAULT_CURSOR));
frame.getGlassPane().removeMouseListener(mouseAdapter);
frame.getGlassPane().setVisible(false);
}

private static void startEventTrap(javax.swing.JFrame frame) {
frame.getGlassPane().addMouseListener(mouseAdapter);
frame.getGlassPane().setVisible(true);
}

private static void stopEventTrap(javax.swing.JFrame frame) {
java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue();
frame.getGlassPane().removeMouseListener(mouseAdapter);
frame.getGlassPane().setVisible(false);
}

private static final java.awt.event.MouseAdapter mouseAdapter = new java.awt.event.MouseAdapter() {
};

public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {

public void run() {
new BusyCursorTest2();
}

});

}

private static final int DELAY_MS = 250;

}

Again, EDT if at all possible must not be blocked. But if you have to, you can have a working busy cursor as above.

Any comments are welcome.



Related Topics



Leave a reply



Submit