Sample : JDK1.5.0_10以上使用時のKey処理不具合を回避する

実装サンプル:なし
[サンプル提供]: jp.ne.mki.wedge.education.focusmanager.EnqueueNoWaitKeyboardFocusManager source

1.回避する不具合の概要
2.不具合状態での動き
2-1.Swingハードコーディングの場合
2-2.Webtribe,VisualFrameのRunClientの場合
3.設定方法

回避する不具合の概要↑top

JDK1.5.0_10以降 (2007/06/05現在 JDK1.5.0_11, JDK1.5.0_12 も含む) のJDKを使用した場合、 フォーカスを取得していないComponentに対して、KeyEventが処理されComponent に反映してしまう現象が発生します。
この現象は、重い処理中(メインスレッドにて何かの処理中)に、TABにてFocus移動を含んだ KEY入力を行った際に、テキストのフォーカス移動"後" (After処理後) に Keyが反映されるという 不具合を招くことになり、内部データと表示データとの整合性が合わない という現象になって 現れます。

JDK1.5.0_10での変更の詳細については、Javaネイティブコードに達する為、詳細把握はできていませんが、、 java.awt.DefaultKeyboardFocusManager#enqueueKeyEvents(long, java.awt.Component)の 第一引数の long の時間を、java.awt.DefaultKeyboardFocusManager#dispatchKeyEvent(java.awt.event.KeyEvent) のタイミングの java.awt.event.KeyEvent#getWhen() の値に置き換える処理を組み込むことにより、 JDK1.5.0_09以前の動きと擬似的に同じできるのを確認できたため、 その処理を組み込んだKeyboardFocusManagerをサンプルとしての提供になります。

この対応方法は、根本的な解決ではないため、これによる副作用がある可能性があります。
(製品のテストケースは全て通るのは確認していますが、予想外の副作用が潜んでいる可能性があります。)

このJDK1.5.0_09とJDK1.5.0_10との挙動の差は JDK1.5.0_10 のリリースノートに記述されている、 バグID:6448190 「JFrame と JButton でフォーカスの問題」 での修正に依るものと考えられますので、 その修正よる不具合解決はできなくなる可能性があります。

この不具合はSwingハードコーディングにてInputVerifierを使ったテストでも不具合が発生するため、 Swing自体の不具合と判断しています。
そのため、製品本体にて不具合回避を行うのではなく、サンプルとしての提供となりますので、ご了承下さい
また、 新しいJDKがリリースされて、この不具合が解決された場合、 このKeyboardFocusManagerクラスを設定せずとも 不具合が解消される可能性があります。

不具合状態での動き↑top

Swingハードコーディングの場合↑top

Buttonの処理にてwaitをかけた場合

