更新:我已經瞭解了更多關於正在發生的事情,並在底部添加了新的信息。tomcat類加載器錯誤?
我有2個應用程序運行在tomcat下。首先加載App1,然後加載App2。如果應用1啓動過程中運行到任何類型的錯誤,未能成功加載,我應用2的啓動過程中出現此錯誤:
Caused by: java.security.NoSuchAlgorithmException: No such algorithm: RSA/NONE/OAEPWithSHA1AndMGF1Padding
at javax.crypto.Cipher.getInstance(DashoA13*..)
at javax.crypto.Cipher.getInstance(DashoA13*..)
at com.jp.protection.security.BouncyCastleSecurityProvider.getCipher(BouncyCastleSecurityProvider.java:139)
at com.jp.protection.security.BouncyCastleSecurityProvider.decode(BouncyCastleSecurityProvider.java:110)
... 70 more
Caused by: java.lang.NullPointerException
at org.bouncycastle.jcajce.provider.util.DigestFactory.getDigest(DigestFactory.java:86)
at org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.initFromSpec(CipherSpi.java:83)
at org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineSetPadding(CipherSpi.java:214)
at javax.crypto.Cipher$r.a(DashoA13*..)
... 74 more
注意,最終的原因是一個NullPointerException異常。我下載了DigestFactory源和它看起來像這樣(只摘錄了相關部分):
package org.bouncycastle.jcajce.provider.util;
public class DigestFactory
{
private static Set sha1 = new HashSet();
static
{
sha1.add("SHA1");
sha1.add("SHA-1");
}
public static Digest getDigest(
String digestName)
{
digestName = Strings.toUpperCase(digestName);
if (sha1.contains(digestName)) ** line 86 where npe occurs**
{
return new SHA1Digest();
}
[...]
在線路86獲得一個NPE的唯一方法是,如果SHA1爲null。 (如果digestName爲null,NPE將在對Strings.toUpperCase的調用中發生)。事實上,如果我在此處放置斷點,則在錯誤情況下,調試器顯示sha1(以及所有其他類似靜態初始化的字段)爲null。這些字段是私人的,沒有方法允許修改這些字段。
這怎麼可能?我認爲也許我的DigestFactory源代碼與我運行的jar不完全匹配,所以調試器誤導了我,但它應該是正確的版本,而其他所有東西似乎都是一致的。
在調試器下,在異常發生之前,我嘗試在App2初始化的早期階段調用DigestFactory.getDigest(「SHA-1」)(使用調試器的求值表達式),併成功返回。這表明DigestFactory的靜態字段已成功初始化,然後以某種方式後來設置爲null,或者另一個類加載器具有不同類型的版本(即使這種情況,並不能解釋它們如何爲空)。
這個異常發生在第三方代碼的深層2層(jproductivity保護包使用了bouncycastle),所以我對這種情況的控制是有限的。不過,我想首先了解這是如何可能的,並希望我能夠如何預防或解決此問題。另一個神祕之處是爲什麼第一個應用程序的錯誤對第二個應用程序有任何影響 - 在tomcat下它們應該有單獨的類加載器。但是如果第一個應用程序沒有錯誤,那麼在第二個應用程序中就不會出現這個問題。
更新:由於我發佈了這個,我學到了更多。當tomcat停止webapp時(在這種情況下是因爲啓動錯誤),tomcat將在其類中的靜態字段爲空以避免內存泄漏。所以這解釋了我的不可變靜態字段是如何設置爲空的。但是,這不應該影響App2,因爲它應該使用單獨的類加載器。但是我看過調試器,實際上這兩個webapps中的DigestFactory類都使用了相同的類加載器。這與我可以找到的所有tomcat文檔相矛盾。對於其他類(我自己的類),有不同的類加載器。我想知道是否它與DigestFactory是靜態的和不可變的,所以從理論上講它並不重要。
因此,作爲一個實驗,我從兩個webapps中刪除了包含DigestFactory的jar,並將它添加到tomcat/lib(以便它被共享,而不是任何webapp的一部分)。這就解決了這個問題 - 它的領域並沒有被消除,大概是因爲它不是犯罪Web應用程序的一部分。但是,這種方法是不可取的,不應該是必要的。這是一個tomcat的bug嗎?
第一個應用程序出現什麼樣的問題?這可能是解決這個難題的一個非常重要的信息。 –
它在初始化彈簧上下文時發生在各種場景中。出於測試目的,我在ContextLoaderListener.contextInitialized()實現中強制使用空指針異常。 – Dana