Java Swing中的事件调度过滤器链

Java Swing中的事件调度过滤器链,java,swing,awt,Java,Swing,Awt,我正试着整理一下。类似于或 被发现是为了这个?。。但在EventQueue.dispatchEventEventQueueDelegate.Delegate中出现异常的情况下,它对此一无所知,并且丑陋的.handleException会出现在场景中 自从JavaSE1.1以来,这个“临时黑客”还没有解决吗 我也期待着打电话进来。但它似乎不适合这种情况,因为这种方法是受保护的,它需要额外的手鼓舞蹈才能使事情顺利进行,代码也变得不那么可爱 有更好的解决办法吗 下一步是在EventQueueDe

我正试着整理一下。类似于或

被发现是为了这个?。。但在
EventQueue.dispatchEvent
EventQueueDelegate.Delegate
中出现异常的情况下,它对此一无所知,并且丑陋的
.handleException
会出现在场景中

  • 自从JavaSE1.1以来,这个“临时黑客”还没有解决吗
我也期待着打电话进来。但它似乎不适合这种情况,因为这种方法是受保护的,它需要额外的手鼓舞蹈才能使事情顺利进行,代码也变得不那么可爱

  • 有更好的解决办法吗

下一步是在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;
    }
}

要同步测试和测试中的应用程序,这里有Sun的资料:
#realSync()

#waitForIdle()

这不是个好主意,我的问题是为什么,顺便说一句,方法
push()
我想跟踪EDT的繁忙情况,以便进行测试。但是系统中已经注册了自定义EventQueue。因此,我需要添加新的事件队列,而不是破坏以前的事件队列。push()'替换现有的EventQueue',但我需要一个特定的筛选层。正如我所知,只有一个EventQueue,您可以等待完成或替换事件。请参见此内容。+1表示代码,1Millions time-1表示原因,请使用invokeAndWait()当运行集成测试时,我需要等待EDT完成GUI重建,然后在某个按钮上断言标签文本。我也在等待背景任务完成,但这是不同的故事。invokeAndWait()将等待分派当前添加到EventQueue的事件。但是,如果在处理一些新事件时,会将其添加到队列中,并且如果此处理可能与完全重建gui控件树一样重要呢?.1)(
运行集成测试我需要等待EDT
)使用
javax.swing.Action
,2)(
要完成的后台任务,但
)有时您需要从
javax.swing.Timer
调用
javax.swing.Action
,如果您想在EDT上测试某个东西,那么将其放在那里,并将这些错误保存到日志中,在大多数情况下,此引擎在EDT测试中为true,注意不是集成测试
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);
    }
}
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));
        }
    }
}
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");
    }
}