/*
 * Copyright 2016-2017 Talsma ICT
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package nl.talsmasoftware.context.mdc;

import nl.talsmasoftware.context.Context;
import nl.talsmasoftware.context.ContextManager;
import org.slf4j.MDC;

import java.util.Map;

/**
 * @author Sjoerd Talsma
 */
public class MdcManager implements ContextManager<Map<String, String>> {

    public Context<Map<String, String>> initializeNewContext(final Map<String, String> value) {
        final Map<String, String> previous = MDC.getCopyOfContextMap();
        setMdc(value);
        return new Context<Map<String, String>>() {
            public Map<String, String> getValue() {
                return value;
            }

            public void close() {
                setMdc(previous);
            }
        };
    }

    public Context<Map<String, String>> getActiveContext() {
        return new MdcSnapshot(MDC.getCopyOfContextMap());
    }

    private static void setMdc(Map<String, String> contextMap) {
        if (contextMap == null) MDC.clear();
        else MDC.setContextMap(contextMap);
    }

    private static class MdcSnapshot implements Context<Map<String, String>> {
        private final Map<String, String> snapshot;

        private MdcSnapshot(Map<String, String> snapshot) {
            this.snapshot = snapshot;
        }

        public Map<String, String> getValue() {
            return snapshot;
        }

        public void close() {
            // no-op for now
        }
    }

}

@@ -776,11 +776,128 @@
 ap;%0A
-%0A/*
+import java.util.concurrent.atomic.AtomicBoolean;%0A%0A/**%0A * Manager to propagate MDC content from one thread to another.%0A 
 *%0A *
@@ -1105,335 +1105,285 @@
     
-final Map%3CString, String%3E previous = MDC.getCopyOfContextMap();%0A        setMdc(value);%0A        return new Context%3CMap%3CString, String%3E%3E() %7B%0A            public Map%3CString, String%3E getValue() %7B%0A                return value;%0A            %7D%0A%0A            public void close() %7B%0A                setMdc(previous);%0A            %7D%0A        %7D
+// Capture current MDC as 'previous' and make value the new current MCC.%0A        final Map%3CString, String%3E previous = MDC.getCopyOfContextMap();%0A        if (value == null) MDC.clear();%0A        else MDC.setContextMap(value);%0A        return new MdcContext(previous, value, false)
 ;%0A  
@@ -1460,196 +1460,130 @@
     
-r
+// R
 eturn 
-new MdcSnapshot(MDC.getCopyOfContextMap());%0A    %7D%0A%0A    private static void setMdc(Map%3CString, String%3E contextMap) %7B%0A        if (contextMap == 
+fresh context that is 'already-closed' without a previous mdc.%0A        return new MdcContext(
 null
-)
+,
  MDC.
-clear();%0A        else MDC.set
+getCopyOf
 Cont
@@ -1593,18 +1593,15 @@
 Map(
-contextMap
+), true
 );%0A 
@@ -1625,16 +1625,22 @@
  static 
+final 
 class Md
@@ -1640,23 +1640,22 @@
 lass Mdc
-Snapsho
+Contex
 t implem
@@ -1732,24 +1732,75 @@
 String%3E 
-snapshot
+previous, value;%0A        private final AtomicBoolean closed
 ;%0A%0A     
@@ -1817,15 +1817,14 @@
  Mdc
-Snapsho
+Contex
 t(Ma
@@ -1845,56 +1845,184 @@
 ng%3E 
-snapshot) %7B%0A            this.snapshot = snapshot
+previous, Map%3CString, String%3E value, boolean closed) %7B%0A            this.previous = previous;%0A            this.value = value;%0A            this.closed = new AtomicBoolean(closed)
 ;%0A  
@@ -2101,16 +2101,13 @@
 urn 
-snapshot
+value
 ;%0A  
@@ -2161,40 +2161,346 @@
     
-// no-op for now%0A        %7D%0A    %7D%0A
+if (closed.compareAndSet(false, true)) %7B%0A                if (previous == null) MDC.clear();%0A                else MDC.setContextMap(previous);%0A            %7D%0A        %7D%0A%0A        @Override%0A        public String toString() %7B%0A            return closed.get() ? %22MdcContext%7Bclosed%7D%22 : %22MdcContext%22 + (value == null ? %22%7B%7D%22 : value);%0A        %7D%0A    %7D
 %0A%7D%0A

Implement close-once semantic on MdcContext.