001/*
002 * SPDX-License-Identifier: Apache-2.0
003 *
004 * Copyright 2024-2026 The Enola <https://enola.dev> Authors
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *     https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package dev.enola.common.context;
019
020import org.jspecify.annotations.Nullable;
021
022import java.util.concurrent.ThreadFactory;
023
024public final class ContextAwareThreadFactory implements ThreadFactory {
025
026    private final ThreadFactory delegate;
027
028    public ContextAwareThreadFactory() {
029        this(java.util.concurrent.Executors.defaultThreadFactory());
030    }
031
032    public ContextAwareThreadFactory(ThreadFactory delegate) {
033        this.delegate = delegate;
034    }
035
036    @Override
037    public Thread newThread(Runnable runnable) {
038        Context currentTLC = TLC.get();
039        // TODO rm: if (currentTLC == null) throw new IllegalStateException("No TLC found?!");
040        Thread thread = delegate.newThread(new RunnableWrapper(runnable, currentTLC));
041        return thread;
042    }
043
044    private static class RunnableWrapper implements Runnable {
045        private final Runnable delegate;
046        private final @Nullable Context outerContext;
047
048        public RunnableWrapper(Runnable delegate, @Nullable Context outerContext) {
049            this.delegate = delegate;
050            this.outerContext = outerContext;
051        }
052
053        @Override
054        public void run() {
055            Context currentTLC = TLC.get();
056            if (currentTLC != null)
057                throw new IllegalStateException(
058                        "New Thread should not have a TLC already?! " + currentTLC);
059            if (outerContext != null) TLC.setThreadLocalContext(outerContext);
060            delegate.run();
061        }
062    }
063}