Как я могу повторно отправить любое событие в Java? - PullRequest
3 голосов
/ 24 июня 2010

У меня есть возможность реализовать в приложении на основе Java 1.5 Swing. Если при обработке события AWTEvent возникает определенное исключение, мне нужно открыть альтернативную форму, устранить проблему и продолжить обработку исходного события. Когда я повторно отправляю событие компоненту, ничего не происходит. Когда я помещаю событие в очередь событий, ничего не происходит. Я предполагаю, что в событии есть некоторые поля состояния, которые помечают его как обработанный, поэтому компоненты не получают его. Пока я не могу найти способ воссоздать клон мероприятия. И пользовательское событие здесь не поможет, потому что я хочу, чтобы предыдущее событие было обработано.

В приложении Swing существующая очередь событий заменяется внутренней очередью.

private class ApplicationEventQueue extends EventQueue  
{  
    private final ArrayList listeners=new ArrayList();  
    protected void initialize()  
    {  
        Toolkit.getDefaultToolkit().getSystemEventQueue().push(this);  
    }
.
.
.
}

Как часть события отправки, класс перехватывает вызов и делегаты суперкласса. Если возникнет исключение, появится всплывающее окно с сообщением «извините за неудобства».

@Override  
protected void dispatchEvent(AWTEvent event)  
{  
    try  
    {  
        super.dispatchEvent(event);  
        if (peekEvent() != null && userEventDispatched)  
        {  
            raiseIdleEvent();  
            userEventDispatched = false;  
        }  
        else  
        {  
            int eventId = event.getID();  
            if (eventId == KeyEvent.KEY_TYPED || eventId == MouseEvent.MOUSE_CLICKED)  
            {  
                userEventDispatched = true;  
            }  
        }  
    }  
    catch (Throwable ex)  
    {  
        onError(ex);  
    }  
}

Требуемая функция заключается в возможности тайм-аута сеанса пользователя. Сервер выдаст определенное исключение, когда время сеанса истечет. По истечении времени ожидания пользователю предлагается выполнить повторную регистрацию, и исходное действие, которое было прервано, будет продолжено. То, что я хотел сделать, как часть onError, я обработаю исключение, отображая форму. Это конкретное событие будет использовано, но после повторной аутентификации я могу повторно отправить это событие в приложение или, возможно, поместить его в очередь событий. Оба подхода потерпели неудачу, так как я предполагаю, что у события есть флаги, указывающие, было ли оно отправлено и использовано.

  1. Происходящее событие может быть любым событием (будь то нажатие клавиши мыши).
  2. Определение пользовательского события не решит их проблему, так как мне нужно воспроизвести то же событие.
  3. Я рассмотрел клонирование события, но AWTEvent не поддерживает клонирование.
  4. Глубокое копирование путем сериализации, а затем десериализации события не работает, поскольку некоторые отправляемые события не сериализуются.
  5. Я рассматриваю возможность сброса любых переменных состояния в событии путем отражения, но это кажется опасным.

Извините за паршивое форматирование, я еще не разобрался с разметкой. Любая помощь здесь будет оценена.

ИСПРАВЛЕНО: Спасибо за все ответы. Исправление (не найденное мной) состояло в том, чтобы перехватить исключение тайм-аута сеанса, когда был сделан вызов. Приложение выскочило диалоговое окно и попросил пользователя повторно пройти аутентификацию. После того, как аутентификация прошла успешно, диалог был закрыт. Это сработало, к моему удивлению.

Я не знаю наверняка, но кажется, что событие оставалось в очереди, когда диалоговое окно отображалось, и как только диалоговое окно было закрыто, оно доставлялось элементам управления, так или иначе.

Ответы [ 3 ]

2 голосов
/ 02 июля 2010

Я бы не пытался решить это с точки зрения события.Система событий не предназначена для такой работы.

Я бы определил интерфейс (X), который инкапсулирует взаимодействия с сервером.Реализация Y сохранит параметры последнего запроса к серверу.После истечения времени ожидания и повторной аутентификации пользователя я могу попросить Y повторно отправить последний запрос.

