问题
I'm trying to organize filter chain of EventQueue.dispatchEvent. Something like java.io.FilterInputStream or javax.servlet.Filter.
Found EventQueueDelegate.Delegate to be intended for this?.. But in case of exception in EventQueue.dispatchEvent
EventQueueDelegate.Delegate
it knows nothing about this and ugly java.awt.EventDispatchThread.handleException
appears on the scene.
- Is this 'temporary hack' not yet solved since Java SE 1.1???!!!
I've also looked toward EventQueue.dispatchEvent to call in chain. But it appeared not suitable for this due to this method is protected and it require additional dancing with tambourines to make things work and code becomes not so lovely.
- Any better solution?
回答1:
Next is dancing with tambourines around EventQueueDelegate.Delegate...
AwtExceptionHandler.java
package example;
/**
* @see java.awt.EventDispatchThread#handleException(Throwable thrown)
*/
public interface AwtExceptionHandler {
void handle(Throwable t) throws Throwable;
}
FilterEventQueueDelegate.java
package example;
import java.awt.AWTEvent;
import java.awt.EventQueue;
import java.lang.reflect.Method;
import java.util.ConcurrentModificationException;
import sun.awt.EventQueueDelegate;
/**
* Aims to organise filter chain of {@link EventQueueDelegate.Delegate}.
*
* <pre>
* private static final AwtResponsivenessMonitor instance = FilterEventQueueDelegate.chain(new AwtResponsivenessMonitor());
* </pre>
*
* @author Mykhaylo Adamovych
*/
public abstract class FilterEventQueueDelegate implements EventQueueDelegate.Delegate, AwtExceptionHandler {
public static final class ExceptionHandler {
private static AwtExceptionHandler currentExceptionHandler;
public void handle(Throwable t) throws Throwable {
currentExceptionHandler.handle(t);
}
}
private static final class SimpleFilterEventQueueDelegate extends FilterEventQueueDelegate {
private EventQueueDelegate.Delegate thirdPartyDelegate;
private Object thirdPartyExceptionHandler;
@Override
public void afterDispatch(AWTEvent arg0, Object arg1) throws InterruptedException {
if (thirdPartyDelegate != null)
thirdPartyDelegate.afterDispatch(arg0, arg1);
}
@Override
public Object beforeDispatch(AWTEvent arg0) throws InterruptedException {
if (thirdPartyDelegate != null)
return thirdPartyDelegate.beforeDispatch(arg0);
return arg0;
}
@Override
public AWTEvent getNextEvent(EventQueue arg0) throws InterruptedException {
if (thirdPartyDelegate != null)
return thirdPartyDelegate.getNextEvent(arg0);
return arg0.getNextEvent();
}
@Override
public void handle(Throwable t) throws Throwable {
if (thirdPartyExceptionHandler != null)
try {
Class<? extends Object> c = thirdPartyExceptionHandler.getClass();
Method m = c.getMethod("handle", new Class[] { Throwable.class });
m.invoke(thirdPartyExceptionHandler, new Object[] { t });
} catch (Throwable x) {
thirdPartyExceptionHandler = null; /* Do not try this again */
throw t;
}
else
throw t;
}
public void setEventQueueDelegate(EventQueueDelegate.Delegate delegate) {
thirdPartyDelegate = delegate;
}
public void setExceptionHandler(Object exceptionHandler) {
thirdPartyExceptionHandler = exceptionHandler;
}
}
public static <T extends FilterEventQueueDelegate> T chain(T delegate) {
synchronized (EventQueueDelegate.class) {
EventQueueDelegate.Delegate currentDelegate = EventQueueDelegate.getDelegate();
FilterEventQueueDelegate currentFilterDelegate = null;
if (currentDelegate instanceof FilterEventQueueDelegate)
currentFilterDelegate = (FilterEventQueueDelegate) currentDelegate;
else {
SimpleFilterEventQueueDelegate simpleFilterDelegate = new SimpleFilterEventQueueDelegate();
if (currentDelegate != null)
simpleFilterDelegate.setEventQueueDelegate(currentDelegate);
Object currentExceptionHandler = null;
try {
currentExceptionHandler = Class.forName(System.getProperty("sun.awt.exception.handler")).newInstance();
} catch (Exception e) {
}
if (currentExceptionHandler != null)
simpleFilterDelegate.setExceptionHandler(currentExceptionHandler);
System.setProperty("sun.awt.exception.handler", ExceptionHandler.class.getName());
currentFilterDelegate = simpleFilterDelegate;
}
delegate.setNext(currentFilterDelegate);
EventQueueDelegate.setDelegate(delegate);
if (EventQueueDelegate.getDelegate() != delegate)
throw new ConcurrentModificationException();
ExceptionHandler.currentExceptionHandler = delegate;
return delegate;
}
}
protected FilterEventQueueDelegate next;
@Override
public void afterDispatch(AWTEvent arg0, Object arg1) throws InterruptedException {
next.afterDispatch(arg0, arg1);
}
@Override
public Object beforeDispatch(AWTEvent arg0) throws InterruptedException {
return next.beforeDispatch(arg0);
}
@Override
public AWTEvent getNextEvent(EventQueue arg0) throws InterruptedException {
return next.getNextEvent(arg0);
}
@Override
public void handle(Throwable t) throws Throwable {
next.handle(t);
}
private void setNext(FilterEventQueueDelegate eventQueueDelegate) {
next = eventQueueDelegate;
}
}
AwtResponsivenessMonitor.java
package example;
import java.awt.AWTEvent;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* Monitors {@code EventDispatchThread} responsiveness.
* <p>
* Singleton is initialised on first access.
*
* @author Mykhaylo Adamovych
*/
public class AwtResponsivenessMonitor extends FilterEventQueueDelegate {
private static final class DeamonThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
Thread result = new Thread(r);
result.setName(AwtResponsivenessMonitor.class.getSimpleName());
result.setDaemon(true);
return result;
}
}
private static final class NotResponsive extends RuntimeException {
private static final long serialVersionUID = -1445765918431458354L;
}
public static final long DEFAULT_RESPONSIVENESS_TIMEOUT_S = 2;
public static final long RESPONSIVENESS_WATCHDOG_MS = 50;
private static final AwtResponsivenessMonitor instance = FilterEventQueueDelegate.chain(new AwtResponsivenessMonitor());
public static AwtResponsivenessMonitor getInstance() {
return instance;
}
public static long getResponsivenessTimeout() {
return instance.responsivenessTimeoutMs.get();
}
public static void setResponsivenessTimeout(long timeoutMs) {
instance.responsivenessTimeoutMs.set(timeoutMs);
}
private final AtomicLong responsivenessTimeoutMs = new AtomicLong(TimeUnit.SECONDS.toMillis(DEFAULT_RESPONSIVENESS_TIMEOUT_S));
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(new DeamonThreadFactory());
private long eventDispatchStartTime;
private Thread currentWorkingThread;
public AwtResponsivenessMonitor() {
executor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
checkResponsiveness();
}
}, RESPONSIVENESS_WATCHDOG_MS, RESPONSIVENESS_WATCHDOG_MS, TimeUnit.MILLISECONDS);
}
@Override
public synchronized void afterDispatch(AWTEvent arg0, Object arg1) throws InterruptedException {
eventDispatchStartTime = 0;
super.afterDispatch(arg0, arg1);
}
@Override
public synchronized Object beforeDispatch(AWTEvent arg0) throws InterruptedException {
eventDispatchStartTime = System.currentTimeMillis();
currentWorkingThread = Thread.currentThread();
return super.beforeDispatch(arg0);
}
private synchronized void checkResponsiveness() {
if (eventDispatchStartTime != 0 && currentWorkingThread != null && System.currentTimeMillis() > eventDispatchStartTime + responsivenessTimeoutMs.get()) {
Exception e = new NotResponsive();
e.setStackTrace(currentWorkingThread.getStackTrace());
e.printStackTrace();
currentWorkingThread = null;
}
}
@Override
public synchronized void handle(Throwable t) throws Throwable {
eventDispatchStartTime = 0;
super.handle(t);
}
}
AwtIdleTracker.java
package example;
import java.awt.AWTEvent;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.LockSupport;
import javax.swing.SwingUtilities;
import sun.awt.SunToolkit;
/**
* Tracks {@code EventDispatchThread} idleness.
* <p>
* Singleton is initialised on first access.
*
* @author Mykhaylo Adamovych
*/
public class AwtIdleTracker extends FilterEventQueueDelegate {
public static final long DEFAULT_IDLE_TIME_TO_TRACK_MS = 1000;
private static final long IDLE_TIME_WATCHDOG_MS = 10;
private static final AwtIdleTracker instance = FilterEventQueueDelegate.chain(new AwtIdleTracker());
public static AwtIdleTracker getInstance() {
return instance;
}
private volatile boolean inProgress;
private final AtomicLong lastDispatchTime = new AtomicLong(0);
@Override
public void afterDispatch(AWTEvent arg0, Object arg1) throws InterruptedException {
lastDispatchTime.set(System.currentTimeMillis());
inProgress = false;
super.afterDispatch(arg0, arg1);
}
@Override
public Object beforeDispatch(AWTEvent arg0) throws InterruptedException {
inProgress = true;
return super.beforeDispatch(arg0);
}
@Override
public void handle(Throwable t) throws Throwable {
lastDispatchTime.set(System.currentTimeMillis());
inProgress = false;
super.handle(t);
}
public boolean isIdle() {
return this.isIdle(DEFAULT_IDLE_TIME_TO_TRACK_MS);
}
public boolean isIdle(long idleTimeToTrackMs) {
return !inProgress && SunToolkit.isPostEventQueueEmpty() && System.currentTimeMillis() > lastDispatchTime.get() + idleTimeToTrackMs;
}
public void waitForIdle() {
waitForIdle(DEFAULT_IDLE_TIME_TO_TRACK_MS);
}
public void waitForIdle(long idleTimeToTrackMs) {
waitForIdle(idleTimeToTrackMs, TimeUnit.DAYS.toMillis(365));
}
public void waitForIdle(long idleTimeToTrackMs, long timeoutMs) {
if (SwingUtilities.isEventDispatchThread())
throw new IllegalAccessError();
long staleThreshold = System.currentTimeMillis() + timeoutMs;
while (!isIdle(idleTimeToTrackMs)) {
if (System.currentTimeMillis() > staleThreshold)
throw new RuntimeException("GUI still is not idle.");
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(IDLE_TIME_WATCHDOG_MS));
}
}
}
Example.java
package example;
import java.awt.AWTEvent;
import java.awt.EventQueue;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import sun.awt.EventQueueDelegate;
public class Example {
public static class ThirdPartyEventQueueDelegate implements EventQueueDelegate.Delegate {
public static final void registerEventQueueDelegate() {
EventQueueDelegate.setDelegate(new ThirdPartyEventQueueDelegate());
}
@Override
public void afterDispatch(AWTEvent arg0, Object arg1) throws InterruptedException {
System.out.println("Third party even queue delegate was not broken.");
}
@Override
public Object beforeDispatch(AWTEvent arg0) throws InterruptedException {
return arg0;
}
@Override
public AWTEvent getNextEvent(EventQueue arg0) throws InterruptedException {
return arg0.getNextEvent();
}
}
public static class ThirdPartyExceptionHandler {
public static void registerExceptionHandler() {
System.setProperty("sun.awt.exception.handler", ThirdPartyExceptionHandler.class.getName());
}
public void handle(Throwable t) {
System.out.println("Third party Exception handler was not broken.");
}
}
private static boolean wasIdle = false;
private static boolean isFistTime = true;
public static synchronized void log(String msg) {
System.out.println(new SimpleDateFormat("mm:ss.SSS").format(new Date()) + "\t" + msg);
}
public static void main(String[] args) {
// let suppose there are some related stuff already
ThirdPartyExceptionHandler.registerExceptionHandler();
ThirdPartyEventQueueDelegate.registerEventQueueDelegate();
// initialise singletons, build filter chain
AwtIdleTracker.getInstance();
AwtResponsivenessMonitor.setResponsivenessTimeout(TimeUnit.SECONDS.toMillis(2));
testWaitForIdle();
// testSomeGui();
}
public static void testSomeGui() {
// some test with visible GUI
JFrame frame = new JFrame();
frame.setSize(300, 300);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
while (true) {
boolean isIdle = AwtIdleTracker.getInstance().isIdle();
if (isFistTime || wasIdle != isIdle) {
isFistTime = false;
wasIdle = isIdle;
String msg = isIdle
? "idle"
: "busy";
log("system becomes " + msg);
}
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1));
}
}
public static void testWaitForIdle() {
// some long operation
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
log("task started");
// throw new RuntimeException();
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5));
log("task finished");
}
});
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
log("started waiting for idle");
AwtIdleTracker.getInstance().waitForIdle();
log("stopped waiting for idle");
}
}
回答2:
To synchronise tests and application under test here is available Sun's stuff:
SunToolkit#realSync()
SunToolkit#waitForIdle()
来源:https://stackoverflow.com/questions/8080018/event-dispatching-filter-chain-in-swing