我正在尝试组织filter chainEventQueue.dispatchEventjava.io.FilterInputStreamjavax.servlet.Filter之类的东西。

发现EventQueueDelegate.Delegate是否打算用于此操作?


自Java SE 1.1以来,这种“临时破解”是否尚未解决?


我也期待java.awt.EventDispatchThread进行链式调用。但由于此方法受到保护,因此似乎不适合此方法,并且需要与铃鼓进行额外的跳舞才能使事情正常进行,并且代码变得不太可爱。


有更好的解决方案吗?

最佳答案

接下来是围绕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));
        }
    }
}


范例.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");
    }
}

关于java - Swing中的事件调度筛选器链,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/8080018/

10-09 07:18