ゆっちの秘密基地
Master - Yucchi
Since - 2012/05/05


Java秘密基地

JSR296 on NetBeans バックグラウンドタスクを試す
NetBeans 6 から追加された Swing Application Framework を試してみます。

この機能は次期 JDK7 に搭載される予定になってます。

NetBeans に搭載されたこの機能を使って JSR296 についてちょっと調べたことを

ブログに綴ってあります。

*注意 Sun が Oracle に買収されてから Swing Application Framework は開発が途中で終了したようです。

よって、下記ブログのリンクは削除しました。


JSR296 on NetBeans その1

JSR296 on NetBeans その2

JSR296 on NetBeans その3

JSR296 on NetBeans その4

JSR296 on NetBeans その5

JSR296 on NetBeans その6

JSR296 on NetBeans その7

JSR296 on NetBeans その8

JSR296 on NetBeans その9

JSR296 on NetBeans その10

JSR296 on NetBeans その11

JSR296 on NetBeans その12

JSR296 on NetBeans その13

JSR296 on NetBeans その14

JSR296 on NetBeans その15

JSR296 on NetBeans その16


今回はその続きというような感じです。

で、何を試すかというと SwingWorker と同じようにバックグランドタスクを実行できるということです。

しかも簡単に!

それでは、 SwingWorker を試す その2 で作ったプログラムと同じようなものを作ってみます。

開発環境は

OS   Windows Vista Ultimate 64bit

JDK6u10

NetBeans IDE 6.5 RC1

です。

作成するプログラムはテキストエリアに文字を追加表示させていくというものです。

キャンセルすることも可能とします。

実際のプログラムのスクリーンショットは下図のようになります。

START ボタンを押すと文字がテキストエリアに表示されていきます。

START ボタンが無効になります。

CANCEL ボタンが有効になります。

プログレスバーの進捗状況が表示されます。

今回はこれらの機能だけを優先します。

JSR296 には開発者に容易に複雑な Swing アプリケーションを簡単に作れるような技術があります。

今回はその一部だけを試します。





処理が完了すると、スタートボタンが有効になり、キャンセルボタンが無効になります。





処理の途中でキャンセルするとテキストエリアに「キャンセルしました」と追加表示して終了します。






実際に動作させたい方は Java Web Start を配備したのでどうぞ(^^)

Launch JSR296 TEST


このようなプログラムを作成します。

NetBeans 6.5 RC1 を起動します。

[ ファイル ] → [ 新規プロジェクト ] で新規プロジェクトを作ります。





[ Java ] → [ Java デスクトップアプリケーション ] を選択し [ 次へ > ] ボタンを押します。





下記のように設定します。






[ 完了 ] ボタンを押すと下図のようなプロジェクトが作成されます。







[ ファイル ] → [ プロジェクトのプロパティ ] を選択します。







プロジェクトのプロパティを設定します。







必要に応じて各カテゴリの設定をします。







Java SE Development Kit 6u10 から Nimbus ルック&フィールが使用できるので使ってみます。

ルック&フィール: のプルダウンメニューから com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel を選択します。

プロジェクトの設定が完了したら [ 了解 ] ボタンを押します。







自動生成された BgTaskView.java のデザイン画面で NetBeans の強力な GUI ビルダーを使用してプログラムの GUI を作ります。

mainPanel の上にテキストエリアを作ります。

パレットから [ テキスト領域 ] ボタンを mainPanel 上にドラッグ&ドロップします。







そしてサイズを調整します。





次に、プログラム開始用の [ START ] ボタンとキャンセル用の [ CANCEL ] ボタンを作ります。

パレットより [ ボタン ] を mainPanel 上にドラッグ&ドロップします。






適当にサイズを調整して下図のように配置します。






では、ボタンにアクションを設定します。

まず、プログラム開始用の [ START ] ボタンから設定します。

jButton1 を右クリックし、[ アクションを設定... ] をクリックします。







[ アクションを設定... ] ウインドゥが表示されます。







下図のように各プロパティを設定します。

[ START ] ボタンのアクションによって時間のかかる処理をバックグランドで行うため

バックグラウンドタスクのチェックマークを必ず入れます。








属性プロパティを詳細タブを押して詳細設定に切り換えます。

