awslabs / awslabs/aws-crt-java

AwsEventLoop always uses the system classloader

Open
#847 2 comments 0 reactions 0 assignees View on GitHub
bug investigating p2
Dominant language
Java
Stars
76
Forks
46
PR merge metrics
No merged PRs in 30d

Description

### Describe the bug

The `AwsEventLoop` thread uses the system class loader, which causes a `ClassNotFoundException` for custom `javax.xml.stream.XMLInputFactory` implementations that should be loaded by a child class loader. This issue becomes apparent when running a CRT client inside a Tomcat environment with the `-Djavax.xml.stream.XMLInputFactory` system property pointing to an implementation that is loaded by the custom child class loader instead of the system class loader.

### Regression Issue

- [ ] Select this option if this issue appears to be a regression.

### Expected Behavior

`AwsEventLoop` should use the context class loader from which it was spawned instead of defaulting to a null value, which automatically defaults to the system class loader.

### Current Behavior

Exception in thread "main" java.util.concurrent.ExecutionException: software.amazon.awssdk.core.exception.SdkClientException: Unable to execute HTTP request: Provider com.example.CustomInputFactory not found
at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396)
at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2073)
at org.example.App.aws(App.java:30)
at org.example.App.main(App.java:25)
Caused by: software.amazon.awssdk.core.exception.SdkClientException: Unable to execute HTTP request: Provider com.example.CustomInputFactory not found
at software.amazon.awssdk.core.exception.SdkClientException$BuilderImpl.build(SdkClientException.java:111)
at software.amazon.awssdk.core.exception.SdkClientException.create(SdkClientException.java:47)
at software.amazon.awssdk.core.internal.http.pipeline.stages.utils.RetryableStageHelper2.setLastException(RetryableStageHelper2.java:226)
at software.amazon.awssdk.core.internal.http.pipeline.stages.utils.RetryableStageHelper2.setLastException(RetryableStageHelper2.java:221)
at software.amazon.awssdk.core.internal.http.pipeline.stages.AsyncRetryableStage2$RetryingExecutor.maybeRetryExecute(AsyncRetryableStage2.java:151)
at software.amazon.awssdk.core.internal.http.pipeline.stages.AsyncRetryableStage2$RetryingExecutor.lambda$attemptExecute$1(AsyncRetryableStage2.java:113)
at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863)
at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2194)
at software.amazon.awssdk.utils.CompletableFutureUtils.lambda$forwardExceptionTo$0(CompletableFutureUtils.java:78)
at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863)
at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2194)
at software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage.lambda$execute$0(MakeAsyncHttpRequestStage.java:108)
at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863)
at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2194)
at software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage.completeResponseFuture(MakeAsyncHttpRequestStage.java:255)
at software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage.lambda$executeHttpRequest$3(MakeAsyncHttpRequestStage.java:167)
at java.base/java.util.concurrent.CompletableFuture.uniHandle(CompletableFuture.java:934)
at java.base/java.util.concurrent.CompletableFuture$UniHandle.tryFire(CompletableFuture.java:911)
at java.base/java.util.concurrent.CompletableFuture$Completion.run(CompletableFuture.java:482)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: javax.xml.stream.FactoryConfigurationError: Provider com.example.CustomInputFactory not found
at java.xml/javax.xml.stream.FactoryFinder.newInstance(FactoryFinder.java:184)
at java.xml/javax.xml.stream.FactoryFinder.newInstance(FactoryFinder.java:135)
at java.xml/javax.xml.stream.FactoryFinder.find(FactoryFinder.java:248)
at java.xml/javax.xml.stream.FactoryFinder.find(FactoryFinder.java:210)
at java.xml/javax.xml.stream.XMLInputFactory.newInstance(XMLInputFactory.java:166)
at software.amazon.awssdk.protocols.query.unmarshall.XmlDomParser.createXmlInputFactory(XmlDomParser.java:131)
at java.base/java.lang.ThreadLocal$SuppliedThreadLocal.initialValue(ThreadLocal.java:357)
at java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:225)
at java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
at java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
at software.amazon.awssdk.protocols.query.unmarshall.XmlDomParser.parse(XmlDomParser.java:51)
at software.amazon.awssdk.protocols.xml.internal.unmarshall.XmlResponseParserUtils.parse(XmlResponseParserUtils.java:65)
at software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlPredicatedResponseHandler.parseResponse(AwsXmlPredicatedResponseHandler.java:116)
at software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlPredicatedResponseHandler.handleResponse(AwsXmlPredicatedResponseHandler.java:96)
at software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlPredicatedResponseHandler.handle(AwsXmlPredicatedResponseHandler.java:85)
at software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlPredicatedResponseHandler.handle(AwsXmlPredicatedResponseHandler.java:43)
at software.amazon.awssdk.core.internal.handler.BaseClientHandler.lambda$successTransformationResponseHandler$7(BaseClientHandler.java:279)
at software.amazon.awssdk.core.internal.http.async.AsyncResponseHandler.lambda$prepare$0(AsyncResponseHandler.java:92)
at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1150)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2179)
at software.amazon.awssdk.core.internal.http.async.AsyncResponseHandler$BaosSubscriber.onComplete(AsyncResponseHandler.java:135)
at software.amazon.awssdk.core.internal.metrics.BytesReadTrackingPublisher$BytesReadTracker.onComplete(BytesReadTrackingPublisher.java:74)
at software.amazon.awssdk.utils.async.SimplePublisher.doProcessQueue(SimplePublisher.java:275)
at software.amazon.awssdk.utils.async.SimplePublisher.processEventQueue(SimplePublisher.java:224)
at software.amazon.awssdk.utils.async.SimplePublisher.complete(SimplePublisher.java:157)
at software.amazon.awssdk.services.s3.internal.crt.S3CrtResponseHandlerAdapter.onSuccessfulResponseComplete(S3CrtResponseHandlerAdapter.java:159)
at software.amazon.awssdk.services.s3.internal.crt.S3CrtResponseHandlerAdapter.onFinished(S3CrtResponseHandlerAdapter.java:154)
at software.amazon.awssdk.crt.s3.S3MetaRequestResponseHandlerNativeAdapter.onFinished(S3MetaRequestResponseHandlerNativeAdapter.java:25)
Caused by: java.lang.ClassNotFoundException: com/example/CustomInputFactory
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:534)
at java.base/java.lang.Class.forName(Class.java:513)
at java.xml/javax.xml.stream.FactoryFinder.getProviderClass(FactoryFinder.java:108)
at java.xml/javax.xml.stream.FactoryFinder.newInstance(FactoryFinder.java:173)
... 28 more

### Reproduction Steps
https://github.com/justinas-dabravolskas/aws-crt-classloader-reproduction/blob/main/app/src/main/java/org/example/App.java
This Gradle project simulates the `ClassNotFoundException` that occurs when running a CRT client within a Tomcat server, where the `XmlInputFactory` is loaded by the Tomcat class loader instead of the default system class loader.

To run the project, update the bucket and credentials in the `build.gradle` file.

### Possible Solution

`AwsEventLoop` should take into account parent thread class loader.

### Additional Information/Context

CRT clients should support a custom, system-wide `XMLInputFactory`, as it is common practice to enhance the performance and security of XML processing.

### aws-crt-java version used

0.33.1

### Java version used

21

### Operating System and version

MacOS,Linux

Contributor guide

Open the contributing guide

Research direction

Start with AwsEventLoop and run the linked aws-crt-classloader-reproduction project after configuring its bucket and credentials in build.gradle. Trace how the event-loop thread gets its class loader and verify the reproduction involving a child-loaded XMLInputFactory. Done means the custom provider loads successfully in the Tomcat-like setup without the reported ClassNotFoundException.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.