001package com.streamconverter.context; 002 003import java.util.Collections; 004import java.util.Map; 005import java.util.concurrent.ConcurrentHashMap; 006import org.slf4j.MDC; 007 008/** 009 * パイプライン内でコマンド間の共有値を管理するコンテキスト。 010 * 011 * <p>パイプライン実行中に、あるコマンドが抽出した値(例: XMLヘッダのorderId)を 他のコマンドのログ出力に自動反映するための仕組みを提供する。 012 * 013 * <p>共有値は {@link com.streamconverter.logging.PipelineContextTurboFilter} により、 014 * ログ出力直前にMDCへ自動的にマージされる。 015 * 016 * <p>使用例: 017 * 018 * <pre>{@code 019 * // コマンド内でストリームデータから値を抽出してMDCに伝搬 020 * PipelineContext.putShared("orderId", extractedOrderId); 021 * 022 * // 他のコマンドのログ出力時に自動的にMDCに反映される 023 * }</pre> 024 * 025 * <p><b>MDC 関連クラスの全体像:</b> 026 * 027 * <ul> 028 * <li>{@link com.streamconverter.command.rule.MdcPropagatingRule} — ストリームから抽出した値を このコンテキスト経由で MDC 029 * に伝搬する Rule 実装。コマンドから MDC へ値を書き込む際の推奨手段。 030 * <li>{@link com.streamconverter.logging.PipelineContextTurboFilter} — ログ出力直前に {@link 031 * #syncToMDC()} を呼び出し、共有値を MDC へ自動反映する Logback TurboFilter。 032 * <li>{@link com.streamconverter.logging.MDCInitializer} — {@code InheritableMDCAdapter} を 033 * インストールし、MDC コンテキストを子スレッドへ自動継承させる。アプリ起動時に一度呼ぶ。 034 * </ul> 035 */ 036public final class PipelineContext { 037 038 /** パイプラインコンテキストを新規作成する。 */ 039 public PipelineContext() {} 040 041 private static final ThreadLocal<PipelineContext> HOLDER = new ThreadLocal<>(); 042 043 private final ConcurrentHashMap<String, String> sharedValues = new ConcurrentHashMap<>(); 044 045 /** 046 * 共有値を設定し、呼び出しスレッドのMDCにも即座に反映する。 047 * 048 * <p>PipelineContextが未設定のスレッドから呼ばれた場合は何もしない。 049 * 050 * @param key MDCキー名 051 * @param value 値。nullの場合はキーを削除する 052 * @throws IllegalArgumentException keyがnullの場合 053 */ 054 public static void putShared(String key, String value) { 055 if (key == null) { 056 throw new IllegalArgumentException("key must not be null"); 057 } 058 PipelineContext ctx = HOLDER.get(); 059 if (ctx == null) { 060 return; 061 } 062 if (value != null) { 063 ctx.sharedValues.put(key, value); 064 MDC.put(key, value); 065 } else { 066 ctx.sharedValues.remove(key); 067 MDC.remove(key); 068 } 069 } 070 071 /** 072 * 共有値を取得する。 073 * 074 * @param key MDCキー名 075 * @return 値。キーが存在しない場合またはPipelineContext未設定の場合はnull 076 */ 077 public static String getShared(String key) { 078 PipelineContext ctx = HOLDER.get(); 079 if (ctx == null) { 080 return null; 081 } 082 return ctx.sharedValues.get(key); 083 } 084 085 /** 086 * 全共有値を呼び出しスレッドのMDCにマージする。 087 * 088 * <p>PipelineContextが未設定の場合は何もしない。 089 */ 090 public static void syncToMDC() { 091 PipelineContext ctx = HOLDER.get(); 092 if (ctx == null) { 093 return; 094 } 095 ctx.sharedValues.forEach(MDC::put); 096 } 097 098 /** 099 * 現在のスレッドに紐づくPipelineContextを取得する。 100 * 101 * @return PipelineContext、未設定の場合はnull 102 */ 103 static PipelineContext current() { 104 return HOLDER.get(); 105 } 106 107 /** 108 * 共有値のスナップショットを返す。 109 * 110 * @return 共有値の不変ビュー 111 */ 112 Map<String, String> getSharedValues() { 113 return Collections.unmodifiableMap(sharedValues); 114 } 115 116 /** 117 * 現在のスレッドにPipelineContextを設定する。 118 * 119 * <p>StreamConverterがパイプラインの各コマンドスレッドで呼び出す。 120 * 121 * @param context 設定するPipelineContext 122 */ 123 public static void set(PipelineContext context) { 124 HOLDER.set(context); 125 } 126 127 /** 現在のスレッドからPipelineContextをクリアする。 */ 128 public static void clear() { 129 HOLDER.remove(); 130 } 131}