Scrum Boot Camp in 福岡 開催お手伝いしました。

まずは、1年以上ブログを更新していないことに愕然…。最近軽い記事はQiitaとかに書いてるしなあ。

さて、気を取り直して。タイトル通り、Scrum Boot Camp in 福岡というイベントの開催をお手伝いしました。元々は、 @haradakiro さんより福岡開催してみない?と昨年あたりからそそのかされていて、まずはどんなものかと第三回に参加してたりしたのですが、なんだかズルズルといってこの時期の開催と相成りました。

日程的にも明星和楽というビックイベントに被ってしまい、集客も不安があったのですが、最終的には27名もの方に参加頂きました。長崎から朝6時に出発して車で来た猛者4人組(といっても3名女性!)もいらっしゃって、ホントに有り難かったです。


内容は、ざっくりまとめると以下の通りでした(間違えているかも)。

(昼食)

  • ざっくりScrum
  • [マルチタスクで単純作業してみる]
  • ロールと責任
  • [2つのボール回し]
  • プロダクトバックログとユーザーストーリー
  • [飲み会開催支援システムのプロダクトバックログを作る]
  • PBIと相対見積もり
  • [プランニングポーカーでPBIを見積もる]
  • Scrumの高度な話&応用などなど

[]は参加者が手を動かしたもの。

神戸では午後にやった(と記憶している)紙ヒコーキが、福岡では午前に変更されていたりしたのですが、その場では特になんとも思いませんでした。しかし、その後のScrumの導入のところで「スプリントとは、さっきの紙ヒコーキの時の…」という説明を聞いてやっとわかりました。説明に対する理解度が上がるよう、先に体感させるという検査と適用がなされた結果だったんですね。すごい。

細かい内容は後日補足を書くかもしれませんが、まずは、開催の報告まで。

最後になりましたが、講師のみなさまに感謝!

  • 当日メイン講師を務めて頂いた @ryuzee さん
    • 当日マイクの存在を伝え忘れていて、喉を痛めさせて申し訳なかったです m(_ _)m
    • ほぼ一日しゃべりっぱなしで、本当にお疲れ様でした!
  • PBIの見積もりなどでサブ講師を務めて頂いた @kappa4 さん
    • サブ講師デビュー戦とは思えなかったですYO!
  • 全般的なサポートを頂いた @takaesu0 さん
    • 持続可能なペースとは何か体を張って教えていだけましたw
  • 最初に声をかけて頂いた @haradakiro さん
    • やっと開催できました!



せっかく、九州でScrumに関心をもっている方と繋がりをもつことができましたので、今後コミュニティに発展させて行きたいなあーと思っています。
まずは瀕死?のアジャイルサムライ福岡道場の立て直しからだな!

Scrum Boot Camp in 福岡 開催お手伝いしました。

まずは、1年以上ブログを更新していないことに愕然…。最近軽い記事はQiitaとかに書いてるしなあ。

さて、気を取り直して。タイトル通り、Scrum Boot Camp in 福岡というイベントの開催をお手伝いしました。元々は、 @haradakiro さんより福岡開催してみない?と昨年あたりからそそのかされていて、まずはどんなものかと第三回に参加してたりしたのですが、なんだかズルズルといってこの時期の開催と相成りました。

日程的にも明星和楽というビックイベントに被ってしまい、集客も不安があったのですが、最終的には27名もの方に参加頂きました。長崎から朝6時に出発して車で来た猛者4人組(といっても3名女性!)もいらっしゃって、ホントに有り難かったです。

内容は、ざっくりまとめると以下の通りでした(間違えているかも)。

  • ざっくりアジャイル
  • [紙ヒコーキワークショップ]
  • ざっくりScrum
  • [マルチタスクで単純作業してみる]
  • 昼食
  • ロールと責任
  • [2つのボール回し]
  • プロダクトバックログとユーザーストーリー
  • [飲み会開催支援システムのプロダクトバックログを作る]
  • PBIと相対見積もり
  • [プランニングポーカーでPBIを見積もる]
  • Scrumの高度な話&応用などなど

