001package com.streamconverter.command.rule;
002
003import com.streamconverter.context.ExecutionContext;
004import java.util.Objects;
005
006/**
007 * MDC設定用のルール
008 *
009 * <p>XMLやJSONなどから抽出した値を、ExecutionContextの共有コンテキストに設定します。
010 * 共有コンテキストに設定された値は、全ての並列実行中のコマンドから即座にアクセス可能になり、 applyToMDC()呼び出し時にMDCに反映されます。
011 *
012 * <p><b>使用例:</b>
013 *
014 * <pre>{@code
015 * // ExecutionContextを作成
016 * ExecutionContext context = ExecutionContext.create();
017 *
018 * // MdcSetupRuleを使用してXMLからuserIdを抽出してMDCに設定
019 * IStreamCommand xmlNavigate = new XmlNavigateCommand(
020 *     TreePath.of("request", "userId"),
021 *     new MdcSetupRule(context, "userId")
022 * );
023 *
024 * // パイプライン実行
025 * StreamConverter.createWithContext(context, xmlNavigate, otherCommands...)
026 *     .run(inputStream, outputStream);
027 *
028 * // 全てのコマンドのログに [userId:USER12345] が出力される
029 * }</pre>
030 *
031 * <p><b>スレッドセーフ性:</b> このルールはスレッドセーフです。複数のスレッドから同時に呼び出されても、
032 * ExecutionContextの共有コンテキスト(ConcurrentHashMap)により安全に動作します。
033 */
034public class MdcSetupRule implements IRule {
035
036  private final ExecutionContext context;
037  private final String mdcKey;
038
039  /**
040   * MdcSetupRuleを作成します
041   *
042   * @param context ExecutionContextインスタンス
043   * @param mdcKey MDCキー名(共有コンテキストのキーとして使用される)
044   * @throws NullPointerException contextまたはmdcKeyがnullの場合
045   */
046  public MdcSetupRule(ExecutionContext context, String mdcKey) {
047    this.context = Objects.requireNonNull(context, "context cannot be null");
048    this.mdcKey = Objects.requireNonNull(mdcKey, "mdcKey cannot be null");
049  }
050
051  /**
052   * 抽出された値を共有コンテキストに設定します
053   *
054   * <p>このメソッドは値を変更せずそのまま返しますが、副作用として ExecutionContextの共有コンテキストに値を設定します。 MDCへの同期はLogback
055   * TurboFilterが自動的に行うため、呼び出し側は同期を意識する必要がありません。
056   *
057   * @param extractedValue 抽出された値(nullの場合は共有コンテキストから削除)
058   * @return 入力値をそのまま返す
059   */
060  @Override
061  public String apply(String extractedValue) {
062    context.setSharedContext(mdcKey, extractedValue);
063    return extractedValue;
064  }
065
066  /**
067   * このルールが使用するMDCキー名を取得します
068   *
069   * @return MDCキー名
070   */
071  public String getMdcKey() {
072    return mdcKey;
073  }
074
075  @Override
076  public String toString() {
077    return String.format("MdcSetupRule{mdcKey='%s'}", mdcKey);
078  }
079}