I deployed my application war to tomcat, threw StackOverflowError
after startup, the stacktrace is shown in the picture below.
According to the stacktrace tip and source code, i can find the cyclic inheritance dependencies are: org.bouncycastle.asn1.ASN1Boolean extends org.bouncycastle.asn1.DERBoolean(org.bouncycastle:bcprov-jdk15:1.45) org.bouncycastle.asn1.DERBoolean extends org.bouncycastle.asn1.ASN1Boolean(org.bouncycastle:bcprov-jdk15on:1.60)
key source code:
protected void checkHandlesTypes(JavaClass javaClass,
Map<String,JavaClassCacheEntry> javaClassCache) {
// ....
String className = javaClass.getClassName();
Class<?> clazz = null;
if (handlesTypesNonAnnotations) {
populateJavaClassCache(className, javaClass, javaClassCache);
JavaClassCacheEntry entry = javaClassCache.get(className);
if (entry.getSciSet() == null) {
try {
// soe throws because of this code(recursive call)
populateSCIsForCacheEntry(entry, javaClassCache);
} catch (StackOverflowError soe) {
throw new IllegalStateException(sm.getString(
"contextConfig.annotationsStackOverflow",
context.getName(),
classHierarchyToString(className, entry, javaClassCache)));
}
}
}
}
private void populateJavaClassCache(String className, JavaClass javaClass,
Map<String,JavaClassCacheEntry> javaClassCache) {
if (javaClassCache.containsKey(className)) {
return;
}
// Add this class to the cache
javaClassCache.put(className, new JavaClassCacheEntry(javaClass));
populateJavaClassCache(javaClass.getSuperclassName(), javaClassCache);
for (String interfaceName : javaClass.getInterfaceNames()) {
populateJavaClassCache(interfaceName, javaClassCache);
}
}
private void populateJavaClassCache(String className,
Map<String,JavaClassCacheEntry> javaClassCache) {
if (!javaClassCache.containsKey(className)) {
String name = className.replace('.', '/') + ".class";
try (InputStream is = context.getLoader().getClassLoader().getResourceAsStream(name)) {
if (is == null) {
return;
}
ClassParser parser = new ClassParser(is);
JavaClass clazz = parser.parse();
populateJavaClassCache(clazz.getClassName(), clazz, javaClassCache);
} catch (ClassFormatException e) {
log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
className), e);
} catch (IOException e) {
log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
className), e);
}
}
}
---------------edits--------------
I’ve known how to fix the error,my point is how the recursive call happen in populateSCIsForCacheEntry
method.
I read the populateJavaClassCache
method,after the org.bouncycastle:bcprov-jdk15:1.45 jar parsed,the javaCacheMap
contains:
"org.bouncycastle.asn1.ASN1Boolean" -> classEntry["org.bouncycastle.asn1. DERBoolean","interface stuff"],
"org.bouncycastle.asn1.DERBoolean" -> classEntry["Java.lang.Object","interface stuff"],
and when org.bouncycastle:bcprov-jdk15on:1.60 jar is parsing,keys for class org.bouncycastle.asn1.ASN1Boolean
and org.bouncycastle.asn1.DERBoolean
won't be put into javaCacheMap
again because of map's contains method check.
so the javaCacheMap
does not contain the cyclic dependencies below:
"org.bouncycastle.asn1.ASN1Boolean" -> classEntry["org.bouncycastle.asn1. DERBoolean","interface stuff"],
"org.bouncycastle.asn1.DERBoolean" -> classEntry["org.bouncycastle.asn1.ASN1Boolean","interface stuff"],
and how does the recursive call happen?