JButton, JTextField, JTextField を配置し、Buttonにフォーカスがある状態で、SPACE, TAB, 1, TAB, 1 Keyを続けて押下 してテストを行いました。
各Componentにはそれぞれ、java.awt.event.FocusListener, javax.swing.InputVerifier を設定しています。

  • JDK1.5.0_08の場合
    正常に動作します
    images/Swing1.jpg
    操作orイベント KEYイベントの種類 getWhenの時間 処理Component
    FocusListener#focusGaind on BUTTON(0
    SPACE KEY_PRESSED 1179812786328 on BUTTON(0)
    SPACE KEY_TYPED 1179812786328 on BUTTON(0)
    SPACE KEY_RELEASED 1179812786468 on BUTTON(0)
    ActionListener#actionPerformed on BUTTON(0)
    TAB KEY_PRESSED 1179812787031 on BUTTON(0)
    InputVerifier#verify on BUTTON(0)
    TAB KEY_TYPED 1179812787031 on BUTTON(0)
    FocusListener#focusGaind on TEXT(1)
    TAB KEY_RELEASED 1179812787109 on TEXT(1)
    1 KEY_PRESSED 1179812787172 on TEXT(1)
    1 KEY_TYPED 1179812787172 on TEXT(1)
    1 KEY_RELEASED 1179812787250 on TEXT(1)
    TAB KEY_PRESSED 1179812787312 on TEXT(1)
    InputVerifier#verify on TEXT(1)
    TAB KEY_TYPED 1179812787312 on TEXT(1)
    FocusListener#focusGaind on TEXT(2)
    TAB KEY_RELEASED 1179812787406 on TEXT(2)
    1 KEY_PRESSED 1179812787468 on TEXT(2)
    1 KEY_TYPED 1179812787468 on TEXT(2)
    1 KEY_RELEASED 1179812787531 on TEXT(2)

  • JDK1.5.0_10の場合
    処理中の2回目のTABによるフォーカス移動が行われない。
    最後の 1 のKEYは、TEXT(1)のInputVefirier#verifyが処理された後に、TEXT(1)に対して反映している。
    そのため、InputVefirierではチェック漏れが発生するという不具合が発生する。
    images/Swing3.jpg
    操作orイベント KEYイベントの種類 getWhenの時間 処理Component
    FocusListener#focusGaind on BUTTON(0)
    SPACE KEY_PRESSED 1179812682390 on BUTTON(0)
    SPACE KEY_TYPED 1179812682390 on BUTTON(0)
    SPACE KEY_RELEASED 1179812682515 on BUTTON(0)
    ActionListener#actionPerformed on BUTTON(0)
    TAB KEY_PRESSED 1179812683172 on BUTTON(0)
    InputVerifier#verify on BUTTON(0)
    TAB KEY_TYPED 1179812683172 on BUTTON(0)
    FocusListener#focusGaind on TEXT(1)
    TAB KEY_RELEASED 1179812683265 on TEXT(1)
    1 KEY_PRESSED 1179812683375 on TEXT(1)
    1 KEY_TYPED 1179812683375 on TEXT(1)
    1 KEY_RELEASED 1179812683453 on TEXT(1)
    TAB KEY_PRESSED 1179812683562 on TEXT(1)
    InputVerifier#verify on TEXT(1)
    TAB KEY_TYPED 1179812683562 on TEXT(1)
    TAB KEY_RELEASED 1179812683640 on TEXT(1)
    1 KEY_PRESSED 1179812683765 on TEXT(1)
    1 KEY_TYPED 1179812683765 on TEXT(1)
    1 KEY_RELEASED 1179812683828 on TEXT(1)
    FocusListener#focusGaind on TEXT(2)

TextのInputVerifierにてwaitをかけた場合