ブロック化タイプに [ ACTION ] を設定してバックグラウンドタスク実行中は

[ START ] ボタンを非活性化にします。

ちなみに、ブロックタイプは以下のものがあります。

NONE               ブロックしない

ACTION      アクションをブロック

COMPONENT    当該のコンポーネントのみブロック

WINDOW     ウィンドゥをブロック

APPLICATION   アプリケーション全体をブロック

設定が終了したら [ 了解 ] ボタンを押します。







ブロック化タイプを指定した @Action アノテーションの付いた countStart() メソッドが自動で作成されます。

CountStartTask クラスを返すようになってますね。






countStart() メソッドで返される CountStartTask クラスオブジェクトを便宜上 cst とします。











実行中プログラムのキャンセル用の [ CANCEL ] ボタンのアクションも同様に設定します。







[ アクションを設定... ] で [ CANCEL ] ボタンは実行中のプログラムをキャンセルするだけなので

バックグラウンドタスクのチェックマークはいりません。

必要事項を設定したら [ 了解 ] ボタンを押します。







@Action アノテーションの付いた countStop() メソッドが自動で作成されます。





countStop() メソッドの処理を下記のようにします。

[ CANCEL ] ボタンが押されたら cancel() メソッドを呼び出し、バックグラウンドタスクを途中終了させます。

[ CANCEL ] ボタンを非活性化状態に戻します。




プログラムを起動しただけの状態では [ CANCEL ] ボタンは非活性化状態でいいので

[ CANCEL ] ボタン jButton2 のプロパティを編集します。






enabled のチェックマークを外します。






そしてプログラムが [ START ] ボタンが押され開始されたら活性化するようにします。

jButton2.setEnabled(true); と CountStartTask クラスのコンストラクタに記述します。

これで [ START ] ボタンが押されたら [ CANCEL ] ボタンが活性化します。






それではプログラムのバックグラウンドタスクの処理を記述します。

単純に1から10までカウントするだけの処理です。

時間をかけるためにループ毎に900ミリ秒スレッドを停止させます。

そして setProgress() メソッドによりプログレスバーの進捗状態を更新します。







publish() メソッドで処理の途中経過を process() メソッドに渡すため型を変更します。





Task<Object , Void> から Task<Object , Integer> へ






それでは process() メソッドを作りましょう。

doInBackground() メソッドの後にカーソルを移動して右クリックします。

ポップアップメニューより [ コードを挿入... ] を選択します。






生成ウィンドウが表示されるので [ メソッドをオーバーライド ] を選択します。







[ オーバーライドメソッドの生成 ] ウインドゥが表示されます。

process(List<V>values) にチェックマークを入れ、[ 生成 ] ボタンを押します。






process() メソッドが自動生成されました。







process() メソッドに下記のような処理を記述します。

これは process() メソッドがイベントディスパッチスレッド上で publish() メソッドから非同期で

送られてくるデータチャンクを受信し GUI の更新をさせてます。







次に、Task クラスのメソッドを挿入します。

[ コードを挿入... ] をクリックします。






[ メソッドをオーバーライド... ] を選択します。







[ オーバーライドメソッドの生成 ] ウィンドウが表示されます。

cancelled()

failed(Throwable cause)

finished()

以上のメソッドのチェックボックスにチェックを入れ、[ 生成 ] ボタンを押します。







@Override アノテーションが付いたメソッドが自動生成されます。







ここからプログラムの終了処理とキャンセル処理を実装します。

しかし、この処理方法は間違っています。