[]は参加者が手を動かしたもの。

神戸では午後にやった紙ヒコーキが、福岡では午前に変更されていたりしたのですが、その場では特になんとも思いませんでした。しかし、その後のScrumの導入のところで「スプリントとは、さっきの紙ヒコーキの時の…」という説明を聞いてやっとわかりました。説明に対する理解度が上がるよう、先に体感させるという検査と適用がなされた結果だったんですね。すごい。

細かい内容は後日補足を書くかもしれませんが、まずは、開催の報告まで。

最後になりましたが、講師のみなさまに感謝!

  • 当日メイン講師を務めて頂いた @ryuzee さん
    • 当日マイクの存在を伝え忘れていて、喉を痛めさせて申し訳なかったです m(_ _)m
    • ほぼ一日しゃべりっぱなしで、本当にお疲れ様でした!
  • PBIの見積もりなどでサブ講師を務めて頂いた @kappa4 さん
    • サブ講師デビュー戦とは思えなかったですYO
  • 全般的なサポートを頂いた @takaesu0 さん
    • 持続可能なペースとは何か体を張って教えていだけましたw
  • 最初に声をかけて頂いた @haradakiro さん
    • やっと開催できました!


せっかく、九州でScrumに関心をもっている方と繋がりをもつことができましたので、今後コミュニティに発展させて行きたいなあーと思っています。
まずは瀕死?のアジャイルサムライ福岡道場の立て直しからだな!

社内ハッカソンを成功させる5つのポイント

先日、社内ハッカソンを実施しました。こちらの記事をリツイートしている社員を見つけたのがきっかけです。

社内ハッカソンをしよう - Publickey

すぐさま「やりましょう」と言っちゃいました。上のブログの通り30時間でアウトプットを出す(つまり2日がかり)というルールを適用したのですが、結果としてはかなり盛り上がり、質はバラバラでしたが、それなりに「おおっ」となるプロダクトがアウトプットされました。初回にしては成功したと思います。

その要因をまとめてみようと思います。

名前を付ける

会社名がハウという略称で呼ばれているので、「ハウッカソン」という名前にしました(安易ですが)。「社内ハッカソン」という呼び名でやるよりも、自分たちで作っている感がでます。参加していない人へのアピールにも使えますし、お奨めです。

非日常である事を強調する

今回は参加者が「ビール飲みたい!」と言い出したのがきっかけで、特に意図もなく飲み始めたのですが、会社の中で昼間っからビール片手にコード書くと、何だかいたずらをしているような高揚感を覚えました。

社内なので参加者間には下手に色々な先入観や人間関係があります。最初はどうしても会話がぎこちなかったり、発想が偏ってしまうこともあると思います。そういう時はアルコールを注入してみるというのも手だと思います。

もちろんあまりに注入しすぎると、コーディングどころじゃなくなってしまうのでご注意を。とはいえ、2日目は時間が無くなってきたせいもあって、ほとんどの人はアルコールに手をのばさず、コーディングに集中していましたが…。

あくまで関係性をほぐしたり、非日常の思考にシフトするきっかけづくりが出来ればいいので、アルコールに限らず、勤務中はタブーだろうという事をあえてやってみるとよいと思います。

リソースをうまく使う

社内なのでリソースは色々あると思います。パッケージ製品やサービスなどを持っていれば、それを土台にすることが出来ます。ジョーク的に機能をつけてみるという方向もあれば、コードベースだけ流用するという方向もあります。

他部署のコードを見るいい機会にもなりますし、いずれにせよ、アイディアの実装に集中できるので成果が見えやすいというメリットは活かすべきでしょう。

食事はその場で食べれるものを

実際始めると時間が全然足りなくなり、食事をとるのも惜しいという状態になります。いそいそと各人コンビニに買い物に行くのもいいのですが、サンドウィッチやおにぎりなど、つまみやすいものをドーンと用意できるのであればその方がベターです。

今回はカミさんが手料理を差し入れてくれて、お昼タイムはいい憩いの時間になりました。Thank you honey!

成果を公開する

