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}