ちゃんと API ドキュメントを読まなかった(英語なので読めなかった(^^;;;)私のミスです。


done() メソッドがオーバーライドできないので finished() メソッドに終了処理を記述します。

下記のようにテキストエリアに \n(u_u) クゥゥゥ。o◯\n 表示させ、

音を鳴らし、

[ CANCEL ] ボタンを非活性化します。






[ CANCEL ] ボタンが押されたら、テキストエリアに \nキャンセルしました。\n\n と表示させます。






ここまでのプログラムの動作確認をします。

[ 実行 ] → [ 主プロジェクトを実行 ] をクリックしてプログラムを実行します。

おやっ?

終了処理が早いですね(><)






キャンセル処理の後にも終了処理が・・・(T-T)






process() メソッドが完了する前に finished() メソッドの終了処理が開始されてるようですね。

とりあえず、プログラムの流れを予想と同じか確認してみます。


こんな時のために NetBeans には強力なデバッガがあるんですよね(^^)

今回はデバッガを使うまでもないのですが確認ということで(^^;

finished() メソッドの終了処理の所にブレークポイントを置きます。

そう、テキストエリアに \n(u_u) クゥゥゥ。o◯\n と表示させるところです。






あと、process() メソッドにも

テキストエリアに 羊が i 匹 と表示させるところです。






最後に publish() メソッドにも






ここで、publish() メソッド側の変数 n と process() メソッドの変数 i の関係も調べるために

ウォッチポイントを指定します。

publish() メソッドの引数の n を選択し、

ツールバーの[ 新規ウォッチポイント...] ボタンを押します。











すると[ 新規ウォッチポイント ] ウィンドウが出ますので

ウォッチ表現に n が入力されていることを確認し、[ 了解 ] ボタンを押します。







同様に、process() メソッドの変数 i もウォッチポイントに追加します。








それでは、ツールバーの [ 主プロジェクトをデバッグ ] ボタンを押してデバッグ開始します。







下図のようなデバッグ画面がでます。

プログラムの [ STRAT ] ボタンを押してプログラムのデバッグを開始します。






ブレークポイントである publish() メソッドで停止してます。

この時の変数 n の値は下のウォッチポイントで確認できます。

この時点では 1 ですね。







ツールバーの [ 続行 ] ボタンを押してプログラムを進めます。






プログラムの処理が process() メソッドに移ってきました。

process() メソッドの変数 i は 1 ですね。

それではさらに処理を進めます。

左下に[ 新しいブレークポイントのヒット ] とありますね。

矢印を押して次のブレークポイントを確認し処理を進めます。












処理がpublish() メソッドの所に戻ってきました。

変数 n の値は 2 になってます。

[ 続行 ] ボタンを押して処理を進めます。







処理が process() メソッドに移ってきました。

変数 i の値は 2 です。

同様に処理をさらに進めます。






publish() メソッドの変数 n の値が 10 になりました。

この先がどうなるのか興味津々です。

さあ、処理を進めましょう。







finished() メソッドに処理が移りました。

テキストエリアには「羊が9匹」まで表示されてます。

process() メソッドの処理の前に finished() メソッドの処理が始まってます。







finished() メソッドの処理が終わってから process() メソッドの処理が始まってます。

これでは上手く動かない訳ですね(><)






さらに処理を進めてプログラムを終了します。






予想通りの流れでしたね

どうやら finished() メソッドは SwingWorker の done() メソッドの代わりになるものではないですね。

もう少し、プログラムの流れを「見える化」するために下記のように小細工をします。














それではプログラムを実行します。






process() メソッドの処理が最後にくるんですねぇ・・・

確か、publish() メソッドが呼ばれるとすぐに process() メソッドが呼ばれるわけでなく、

process() メソッドはイベントディスパッチスレッドの中でスケジュールされて呼ばれます。

つまり、前回呼ばれてから次に呼ばれるまでの間の publish() メソッドで引き渡されたデータがリストとなって、process() メソッドに渡されます。

このように非同期で動いてるから process() メソッドが終了する前にバックグラウンドタスクの終了処理が完了してしまうのだろう。

こうなったら、豚肉牛肉、苦肉の策で process() メソッド内に終了処理を記述します。






それでは修正したプログラムを実行して確認します。





狙い通りの動作になりましたね。

この方法が正しいかどうかはわかりませんがど素人のやることだから笑って許してください。(^^;

ちなみに私が勘違いした finished() メソッドの API ドキュメントには

Called unconditionally (in a finally clause) after one of the completion methods, succeeded, failed, cancelled, or interrupted, runs.
Subclasses can override this method to cleanup before the done method returns. This method runs on the EDT. It does nothing by default.

となってます。

succeeded(), failed(), cancelled() もしくは nterrupted() 各メソッドが呼ばれたら finished() メソッドが呼ばれるようです。

だからキャンセルさせても終了処理が実行されたわけですね。



せっかくだからもうちょっと遊んでみましょう。

TaskMonitor に PropertyChangeListener を実装し、プログラムの状態を取得しています。

そのことを利用して GUI の更新をしてみましょう。

プログレスバーの進捗状態は doInBackground() メソッド内の setProgress() メソッドで行ってます。

それをpropertyChange() メソッド内で GUI の処理してます。

そこで、プログレスバーの進捗状態を数値表示させてみます。

propertyChange() メソッド内の propertyName が started ならの if 文のところに

progressBar.setStringPainted(true);

を追加記述します。

これで進捗状態の数値表示が可能になります。











次はステータスラベルにメッセージを表示させてみましょう。

doInBackground() メソッド内に

setMessage("プログラムを実行中です。”);

と追加するだけです。





同様に、

finished() メソッド内に

setMessage(isCancelled() ? "プログラムをキャンセルしました。" : "プログラムを終了しました。");

と記述します。

これは、終了時とキャンセル時に対応させてます。

finished() メソッドはキャンセル時にも呼ばれるからですね。







それでは確認してみましょう。















狙い通り上手くできました(^^)

JSR296 Swing Application Framework を使うと簡単にいろんなことができてしまいます。(^^)

今回はリソースファイルをいじるようなことはしませんでしたが、それを利用することによって

細かな設定が可能となります。

プログラムの国際化もリソースファイルを利用して簡単に行えてしまいます。

さて、このプログラム作成にて使用した技術について少しだけ簡単に調べてみます。



まず、バックグラウンドタスクを可能とする SwingWorker<T,V>クラスを継承した Task<T,V> クラスをみてみましょう。

Class Task<T,V>

java.lang.Object
   |
   |___org.jdesktop.swingworker.SwingWorker<T,V>
         |
         |___org.jdesktop.application.Task<T,V>


上記のような継承関係になってます。

Task<T,V> クラスはバックグランドタスク実行中のブロック化のプロパティを設定することができます。

今回作ったプログラムでは [ START ] ボタンに 

@Action(block = Task.BlockingScope.ACTION)

と設定されてます。

Task<T,V> クラスには TaskListener があります。

SwingWorker メソッドの doInBackground()、 process()、done() メソッドをモニターします。

バックグラウンドタスクが終了すると done() メソッドが

succeeded() メソッド、cancelled() メソッド、interrupted() メソッド、failed() メソッド

の何れか一つによって呼ばれます。

finished() メソッドは最終的に無条件で

succeeded() メソッド、cancelled() メソッド、interrupted() メソッド、failed() メソッドの何れか一つに呼ばれます。

これは done() メソッドが返される前に呼ばれます。

finished() メソッド、succeeded() メソッド、cancelled() メソッド、interrupted() メソッド、failed() メソッドこれら五つの

Task<T,V> クラスのメソッドはサブクラスでオーバーライドするように設計されてます。

リソースファイル(リソースマップ)は自動的に Task<T,V> のサブクラス、Task<T,V> クラスと読み込まれます。

これらと違う方法でのリソースファイルの取得方法ではコンストラクタで指定する方法や

getResourceMap() メソッドでの引数で指定する方法もあります。




次は TaskMonitor クラスについて調べてみます。

Class TaskMonitor
    |
    |___ java.lang.Object
            |
            |___ org.jdesktop.application.AbstractBean
                     |
                     |___org.jdesktop.application.TaskMonitor


上記のような継承関係になってます。

このクラスはアプリケーションのバックグラウンドタスクの状態を表示します。

ステータスバーのような GUI コンポーネントのためのものです。

今回作成したプルグラムでも TaskMonitor クラスに PropertyChangeListener が初めからあります。





TaskMonitor クラスの prppertyChange() メソッドによってバックグラウンドタスクの状態を取得します。

これによって、プログレスバーの進捗状態やステータスバーのメッセージを表示させてます。

イベントディスパッチスレッドの GUI 更新はこちらを利用したほうが便利そうですね。

まだまだ隠れた技術が使われてます。

興味のある方は API ドキュメントを読んでください。

私は英語がダメダメなので上記の情報は間違ってるかもしれません。

JDK7 にはまだまだ新しい技術が採り入れられるそうです。

その技術の一部を NetBeans で使えるのは本当に楽しいことですね。(^^)



    Home  Index