ゴミ箱.net

汚物は消毒

JavaMailが文字化けする件

JavaMail APIを使ってメールを送信するアプリケーションをJava Service Wrapperによってサービス化したら、なぜか送るメールがことごとく文字化けしてしまう。
文字セットとしてISO-2022-JPを指定しているにもかかわらず、データを見るとUTF-8で送信されているようだ。

マルチバイト文化圏の住民にありがちな話だと思ってぐぐっても、なぜか同じ症状の話がまったく出てこない。基本的な設定ミスの話ばかり引っかかるが、そもそも設定自体はできているのだ。というのも、同じ処理をスタンドアロンで実行したら文字化けせずにメールを遅れるのに、Java Service Wrapper経由で処理を実行したときだけ文字化けするのだ。

仕方がないので、JavaMailのソースコードを追っかけていって調べる羽目になった。
その結果、どうにか原因らしきものを突き止めることはできたのだが…

結論から言うと、JavaMailの初期化の仕組みと、Java Service Wrapperのクラスローダの仕組みが絡み合って発生した不具合であった。

JavaMailは、JavaBeans Activation Framework(JAF)というライブラリを呼び出して自身の初期化を行う。
具体的には、JavaMailの初期化処理においてJAFを呼び出し、その中でJavaMailのJARファイルに含まれているリソースであるMETA-INF/mailcapをロードする処理が存在する。

この処理にちょっと面倒な問題があるらしい。
JAFがリソースをロードするときは、アプリケーションのクラスローダを使用するように固定で実装されているようなのだ。本来ならば、JavaMailのJARファイルを読み込んだクラスローダを使用しなければならないのだが。
通常、これが問題になることはあまりない。しかし、Java Service WrapperによってJavaアプリケーションをサービス化する場合、Java Service Wrapper自体のクラスローダと、Java Service Wrapper上で実行されるサービス化の対象となるアプリケーションのクラスローダが別になっているのだ。(面倒なのでそれぞれ親クラスローダ、子クラスローダと呼ぶ。)このようにクラスローダが複数存在しているときに問題が発生する。
JavaMailのJARファイルは子クラスローダ上で読み込まれるため、親クラスローダはJavaMailのJARファイルのリソースにアクセスすることはできない。にもかかわらず、JAFは親クラスローダを用いてリソースを読み込もうとするので、リソースが見つからずに初期化が失敗してしまうのだ。(さらにたちの悪いことに、初期化が失敗しても見かけ上は普通に処理が続行される。)

同様の問題は、Tomcatのようなアプリケーションサーバでも発生しうる。Tomcat本体と、個別のウェブアプリケーションはクラスローダが分離されているため、ウェブアプリケーションの個別のクラスパスにJavaMailのJARファイルが含まれていると同じことが起こりうるのだ。(ただ、こちらの場合は症状が起こったり起こらなかったりし、再現性が低い。何か見落としがありそうだが調べる気も起きない…)
すまんあれは嘘だった


対策としては、Java Service WrapperやTomcatのような、別のアプリケーションを別のクラスローダを使って読み込んで本体の上で動作させるタイプのアプリケーションでは、JavaMailのJARファイルをアプリケーション本体(Java Service Wrapper、Tomcat)のクラスパスに置いておく必要がある。

関連するかどうか分からないが、こんな情報があった。
JavaMail in OSGi | Technocratic Dilemmas
JavaMailとJAFはもともとどちらも独立したライブラリだったが、Java6からはJAFだけが標準ライブラリに取り込まれ、JavaMailだけが外部のライブラリとして残った。これが尾を引いていそうな気がする…

スポンサーサイト

PageTop

コメント


管理者にだけ表示を許可する