mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-22 22:19:03 +00:00
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
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core;
|
||||
|
||||
import io.micrometer.context.ContextRegistry;
|
||||
import io.micrometer.context.ContextSnapshot;
|
||||
import io.micrometer.context.ContextSnapshotFactory;
|
||||
import kotlin.coroutines.AbstractCoroutineContextElement;
|
||||
import kotlin.coroutines.CoroutineContext;
|
||||
import kotlinx.coroutines.ThreadContextElement;
|
||||
import kotlinx.coroutines.reactor.ReactorContext;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import reactor.util.context.ContextView;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link ThreadContextElement} that ensures that contexts registered with the
|
||||
* Micrometer Context Propagation library are captured and restored when
|
||||
* a coroutine is resumed on a thread. This is typically being used for
|
||||
* Micrometer Tracing support in Kotlin suspended functions.
|
||||
*
|
||||
* <p>It requires the {@code io.micrometer:context-propagation} library. If the
|
||||
* {@code org.jetbrains.kotlinx:kotlinx-coroutines-reactor} dependency is also
|
||||
* on the classpath, this element also supports Reactor {@code Context}.
|
||||
*
|
||||
* <p>{@code PropagationContextElement} can be used like this:
|
||||
*
|
||||
* <pre class="code">
|
||||
* fun main() {
|
||||
* runBlocking(Dispatchers.IO + PropagationContextElement()) {
|
||||
* suspendingFunction()
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* suspend fun suspendingFunction() {
|
||||
* delay(1)
|
||||
* logger.info("Log statement with traceId")
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Sebastien Deleuze
|
||||
* @since 7.0
|
||||
*/
|
||||
public final class PropagationContextElement extends AbstractCoroutineContextElement implements ThreadContextElement<ContextSnapshot.Scope> {
|
||||
|
||||
/**
|
||||
* {@code PropagationContextElement} key.
|
||||
*/
|
||||
public static final Key Key = new Key();
|
||||
|
||||
private static final ContextSnapshotFactory contextSnapshotFactory = ContextSnapshotFactory.builder()
|
||||
.contextRegistry(ContextRegistry.getInstance()).build();
|
||||
|
||||
private static final boolean coroutinesReactorPresent = ClassUtils.isPresent("kotlinx.coroutines.reactor.ReactorContext",
|
||||
PropagationContextElement.class.getClassLoader());
|
||||
|
||||
private final ContextSnapshot threadLocalContextSnapshot;
|
||||
|
||||
|
||||
public PropagationContextElement() {
|
||||
super(Key);
|
||||
this.threadLocalContextSnapshot = contextSnapshotFactory.captureAll();
|
||||
}
|
||||
|
||||
public void restoreThreadContext(CoroutineContext context, ContextSnapshot.Scope oldState) {
|
||||
oldState.close();
|
||||
}
|
||||
|
||||
public ContextSnapshot.Scope updateThreadContext(CoroutineContext context) {
|
||||
ContextSnapshot contextSnapshot;
|
||||
if (coroutinesReactorPresent) {
|
||||
contextSnapshot = ReactorDelegate.captureFrom(context);
|
||||
if (contextSnapshot == null) {
|
||||
contextSnapshot = this.threadLocalContextSnapshot;
|
||||
}
|
||||
}
|
||||
else {
|
||||
contextSnapshot = this.threadLocalContextSnapshot;
|
||||
}
|
||||
return contextSnapshot.setThreadLocals();
|
||||
}
|
||||
|
||||
public static final class Key implements CoroutineContext.Key<PropagationContextElement> {
|
||||
}
|
||||
|
||||
private static final class ReactorDelegate {
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public static ContextSnapshot captureFrom(CoroutineContext context) {
|
||||
ReactorContext reactorContext = (ReactorContext)context.get((CoroutineContext.Key)ReactorContext.Key);
|
||||
ContextView contextView = reactorContext != null ? reactorContext.getContext() : null;
|
||||
if (contextView != null) {
|
||||
return contextSnapshotFactory.captureFrom(contextView);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core
|
||||
|
||||
import io.micrometer.context.ContextRegistry
|
||||
import io.micrometer.context.ContextSnapshot
|
||||
import io.micrometer.context.ContextSnapshotFactory
|
||||
import kotlinx.coroutines.ThreadContextElement
|
||||
import kotlinx.coroutines.reactor.ReactorContext
|
||||
import org.springframework.util.ClassUtils
|
||||
import reactor.util.context.ContextView
|
||||
import kotlin.coroutines.AbstractCoroutineContextElement
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
|
||||
/**
|
||||
* [ThreadContextElement] that ensures that contexts registered with the
|
||||
* Micrometer Context Propagation library are captured and restored when
|
||||
* a coroutine is resumed on a thread. This is typically being used for
|
||||
* Micrometer Tracing support in Kotlin suspended functions.
|
||||
*
|
||||
* It requires the `io.micrometer:context-propagation` library. If the
|
||||
* `org.jetbrains.kotlinx:kotlinx-coroutines-reactor` dependency is also
|
||||
* on the classpath, this element also supports Reactor `Context`.
|
||||
*
|
||||
* `PropagationContextElement` can be used like this:
|
||||
*
|
||||
* ```kotlin
|
||||
* fun main() {
|
||||
* runBlocking(Dispatchers.IO + PropagationContextElement()) {
|
||||
* suspendingFunction()
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* suspend fun suspendingFunction() {
|
||||
* delay(1)
|
||||
* logger.info("Log statement with traceId")
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Sebastien Deleuze
|
||||
* @since 7.0
|
||||
*/
|
||||
class PropagationContextElement : ThreadContextElement<ContextSnapshot.Scope>,
|
||||
AbstractCoroutineContextElement(Key) {
|
||||
|
||||
companion object Key : CoroutineContext.Key<PropagationContextElement> {
|
||||
|
||||
private val contextSnapshotFactory =
|
||||
ContextSnapshotFactory.builder().contextRegistry(ContextRegistry.getInstance()).build()
|
||||
|
||||
private val coroutinesReactorPresent =
|
||||
ClassUtils.isPresent("kotlinx.coroutines.reactor.ReactorContext",
|
||||
PropagationContextElement::class.java.classLoader);
|
||||
}
|
||||
|
||||
// Context captured from the the ThreadLocal where the PropagationContextElement is instantiated
|
||||
private val threadLocalContextSnapshot: ContextSnapshot = contextSnapshotFactory.captureAll()
|
||||
|
||||
override fun restoreThreadContext(context: CoroutineContext, oldState: ContextSnapshot.Scope) {
|
||||
oldState.close()
|
||||
}
|
||||
|
||||
override fun updateThreadContext(context: CoroutineContext): ContextSnapshot.Scope {
|
||||
val contextSnapshot = if (coroutinesReactorPresent) {
|
||||
ReactorDelegate().captureFrom(context) ?: threadLocalContextSnapshot
|
||||
} else {
|
||||
threadLocalContextSnapshot
|
||||
}
|
||||
return contextSnapshot.setThreadLocals()
|
||||
}
|
||||
|
||||
private class ReactorDelegate {
|
||||
|
||||
fun captureFrom(context: CoroutineContext): ContextSnapshot? {
|
||||
val contextView: ContextView? = context[ReactorContext]?.context
|
||||
if (contextView != null) {
|
||||
return contextSnapshotFactory.captureFrom(contextView)
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user