Spring/ サーバ停止時にNPE

■背景
Spring使用のWEBアプリ
PiyoPiyoクラスにstaticメソッドあり

■現象
PATTERN A
デプロイ
※PiyoPiyoクラスをprototypeスコープでBean定義
サーバ始動
サーバ停止
(もしくはオートデプロイ)
→NullPointerException

PATTERN B
デプロイ
※PiyoPiyoをsigletonスコープでBean定義
サーバ始動
サーバ停止
(もしくはオートデプロイ)
→エラーなし

PATTERN C
デプロイ
※PiyoPiyoをprototypeスコープでBean定義
サーバ始動
リクエスト実行
サーバ停止
(もしくはオートデプロイ)
→エラーなし

■ログ
-error--------------------------------------------------------------------------
2008/10/08 00:00:11 org.apache.coyote.http11.Http11BaseProtocol pause
情報: Coyote HTTP/1.1を http-8080 で一時停止します
2008/10/08 00:00:12 org.apache.catalina.core.StandardService stop
情報: サービス Catalina を停止します
2008/10/08 00:00:12 INFO [/hoge] - Destroying Spring FrameworkServlet 'hoge'
...
java.lang.NullPointerException
...
at hoge.PiyoPiyo(PiyoPiyo.java:18)
...
at org.apache.catalina.loader.WebappClassLoader.clearReferences(WebappClassLoader.java:1615)
at org.apache.catalina.loader.WebappClassLoader.stop(WebappClassLoader.java:1496)
...
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:433)
-/error-------------------------------------------------------------------------

■原因
PATTERN A
prototypeでクラスロード時には、インスタンスは生成されないため、staticメソッドも呼ばれない。
結果、アンロード時に始めてstaticメソッドが呼ばれるが、
staticメソッド内で使うクラスのアンロードと順序が前後してしまい、
staticメソッドでstaticメソッド内で使う筈のクラスがNotFoundとなってしまう。

PATTERN B
singletonスコープなため、ロードにインスタンスが生成される。その際にstaticメソッドも呼ばれる。
この段階では、staticメソッド内で使う筈のクラスもクラスローダに読み込まれているので、NotFoundは生じない。
アンロード時でも、staticメソッド内で使う筈のクラスが既にアンロードされていても、
staticメソッドが再度呼ばれることはないので、NotFoundにはならない。

PATTERN C
prototypeでクラスロード時には、インスタンスは生成されないため、staticメソッドも呼ばれない。
しかし、リクエストを投げた時点でprototype指定のクラスは正式にインスタンス化され、
その際に、staticメソッドも呼ばれる。
この段階ではstaticメソッド内で使う筈のクラスは、クラスローダに読み込まれているので、NotFoundは生じない。
また、アンロード時にstaticメソッドが呼ばれることもないため、パターン②と同様に、NotFoundにはならない。

■結論
Beanのスコープをsingletonにすれば、本現象は回避できる。
singletonに出来ないんであれば、staticメソッドを変更せよ!

■追記
※staticメソッドとかでファイル読み込みは特にNG!
∵Tomcat#org.apache.catalina.loader.WebappClassLoader#stop()は
 ファイル群を消した後に、jarファイル群を消す。

-WebappClassLoader------------------------------------------
1488 /**
1489 * Stop the class loader.
1490 *
1491 * @exception LifecycleException if a lifecycle error occurs
1492 */
1493 public void stop() throws LifecycleException {
1494
1495 // Clearing references should be done before setting started to
1496 // false, due to possible side effects
1497 clearReferences();
1498
1499 started = false;
1500
1501 int length = files.length;
1502 for (int i = 0; i < length; i++) {
1503 files[i] = null;
1504 }
1505
1506 length = jarFiles.length;
1507 for (int i = 0; i < length; i++) {
1508 try {
1509 if (jarFiles[i] != null) {
1510 jarFiles[i].close();
1511 }
1512 } catch (IOException e) {
1513 // Ignore
1514 }
1515 jarFiles[i] = null;
1516 }
1517
1518 notFoundResources.clear();
1519 resourceEntries.clear();
1520 resources = null;
1521 repositories = null;
1522 repositoryURLs = null;
1523 files = null;
1524 jarFiles = null;
1525 jarRealFiles = null;
1526 jarPath = null;
1527 jarNames = null;
1528 lastModifiedDates = null;
1529 paths = null;
1530 hasExternalRepositories = false;
1531 parent = null;
1532
1533 permissionList.clear();
1534 loaderPC.clear();
1535
1536 if (loaderDir != null) {
1537 deleteDir(loaderDir);
1538 }
1539
1540 }
-/WebappClassLoader------------------------------------------

ref:
http://webui.sourcelabs.com/tomcat/issues/41059
http://www.docjar.com/html/api/org/apache/catalina/loader/WebappClassLoader.java.html

tag : spring bean NPE

2008-10-17 07:41 : __fw__spring : コメント : 0 : トラックバック : 0 :
コメントの投稿
非公開コメント

« next  ホーム  prev »

search

ad



counter


tag cloud

category cloud