出来たコードや動いているサービス、そしてハッカソンに参加して得られたことを、積極的に公開することはとても大切です。参加者にとっては、ハッカソンという非日常な集中の場で得られたことの価値に気づくきっかけとなりますし、その他の人には、会社の潜在能力を知るいい機会となります。

とはいえ、社内ハッカソンで産み出されたものが、そのまま事業に繋がるかどうかは分かりません。それを過度に期待することはハッカソンの存在意義を歪めてしまうことになるでしょう。

まとめ

社内ハッカソンは、いわば競技であって事業ではありません。産み出したものそのものよりも、その結果得たものの方が大切です。日常のしがらみを解放し、とにかく目の前のコードに集中することを純粋に楽しむ…それが社内ハッカソンの神髄だと思います。

ハウッカソン当日の、とあるチームのまとめがこちらにありますので、これからやってみようという方には参考になると思います。

http://d.hatena.ne.jp/H_Yamaguchi/20110802/p1

旧Twitterの画面に戻す方法

twitter.comのWeb画面を旧インターフェースに戻したいという方は、結構いらっしゃるようですね。Twitterの方針としてはそのうち旧インターフェースは廃止するようですが、2011/5/5現在、まだ猶予期間中のはずなのに戻せなくなっているようでした。
そこで、戻す方法をメモします。ブラウザはFireFoxを例にしますが、他のブラウザでも「Cookieを消す」という基本は同じはずです。

さて、旧インターフェースを使っていると次のようなバーが上部に表示されます。

「新Twitterに切り替える」というボタンを押すと、新インターフェースに切り替わるのですが、問題は切り替えたあと戻せなくなってしまう、ということです。正確にいうと、新インターフェースの右上にある自分のアカウントをクリックすると「旧Twitterへ」というメニューが出てくるのですが、これを選択してもエラー画面が表示されてしまい、戻れないのです。


これは、Twitter側の問題ですので、そのうち解消されるかも知れませんが…。さて、戻す方法ですが、FireFoxのオプションを開き、プライバシーを選択します。

Cookieを個別に削除」というリンクをクリックすると、以下の画面が表示されます。ここで、検索ボックスに「phx」と入力すると以下のように、サイトがtwitter.com、Cookie名がphx_seen_bannerという項目が表示されますので、それをクリックします(もし、複数表示された場合は、サイトとCookie名が一致するものを選択してください)。その後、「Cookieを削除」というボタンをクリックし、ウィンドウを閉じます(オプションウィンドウも)。

そしてブラウザの再読込みボタンを押して、twitter.comを再表示させると、再び「#NewTwitterへようこそ!」というバーが上部に表示されるようになります。

このバーの「旧Twitter」というリンクをクリックすると、旧インターフェースに戻ることができます。

JIP -- Java Interactive Profiler

最近技術ネタ続きですね。というか、久しぶりにコンスタントに投稿しているからそう見えているだけかもしれません。

さて、今、とあるモジュールをプロファイリングしていて、完了するのに1時間ほどかかるのでその間に使っているプロファイラについてポストします。

本当は、今やっているプロファイリングは、NetBeans Profilerを使って行うはずだったのです。ところが、NetBeans Profilerのエージェントが対象のクラスをあらかたキャッシュし終えて、そろそろ開始かなと思ったとたんに接続が切れてしまう…という現象が、対象の環境で発生しました。いろいろ試行錯誤したのですが、結局原因がわからず、急遽別のプロファイラに乗り換えることにしました。もちろん無償で使えるモノ…。

そこで見つけたのがJIPです。結構歴史は古い(2005年SourceForge登録)ようですが、日本語での紹介記事がほとんどなく、意外と知られていないのかな?




http://jiprof.sourceforge.net/


JVM付属の)hprofおよびNetBeans Profilerと比較しつつ、JIPのいいところを3つ挙げてみたいと思います。

JIPの特徴

1. インタラクティブなプロファイリング

