Диспетчерская цепочка событий в Swing - PullRequest
2 голосов
/ 10 ноября 2011

Я пытаюсь организовать цепочку фильтров из EventQueue.dispatchEvent . Что-то вроде java.io.FilterInputStream или javax.servlet.Filter .

Найдено EventQueueDelegate.Delegate , которое должно быть предназначено для этого? .. Но в случае исключения в EventQueue.dispatchEvent EventQueueDelegate.Delegate оно ничего не знает об этом и уродливо java.awt.EventDispatchThread .handleException появляется на сцене.

  • Разве этот временный взлом еще не решен с Java SE 1.1 ??? !!!

Я также посмотрел на EventQueue.dispatchEvent для вызова по цепочке. Но он оказался неподходящим для этого, потому что этот метод защищен и требует дополнительных танцев с бубнами, чтобы заставить вещи работать, и код становится не таким приятным.

  • Есть ли лучшее решение?

Ответы [ 2 ]

1 голос
/ 29 декабря 2011

Для синхронизации тестов и тестируемого приложения здесь доступны вещи Sun:
SunToolkit # realSync ()
SunToolkit # waitForIdle ()

1 голос
/ 11 ноября 2011

Далее танцует с бубнами вокруг EventQueueDelegate.Delegate ...

AwtExceptionHandler.java

package example;

/**
 * @see java.awt.EventDispatchThread#handleException(Throwable thrown)
 */
public interface AwtExceptionHandler {
    void handle(Throwable t) throws Throwable;
}

FilterEventQueueDelegate.java

<code>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());
 * 
* * @author Михаил Михайлович Адамович * / открытый абстрактный класс FilterEventQueueDelegate реализует EventQueueDelegate.Delegate, AwtExceptionHandler { открытый статический финальный класс ExceptionHandler { приватная статическая AwtExceptionHandler currentExceptionHandler; публичная ручка void (Throwable t) создает Throwable { currentExceptionHandler.handle (т); } } закрытый статический финальный класс SimpleFilterEventQueueDelegate extends FilterEventQueueDelegate { private EventQueueDelegate.Delegate thirdPartyDelegate; закрытый объект thirdPartyExceptionHandler; @Override public void afterDispatch (AWTEvent arg0, Object arg1) выдает InterruptedException { if (thirdPartyDelegate! = null) thirdPartyDelegate.afterDispatch (arg0, arg1); } @Override public Object beforeDispatch (AWTEvent arg0) выдает InterruptedException { if (thirdPartyDelegate! = null) return thirdPartyDelegate.beforeDispatch (arg0); вернуть arg0; } @Override public AWTEvent getNextEvent (EventQueue arg0) выдает InterruptedException { if (thirdPartyDelegate! = null) return thirdPartyDelegate.getNextEvent (arg0); return arg0.getNextEvent (); } @Override публичная ручка void (Throwable t) создает Throwable { if (thirdPartyExceptionHandler! = null) пытаться { Класс <? extends Object> c = thirdPartyExceptionHandler.getClass (); Метод m = c.getMethod («дескриптор», новый класс [] {Throwable.class}); m.invoke (thirdPartyExceptionHandler, new Object [] {t}); } catch (Throwable x) { thirdPartyExceptionHandler = null; / * Не пытайтесь сделать это снова * / бросить т; } еще бросить т; } public void setEventQueueDelegate (EventQueueDelegate.Delegate делегат) { thirdPartyDelegate = делегат; } public void setExceptionHandler (Object exceptionHandler) { thirdPartyExceptionHandler = exceptionHandler; } } public static T цепочка (T делегат) { synchronized (EventQueueDelegate.class) { EventQueueDelegate.Delegate currentDelegate = EventQueueDelegate.getDelegate (); FilterEventQueueDelegate currentFilterDelegate = null; if (currentDelegate instanceof FilterEventQueueDelegate) currentFilterDelegate = (FilterEventQueueDelegate) currentDelegate; еще { SimpleFilterEventQueueDelegate simpleFilterDelegate = new SimpleFilterEventQueueDelegate (); if (currentDelegate! = null) simpleFilterDelegate.setEventQueueDelegate (currentDelegate); Объект currentExceptionHandler = null; пытаться { currentExceptionHandler = Class.forName (System.getProperty ("sun.awt.exception.handler")). newInstance (); } catch (исключение e) { } if (currentExceptionHandler! = null) simpleFilterDelegate.setExceptionHandler (currentExceptionHandler); System.setProperty ("sun.awt.exception.handler", ExceptionHandler.class.getName ()); currentFilterDelegate = simpleFilterDelegate; } delegate.setNext (currentFilterDelegate); EventQueueDelegate.setDelegate (делегат); if (EventQueueDelegate.getDelegate ()! = делегат) бросить новый ConcurrentModificationException (); ExceptionHandler.currentExceptionHandler = делегат;возврат делегата;}} Защищенный FilterEventQueueDelegate next;@Override public void afterDispatch (AWTEvent arg0, Object arg1) выдает InterruptedException {next.afterDispatch (arg0, arg1);} @Override public Object beforeDispatch (AWTEvent arg0) выдает InterruptedException {return next.beforeDispatch (arg0);} @Override public AWTEvent getNextEvent (EventQueue arg0) выдает 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");
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...