JTextField, JTextField, JTextField を配置し、一つ目のJTextFieldのInputVerifier#verify にてwaitをかけます。
1つめのJTextFieldにフォーカスがある状態で、TAB, 1, TAB, 1, TAB, 1 のKeyを続けて押下 してテストを行いました。
各Componentにはそれぞれ、java.awt.event.FocusListener, javax.swing.InputVerifier を設定しています。

  • JDK1.5.0_08の場合
    正常に動作します
    images/Swing4.jpg
    操作orイベント KEYイベントの種類 getWhenの時間 処理Component
    FocusListener#focusGaind on TEXT(1)
    TAB KEY_PRESSED 1179812205140 on TEXT(1)
    InputVerifier#verify on TEXT(1)
    TAB KEY_TYPED 1179812205140 on TEXT(1)
    FocusListener#focusGaind on TEXT(2)
    TAB KEY_RELEASED 1179812205234 on TEXT(2)
    1 KEY_PRESSED 1179812205375 on TEXT(2)
    1 KEY_TYPED 1179812205375 on TEXT(2)
    1 KEY_RELEASED 1179812205468 on TEXT(2)
    TAB KEY_PRESSED 1179812205593 on TEXT(2)
    InputVerifier#verify on TEXT(2)
    TAB KEY_TYPED 1179812205593 on TEXT(2)
    FocusListener#focusGaind on TEXT(3)
    TAB KEY_RELEASED 1179812205687 on TEXT(3)
    1 KEY_PRESSED 1179812205812 on TEXT(3)
    1 KEY_TYPED 1179812205812 on TEXT(3)
    1 KEY_RELEASED 1179812205906 on TEXT(3)
    TAB KEY_PRESSED 1179812206062 on TEXT(3)
    InputVerifier#verify on TEXT(3)
    TAB KEY_TYPED 1179812206062 on TEXT(3)
    FocusListener#focusGaind on TEXT(4)
    TAB KEY_RELEASED 1179812206156 on TEXT(4)
    1 KEY_PRESSED 1179812206312 on TEXT(4)
    1 KEY_TYPED 1179812206312 on TEXT(4)
    1 KEY_RELEASED 1179812206390 on TEXT(4)

  • JDK1.5.0_10の場合
    処理中の2回目のTABによるフォーカス移動が行われない。
    最後の 1 のKEYは、TEXT(2)のInputVefirier#verifyが処理された後に、TEXT(2)に対して反映している。
    そのため、InputVefirierではチェック漏れが発生するという不具合が発生する。
    images/Swing2.jpg
    操作orイベント KEYイベントの種類 getWhenの時間 処理Component
    InputVerifier#verify on TEXT(0)
    FocusListener#focusGaind on TEXT(1)
    TAB KEY_PRESSED 1179811916437 on TEXT(1)
    InputVerifier#verify on TEXT(1)
    TAB KEY_TYPED 1179811916437 on TEXT(1)
    FocusListener#focusGaind on TEXT(2)
    TAB KEY_RELEASED 1179811916515 on TEXT(2)
    1 KEY_PRESSED 1179811916672 on TEXT(2)
    1 KEY_TYPED 1179811916672 on TEXT(2)
    1 KEY_RELEASED 1179811916750 on TEXT(2)
    TAB KEY_PRESSED 1179811916937 on TEXT(2)
    InputVerifier#verify on TEXT(2)
    TAB KEY_TYPED 1179811916937 on TEXT(2)
    TAB KEY_RELEASED 1179811917031 on TEXT(2)
    1 KEY_PRESSED 1179811917187 on TEXT(2)
    1 KEY_TYPED 1179811917187 on TEXT(2)
    1 KEY_RELEASED 1179811917250 on TEXT(2)
    TAB KEY_PRESSED 1179811917453 on TEXT(2)
    InputVerifier#verify on TEXT(2)
    TAB KEY_TYPED 1179811917453 on TEXT(2)
    TAB KEY_RELEASED 1179811917531 on TEXT(2)
    1 KEY_PRESSED 1179811917718 on TEXT(2)
    1 KEY_TYPED 1179811917718 on TEXT(2)
    1 KEY_RELEASED 1179811917781 on TEXT(2)
    FocusListener#focusGaind on TEXT(3)

Webtribe,VisualFrameのRunClientの場合↑top

Button処理にてwaitをかけた場合

Button, TextField, TextField を配置し、Buttonにフォーカスがある状態で、SPACE, TAB, 1, TAB, 1 Keyを続けて押下 してテストを行いました。

RunClientの仕様として「処理中のフォーカス移動不可」があるため、 JDK1.5.0_10以上にすることによる動作差、不具合は発生しません。

  • JDK1.5.0_09、JDK1.5.0_10
    正常に動作します
    images/RunClient1.jpg
    操作orイベント KEYイベントの種類 getWhenの時間 処理Component
    enqueueKeyEvents on Button
    SPACE KEY_PRESSED 1179823004608 on Button
    SPACE KEY_TYPED 1179823004608 on Button
    SPACE KEY_RELEASED 1179823004702 on Button
    BeforeStart on Button
    BeforeEnd on Button
    AfterStart on Button
    [実行リスト処理開始] 1179823004703
    AfterEnd on Button
    TAB KEY_PRESSED 1179823005202 on Button
    [requestFocus取消]
    TAB KEY_TYPED 1179823005202 on Button
    TAB KEY_RELEASED 1179823005296 on Button
    1 KEY_PRESSED 1179823005405 on Button
    1 KEY_TYPED 1179823005405 on Button
    1 KEY_RELEASED 1179823005514 on Button
    TAB KEY_PRESSED 1179823005624 on Button
    [requestFocus取消]
    TAB KEY_TYPED 1179823005624 on Button
    TAB KEY_RELEASED 1179823005702 on Button
    1 KEY_PRESSED 1179823005874 on Button
    1 KEY_TYPED 1179823005874 on Button
    1 KEY_RELEASED 1179823005952 on Button
    [実行リスト処理終了]

TextのAfterにてwaitをかけた場合

