2013/03/10 このエントリーをはてなブックマークに追加 はてなブックマーク - 【Java】アップキャストでClassCastException

【Java】アップキャストでClassCastException



久しぶりにブログ書きます。

最近ちょっとずつ、本業務の方が忙しくなってきたので、
疎かになってしまっておりました。。。




○ClassNotFoundExceptionのワナ





JavaでClassNotFoundExceptionが発生するとき、
あなたならまず何を疑いますか?


1.パスが通っていない
2.クラス名のスペルミス
3.そもそもクラスファイル(.class)が存在していない


大体、経験を積んできた皆さんであれば、こういった予想に辿り着くでしょう。


それで、まぁ僕も例のごとくこの可能性を探ったんですが、
一向に問題を解決出来ないという局面に立たされてしまいました。
僕のケースを説明します。


---------前提条件---------

APサーバー
・JBossEAP6

デプロイしてるもの
・アプリ(Earファイル)
・リソースアダプター(rarファイル)

---------------------------






1.パスが通っていない




まず、これを考えますよね。必要なjarファイル、またはclassファイルは存在しているのか。
でも、デプロイされたrarからはそのクラスが呼び出せている。
それなのにEarからは呼び出せていない。。。怪しい。







2.クラス名のスペルミス


これはめちゃくちゃ確認しましたが、大丈夫でした笑








3.そもそもクラスファイル(.class)が存在していない


rarファイル内のjar内に該当クラスがあることは確認しています。





1のクラスパスの怪しさはあるものの、JBossからは該当クラスは見ることが出来ています。
アプリケーション側(Ear側)から見ることが出来ない。



その場しのぎで、必要なjarをEar/libの直下に入れてみることにしました。
その結果、ClassNotFoundは解消されましたが・・・・・・・・








○ClassCastException発生





クラスが見えるようになってひと安心。と思いきや、キャストエラーが発生してしまいました。

継承関係から言って、スーパークラスへのキャストなので、例外が発生するはず無いのに!あれ???
はじめは別のパッケージの同名クラスでも見てしまってるのかと思いましたが、
違いました。 














○原因はクラスローダー






型や、継承関係が不正でなくてもクラスローダーが原因でキャスト例外が発生してしまうことがあるのです。
散々悩んでいましたが、盲点でした。
同じクラスを別のクラスローダーで重複して読み込むとClassCastExceptionが発生することがあります。



僕のケースでは、アプリ(Ear)側のクラスローダーで読み込んだクラスからリソースアダプター(rar)側のクラスローダーで読み込んだクラスへキャストしようとしたため、
キャストが出来なかったのです。



実際にこの状態で以下のコードを埋め込んでみたところ、別のクラスローダーからクラスを参照しているため、
正しくインスタンスを認識できていませんでした。


//リソースアダプターからJNDIバインドされたクラスをルックアップ
Context ctx = new InitialContext(env);  
Object ref = ctx.lookup("java:/jboss/JndiBindedClass");  


System.out.println(ref.getClass().getClassLoader());//←EARのクラスローダー
System.out.println(JndiBindedClass.class.getClassLoader());//←rarのクラスローダー


//実装インターフェースの取得
String[] ifs = ref.getClass().getInterfaces();

for(int i=0; i < ifs.length; i++){

    System.out.println("実装インターフェース" + ifs[i]);

}//←実装しているインターフェースは同じでした


if (ref instanceof JndiBindedClass){
    System.out.println("JndiBindedClassのインスタンス");
}//←falseになってしまう


if (JndiBindedSuperClass.class.isAssignableFrom(ref.getClass()){
    System.out.pritnln("JndiBindedSuperClassを継承している。");
}//←falseになってしまう
    
    
(JndiBindedSuperClass) ref.DoSuperMethod();//←ここでキャストエラー
















結局、振り出しに戻り、クラスパスを整理したところ、同じクラスローダーを参照するようになり、 ClassNotFoundとClassCastの問題は解決しましたとさ・・・・。


この問題は、JBossEAP6は階層型クラスローダーではなくモジュール型のクラスローダーであることが要因として大きいのかもな。


思わぬところでキャスト例外が発生したとき、このエントリが皆さんの参考になれば嬉しいです。




0 件のコメント:

コメントを投稿