計測ON/OFFや除外クラスの変更などを、付属のコマンドを使ってJVMを再起動することなくほぼ一瞬で操作することができます。
hprofだと再起動しない限りON/OFFできませんので、とあるリクエストの処理だけ計測する…ということができずWebサーバ起動までの処理もみんな一緒くたに集計されてしまいます。一方、NetBeansは、ON/OFF可能ですが、除外クラスの変更はキャッシュを作り直しているのか結構時間がかかります。

2. Pure Java

普通のプロファイラは、*.soとか*.dllといった共有ライブラリが付属していますが、JIPはPure Javaです。プラットフォームを選ばずに動作します。それだけでなく、JIPはASMを利用した非常にシンプルな作りになっているため、自分で独自の集計ができるように改良する、なんてこともやりやすいでしょう。

作者による、オレオレプロファイラの作り方を書いた、この記事にエッセンスが詰まっています。

http://www.ibm.com/developerworks/jp/java/library/j-jip/

3. フィルタの充実

プロファイリングでは、計測対象のクラスのフィルタリングが非常に大事です。多すぎると遅くなって計れないし、少なすぎるとボトルネックを絞り込めません。JIPはクラスローダーによるフィルタリングをベースに、クラスのincludeとexcludeを合わせて指定できます。
hprofは何も指定できないので論外。NetBeansは意外にもincludeか、excludeかどちらかしか選べないのです。その点、JIPは柔軟に指定できます。

使い方

JIPをダウンロードして解凍したら(解凍ディレクトリをJIP_HOMEとします)、計測対象のアプリケーションを起動させましょう。Tomcatで動作するWebアプリケーションなら、以下のような環境変数を指定します。

> SET JAVA_OPTS=-javaagent:%JIP_HOME%\profile\profile.jar -Dprofile.properties=%JIP_HOME%\profile\webapp.profile.properties

なお、ここで出てくるwebapp.profiler.propertiesは、Tomcat用に予め用意された設定ファイルで、必要に応じて変更してください。

そうしたら、あとはTomcatを起動するだけです。うまく設定できていれば、Tomcatのログに以下のような出力が含まれるはずです。

・・・
profiler: off
remote: on
port: 15599
thread-depth: compact
・・・

感覚的に普通の起動より5倍以上時間がかかりますので、コーヒーでも入れながら気長にトライしてみてください。これでも、hprofよりはましなはずです。

起動したら、計測したい箇所まで画面遷移させたら、プロファイラをオンにして計測を開始します。

> cd %JIP_HOME%\client
> start.bat localhost 15599

処理が終わったらプロファイラを止めて結果をダンプさせます。

> finish.bat localhost 15599

そうすると、結果のprofile.txtとprofile.xmlが、Tomcatの実行ディレクトリ(大抵の場合CATALINA_HOME)に出力されるはずです。

profile.txtをテキストエディタで見るだけでも十分なのですが、NetBeansに比べると見劣りしてしまいますよね。そういうときは、jipViewerの出番です。引数にprofile.xmlへのパスを指定して下さい。

> cd %JIP_HOME%\tools
> jipViewer.bat path\to\profile.xml

すると、こんな感じでプロファイリング結果が表示されます。

実行時間でソートしたり、呼び出し元をたどったりと、必要十分な感じです。まあ、欲をいうと、値をコピーしてExcelとかに貼り付けられるようになると、もっと便利な気はしますけども。

そんなこんなで、是非使って見てください。

IS01ではBluetoothで遊べない?

手持ちのBluetooth対応のサーマルプリンタにIS01から印刷させてみようと試みましたが、どうもうまくいきません。
backport-android-bluetoothで組んでいて、デバイスの検索とかはちゃんと動作するのですが、いざRFCOMMで通信しようとBluetoothDevice#createRfcommSocketToServiceRecordを呼ぶと、以下のようなエラーが発生してしまいます。

