Skip to content

Commit baaddae

Browse files
committed
Convert PropagationContextElement to a Java class
In order to avoid having Java types depending on Kotlin types which breaks the compilation in Eclipse IDE. Closes gh-35661
1 parent 6d82458 commit baaddae

2 files changed

Lines changed: 117 additions & 98 deletions

File tree

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright 2002-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core;
18+
19+
import io.micrometer.context.ContextRegistry;
20+
import io.micrometer.context.ContextSnapshot;
21+
import io.micrometer.context.ContextSnapshotFactory;
22+
import kotlin.coroutines.AbstractCoroutineContextElement;
23+
import kotlin.coroutines.CoroutineContext;
24+
import kotlinx.coroutines.ThreadContextElement;
25+
import kotlinx.coroutines.reactor.ReactorContext;
26+
import org.jspecify.annotations.Nullable;
27+
import reactor.util.context.ContextView;
28+
29+
import org.springframework.util.ClassUtils;
30+
31+
/**
32+
* {@link ThreadContextElement} that ensures that contexts registered with the
33+
* Micrometer Context Propagation library are captured and restored when
34+
* a coroutine is resumed on a thread. This is typically being used for
35+
* Micrometer Tracing support in Kotlin suspended functions.
36+
*
37+
* <p>It requires the {@code io.micrometer:context-propagation} library. If the
38+
* {@code org.jetbrains.kotlinx:kotlinx-coroutines-reactor} dependency is also
39+
* on the classpath, this element also supports Reactor {@code Context}.
40+
*
41+
* <p>{@code PropagationContextElement} can be used like this:
42+
*
43+
* <pre class="code">
44+
* fun main() {
45+
* runBlocking(Dispatchers.IO + PropagationContextElement()) {
46+
* suspendingFunction()
47+
* }
48+
* }
49+
*
50+
* suspend fun suspendingFunction() {
51+
* delay(1)
52+
* logger.info("Log statement with traceId")
53+
* }
54+
* </pre>
55+
*
56+
* @author Brian Clozel
57+
* @author Sebastien Deleuze
58+
* @since 7.0
59+
*/
60+
public final class PropagationContextElement extends AbstractCoroutineContextElement implements ThreadContextElement<ContextSnapshot.Scope> {
61+
62+
/**
63+
* {@code PropagationContextElement} key.
64+
*/
65+
public static final Key Key = new Key();
66+
67+
private static final ContextSnapshotFactory contextSnapshotFactory = ContextSnapshotFactory.builder()
68+
.contextRegistry(ContextRegistry.getInstance()).build();
69+
70+
private static final boolean coroutinesReactorPresent = ClassUtils.isPresent("kotlinx.coroutines.reactor.ReactorContext",
71+
PropagationContextElement.class.getClassLoader());
72+
73+
private final ContextSnapshot threadLocalContextSnapshot;
74+
75+
76+
public PropagationContextElement() {
77+
super(Key);
78+
this.threadLocalContextSnapshot = contextSnapshotFactory.captureAll();
79+
}
80+
81+
public void restoreThreadContext(CoroutineContext context, ContextSnapshot.Scope oldState) {
82+
oldState.close();
83+
}
84+
85+
public ContextSnapshot.Scope updateThreadContext(CoroutineContext context) {
86+
ContextSnapshot contextSnapshot;
87+
if (coroutinesReactorPresent) {
88+
contextSnapshot = ReactorDelegate.captureFrom(context);
89+
if (contextSnapshot == null) {
90+
contextSnapshot = this.threadLocalContextSnapshot;
91+
}
92+
}
93+
else {
94+
contextSnapshot = this.threadLocalContextSnapshot;
95+
}
96+
return contextSnapshot.setThreadLocals();
97+
}
98+
99+
public static final class Key implements CoroutineContext.Key<PropagationContextElement> {
100+
}
101+
102+
private static final class ReactorDelegate {
103+
104+
@Nullable
105+
@SuppressWarnings({"unchecked", "rawtypes"})
106+
public static ContextSnapshot captureFrom(CoroutineContext context) {
107+
ReactorContext reactorContext = (ReactorContext)context.get((CoroutineContext.Key)ReactorContext.Key);
108+
ContextView contextView = reactorContext != null ? reactorContext.getContext() : null;
109+
if (contextView != null) {
110+
return contextSnapshotFactory.captureFrom(contextView);
111+
}
112+
else {
113+
return null;
114+
}
115+
}
116+
}
117+
}

spring-core/src/main/kotlin/org/springframework/core/PropagationContextElement.kt

Lines changed: 0 additions & 98 deletions
This file was deleted.

0 commit comments

Comments
 (0)