TextField, TextField, TextField を配置し、 1つめ、2つめ のTextのAfterにwaitをかける設定を行います。
1つめのTextFieldにフォーカスがある状態で TAB, 1, TAB, 1 とKEY入力してテストを行いました。

  • JDK1.5.0_09
    正常に動作します
    images/RunClient2.jpg
    操作orイベント KEYイベントの種類 getWhenの時間 処理Component
    TAB KEY_PRESSED 1179824346937 on text1
    AfterStart on text1
    [実行リスト処理開始] 1179824346937
    AfterEnd on text1
    enqueueKeyEvents on text2
    BeforeStart on text2
    BeforeEnd on text2
    TAB KEY_TYPED 1179824346937 on text1
    [実行リスト処理終了] 1179824351937
    TAB KEY_RELEASED 1179824347015 on text2
    1 KEY_PRESSED 1179824347109 on text2
    1 KEY_TYPED 1179824347109 on text2
    1 KEY_RELEASED 1179824347203 on text2
    TAB KEY_PRESSED 1179824347281 on text2
    AfterStart on text2
    [実行リスト処理開始] 1179824351953
    AfterEnd on text2
    enqueueKeyEvents on text3
    BeforeStart on text3
    BeforeEnd on text3
    TAB KEY_TYPED 1179824347281 on text2
    [実行リスト処理終了] 1179824352953
    TAB KEY_RELEASED 1179824347359 on text3
    1 KEY_PRESSED 1179824347468 on text3
    1 KEY_TYPED 1179824347468 on text3
    1 KEY_RELEASED 1179824347547 on text3

    ■補足
    処理順番と getWhenでの時間が一致していない (KeyEventの処理タイミングが遅れている) のは、Swing標準のKeyboardFocusManager の仕様のためです。

  • JDK1.5.0_11
    Swingハードコーディングと同様の不具合が発生します。
    この操作を行った場合、 処理終了時はの text2のテキスト上の文字は 「11」となりますが、 text2のAfterのタイミングでは 「1」として処理が行われるため、画面での表示とアイテム内データ値とに 不整合が発生することになります。
    images/RunClient3.jpg
    操作orイベント KEYイベントの種類 getWhenの時間 処理Component
    TAB KEY_PRESSED 1179824465468 on text1
    AfterStart on text1
    [実行リスト処理開始] 1179824465484
    AfterEnd on text1
    enqueueKeyEvents on text2
    BeforeStart on text2
    BeforeEnd on text2
    TAB KEY_TYPED 1179824465468 on text1
    [実行リスト処理終了] 1179824470484
    TAB KEY_RELEASED 1179824465578 on text2
    1 KEY_PRESSED 1179824465656 on text2
    1 KEY_TYPED 1179824465656 on text2
    1 KEY_RELEASED 1179824465750 on text2
    TAB KEY_PRESSED 1179824465859 on text2
    AfterStart on text2
    [実行リスト処理開始] 1179824470500
    AfterEnd on text2
    enqueueKeyEvents on text3
    BeforeStart on text3
    BeforeEnd on text3
    TAB KEY_TYPED 1179824465859 on text2
    TAB KEY_RELEASED 1179824465953 on text2
    1 KEY_PRESSED 1179824466047 on text2
    1 KEY_TYPED 1179824466047 on text2
    1 KEY_RELEASED 1179824466125 on text2
    [実行リスト処理終了] 1179824471500

設定方法↑top

[サンプル提供]: jp.ne.mki.wedge.education.focusmanager.EnqueueNoWaitKeyboardFocusManager source のソースを各プロジェクト用にカスタマイズ(パッケージ名、クラス名、JavaDocなどを変更)し、 RunClient起動時のクラスパスにクラスを設定します。

RunClient.wdg(Config)ファイルの 「KeyboradFocusManagerの設定」 の keybordfocusmanagerのvalueに、クラス名を指定します。 このXML要素はデフォルトではコメントになっていますので、コメントから外します。

■補足
KeyboardFocusManagerを任意のクラスに置き換える場合は、 AWTPermission "replaceKeyboardFocusManager" のアクセス権が必要です。
JWS や Applet にて起動する場合には、署名を行うなどしてアクセス権を許可する環境にする必要があります。

Valid XHTML 1.1