11-21 19:52:55.787: ERROR/AndroidRuntime(16239): Uncaught handler: thread main exiting due to uncaught exception
11-21 19:52:55.887: ERROR/AndroidRuntime(16239): java.lang.ExceptionInInitializerError
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at backport.android.bluetooth.BluetoothSocket.<init>(BluetoothSocket.java:69)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at backport.android.bluetooth.BluetoothDevice.createRfcommSocketToServiceRecord(BluetoothDevice.java:382)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at jp.haw.go.printit.PrintIt.onActivityResult(PrintIt.java:165)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at android.app.Activity.dispatchActivityResult(Activity.java:3636)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at android.app.ActivityThread.deliverResults(ActivityThread.java:3414)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at android.app.ActivityThread.handleSendResult(ActivityThread.java:3460)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at android.app.ActivityThread.access$2700(ActivityThread.java:134)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1969)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at android.os.Handler.dispatchMessage(Handler.java:99)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at android.os.Looper.loop(Looper.java:123)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at android.app.ActivityThread.main(ActivityThread.java:4403)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at java.lang.reflect.Method.invokeNative(Native Method)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at java.lang.reflect.Method.invoke(Method.java:521)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:799)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at dalvik.system.NativeStart.main(Native Method)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239): Caused by: java.lang.UnsatisfiedLinkError: classInitNative
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at android.bluetooth.RfcommSocket.classInitNative(Native Method)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     at android.bluetooth.RfcommSocket.<clinit>(RfcommSocket.java:152)
11-21 19:52:55.887: ERROR/AndroidRuntime(16239):     ... 16 more

どうも、RfcommSocketのインスタンスを生成しようとしているところで、classIntNativeというネイティブ関数を呼び出そうとして、UnsatisfiedLinkErrorが発生しているようです。と、言うことは、もしかしてIS01はRfcommSocketに関連するネイティブ関数を削除したりしているのでは…。
それを確認するために、

adb pull /system/lib/libandroid_runtime.so

して共有ライブラリをローカルに持ってきて、シンボルテーブルを確認してみました。すると、以下のように、RfcommSocketは見あたりませんでした。

$ objdump -T /home/rd/serial/libandroid_runtime.so | grep android_bluetooth
00065d09 g    DF .text  00000028 _ZN7android36register_android_bluetooth_ScoSocketEP7_JNIEnv
00065589 g    DF .text  00000028 _ZN7android48register_android_bluetooth_BluetoothAudioGatewayEP7_JNIEnv
00064539 g    DF .text  00000028 _ZN7android38register_android_bluetooth_HeadsetBaseEP7_JNIEnv
0006f6a5 g    DF .text  00000028 _ZN7android46register_android_bluetooth_BluetoothDunGatewayEP7_JNIEnv
0006f9fd g    DF .text  00000108 _ZN7android42register_android_bluetooth_BluetoothSocketEP7_JNIEnv

ん〜IS01ではRfcommSocketが削除されているんだろうか(これだけで判断していいのか自信がないので、他の端末でも調べます)。
しかし、まあ、そうだとしたら、2.x系へのアップデートが無いのはいいとして、せめて遊べるようなアップデートはしてほしいなあ…IS01

投稿直後に追記:

あれ、でも、よく見たら、BuletoothSocketなんてものがありますね。そのほかにもBluetoothDunGatewayとかも気になります。もしかすると、この辺りの実装を使っているのでしょうか??

http://www.adakoda.com/adakoda/2010/03/android-oesf-android-em1.html

NetBeansからWebLogicのアプリケーションをプロファイリング

WebLogicで動作しているアプリケーションをプロファイリングしたいという要請があり、NetBeansのプロファイラを接続するという脱線をしましたのでメモ。
使用したのは、ノードマネージャで管理されLinuxサーバ上で動作している WebLogic 10 と、NetBeans 6.9.1 です。

接続先の設定

まず、NetBeansのメニューから[プロファイラ]->[プロファイラを接続…]を選択して、ダイアログを出します。既にプロファイラを接続したことがある場合は、接続モードの[変更…]リンクをクリックします。まだ、一度もプロファイラを使ったことがない場合は、[接続]ボタンをクリックします。

まずターゲットの種類を設定。右のように J2EE Web/アプリケーションサーバ および WebLogic 9+ を選択します。接続方法はリモートを選びます。

