001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.util.concurrent;
018
019import java.util.NoSuchElementException;
020import java.util.function.Supplier;
021
022/**
023 * A context value abstraction that provides thread-scoped data sharing.
024 * <p>
025 * This interface provides a unified API for sharing data within a thread context, with implementations that use either
026 * {@link ThreadLocal} (for JDK 17+) or ScopedValue (for JDK 25+ with virtual threads).
027 * <p>
028 * The implementation is chosen automatically based on the JDK version and whether virtual threads are enabled via the
029 * {@code camel.threads.virtual.enabled} system property.
030 * <p>
031 * <b>Usage patterns:</b>
032 * <ul>
033 * <li><b>Read-only context passing:</b> Use {@link #where(ContextValue, Object, Runnable)} to bind a value for the
034 * duration of a code block</li>
035 * <li><b>Mutable state:</b> Use {@link #newThreadLocal(String)} for state that needs to be modified after
036 * initialization</li>
037 * </ul>
038 * <p>
039 * <b>Important:</b> When using {@link #newThreadLocal(String, Supplier)}, the values should be <b>lightweight
040 * objects</b>. Heavy objects stored in ThreadLocal can lead to memory leaks (if threads are pooled) and increased
041 * memory consumption (one instance per thread). Consider whether the object truly needs per-thread state, or if it can
042 * be shared or passed as a parameter instead.
043 * <p>
044 * <b>Example:</b>
045 *
046 * <pre>{@code
047 * private static final ContextValue<String> ROUTE_ID = ContextValue.newInstance("routeId");
048 *
049 * // Bind a value for a scope
050 * ContextValue.where(ROUTE_ID, "myRoute", () -> {
051 *     // Code here can access ROUTE_ID.get()
052 *     processRoute();
053 * });
054 * }</pre>
055 *
056 * @param <T> the type of value stored in this context
057 * @see       java.lang.ThreadLocal
058 * @see       ScopedValue (JDK 25+)
059 */
060public interface ContextValue<T> {
061
062    /**
063     * Returns the value of this context variable for the current thread.
064     * <p>
065     * For ScopedValue-based implementations (JDK 21+), this will throw {@link NoSuchElementException} if called outside
066     * a binding scope. For ThreadLocal-based implementations, this returns the value set via {@link #set(Object)} or
067     * {@code null} if not set.
068     *
069     * @return                        the current value
070     * @throws NoSuchElementException if no value is bound (ScopedValue implementation only)
071     */
072    T get();
073
074    /**
075     * Returns the value of this context variable for the current thread, or the given default value if no value is
076     * bound.
077     *
078     * @param  defaultValue the value to return if no value is bound
079     * @return              the current value, or {@code defaultValue} if not bound
080     */
081    T orElse(T defaultValue);
082
083    /**
084     * Returns whether a value is currently bound for this context variable.
085     *
086     * @return {@code true} if a value is bound, {@code false} otherwise
087     */
088    boolean isBound();
089
090    /**
091     * Sets the value for this context variable (ThreadLocal-based implementations only).
092     * <p>
093     * This method is only supported by ThreadLocal-based implementations. For ScopedValue-based implementations, use
094     * {@link #where(ContextValue, Object, Runnable)} instead.
095     *
096     * @param  value                         the value to set
097     * @throws UnsupportedOperationException if called on a ScopedValue-based implementation
098     */
099    void set(T value);
100
101    /**
102     * Removes the value for this context variable (ThreadLocal-based implementations only).
103     * <p>
104     * This method is only supported by ThreadLocal-based implementations.
105     *
106     * @throws UnsupportedOperationException if called on a ScopedValue-based implementation
107     */
108    void remove();
109
110    /**
111     * Returns the name of this context value (for debugging purposes).
112     *
113     * @return the name
114     */
115    String name();
116
117    /**
118     * Creates a new context value with the given name.
119     * <p>
120     * The implementation will use ScopedValue on JDK 21+ when virtual threads are enabled, otherwise it will use
121     * ThreadLocal.
122     *
123     * @param  <T>  the type of value
124     * @param  name the name for debugging purposes
125     * @return      a new context value
126     */
127    static <T> ContextValue<T> newInstance(String name) {
128        return ContextValueFactory.newInstance(name);
129    }
130
131    /**
132     * Creates a new ThreadLocal-based context value with the given name.
133     * <p>
134     * This always uses ThreadLocal, regardless of JDK version or virtual thread settings. Use this when you need
135     * mutable state that can be modified after initialization.
136     *
137     * @param  <T>  the type of value
138     * @param  name the name for debugging purposes
139     * @return      a new ThreadLocal-based context value
140     */
141    static <T> ContextValue<T> newThreadLocal(String name) {
142        return ContextValueFactory.newThreadLocal(name);
143    }
144
145    /**
146     * Creates a new ThreadLocal-based context value with the given name and initial value supplier.
147     * <p>
148     * This always uses ThreadLocal regardless of JDK version or virtual thread settings. The supplier is called to
149     * provide the initial value when {@link #get()} is called and no value has been set.
150     *
151     * @param  <T>      the type of value
152     * @param  name     the name for debugging purposes
153     * @param  supplier the supplier for the initial value
154     * @return          a new ThreadLocal-based context value with initial value support
155     */
156    static <T> ContextValue<T> newThreadLocal(String name, Supplier<T> supplier) {
157        return ContextValueFactory.newThreadLocal(name, supplier);
158    }
159
160    /**
161     * Executes the given operation with the context value bound to the specified value.
162     * <p>
163     * The binding is only visible to the current thread and threads created within the operation (for ScopedValue
164     * implementations).
165     *
166     * @param  <T>       the type of value
167     * @param  <R>       the return type
168     * @param  key       the context value to bind
169     * @param  value     the value to bind
170     * @param  operation the operation to execute
171     * @return           the result of the operation
172     */
173    static <T, R> R where(ContextValue<T> key, T value, Supplier<R> operation) {
174        return ContextValueFactory.where(key, value, operation);
175    }
176
177    /**
178     * Executes the given operation with the context value bound to the specified value.
179     *
180     * @param <T>       the type of value
181     * @param key       the context value to bind
182     * @param value     the value to bind
183     * @param operation the operation to execute
184     */
185    static <T> void where(ContextValue<T> key, T value, Runnable operation) {
186        ContextValueFactory.where(key, value, operation);
187    }
188}