更新:我已经了解了更多关于正在发生的事情,并在底部添加了新的信息。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