-
Notifications
You must be signed in to change notification settings - Fork 298
Description
Issue Summary
When using Strata as a dependency in applications with complex classloader hierarchies (application servers, OSGi containers, multi-module applications), ExtendedEnum initialization fails with ClassNotFoundException when first accessed from ForkJoinPool worker threads or parallel streams.
Stack Trace
Failed to load ExtendedEnum for interface com.opengamma.strata.basics.date.DayCount:
java.lang.IllegalArgumentException: Unable to find enum provider class:
com.opengamma.strata.basics.date.StandardDayCounts
at com.opengamma.strata.collect.named.ExtendedEnum.parseProviders(ExtendedEnum.java:176)
Caused by: java.lang.ClassNotFoundException:
com.opengamma.strata.basics.date.StandardDayCounts
at org.joda.convert.RenameHandler.lookupType(RenameHandler.java:197)
at com.opengamma.strata.collect.named.ExtendedEnum.parseProviders(ExtendedEnum.java:174)
Root Cause
Current Implementation (ExtendedEnum.java:174):
cls = RenameHandler.INSTANCE.lookupType(key);RenameHandler.lookupType() uses the thread context classloader (TCCL) as its primary mechanism to load classes. When this is unavailable or incorrect, it falls back to the classloader that loaded RenameHandler itself.
Problem in Production Environments:
In complex classloader hierarchies (common in application servers, OSGi, etc.):
-
Common/Shared ClassLoader (parent)
- Contains: Joda-Convert (where
RenameHandlerlives), Joda-Beans, Guava - Does NOT contain: Strata classes
- Contains: Joda-Convert (where
-
Application ClassLoader (child)
- Contains: Strata JARs with
ExtendedEnumand provider classes likeStandardDayCounts - Can see parent classes via delegation
- Contains: Strata JARs with
-
ForkJoinPool Worker Threads
- Thread Context ClassLoader (TCCL):
nullor incorrect
- Thread Context ClassLoader (TCCL):
When ExtendedEnum (child) calls RenameHandler.lookupType() (parent):
- TCCL is
null→ skipped RenameHandler's classloader is the parent → cannot seeStandardDayCountsin child- Result:
ClassNotFoundException
Reproduction
The issue can be reproduced using a standalone Java program that simulates production classloader hierarchy (attaching it in a comment - unzip it at the project root):
cd standalone-reproducer
./run-production-sim.shBefore Fix:
Caused by: java.lang.ClassNotFoundException: com.opengamma.strata.basics.date.StandardDayCounts
at org.joda.convert.RenameHandler.lookupType(RenameHandler.java:197)
at com.opengamma.strata.collect.named.ExtendedEnum.parseProviders(ExtendedEnum.java:174)
Why Maven Tests Cannot be Used to Reproduce:
Maven's Surefire plugin uses a simple classloader hierarchy where all classes (Joda-Convert, Strata, test classes) are loaded by the same classloader. This allows RenameHandler's fallback mechanisms to succeed. The issue only manifests in production environments with parent/child classloader separation.
Proposed Solution
Replace RenameHandler.INSTANCE.lookupType() with Class.forName() using an explicit classloader:
cls = Class.forName(key, true, ExtendedEnum.class.getClassLoader());Why This Works:
- Uses the classloader that loaded
ExtendedEnum(the child/application classloader) - This classloader has visibility to all provider classes
- Thread-independent: doesn't rely on TCCL
- Follows standard Java classloading best practices
Files to Change:
ExtendedEnum.parseProviders()- line 174CombinedExtendedEnum.parseChildren()- line 97
Benefits:
- More explicit and predictable classloading
- Thread-independent (safer for concurrent usage)
- Works reliably across all deployment environments
- Follows Java best practices
Environment Details
- Affected: Production applications using Strata as a Maven dependency
- Deployment Patterns: Application servers (Tomcat, WebLogic, etc.), OSGi, multi-module applications
- Concurrency Patterns: ForkJoinPool, parallel streams, CompletableFuture
- Strata Version: All versions using current
ExtendedEnumimplementation