AWT/Swing でエラーダイアログを表示する。(Sunの実装限定)
後でこのプロパティ名で検索したら、sun.awt.exception.handler - ある学生さんのシステム開発日記に書かれていた。
うぐぐ、 "awt 例外 キャッチ" とか "awt 例外 ダイアログ" とかで検索して見つからなかったから、諦めて調べたが…。 "awt 例外 ハンドラ" と検索すればよかったのか。(2番目に出てくる)
良い検索キーワードが閃けば、時間を節約できるなあ…。
AWT/Swingのイベントハンドラで例外が発生すると、ユーザにはそんなことは何も分からずに、ただスタックトレースが吐き出されるだけである。
これを自動的に例外が発生したら、エラーダイアログを出すなり何なりの処理をさせたいと思った。
しかし、例えば addExceptionHandler() とか、そのような目的のためのAPIは無いようだ。
"sun.awt.exception.handler" に例外処理クラスを設定
AWTの例外処理ソースを追っていくと、
- EventDispatchThread.pumpOneEventForHierarchy で例外が catch される。
- EventDispatchThread.processException
- EventDispatchThread.handleException
という流れで例外が処理されている。この handleException の中のソースを見て、どうすればよいかが分かった。
次の例外処理クラスを作って、
public class AWTExceptionHandler { public static void setup() { System.setProperty("sun.awt.exception.handler", AWTExceptionHandler.class.getName()); } public void handle(Throwable ex) { // エラー処理を書いてくれぃっ! ex.printStacktrace(); } }
アプリケーションの初期化処理の中で、
AWTExceptionHandler.setup();
を呼べばよい。
これにより、イベントハンドラで発生した例外すべてを、 handle() の中で処理できる。
もちろんこの中で、ダイアログ表示も可能。
ThreadGroup.uncaughtException を使う方法
ちなみに、
catch を書かなくても例外発生時にエラーメッセージを出したい - Java Solution
ThreadGroup.uncaughtException を使う方法もあるようだ。
発見した TMail-1.2.3.1 のバグ各種
TMail-1.2.3.1 のバグを色々直した。それぞれ詳しいことは以下。各修正を適用したTMailのgemをダウンロードに置いてあるので、手っ取り早く使いたい方は、これをダウンロードして、 gem install tmail-1.2.3.1.gem でインストールしてほしい。
ついでにこの gem には、leave a note [message] behind on Rails: RailsのMailer(TMail)のメールアドレスドット問題の修正も取り込ませていただいた。
正しく動作する保証は無いですが、不具合があればコメントしていただけると助かります。
Content-Typeで値がクォートを含んだまま取り込まれる
TMailプロジェクトに送信したパッチ: RubyForge: TMail: Modify: 23165 - ContentTypeHeader should unquote parameter's value on encode
例えば
Content-Type: image/jpeg; name="\e$B4A;z\e(B.jpg"
という生JISのファイル名がクォートされて指定されていると、なぜかエンコードした後に、
Content-Type: image/jpeg; name*=iso-2022-jp'ja'%22%1b$B4A%3bz%1b%28B.jpg%22
%22(")が前後にくっついてしまう。本当は、
Content-Type: image/jpeg; name*=iso-2022-jp'ja'%1b$B4A%3bz%1b%28B.jpg
でないといけない。
AppleMail 添付ファイルパートの Content-Type が解析エラー
TMailプロジェクトに送信したパッチ: RubyForge: TMail: Modify: 23681 - Fix parse error on AppleMail's bad Content-Type parameter value - unquoted but bencoded
AppleMailに日本語ファイル名を添付させてみよう。するとこんなパートが生成される。
--Apple-Mail-1-993553537 Content-Transfer-Encoding: base64 Content-Type: application/pdf; x-mac-type=50444620; x-unix-mode=0644; x-mac-creator=4341524F; name==?ISO-2022-JP?B?GyRCQXdFRTdPRX0/XhsoQi5wZGY=?= Content-Disposition: inline; filename*=ISO-2022-JP''%1B%24BAwEE7OE%7D%3F%5E%1B%28B.pdf
これをパースし、このパートのcontent_typeを得ようとすると
part.content_type => nil
nilが帰ってくる。さらに、encodedでエンコードすると、
--Apple-Mail-1-993553537 Content-Transfer-Encoding: base64 Content-Disposition: inline; filename*=ISO-2022-JP''%1B%24BAwEE7OE%7D%3F%5E%1B%28B.pdf
Content-Type そのものが消えてしまう。(メーラ上の動作では、本文として展開されてしまう)
実は、Content-Type をパースする際に構文エラーが発生しているのだ。 name==?ISO- の2つ目の = に出会った時点で、これは有効なトークンではないとエラーになってしまう。確かに、RFC 2045(対訳)多目的インターネットメール拡張 パート1 Content-Type ヘッダフィールドの文法 によれば、これはクォートしないといけない。 AppleMail が間違っているのだ。
Content-Type をパースする前に、このようなパターンを見つけてクォートさせてやることで解決した。
ちなみに、AppleMailに長いファイル名で添付をさせると
Content-Type: application/pdf; x-unix-mode=0644; name="=?ISO-2022-JP?B?GyRCRDkhPCQkRDkhPCQkRDkhPCQkRDkhPCQkGyhC?= =?ISO-2022-JP?B?GyRCRDkhPCQkRDkhPCQkRDkhPCQkRDkhPCQkGyhC?= =?ISO-2022-JP?B?GyRCRDkhPCQkRDkhPCQkRDkhPCQkRDkhPCQkGyhC?= =?ISO-2022-JP?B?GyRCRDkhPCQkRDkhPCQkRDkhPCQkGyhCLnBkZg==?="
ちゃんとクォートされる。1行で収まる場合のみ、クォートしてくれないらしい。
TMailで作成した添付ファイル名の文字化け
TMailで添付ファイル付きのメールを1から作成しようとすると、このようなスクリプトになるが、
filename = "任意の日本語ファイル名" # JISにすること filedata = "ファイルのデータ" require "base64" require "tmail" mail = TMail::Mail.new() part = TMail::Mail.new() part.transfer_encoding = "7bit" part.set_content_type('text', 'plain', 'charset'=>'iso-2022-jp') part.body = "honbun." mail.parts << part part = TMail::Mail.new() part.set_content_type('application', 'octet-stream', 'name' => filename) part.set_disposition("attachment") part.transfer_encoding = "base64" part.body = Base64.encode64(filedata) mail.parts << part mail.encoded
filename によっては、途中でファイル名が化けたりする。
例えば、「何かチラシ.pdf」をこのスクリプトで作成すると、メーラで開いたときにはファイル名が「何か汁初酬.pdf」になってしまう。まあ笑えるからいいか、ってんなわけない。
このヘッダを見ると
Content-Type: application/octet-stream; name*=iso-2022-jp'ja'%1b$B2%3f$+%A%i%7%1b%28B.pdf
となっている。原因は、エンコード時にJIS文字列中の % をエスケープしてくれてないからである。正しくは、
Content-Type: application/octet-stream; name*=iso-2022-jp'ja'%1b$B2%3f$+%25A%25i%257%1b%28B.pdf
でないといけない。
encode.rb の encode_value の中で、 TOKEN_UNSAFE という正規表現を %xx に置き換えている。TOKEN_UNSAFEの宣言は utils.rb にある。それが使っている値である、 aspecial/tspecial に % を追加して修正した。
しかし、これだと他のメソッドにも影響が出そうだが…。ちょっと不安である。それに aspecial/tspecial の定義が、 RFC と違ってきてしまう。
このパッチはトラッカーにsubmitしていない。パッチは以下の通りである。
Index: lib/tmail/utils.rb =================================================================== --- lib/tmail/utils.rb (リビジョン 261) +++ lib/tmail/utils.rb (作業コピー) @@ -109,8 +109,8 @@ # It also provides methods you can call to determine if a string is safe module TextUtils - aspecial = %Q|()<>[]:;.\\,"| - tspecial = %Q|()<>[];:\\,"/?=| + aspecial = %Q|()<>[]:;.\\,"%| + tspecial = %Q|()<>[];:\\,"/?=%| lwsp = %Q| \t\r\n| control = %Q|\x00-\x1f\x7f-\xff|