Дополнительное преимущество: поскольку X является интерфейсом, это упрощает тестирование, поскольку Y можно заменить на макет.объект для проверки графического интерфейса, и тестовый код может вызывать Y без графического интерфейса.

Обновление:

Вот подход, который использует SwingWorker запустить взаимодействие с сервером в потоке bg.ChangeEvent используется для возврата результатов в EDT для обработки.Публикация / процесс SwingWorker используются для обработки взаимодействия пользователя для повторной аутентификации.Преимущество SwingWorker заключается в том, что, если серверу требуется много времени для ответа, пользовательский интерфейс может по-прежнему отвечать на события перерисовки.

class Test extends JPanel {
    private JButton b;
    public Test() {
        b = new JButton(new AbstractAction("Do something") {
            @Override
            public void actionPerformed(ActionEvent e) {
                final JButton btn = (JButton) e.getSource();
                btn.setEnabled(false);
                Object req = new Object(); // Replace w/ apropriate type
                new RequestDispatch(req, new ChangeListener() {
                    @Override
                    public void stateChanged(ChangeEvent e) {
                        final Object req = e.getSource();
                        // Do something with data from 'req'
                        btn.setEnabled(true);
                    }
                });
            }
        });
        add(b);
    }
}

class RequestDispatch extends SwingWorker<Object, Void> {
    private enum DispatchState { Ready, Running, Canceled }
    private final ChangeListener listener;
    private DispatchState dstate = DispatchState.Ready;
    private final Object req;
    RequestDispatch(Object req, ChangeListener listener)
    {
        this.req = req;
        this.listener = listener;
        execute();
    }
    @Override
    protected Object doInBackground() throws Exception {
        while (true) {
            DispatchState st = getDState();
            if (st == DispatchState.Ready)
            {
                try {
                    setDstate(DispatchState.Running);
                    // send request to the server, update req with response
                    return req;
                }
                catch (TimeoutException ex) {
                    this.publish((Void)null);
                    synchronized (this) {
                        wait();
                    }
                }
            }
            if (st == DispatchState.Canceled) {
                return req;
            }
        }
    }
    @Override
    protected void process(List<Void> chunks) {
        // Session has timed out
        // Ask the user to re-authenticate
        int result = JOptionPane.showConfirmDialog(null, "Login");
        if (result == JOptionPane.CANCEL_OPTION) {
            setDstate(DispatchState.Canceled);
        }
        else {
            setDstate(DispatchState.Ready);
        }
    }
    private synchronized DispatchState getDState() {
        return dstate;
    }
    private synchronized void setDstate(DispatchState dstate) {
        this.dstate = dstate;
        notifyAll();
    }
}
1 голос
/ 09 декабря 2010

Вы можете поместить исходное событие в пользовательский класс-оболочку с именем, например AWTEventWrapper, которое расширяет AWTEvent.Затем этот класс затем переопределяет все методы и делегирует их завернутому событию, за исключением consume() и isConsumed() (оставьте реализации по умолчанию), так что вы можете создать иллюзию того, что событие не используется, и разрешить его обработкуеще раз.

0 голосов
/ 23 августа 2010

Глядя на код JDK6, AWTEvent.consumed не может быть установлен в false, поскольку есть только consume(), нет unconsume().

Я думаю, что собственный # 5 ОП - единственный путь, хотя и действительно опасный. (На самом деле я только что узнал, как установить приватное поле из AWTEvent#get_InputEvent_CanAccessSystemClipboard().)

В вашем onError() вы можете создать другую очередь событий, которая переопределяет getNextEvent плюс логический флаг, чтобы при вызове EventQueue#push() новая очередь сохраняла все текущие события в списке, а затем отправляла уведомление об ошибке. , наконец, выдвиньте новую очередь, переверните consumed в случае, если возникла исключительная ситуация, повторно отправьте ее, а затем более ранние события, сохраненные в списке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...