001/* 002 * JDrupes Builder 003 * Copyright (C) 2026 Michael N. Lipp 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <https://www.gnu.org/licenses/>. 017 */ 018 019package org.jdrupes.builder.core; 020 021import java.util.ArrayList; 022import java.util.List; 023import java.util.concurrent.Callable; 024import java.util.concurrent.CopyOnWriteArrayList; 025import java.util.concurrent.ExecutorService; 026import java.util.concurrent.Future; 027 028/// Supports inheriting scoped values in a task submitted to an 029/// executor service. 030/// 031public final class ScopedValueInheritance { 032 033 private static List<ScopedValue<?>> registry = new CopyOnWriteArrayList<>(); 034 035 private ScopedValueInheritance() { 036 // Make javadoc happy 037 } 038 039 /// Adds the value to the registry. 040 /// 041 /// @param value the value 042 /// 043 public static void add(ScopedValue<?> value) { 044 registry.add(value); 045 } 046 047 /// Executes the task with the registered scoped values inherited 048 /// from the current thread. 049 /// 050 /// @param <T> the generic type 051 /// @param executor the executor 052 /// @param task the task 053 /// @return the future 054 /// 055 @SuppressWarnings("unchecked") 056 public static <T> Future<T> submitTo(ExecutorService executor, 057 Callable<T> task) { 058 // Capture values 059 final var scoped = new ArrayList<>(registry); 060 final var values = new ArrayList<>(scoped.size()); 061 for (var scopedVar : scoped) { 062 values.add(scopedVar.isBound() ? scopedVar.get() : null); 063 } 064 065 // Restore values in submitted task 066 return executor.submit(() -> { 067 var scopedIterator = scoped.iterator(); 068 var valuesIterator = values.iterator(); 069 ScopedValue.Carrier carriers = null; 070 if (scopedIterator.hasNext()) { 071 carriers = ScopedValue.where( 072 (ScopedValue<Object>) scopedIterator.next(), 073 valuesIterator.next()); 074 } 075 while (scopedIterator.hasNext()) { 076 carriers = carriers.where( 077 (ScopedValue<Object>) scopedIterator.next(), 078 valuesIterator.next()); 079 } 080 if (carriers == null) { 081 return task.call(); 082 } 083 return carriers.call(task::call); 084 }); 085 } 086}