次に接続先を設定。WebLogicサーバのIPまたはホスト名、OS、JVMを適切に設定します(ここではlinux 32bit)。ちなみに、WebLogicは旧SunのVMで動作させていることを前提としています(JRockitは試していません)。

エージェントプログラムの配置

最後次にリモートパックというプロファイラのエージェントプログラムをWebLogicサーバ上に配置します。右のように手順がテキストで出てきますが、ここでは手順1と2までやります。手動3以降は、WebLogicサーバをシェル起動することを前提としているため、ノードマネージャで管理されているWebLogicだとそのまま適用しにくい内容となっています。

まず手順1については、「リモートパックを生成」ボタンを押すと、profiler-server-linux.zipというファイルが作られますので(対象のプラットフォームによってファイル名は異なります)、それをscpなどでサーバに転送し、WebLogicの実行ユーザが参照できる場所に展開します。例えば /opt/profiler に保存するなら次のような感じです。

$ mkdir /opt/profiler
$ cd /opt/profiler
$ unzip ~/profiler-server-linux.zip

展開が出来たら、手順2に従いWebLogicの実行ユーザキャリブレーションを実行します。

$ sh /opt/profiler/bin/calibrate.sh
Profiler Agent: JNI On Load Initializing...
Profiler Agent: JNI OnLoad Initialized successfully
Starting calibration...
Calibration performed successfully
...

エージェントを起動オプションに設定

さて、次は管理コンソールからWebLogicJVMの起動オプションの設定を行います。

管理コンソールにログインしたら、[サーバ]->(対象サーバ)->[コンフィグレーション]->[サーバの起動]と選択すると、右のような設定フォームとなります。この中にある、[引数]に以下を追加します。

-agentpath:/opt/profiler/lib/deployed/jdk15/linux/libprofilerinterface.so=/opt/profiler/lib,5140

もちろん、パスにはエージェントプログラムを展開した場所を適切に(かつ絶対パスで)指定してください。

変更を保存してサーバを再起動すると、プロファイラの接続待ち状態となり起動が一時停止しますので、そこにプロファイラを接続をします。

ルートメソッドの設定

まず、プロファイリングを開始するメソッド(ルートメソッド)を設定します。これを設定しないとリクエストに関係のない動作でもプロファイラが動き出すため重くなります。まず、「プロファイラを接続」ダイアログで、「アプリケーションの一部」を選択し、「定義…」リンクをクリックします。

プロジェクトから追加でも、手動で追加でも、構いません。大抵の場合、コントローラに相当するクラスを指定すればいいでしょう。今回は Teeda を使用しているアプリでしたので、右のように指定して、Pageクラスの全メソッドを対象としました。

対象クラスの設定

次に、プロファイリングの対象クラスを設定します。これは、ルートメソッドを起点にプロファイルが開始された後、処理時間を記録するメソッドを指定します。これを指定していないと、それこそStringの操作でも全て記録されてしまいますので、やはり重くなります。具体的には、「プロファイラを接続」ダイアログで、フィルタという形で設定します。予め用意されたフィルタもありますし、自分でよく使うパターンを登録しておくこともできますが、ここでは、「簡易フィルタ…」を選びます。

すると、右のようなダイアログが表示されますので、フィルタのパターンを入力します。今回はSQLの実行と結果フェッチのどちらがボトルネックになっているかを知りたかったので、Oracleドライバのクラスを指定したりしています。

やっと接続

最後に「接続」をクリックするとプロファイラがサーバに接続され、一時停止していたサーバが起動を再開します。サーバが完全に起動したら、アプリケーションにアクセスし、ルートメソッドに設定した処理が呼ばれるような操作を行います。そうするとプロファイリングが開始されるはずです。

リンク

NetBeansのプロファイラの詳細な使い方は、以下のリンクを参照してください。

また、プロファイラでの分析について、全般的に知りたい場合は以下の記事がよくまとめられていて良いと思います(NetBeansのバージョンがちょっと古いですが)。

2010/11/11 11:18
見にくかったので見出しを追加しました。あと、ちょこっと接続詞を修正