Added logging context service, refactoring 93/27593/9
authorvempo <vitaliy.emporopulo@amdocs.com>
Mon, 8 Jan 2018 18:59:19 +0000 (20:59 +0200)
committerVitaly Emporopulo <Vitaliy.Emporopulo@amdocs.com>
Tue, 9 Jan 2018 13:41:15 +0000 (13:41 +0000)
Change-Id: Ib040d4579107b60c8da2c7a6da829f49c1cd8dd4
Issue-ID: SDC-772
Signed-off-by: vempo <vitaliy.emporopulo@amdocs.com>
31 files changed:
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/LoggerFactory.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/LoggingContext.java [new file with mode: 0644]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/ServiceBinder.java [new file with mode: 0644]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/context/TaskFactory.java [deleted file]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/provider/LoggerCreationService.java [moved from openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/LoggerCreationService.java with 86% similarity]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/provider/LoggingContextService.java [new file with mode: 0644]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/provider/LoggingServiceProvider.java [new file with mode: 0644]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/api/LoggerFactoryTest.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/api/LoggingContextTest.java [new file with mode: 0644]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/api/ServiceBinderTest.java [moved from openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/context/ContextPropagationService.java with 56% similarity]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/api/context/TaskFactoryTest.java [deleted file]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/SLF4JLoggerCreationService.java [deleted file]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/context/MDCPropagationService.java [deleted file]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/logback/EventTypeDiscriminator.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/BaseMDCCopyingWrapper.java [new file with mode: 0644]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/MDCCallableWrapper.java [new file with mode: 0644]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/MDCRunnableWrapper.java [new file with mode: 0644]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/Markers.java [moved from openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/Markers.java with 92% similarity]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/SLF4JLoggerWrapper.java [new file with mode: 0644]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/SLF4JLoggingServiceProvider.java [new file with mode: 0644]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/resources/META-INF/services/org.openecomp.sdc.logging.provider.LoggingServiceProvider [new file with mode: 0644]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/LoggerFactoryTest.java [deleted file]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/RoutingTest.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/context/MDCPropagationFactoryTest.java [deleted file]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/context/TaskFactoryTest.java [deleted file]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/logback/EventTypeDiscriminatorTest.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/BaseContextPropagationTest.java [new file with mode: 0644]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/CallableContextPropagationTest.java [new file with mode: 0644]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/LoggerFactoryTest.java [new file with mode: 0644]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/LoggingContextTest.java [new file with mode: 0644]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/RunnableContextPropagationTest.java [new file with mode: 0644]

index 1be2fa2..b790a02 100644 (file)
@@ -4,9 +4,9 @@
  * 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.
 
 package org.openecomp.sdc.logging.api;
 
+import org.openecomp.sdc.logging.provider.LoggerCreationService;
+
+import java.util.Objects;
 
 /**
  * <a>Factory to hide a concrete, framework-specific implementation of logger creation.</a>
  * <p>The service used by this factory must implement {@link LoggerCreationService}. If no
- * implementation has been configured or could not be instantiated, a <b>no-op logger</b> will be
+ * implementation has been configured or could be instantiated, a <b>no-op logger</b> will be
  * used, and <b>no events</b> will be logged. This is done to prevent recursion if attempts are
  * being made to log exceptions that resulted from logger initialization. </p>
  *
  * @author evitaliy
- * @see BaseFactory
- * @see LoggerCreationService
  * @since 13/09/2016.
+ *
+ * @see ServiceBinder
+ * @see LoggerCreationService
  */
-@SuppressWarnings("ThrowableInstanceNeverThrown")
-public class LoggerFactory extends BaseFactory {
-
-  private static final LoggerCreationService SERVICE;
+public class LoggerFactory {
 
-  static {
-    LoggerCreationService service;
+    // use the no-op service to prevent recursion in case of an attempt to log an exception as a
+    // result of a logger initialization error
+    private static final LoggerCreationService SERVICE = ServiceBinder.getCreationServiceBinding().orElse(
+            new NoOpLoggerCreationService());
 
-    try {
-      service = locateService(LoggerCreationService.class);
-    } catch (Exception ex) {
-      new RuntimeException("Failed to instantiate logger factory", ex).printStackTrace();
-      // use the no-op service to prevent recursion in case of an attempt to log an exception as a
-      // result of a logger initialization error
-      service = new NoOpLoggerCreationService();
+    private LoggerFactory() {
+        // prevent instantiation
     }
 
-    SERVICE = service;
-  }
-
-  public static Logger getLogger(String clazzName) {
-    return SERVICE.getLogger(clazzName);
-  }
-
-  public static Logger getLogger(Class<?> clazz) {
-    return SERVICE.getLogger(clazz);
-  }
-
-  private static class NoOpLoggerCreationService implements LoggerCreationService {
-    private static final Logger NO_OP_LOGGER = new NoOpLogger();
-
-    private static class NoOpLogger implements Logger {
-      @Override
-      public String getName() {
-        return "No-Op Logger";
-      }
-
-      @Override
-      public boolean isMetricsEnabled() {
-        return false;
-      }
-
-      @Override
-      public void metrics(String msg) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void metrics(String msg, Object arg) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void metrics(String msg, Object arg1, Object arg2) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void metrics(String msg, Object... arguments) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void metrics(String msg, Throwable t) {
-        //this is no_op_method
-      }
-
-      @Override
-      public boolean isAuditEnabled() {
-        return false;
-      }
-
-      @Override
-      public void audit(String msg) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void audit(String msg, Object arg) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void audit(String msg, Object arg1, Object arg2) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void audit(String msg, Object... arguments) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void audit(String msg, Throwable t) {
-        //this is no_op_method
-      }
-
-      @Override
-      public boolean isDebugEnabled() {
-        return false;
-      }
-
-      @Override
-      public void debug(String msg) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void debug(String msg, Object arg) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void debug(String msg, Object arg1, Object arg2) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void debug(String msg, Object... arguments) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void debug(String msg, Throwable t) {
-        //this is no_op_method
-      }
-
-      @Override
-      public boolean isInfoEnabled() {
-        return false;
-      }
-
-      @Override
-      public void info(String msg) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void info(String msg, Object arg) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void info(String msg, Object arg1, Object arg2) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void info(String msg, Object... arguments) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void info(String msg, Throwable t) {
-        //this is no_op_method
-      }
-
-      @Override
-      public boolean isWarnEnabled() {
-        return false;
-      }
-
-      @Override
-      public void warn(String msg) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void warn(String msg, Object arg) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void warn(String msg, Object... arguments) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void warn(String msg, Object arg1, Object arg2) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void warn(String msg, Throwable t) {
-        //this is no_op_method
-      }
-
-      @Override
-      public boolean isErrorEnabled() {
-        return false;
-      }
-
-      @Override
-      public void error(String msg) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void error(String msg, Object arg) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void error(String msg, Object arg1, Object arg2) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void error(String msg, Object... arguments) {
-        //this is no_op_method
-      }
-
-      @Override
-      public void error(String msg, Throwable t) {
-        //this is no_op_method
-      }
+    public static Logger getLogger(String clazzName) {
+        return SERVICE.getLogger(clazzName);
     }
 
-    @Override
-    public Logger getLogger(String className) {
-      return NO_OP_LOGGER;
+    public static Logger getLogger(Class<?> clazz) {
+        return SERVICE.getLogger(clazz);
     }
 
-    @Override
-    public Logger getLogger(Class<?> clazz) {
-      return NO_OP_LOGGER;
+    private static class NoOpLoggerCreationService implements LoggerCreationService {
+
+        private static final Logger NO_OP_LOGGER = new NoOpLogger();
+
+        private static class NoOpLogger implements Logger {
+
+            @Override
+            public String getName() {
+                return "No-Op Logger";
+            }
+
+            @Override
+            public boolean isMetricsEnabled() {
+                return false;
+            }
+
+            @Override
+            public void metrics(String msg) {
+                // no-op
+            }
+
+            @Override
+            public void metrics(String msg, Object arg) {
+                // no-op
+            }
+
+            @Override
+            public void metrics(String msg, Object arg1, Object arg2) {
+                // no-op
+            }
+
+            @Override
+            public void metrics(String msg, Object... arguments) {
+                // no-op
+            }
+
+            @Override
+            public void metrics(String msg, Throwable t) {
+                // no-op
+            }
+
+            @Override
+            public boolean isAuditEnabled() {
+                return false;
+            }
+
+            @Override
+            public void audit(String msg) {
+                // no-op
+            }
+
+            @Override
+            public void audit(String msg, Object arg) {
+                // no-op
+            }
+
+            @Override
+            public void audit(String msg, Object arg1, Object arg2) {
+                // no-op
+            }
+
+            @Override
+            public void audit(String msg, Object... arguments) {
+                // no-op
+            }
+
+            @Override
+            public void audit(String msg, Throwable t) {
+                // no-op
+            }
+
+            @Override
+            public boolean isDebugEnabled() {
+                return false;
+            }
+
+            @Override
+            public void debug(String msg) {
+                // no-op
+            }
+
+            @Override
+            public void debug(String msg, Object arg) {
+                // no-op
+            }
+
+            @Override
+            public void debug(String msg, Object arg1, Object arg2) {
+                // no-op
+            }
+
+            @Override
+            public void debug(String msg, Object... arguments) {
+                // no-op
+            }
+
+            @Override
+            public void debug(String msg, Throwable t) {
+                // no-op
+            }
+
+            @Override
+            public boolean isInfoEnabled() {
+                return false;
+            }
+
+            @Override
+            public void info(String msg) {
+                // no-op
+            }
+
+            @Override
+            public void info(String msg, Object arg) {
+                // no-op
+            }
+
+            @Override
+            public void info(String msg, Object arg1, Object arg2) {
+                // no-op
+            }
+
+            @Override
+            public void info(String msg, Object... arguments) {
+                // no-op
+            }
+
+            @Override
+            public void info(String msg, Throwable t) {
+                // no-op
+            }
+
+            @Override
+            public boolean isWarnEnabled() {
+                return false;
+            }
+
+            @Override
+            public void warn(String msg) {
+                // no-op
+            }
+
+            @Override
+            public void warn(String msg, Object arg) {
+                // no-op
+            }
+
+            @Override
+            public void warn(String msg, Object... arguments) {
+                // no-op
+            }
+
+            @Override
+            public void warn(String msg, Object arg1, Object arg2) {
+                // no-op
+            }
+
+            @Override
+            public void warn(String msg, Throwable t) {
+                // no-op
+            }
+
+            @Override
+            public boolean isErrorEnabled() {
+                return false;
+            }
+
+            @Override
+            public void error(String msg) {
+                // no-op
+            }
+
+            @Override
+            public void error(String msg, Object arg) {
+                // no-op
+            }
+
+            @Override
+            public void error(String msg, Object arg1, Object arg2) {
+                // no-op
+            }
+
+            @Override
+            public void error(String msg, Object... arguments) {
+                // no-op
+            }
+
+            @Override
+            public void error(String msg, Throwable t) {
+                // no-op
+            }
+        }
+
+        @Override
+        public Logger getLogger(String className) {
+            Objects.requireNonNull(className, "Name cannot be null");
+            return NO_OP_LOGGER;
+        }
+
+        @Override
+        public Logger getLogger(Class<?> clazz) {
+            Objects.requireNonNull(clazz, "Class cannot be null");
+            return NO_OP_LOGGER;
+        }
     }
-  }
 }
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/LoggingContext.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/LoggingContext.java
new file mode 100644 (file)
index 0000000..542f709
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright © 2016-2017 European Support Limited
+ *
+ * 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 org.openecomp.sdc.logging.api;
+
+import org.openecomp.sdc.logging.provider.LoggingContextService;
+
+import java.util.Objects;
+import java.util.concurrent.Callable;
+
+/**
+ * <a>Factory to hide a concrete, framework-specific implementation of diagnostic context.</a>
+ * <p>The service used by this factory must implement {@link LoggingContextService}. If no
+ * implementation has been configured or could be instantiated, a <b>no-op context service</b> will be
+ * used, and <b>no context</b> will be stored or propagated. No errors will be generated, so that the application can
+ * still work (albeit without proper logging).</p>
+ *
+ * @author evitaliy
+ * @since 07/01/2018.
+ *
+ * @see ServiceBinder
+ * @see LoggingContextService
+ */
+public class LoggingContext {
+
+    private static final LoggingContextService SERVICE = ServiceBinder.getContextServiceBinding().orElse(
+            new NoOpLoggingContextService());
+
+    private LoggingContext() {
+        // prevent instantiation
+    }
+
+    public static void put(String key, String value) {
+        SERVICE.put(key, value);
+    }
+
+    public static String get(String key) {
+        return SERVICE.get(key);
+    }
+
+    public static void remove(String key) {
+        SERVICE.remove(key);
+    }
+
+    public static void clear() {
+        SERVICE.clear();
+    }
+
+    public static Runnable toRunnable(Runnable runnable) {
+        return SERVICE.toRunnable(runnable);
+    }
+
+    public static <V> Callable<V> toCallable(Callable<V> callable) {
+        return SERVICE.toCallable(callable);
+    }
+
+    private static class NoOpLoggingContextService implements LoggingContextService {
+
+        private static final String KEY_CANNOT_BE_NULL = "Key cannot be null";
+
+        @Override
+        public void put(String key, String value) {
+            Objects.requireNonNull(key, KEY_CANNOT_BE_NULL);
+            // no-op
+        }
+
+        @Override
+        public String get(String key) {
+            Objects.requireNonNull(key, KEY_CANNOT_BE_NULL);
+            return null;
+        }
+
+        @Override
+        public void remove(String key) {
+            Objects.requireNonNull(key, KEY_CANNOT_BE_NULL);
+            // no-op
+        }
+
+        @Override
+        public void clear() {
+            // no-op
+        }
+
+        @Override
+        public Runnable toRunnable(Runnable runnable) {
+            Objects.requireNonNull(runnable, "Runnable cannot be null");
+            return runnable;
+        }
+
+        @Override
+        public <V> Callable<V> toCallable(Callable<V> callable) {
+            Objects.requireNonNull(callable, "Callable cannot be null");
+            return callable;
+        }
+    }
+}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/ServiceBinder.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/ServiceBinder.java
new file mode 100644 (file)
index 0000000..b16f394
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright © 2016-2017 European Support Limited
+ *
+ * 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 org.openecomp.sdc.logging.api;
+
+import org.openecomp.sdc.logging.provider.LoggerCreationService;
+import org.openecomp.sdc.logging.provider.LoggingContextService;
+import org.openecomp.sdc.logging.provider.LoggingServiceProvider;
+
+import java.util.Iterator;
+import java.util.Optional;
+import java.util.ServiceLoader;
+
+/**
+ * <p>Binds to a concrete implementation of logging services.</p>
+ *
+ * <p>In order to use the factory, a particular (e.g. framework-specific) implementation of a service must be
+ * configured as described in
+ * <a href="http://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html">java.util.ServiceLoader</a>).</p>
+ *
+ * @author evitaliy
+ * @since 13/09/2016.
+ *
+ * @see ServiceLoader
+ */
+
+// No advanced logging can be used here because we don't know
+// which underlying implementation will be used
+@SuppressWarnings({"UseOfSystemOutOrSystemErr", "squid:S106"})
+class ServiceBinder {
+
+    private static final LoggingServiceProvider PROVIDER = lookupProvider();
+
+    private ServiceBinder () {
+        // prevent instantiation
+    }
+
+    private static LoggingServiceProvider lookupProvider() {
+
+        ServiceLoader<LoggingServiceProvider> loader = ServiceLoader.load(LoggingServiceProvider.class);
+        Iterator<LoggingServiceProvider> iterator = loader.iterator();
+
+        if (!iterator.hasNext()) {
+            System.err.printf("[ERROR] No provider configured for logging services %s. " +
+                            "Default implementation will be used.\n",
+                    LoggingServiceProvider.class.getName());
+            return null;
+        }
+
+        try {
+
+            LoggingServiceProvider provider = iterator.next();
+            if (!iterator.hasNext()) {
+                return provider;
+            }
+
+            Logger logger = provider.getLogger(ServiceBinder.class);
+            if (logger.isWarnEnabled()) {
+                logger.warn("More than one provider for logging services {} found",
+                        LoggingServiceProvider.class.getName());
+            }
+
+            return provider;
+
+        } catch (Exception e) {
+            // don't fail if the provider cannot be instantiated
+            e.printStackTrace(System.err);
+            return null;
+        }
+    }
+
+    static Optional<LoggingContextService> getContextServiceBinding() {
+        return Optional.ofNullable(PROVIDER);
+    }
+
+    static Optional<LoggerCreationService> getCreationServiceBinding() {
+        return Optional.ofNullable(PROVIDER);
+    }
+}
+
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/context/TaskFactory.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/context/TaskFactory.java
deleted file mode 100644 (file)
index 8621638..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright © 2016-2017 European Support Limited
- *
- * 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 org.openecomp.sdc.logging.api.context;
-
-import org.openecomp.sdc.logging.api.BaseFactory;
-
-/**
- * <p>Should be used to propagate a diagnostic context (for instance <a
- * href="http://www.slf4j.org/manual.html#mdc">MDC</a>) to other threads.</p>
- * <p>Applicable when creating a child thread directly, or submitting tasks for potentially
- * postponed execution via an <a href="http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html">Executor</a>
- * (including any of the <a href="http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html">executor
- * services</a> and <a href="http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.html">ForkJoinPool</a>).</p>
- * <p>The service used by this factory must implement {@link ContextPropagationService}.</p>
- *
- * @author evitaliy
- * @see ContextPropagationService
- * @since 12/09/2016.
- */
-@SuppressWarnings("ThrowableInstanceNeverThrown")
-public class TaskFactory extends BaseFactory {
-
-  private static final ContextPropagationService SERVICE;
-  private static final RuntimeException ERROR;
-
-  static {
-
-    ContextPropagationService service = null;
-    RuntimeException error = null;
-
-    try {
-      service = locateService(ContextPropagationService.class);
-    } catch (Exception ex) {
-      error = new RuntimeException("Failed to instantiate task factory", ex);
-    }
-
-    SERVICE = service;
-    ERROR = error;
-  }
-
-  /**
-   * Modify a task so that a diagnostic context is propagated to the thread when the task runs. Done
-   * in a logging-framework specific way.
-   *
-   * @param task any Runnable that will run in a thread
-   * @return modified (wrapped) original task that runs the same business logic, but also takes care
-   * of copying the diagnostic context for logging
-   */
-  public static Runnable create(Runnable task) {
-
-    if (SERVICE == null) {
-      throw ERROR;
-    }
-
-    return SERVICE.create(task);
-  }
-}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/provider/LoggingContextService.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/provider/LoggingContextService.java
new file mode 100644 (file)
index 0000000..8e725e7
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright © 2016-2017 European Support Limited
+ *
+ * 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 org.openecomp.sdc.logging.provider;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Should be used to implement a framework-specific mechanism of managing a per-thread diagnostic context
+ * (for instance <a href="http://www.slf4j.org/manual.html#mdc">MDC</a>), and propagating it to child threads if needed.
+ * Context propagation should be used when creating a child thread directly, or submitting tasks for potentially
+ * postponed execution via an
+ * <a href="http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html">Executor</a> (including any of
+ * the
+ * <a href="http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html">executor services</a>
+ * and <a href="http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.html">ForkJoinPool</a>).
+ *
+ * @author evitaliy
+ * @since 07/01/2018.
+ */
+
+public interface LoggingContextService {
+
+    /**
+     * Allows to store a key-value pair on thread context
+     */
+    void put(String key, String value);
+
+    /**
+     * Returns the value associated with a key stored on thread context
+     *
+     * @return value or <code>null</code> if the key does not exits
+     */
+    String get(String key);
+
+    /**
+     * Removes a particular key from thread context
+     */
+    void remove(String key);
+
+    /**
+     * Clear logging thread context
+     */
+    void clear();
+
+    /**
+     * Copies logging context of current thread onto a {@link Runnable}, so that the context is available
+     * when this {@link Runnable} runs in another thread.
+     */
+    Runnable toRunnable(Runnable runnable);
+
+    /**
+     * Copies logging context of current thread onto a {@link Callable}, so that the context is available
+     * when this {@link Callable} runs in another thread
+     */
+    <V> Callable<V> toCallable(Callable<V> callable);
+}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/provider/LoggingServiceProvider.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/provider/LoggingServiceProvider.java
new file mode 100644 (file)
index 0000000..baf99f0
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright © 2016-2017 European Support Limited
+ *
+ * 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 org.openecomp.sdc.logging.provider;
+
+/**
+ * <p>From the application code (consumer) perspective, logger creation (factory) and logging context are independent
+ * services. From the service provider perspective, however, these services are related and must be implemented together
+ * using the same underlying mechanism. Therefore, the service provider-facing interface combines the two services
+ * &mdash; to eliminate the chance that their implementations don't work well together.</p>
+ *
+ * @author EVITALIY
+ * @since 07 Jan 18
+ */
+public interface LoggingServiceProvider extends LoggerCreationService, LoggingContextService {
+    // single provider must implement two separate consumer services
+}
index 4b3a1ba..9fde4e5 100644 (file)
@@ -4,9 +4,9 @@
  * 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.
@@ -19,10 +19,8 @@ package org.openecomp.sdc.logging.api;
 import org.testng.annotations.Test;
 
 import java.lang.reflect.Field;
-import java.util.ServiceLoader;
 
 import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
 
 /**
@@ -31,40 +29,48 @@ import static org.testng.Assert.assertNotNull;
  */
 public class LoggerFactoryTest {
 
-  @Test
-  public void testNoOpLoggerService() throws Exception {
+    @Test
+    public void shouldHoldNoOpWhenNoBinding() throws Exception {
+
+        // set up to access the private static field
+        Field factory = LoggerFactory.class.getDeclaredField("SERVICE");
+        factory.setAccessible(true);
+        Object impl = factory.get(null);
 
-    assertFalse(ServiceLoader.load(LoggerCreationService.class).iterator().hasNext());
+        assertEquals(impl.getClass().getName(),
+                "org.openecomp.sdc.logging.api.LoggerFactory$NoOpLoggerCreationService");
+    }
 
-    LoggerFactory.getLogger(LoggerFactoryTest.class);
-    Field factory = LoggerFactory.class.getDeclaredField("SERVICE");
-    factory.setAccessible(true);
-    Object impl = factory.get(null);
-    assertEquals("org.openecomp.sdc.logging.api.LoggerFactory$NoOpLoggerCreationService",
-        impl.getClass().getName());
-  }
+    @Test
+    public void verifyNoOpLoggerWorksWhenGotByClass() {
+        Logger logger = LoggerFactory.getLogger(LoggerFactoryTest.class);
+        verifyLoggerWorks(logger);
+    }
 
-  @Test
-  public void testNoOpLoggerByClass() throws Exception {
-    Logger logger = LoggerFactory.getLogger(LoggerFactoryTest.class);
-    verifyLogger(logger);
-  }
+    @Test
+    public void verifyNoOpLoggerWorksWhenGotByName() {
+        Logger logger = LoggerFactory.getLogger(LoggerFactoryTest.class.getName());
+        verifyLoggerWorks(logger);
+    }
 
-  @Test
-  public void testNoOpLoggerByName() throws Exception {
-    Logger logger = LoggerFactory.getLogger(LoggerFactoryTest.class.getName());
-    verifyLogger(logger);
-  }
+    @Test(expectedExceptions = NullPointerException.class)
+    public void throwNpeWhenGetByNameWithNull() {
+        LoggerFactory.getLogger((String) null);
+    }
 
-  private void verifyLogger(Logger logger) {
-    assertNotNull(logger);
+    @Test(expectedExceptions = NullPointerException.class)
+    public void throwNpeWhenGetByClassWithNull() {
+        LoggerFactory.getLogger((Class<LoggerFactoryTest>) null);
+    }
 
-    // make sure no exceptions are thrown
-    logger.error("");
-    logger.warn("");
-    logger.info("");
-    logger.debug("");
-    logger.audit("");
-    logger.metrics("");
-  }
+    private void verifyLoggerWorks(Logger logger) {
+        assertNotNull(logger);
+        // make sure no exceptions are thrown
+        logger.error("");
+        logger.warn("");
+        logger.info("");
+        logger.debug("");
+        logger.audit("");
+        logger.metrics("");
+    }
 }
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/api/LoggingContextTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/api/LoggingContextTest.java
new file mode 100644 (file)
index 0000000..7899eeb
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright © 2016-2017 European Support Limited
+ *
+ * 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 org.openecomp.sdc.logging.api;
+
+import org.testng.annotations.Test;
+
+import java.lang.reflect.Field;
+import java.util.concurrent.Callable;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * @author EVITALIY
+ * @since 08 Jan 18
+ */
+public class LoggingContextTest {
+
+    @Test
+    public void shouldHoldNoOpWhenNoBinding() throws Exception {
+        Field factory = LoggingContext.class.getDeclaredField("SERVICE");
+        factory.setAccessible(true);
+        Object impl = factory.get(null);
+        assertEquals(impl.getClass().getName(),
+                "org.openecomp.sdc.logging.api.LoggingContext$NoOpLoggingContextService");
+    }
+
+    @Test
+    public void putDoesNotHaveEffectWhenNoBinding() {
+        final String key = "Key";
+        LoggingContext.put(key, "Dummy");
+        assertNull(LoggingContext.get(key));
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void throwNpeWhenPutWithKeyNull() {
+        LoggingContext.put(null, "value");
+    }
+
+    @Test
+    public void getAlwaysReturnsNull() {
+        assertNull(LoggingContext.get("GetKey"));
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void throwNpeWhenGetWithKeyNull() {
+        LoggingContext.get(null);
+    }
+
+    @Test
+    public void removeDoesNotFail() {
+        LoggingContext.remove("RemoveKey");
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void throwNpWhenRemoveWithKeyNull() {
+        LoggingContext.remove(null);
+    }
+
+    @Test
+    public void clearDoesNotFail() {
+        LoggingContext.clear();
+    }
+
+    @Test
+    public void toRunnableReturnsSameInstance() {
+        Runnable test = () -> { /* do nothing */ };
+        assertTrue(test == LoggingContext.toRunnable(test));
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void throwNpeWhenToRunnableWithNull() {
+        LoggingContext.toRunnable(null);
+    }
+
+    @Test
+    public void toCallableReturnsSameInstance() {
+        Callable<String> test = () -> "";
+        assertTrue(test == LoggingContext.toCallable(test));
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void throwNpeWhenToCallableWithNull() {
+        LoggingContext.toCallable(null);
+    }
+}
\ No newline at end of file
@@ -4,9 +4,9 @@
  * 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.
  * limitations under the License.
  */
 
-package org.openecomp.sdc.logging.api.context;
+package org.openecomp.sdc.logging.api;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.*;
 
 /**
- * Should be used to implement a framework-specific mechanism of propagation of a diagnostic context to child threads.
- *
- * @author evitaliy
- * @since 12/09/2016.
+ * @author EVITALIY
+ * @since 08 Jan 18
  */
+public class ServiceBinderTest {
 
-@FunctionalInterface
-public interface ContextPropagationService {
+    @Test
+    public void makeSureNoContextServiceBinding() {
+        assertFalse(ServiceBinder.getContextServiceBinding().isPresent());
+    }
 
-    Runnable create(Runnable task);
-}
+    @Test
+    public void makeSureNoCreationServiceBinding() {
+        assertFalse(ServiceBinder.getCreationServiceBinding().isPresent());
+    }
+}
\ No newline at end of file
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/api/context/TaskFactoryTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/api/context/TaskFactoryTest.java
deleted file mode 100644 (file)
index f5c2187..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.openecomp.sdc.logging.api.context;
-
-import org.testng.annotations.Test;
-
-import java.util.ServiceLoader;
-
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-
-/**
- * @author evitaliy
- * @since 14/09/2016.
- */
-public class TaskFactoryTest {
-
-  @Test(expectedExceptions = RuntimeException.class)
-  public void testNoImplementation() throws Exception {
-
-    assertFalse(ServiceLoader.load(ContextPropagationService.class).iterator().hasNext());
-
-    try {
-      TaskFactory.create(() -> {
-      });
-    } catch (RuntimeException e) {
-      Throwable cause = e.getCause();
-      assertNotNull(cause);
-      assertTrue(cause.getMessage().contains(ContextPropagationService.class.getName()));
-      throw e;
-    }
-  }
-}
\ No newline at end of file
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/SLF4JLoggerCreationService.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/SLF4JLoggerCreationService.java
deleted file mode 100644 (file)
index bc896a9..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright © 2016-2017 European Support Limited
- *
- * 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 org.openecomp.sdc.logging;
-
-import org.openecomp.sdc.logging.api.Logger;
-import org.openecomp.sdc.logging.api.LoggerCreationService;
-import org.slf4j.LoggerFactory;
-
-/**
- * @author evitaliy
- * @since 13/09/2016.
- */
-public class SLF4JLoggerCreationService implements LoggerCreationService {
-
-    @Override
-    public Logger getLogger(String className) {
-        return new SLF4JWrapper(className);
-    }
-
-    @Override
-    public Logger getLogger(Class<?> clazz) {
-        return new SLF4JWrapper(clazz);
-    }
-
-    private class SLF4JWrapper implements Logger {
-
-        private final org.slf4j.Logger logger;
-
-        SLF4JWrapper(Class<?> clazz) {
-            logger = LoggerFactory.getLogger(clazz);
-        }
-
-        SLF4JWrapper(String className) {
-            logger = LoggerFactory.getLogger(className);
-        }
-
-        @Override
-        public String getName() {
-            return logger.getName();
-        }
-
-        @Override
-        public boolean isMetricsEnabled() {
-            return logger.isInfoEnabled(Markers.METRICS);
-        }
-
-        @Override
-        public void metrics(String msg) {
-            logger.info(Markers.METRICS, msg);
-        }
-
-        @Override
-        public void metrics(String msg, Object arg) {
-            logger.info(Markers.METRICS, msg, arg);
-        }
-
-        @Override
-        public void metrics(String msg, Object arg1, Object arg2) {
-            logger.info(Markers.METRICS, msg, arg1, arg2);
-        }
-
-        @Override
-        public void metrics(String msg, Object... arguments) {
-            logger.info(Markers.METRICS, msg, arguments);
-        }
-
-        @Override
-        public void metrics(String msg, Throwable t) {
-            logger.info(Markers.METRICS, msg, t);
-        }
-
-        @Override
-        public boolean isAuditEnabled() {
-            return logger.isInfoEnabled(Markers.AUDIT);
-        }
-
-        @Override
-        public void audit(String msg) {
-            logger.info(Markers.AUDIT, msg);
-        }
-
-        @Override
-        public void audit(String msg, Object arg) {
-            logger.info(Markers.AUDIT, msg, arg);
-        }
-
-        @Override
-        public void audit(String msg, Object arg1, Object arg2) {
-            logger.info(Markers.AUDIT, msg, arg1, arg2);
-        }
-
-        @Override
-        public void audit(String msg, Object... arguments) {
-            logger.info(Markers.AUDIT, msg, arguments);
-        }
-
-        @Override
-        public void audit(String msg, Throwable t) {
-            logger.info(Markers.AUDIT, msg, t);
-        }
-
-        @Override
-        public boolean isDebugEnabled() {
-            return logger.isDebugEnabled();
-        }
-
-        @Override
-        public void debug(String msg) {
-            logger.debug(msg);
-        }
-
-        @Override
-        public void debug(String format, Object arg) {
-            logger.debug(format, arg);
-        }
-
-        @Override
-        public void debug(String format, Object arg1, Object arg2) {
-            logger.debug(format, arg1, arg2);
-        }
-
-        @Override
-        public void debug(String format, Object... arguments) {
-            logger.debug(format, arguments);
-        }
-
-        @Override
-        public void debug(String msg, Throwable t) {
-            logger.debug(msg, t);
-        }
-
-        @Override
-        public boolean isInfoEnabled() {
-            return logger.isInfoEnabled();
-        }
-
-        @Override
-        public void info(String msg) {
-            logger.info(msg);
-        }
-
-        @Override
-        public void info(String format, Object arg) {
-            logger.info(format, arg);
-        }
-
-        @Override
-        public void info(String format, Object arg1, Object arg2) {
-            logger.info(format, arg1, arg2);
-        }
-
-        @Override
-        public void info(String format, Object... arguments) {
-            logger.info(format, arguments);
-        }
-
-        @Override
-        public void info(String msg, Throwable t) {
-            logger.info(msg, t);
-        }
-
-        @Override
-        public boolean isWarnEnabled() {
-            return logger.isWarnEnabled();
-        }
-
-        @Override
-        public void warn(String msg) {
-            logger.warn(msg);
-        }
-
-        @Override
-        public void warn(String format, Object arg) {
-            logger.warn(format, arg);
-        }
-
-        @Override
-        public void warn(String format, Object... arguments) {
-            logger.warn(format, arguments);
-        }
-
-        @Override
-        public void warn(String format, Object arg1, Object arg2) {
-            logger.warn(format, arg1, arg2);
-        }
-
-        @Override
-        public void warn(String msg, Throwable t) {
-            logger.warn(msg, t);
-        }
-
-        @Override
-        public boolean isErrorEnabled() {
-            return logger.isErrorEnabled();
-        }
-
-        @Override
-        public void error(String msg) {
-            logger.error(msg);
-        }
-
-        @Override
-        public void error(String format, Object arg) {
-            logger.error(format, arg);
-        }
-
-        @Override
-        public void error(String format, Object arg1, Object arg2) {
-            logger.error(format, arg1, arg2);
-        }
-
-        @Override
-        public void error(String format, Object... arguments) {
-            logger.error(format, arguments);
-        }
-
-        @Override
-        public void error(String msg, Throwable t) {
-            logger.error(msg, t);
-        }
-    }
-}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/context/MDCPropagationService.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/context/MDCPropagationService.java
deleted file mode 100644 (file)
index 131b8db..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright © 2016-2017 European Support Limited
- *
- * 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 org.openecomp.sdc.logging.context;
-
-import org.openecomp.sdc.logging.api.context.ContextPropagationService;
-import org.slf4j.MDC;
-
-import java.util.Map;
-
-/**
- * Propagates the <a href="http://www.slf4j.org/manual.html#mdc">SLF4J Mapped Diagnostic Context (MDC)</a>
- * of a thread onto a runnable created by that thread, so that the context is available when the runnable is executed
- * in a new thread.
- *
- * @author evitaliy
- * @since 12/09/2016.
- */
-public class MDCPropagationService implements ContextPropagationService {
-
-    public Runnable create(Runnable task) {
-        return new MDCCopyingWrapper(task);
-    }
-
-    private static class MDCCopyingWrapper implements Runnable {
-
-        private final Runnable task;
-        private final Map<String, String> context;
-
-        private MDCCopyingWrapper(Runnable task) {
-            this.task = task;
-            this.context = MDC.getCopyOfContextMap();
-        }
-
-        @Override
-        public void run() {
-
-            Map<String, String> oldContext = MDC.getCopyOfContextMap();
-            replaceMDC(this.context);
-
-            try {
-                task.run();
-            } finally {
-                replaceMDC(oldContext);
-            }
-        }
-
-        private static void replaceMDC(Map<String, String> context) {
-
-            if (context == null) {
-                MDC.clear();
-            } else  {
-                MDC.setContextMap(context);
-            }
-        }
-    }
-}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/BaseMDCCopyingWrapper.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/BaseMDCCopyingWrapper.java
new file mode 100644 (file)
index 0000000..a963542
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2016-2017 European Support Limited
+ *
+ * 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 org.openecomp.sdc.logging.slf4j;
+
+import org.slf4j.MDC;
+
+import java.util.Map;
+
+/**
+ * @author EVITALIY
+ * @since 08 Jan 18
+ */
+abstract class BaseMDCCopyingWrapper {
+
+    private final Map<String, String> context;
+
+    BaseMDCCopyingWrapper() {
+        this.context = MDC.getCopyOfContextMap();
+    }
+
+    final Map<String, String> replace() {
+        Map<String, String> old = MDC.getCopyOfContextMap();
+        replaceMDC(this.context);
+        return old;
+    }
+
+    final void revert(Map<String, String> old) {
+        replaceMDC(old);
+    }
+
+    private static void replaceMDC(Map<String, String> context) {
+
+        if (context == null) {
+            MDC.clear();
+        } else {
+            MDC.setContextMap(context);
+        }
+    }
+}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/MDCCallableWrapper.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/MDCCallableWrapper.java
new file mode 100644 (file)
index 0000000..9cb67de
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2016-2017 European Support Limited
+ *
+ * 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 org.openecomp.sdc.logging.slf4j;
+
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+/**
+ * @author EVITALIY
+ * @since 08 Jan 18
+ */
+class MDCCallableWrapper<V> extends BaseMDCCopyingWrapper implements Callable<V> {
+
+    private final Callable<V> task;
+
+    MDCCallableWrapper(Callable<V> task) {
+        super();
+        this.task = task;
+    }
+
+    @Override
+    public V call() throws Exception {
+
+        Map<String, String> oldContext = replace();
+
+        try {
+            return task.call();
+        } finally {
+            revert(oldContext);
+        }
+    }
+}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/MDCRunnableWrapper.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/MDCRunnableWrapper.java
new file mode 100644 (file)
index 0000000..f1a6986
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright © 2016-2017 European Support Limited
+ *
+ * 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 org.openecomp.sdc.logging.slf4j;
+
+import java.util.Map;
+
+/**
+ * @author EVITALIY
+ * @since 08 Jan 18
+ */
+class MDCRunnableWrapper extends BaseMDCCopyingWrapper implements Runnable {
+
+    private final Runnable task;
+
+    MDCRunnableWrapper(Runnable task) {
+        super();
+        this.task = task;
+    }
+
+    @Override
+    public void run() {
+
+        Map<String, String> oldContext = replace();
+
+        try {
+            task.run();
+        } finally {
+            revert(oldContext);
+        }
+    }
+}
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.openecomp.sdc.logging;
+package org.openecomp.sdc.logging.slf4j;
 
 import org.slf4j.Marker;
 import org.slf4j.MarkerFactory;
@@ -41,6 +41,10 @@ import org.slf4j.MarkerFactory;
  */
 public class Markers {
 
+    private Markers() {
+        // prevent instantiation
+    }
+
     public static final Marker AUDIT = MarkerFactory.getMarker("AUDIT");
     public static final Marker METRICS = MarkerFactory.getMarker("METRICS");
 }
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/SLF4JLoggerWrapper.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/SLF4JLoggerWrapper.java
new file mode 100644 (file)
index 0000000..5d22345
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Copyright © 2016-2017 European Support Limited
+ *
+ * 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 org.openecomp.sdc.logging.slf4j;
+
+import org.openecomp.sdc.logging.api.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author EVITALIY
+ * @since 08 Jan 18
+ */
+class SLF4JLoggerWrapper implements Logger {
+
+    private final org.slf4j.Logger logger;
+
+    SLF4JLoggerWrapper(Class<?> clazz) {
+        logger = LoggerFactory.getLogger(clazz);
+    }
+
+    SLF4JLoggerWrapper(String className) {
+        logger = LoggerFactory.getLogger(className);
+    }
+
+    @Override
+    public String getName() {
+        return logger.getName();
+    }
+
+    @Override
+    public boolean isMetricsEnabled() {
+        return logger.isInfoEnabled(Markers.METRICS);
+    }
+
+    @Override
+    public void metrics(String msg) {
+        logger.info(Markers.METRICS, msg);
+    }
+
+    @Override
+    public void metrics(String msg, Object arg) {
+        logger.info(Markers.METRICS, msg, arg);
+    }
+
+    @Override
+    public void metrics(String msg, Object arg1, Object arg2) {
+        logger.info(Markers.METRICS, msg, arg1, arg2);
+    }
+
+    @Override
+    public void metrics(String msg, Object... arguments) {
+        logger.info(Markers.METRICS, msg, arguments);
+    }
+
+    @Override
+    public void metrics(String msg, Throwable t) {
+        logger.info(Markers.METRICS, msg, t);
+    }
+
+    @Override
+    public boolean isAuditEnabled() {
+        return logger.isInfoEnabled(Markers.AUDIT);
+    }
+
+    @Override
+    public void audit(String msg) {
+        logger.info(Markers.AUDIT, msg);
+    }
+
+    @Override
+    public void audit(String msg, Object arg) {
+        logger.info(Markers.AUDIT, msg, arg);
+    }
+
+    @Override
+    public void audit(String msg, Object arg1, Object arg2) {
+        logger.info(Markers.AUDIT, msg, arg1, arg2);
+    }
+
+    @Override
+    public void audit(String msg, Object... arguments) {
+        logger.info(Markers.AUDIT, msg, arguments);
+    }
+
+    @Override
+    public void audit(String msg, Throwable t) {
+        logger.info(Markers.AUDIT, msg, t);
+    }
+
+    @Override
+    public boolean isDebugEnabled() {
+        return logger.isDebugEnabled();
+    }
+
+    @Override
+    public void debug(String msg) {
+        logger.debug(msg);
+    }
+
+    @Override
+    public void debug(String format, Object arg) {
+        logger.debug(format, arg);
+    }
+
+    @Override
+    public void debug(String format, Object arg1, Object arg2) {
+        logger.debug(format, arg1, arg2);
+    }
+
+    @Override
+    public void debug(String format, Object... arguments) {
+        logger.debug(format, arguments);
+    }
+
+    @Override
+    public void debug(String msg, Throwable t) {
+        logger.debug(msg, t);
+    }
+
+    @Override
+    public boolean isInfoEnabled() {
+        return logger.isInfoEnabled();
+    }
+
+    @Override
+    public void info(String msg) {
+        logger.info(msg);
+    }
+
+    @Override
+    public void info(String format, Object arg) {
+        logger.info(format, arg);
+    }
+
+    @Override
+    public void info(String format, Object arg1, Object arg2) {
+        logger.info(format, arg1, arg2);
+    }
+
+    @Override
+    public void info(String format, Object... arguments) {
+        logger.info(format, arguments);
+    }
+
+    @Override
+    public void info(String msg, Throwable t) {
+        logger.info(msg, t);
+    }
+
+    @Override
+    public boolean isWarnEnabled() {
+        return logger.isWarnEnabled();
+    }
+
+    @Override
+    public void warn(String msg) {
+        logger.warn(msg);
+    }
+
+    @Override
+    public void warn(String format, Object arg) {
+        logger.warn(format, arg);
+    }
+
+    @Override
+    public void warn(String format, Object... arguments) {
+        logger.warn(format, arguments);
+    }
+
+    @Override
+    public void warn(String format, Object arg1, Object arg2) {
+        logger.warn(format, arg1, arg2);
+    }
+
+    @Override
+    public void warn(String msg, Throwable t) {
+        logger.warn(msg, t);
+    }
+
+    @Override
+    public boolean isErrorEnabled() {
+        return logger.isErrorEnabled();
+    }
+
+    @Override
+    public void error(String msg) {
+        logger.error(msg);
+    }
+
+    @Override
+    public void error(String format, Object arg) {
+        logger.error(format, arg);
+    }
+
+    @Override
+    public void error(String format, Object arg1, Object arg2) {
+        logger.error(format, arg1, arg2);
+    }
+
+    @Override
+    public void error(String format, Object... arguments) {
+        logger.error(format, arguments);
+    }
+
+    @Override
+    public void error(String msg, Throwable t) {
+        logger.error(msg, t);
+    }
+}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/SLF4JLoggingServiceProvider.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/SLF4JLoggingServiceProvider.java
new file mode 100644 (file)
index 0000000..015875b
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2016-2017 European Support Limited
+ *
+ * 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 org.openecomp.sdc.logging.slf4j;
+
+import org.openecomp.sdc.logging.api.Logger;
+import org.openecomp.sdc.logging.provider.LoggingServiceProvider;
+import org.slf4j.MDC;
+
+import java.util.Objects;
+import java.util.concurrent.Callable;
+
+/**
+ * @author evitaliy
+ * @since 13/09/2016.
+ */
+public class SLF4JLoggingServiceProvider implements LoggingServiceProvider {
+
+    private static final String KEY_CANNOT_BE_NULL = "Key cannot be null";
+
+    @Override
+    public Logger getLogger(String className) {
+        Objects.requireNonNull(className, "Name cannot be null");
+        return new SLF4JLoggerWrapper(className);
+    }
+
+    @Override
+    public Logger getLogger(Class<?> clazz) {
+        Objects.requireNonNull(clazz, "Class cannot be null");
+        return new SLF4JLoggerWrapper(clazz);
+    }
+
+    @Override
+    public void put(String key, String value) {
+        Objects.requireNonNull(key, KEY_CANNOT_BE_NULL);
+        MDC.put(key, value);
+    }
+
+    @Override
+    public String get(String key) {
+        Objects.requireNonNull(key, KEY_CANNOT_BE_NULL);
+        return MDC.get(key);
+    }
+
+    @Override
+    public void remove(String key) {
+        Objects.requireNonNull(key, KEY_CANNOT_BE_NULL);
+        MDC.remove(key);
+    }
+
+    @Override
+    public void clear() {
+        MDC.clear();
+    }
+
+    @Override
+    public Runnable toRunnable(Runnable runnable) {
+        Objects.requireNonNull(runnable, "Runnable cannot be null");
+        return new MDCRunnableWrapper(runnable);
+    }
+
+    @Override
+    public <V> Callable<V> toCallable(Callable<V> callable) {
+        Objects.requireNonNull(callable, "Runnable cannot be null");
+        return new MDCCallableWrapper<>(callable);
+    }
+
+    @Override
+    public String toString() {
+        return this.getClass().getName();
+    }
+}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/resources/META-INF/services/org.openecomp.sdc.logging.provider.LoggingServiceProvider b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/resources/META-INF/services/org.openecomp.sdc.logging.provider.LoggingServiceProvider
new file mode 100644 (file)
index 0000000..7e438b7
--- /dev/null
@@ -0,0 +1 @@
+org.openecomp.sdc.logging.slf4j.SLF4JLoggingServiceProvider
\ No newline at end of file
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/LoggerFactoryTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/LoggerFactoryTest.java
deleted file mode 100644 (file)
index fe81a46..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.openecomp.sdc.logging;
-
-import org.openecomp.sdc.logging.api.LoggerFactory;
-import org.testng.annotations.Test;
-
-import java.lang.reflect.Field;
-
-import static org.testng.Assert.assertEquals;
-
-/**
- * @author evitaliy
- * @since 12/09/2016.
- */
-public class LoggerFactoryTest {
-
-  @Test
-  public void testCreate() throws Exception {
-    // test that the service loader loads the right implementation
-    LoggerFactory.getLogger(LoggerFactoryTest.class);
-    Field factory = LoggerFactory.class.getDeclaredField("SERVICE");
-    factory.setAccessible(true);
-    Object implementation = factory.get(null);
-    assertEquals(SLF4JLoggerCreationService.class, implementation.getClass());
-  }
-}
\ No newline at end of file
index fd3f590..adc1d8e 100644 (file)
@@ -22,6 +22,7 @@ import ch.qos.logback.classic.sift.SiftingAppender;
 import ch.qos.logback.classic.spi.ILoggingEvent;
 import ch.qos.logback.core.AppenderBase;
 import org.openecomp.sdc.logging.logback.EventTypeDiscriminator;
+import org.openecomp.sdc.logging.slf4j.Markers;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
@@ -48,7 +49,7 @@ public class RoutingTest {
   private static final String METRICS = "Metrics";
 
   private Logger logger;
-  private Map<String, TestAppender> result = new ConcurrentHashMap<>();
+  private final Map<String, TestAppender> result = new ConcurrentHashMap<>();
 
   @BeforeClass
   public void setUp() {
@@ -137,14 +138,14 @@ public class RoutingTest {
    */
   private static class TestAppender extends AppenderBase<ILoggingEvent> {
 
-    private List<ILoggingEvent> events = Collections.synchronizedList(new ArrayList<>(10));
+    private final List<ILoggingEvent> events = Collections.synchronizedList(new ArrayList<>(10));
 
     @Override
     protected void append(ILoggingEvent event) {
       this.events.add(event);
     }
 
-    public boolean contains(Predicate<ILoggingEvent> predicate) {
+    boolean contains(Predicate<ILoggingEvent> predicate) {
       return events.stream().anyMatch(predicate);
     }
   }
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/context/MDCPropagationFactoryTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/context/MDCPropagationFactoryTest.java
deleted file mode 100644 (file)
index 1fda9e8..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright © 2016-2017 European Support Limited
- *
- * 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 org.openecomp.sdc.logging.context;
-
-import org.slf4j.MDC;
-import org.testng.annotations.Test;
-
-import java.util.UUID;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
-
-/**
- * @author evitaliy
- * @since 12/09/2016.
- */
-public class MDCPropagationFactoryTest {
-
-  // Disable if an old version of MDC implementation is being used.
-  // MDCPropagationFactory should be used when MDC is not propagated to child threads.
-  // See https://jira.qos.ch/browse/LOGBACK-422 and https://jira.qos.ch/browse/LOGBACK-624
-  private static final boolean ENABLED = false;
-
-  @Test(enabled = ENABLED)
-  public void testNoPropagation() throws InterruptedException {
-
-    String uuid = UUID.randomUUID().toString();
-    AtomicBoolean complete = new AtomicBoolean(false);
-    MDC.put("data", uuid);
-
-    Runnable runnable = () -> {
-      assertNull(MDC.get("data"), "Data unexpectedly copied to a child thread. " +
-              "Are you using an old version of MDC implementation (e.g. logback)?");
-      complete.set(true);
-    };
-
-    Thread thread = new Thread(runnable);
-    thread.start();
-    thread.join();
-
-    assertEquals(MDC.get("data"), uuid, "Expected data to be retained in this thread");
-    assertTrue(complete.get(), "Expected the inner thread to run");
-  }
-
-  @Test(enabled = ENABLED)
-  public void testPropagation() throws InterruptedException {
-
-    String uuid = UUID.randomUUID().toString();
-    AtomicBoolean complete = new AtomicBoolean(false);
-    MDC.put("data", uuid);
-
-    MDCPropagationService factory = new MDCPropagationService();
-    Runnable runnable = factory.create(() -> {
-      assertEquals(MDC.get("data"), uuid, "Expected data to be propagated to the child thread's MDC");
-      complete.set(true);
-    });
-
-    Thread thread = new Thread(runnable);
-    thread.start();
-
-    thread.join();
-
-    assertEquals(MDC.get("data"), uuid, "Expected data to be retained in this thread");
-    assertTrue(complete.get(), "Expected the inner thread to run");
-  }
-
-  @Test(enabled = ENABLED)
-  public void testReplacement() throws InterruptedException {
-
-    String innerUuid = UUID.randomUUID().toString();
-    AtomicBoolean innerComplete = new AtomicBoolean(false);
-    AtomicBoolean outerComplete = new AtomicBoolean(false);
-
-    MDC.put("data", innerUuid);
-
-    MDCPropagationService factory = new MDCPropagationService();
-
-    // should run with the context of main thread
-    Runnable inner = factory.create(() -> {
-      assertEquals(MDC.get("data"), innerUuid, "Expected data to be propagated to the child thread's MDC");
-      innerComplete.set(true);
-    });
-
-    // pushes its own context, but runs the inner runnable
-    Runnable outer = () -> {
-      String outerUuid = UUID.randomUUID().toString();
-      MDC.put("data", outerUuid);
-      inner.run();
-      assertEquals(MDC.get("data"), outerUuid, "Expected MDC data to be replaced with stored data");
-      outerComplete.set(true);
-    };
-
-
-    Thread thread = new Thread(outer);
-    thread.start();
-    thread.join();
-
-    assertEquals(MDC.get("data"), innerUuid, "Expected data to be retained in this thread");
-    assertTrue(outerComplete.get(), "Expected the outer thread to run");
-    assertTrue(innerComplete.get(), "Expected the inner thread to run");
-  }
-
-  @Test(enabled = ENABLED)
-  public void testEmpty() throws InterruptedException {
-
-    final AtomicBoolean complete = new AtomicBoolean(false);
-
-    MDC.remove("data");
-    assertNull(MDC.get("data"), "Expected MDC data to be empty");
-
-    MDCPropagationService factory = new MDCPropagationService();
-    Runnable runnable = factory.create(() -> {
-      assertNull(MDC.get("data"), "Expected MDC data to be empty");
-      complete.set(true);
-    });
-
-    Thread thread = new Thread(runnable);
-    thread.start();
-    thread.join();
-
-    assertNull(MDC.get("data"), "Expected MDC data to be empty");
-    assertTrue(complete.get(), "Expected the inner thread to run");
-  }
-
-  @Test(enabled = ENABLED)
-  public void testCleanup() throws Exception {
-
-    String innerUuid = UUID.randomUUID().toString();
-    AtomicBoolean innerComplete = new AtomicBoolean(false);
-    AtomicBoolean outerComplete = new AtomicBoolean(false);
-
-    MDC.put("data", innerUuid);
-
-    MDCPropagationService factory = new MDCPropagationService();
-
-    // should run with the context of main thread
-    Runnable inner = factory.create(() -> {
-      assertEquals(MDC.get("data"), innerUuid, "Expected data to be propagated to the child thread's MDC");
-      innerComplete.set(true);
-    });
-
-    // pushes its own context, but runs the inner runnable
-    Runnable outer = () -> {
-      assertNull(MDC.get("data"), "Expected MDC data not to be copied to this thread");
-      inner.run();
-      assertNull(MDC.get("data"), "Expected MDC data to remain empty in this thread");
-      outerComplete.set(true);
-    };
-
-    Thread thread = new Thread(outer);
-    thread.start();
-    thread.join();
-
-    assertEquals(MDC.get("data"), innerUuid, "Expected MDC data to be retained in parent thread");
-    assertTrue(outerComplete.get(), "Expected the outer thread to run");
-    assertTrue(innerComplete.get(), "Expected the inner thread to run");
-  }
-
-  @Test(enabled = ENABLED)
-  public void testCleanupAfterError() throws Exception {
-
-    String innerUuid = UUID.randomUUID().toString();
-    AtomicBoolean innerComplete = new AtomicBoolean(false);
-    AtomicBoolean outerComplete = new AtomicBoolean(false);
-    AtomicBoolean exceptionThrown = new AtomicBoolean(false);
-
-    MDC.put("data", innerUuid);
-
-    MDCPropagationService factory = new MDCPropagationService();
-
-    // should run with the context of main thread
-    Runnable inner = factory.create(() -> {
-      assertEquals(MDC.get("data"), innerUuid, "Expected data to be propagated to the child thread's MDC");
-      innerComplete.set(true);
-      throw new RuntimeException();
-    });
-
-    // pushes its own context, but runs the inner runnable
-    Runnable outer = () -> {
-
-      String outerUuid = UUID.randomUUID().toString();
-      MDC.put("data", outerUuid);
-      assertEquals(MDC.get("data"), outerUuid, "Expected MDC data to be populated in this thread");
-
-      try {
-        inner.run();
-      } catch (RuntimeException e) {
-        exceptionThrown.set(true);
-      } finally {
-        assertEquals(MDC.get("data"), outerUuid, "Expected MDC data to be reverted even in case of exception");
-        outerComplete.set(true);
-      }
-    };
-
-    Thread thread = new Thread(outer);
-    thread.start();
-    thread.join();
-
-    assertEquals(MDC.get("data"), innerUuid, "Expected MDC data to be retained in parent thread");
-    assertTrue(outerComplete.get(), "Expected the outer thread to run");
-    assertTrue(innerComplete.get(), "Expected the inner thread to run");
-    assertTrue(exceptionThrown.get(), "Expected the inner class to throw exception");
-  }
-
-}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/context/TaskFactoryTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/context/TaskFactoryTest.java
deleted file mode 100644 (file)
index dad60d4..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-package org.openecomp.sdc.logging.context;
-
-import org.openecomp.sdc.logging.api.context.TaskFactory;
-import org.testng.annotations.Test;
-
-import java.lang.reflect.Field;
-
-import static org.testng.Assert.assertEquals;
-
-/**
- * @author evitaliy
- * @since 12/09/2016.
- */
-public class TaskFactoryTest {
-
-  @Test
-  public void testCreate() throws Exception {
-    // test that the service loader loads the right implementation
-    TaskFactory.create(() -> {
-    });
-    Field factory = TaskFactory.class.getDeclaredField("SERVICE");
-    factory.setAccessible(true);
-    Object implementation = factory.get(null);
-    assertEquals(MDCPropagationService.class, implementation.getClass());
-  }
-}
\ No newline at end of file
index 9885fc3..050c583 100644 (file)
@@ -4,9 +4,9 @@
  * 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.
@@ -18,7 +18,7 @@ package org.openecomp.sdc.logging.logback;
 
 import ch.qos.logback.classic.Level;
 import ch.qos.logback.classic.spi.LoggingEvent;
-import org.openecomp.sdc.logging.Markers;
+import org.openecomp.sdc.logging.slf4j.Markers;
 import org.slf4j.MarkerFactory;
 import org.testng.annotations.Test;
 
@@ -30,187 +30,187 @@ import static org.testng.Assert.assertEquals;
  */
 public class EventTypeDiscriminatorTest {
 
-  private static final String DEBUG = "Debug";
-  private static final String ERROR = "Error";
-
-  @Test
-  public void testGetDefaultDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    assertEquals(discriminator.getDiscriminatingValue(event), DEBUG);
-  }
-
-  @Test
-  public void testGetErrorDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.ERROR);
-    assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
-  }
-
-  @Test
-  public void testGetWarnDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.WARN);
-    assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
-  }
-
-  @Test
-  public void testGetInfoDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.INFO);
-    assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
-  }
-
-  @Test
-  public void testGetTraceDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.TRACE);
-    assertEquals(discriminator.getDiscriminatingValue(event), DEBUG);
-  }
-
-  @Test
-  public void testGetErrorWithAuditDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.ERROR);
-    event.setMarker(Markers.AUDIT);
-    assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
-  }
-
-  @Test
-  public void testGetErrorWithMetricsDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.ERROR);
-    event.setMarker(Markers.METRICS);
-    assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
-  }
-
-  @Test
-  public void testGetWarnWithAuditDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.WARN);
-    event.setMarker(Markers.AUDIT);
-    assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
-  }
-
-  @Test
-  public void testGetWarnWithMetricsDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.WARN);
-    event.setMarker(Markers.METRICS);
-    assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
-  }
-
-  @Test
-  public void testGetDebugWithAuditDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.DEBUG);
-    event.setMarker(Markers.AUDIT);
-    assertEquals(discriminator.getDiscriminatingValue(event), DEBUG);
-  }
-
-  @Test
-  public void testGetDebugWithMetricsDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.DEBUG);
-    event.setMarker(Markers.METRICS);
-    assertEquals(discriminator.getDiscriminatingValue(event), DEBUG);
-  }
-
-  @Test
-  public void testGetTraceWithAuditDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.TRACE);
-    event.setMarker(Markers.AUDIT);
-    assertEquals(discriminator.getDiscriminatingValue(event), DEBUG);
-  }
-
-  @Test
-  public void testGetTraceWithMetricsDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.TRACE);
-    event.setMarker(Markers.METRICS);
-    assertEquals(discriminator.getDiscriminatingValue(event), DEBUG);
-  }
-
-  @Test
-  public void testGetErrorWithMarkerDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.ERROR);
-    event.setMarker(MarkerFactory.getMarker("Dummy"));
-    assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
-  }
-
-  @Test
-  public void testGetWarnWithMarkerDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.WARN);
-    event.setMarker(MarkerFactory.getMarker("Dummy"));
-    assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
-  }
-
-  @Test
-  public void testGetDebugWithMarkerDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.DEBUG);
-    event.setMarker(MarkerFactory.getMarker("Dummy"));
-    assertEquals(discriminator.getDiscriminatingValue(event), DEBUG);
-  }
-
-  @Test
-  public void testGetTraceWithMarkerDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.TRACE);
-    event.setMarker(MarkerFactory.getMarker("Dummy"));
-    assertEquals(discriminator.getDiscriminatingValue(event), DEBUG);
-  }
-
-  @Test
-  public void testGetInfoWithMarkerDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.INFO);
-    event.setMarker(MarkerFactory.getMarker("Dummy"));
-    assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
-  }
-
-  @Test
-  public void testGetAuditDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.INFO);
-    event.setMarker(Markers.AUDIT);
-    assertEquals(discriminator.getDiscriminatingValue(event), "Audit");
-  }
-
-  @Test
-  public void testGetMetricsMarkerDiscriminatingValue() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    LoggingEvent event = new LoggingEvent();
-    event.setLevel(Level.INFO);
-    event.setMarker(Markers.METRICS);
-    assertEquals(discriminator.getDiscriminatingValue(event), "Metrics");
-  }
-
-  @Test
-  public void testGetKey() throws Exception {
-    EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
-    assertEquals("eventType", discriminator.getKey());
-  }
+    private static final String DEBUG = "Debug";
+    private static final String ERROR = "Error";
+
+    @Test
+    public void testGetDefaultDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        assertEquals(discriminator.getDiscriminatingValue(event), DEBUG);
+    }
+
+    @Test
+    public void testGetErrorDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.ERROR);
+        assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
+    }
+
+    @Test
+    public void testGetWarnDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.WARN);
+        assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
+    }
+
+    @Test
+    public void testGetInfoDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.INFO);
+        assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
+    }
+
+    @Test
+    public void testGetTraceDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.TRACE);
+        assertEquals(discriminator.getDiscriminatingValue(event), DEBUG);
+    }
+
+    @Test
+    public void testGetErrorWithAuditDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.ERROR);
+        event.setMarker(Markers.AUDIT);
+        assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
+    }
+
+    @Test
+    public void testGetErrorWithMetricsDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.ERROR);
+        event.setMarker(Markers.METRICS);
+        assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
+    }
+
+    @Test
+    public void testGetWarnWithAuditDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.WARN);
+        event.setMarker(Markers.AUDIT);
+        assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
+    }
+
+    @Test
+    public void testGetWarnWithMetricsDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.WARN);
+        event.setMarker(Markers.METRICS);
+        assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
+    }
+
+    @Test
+    public void testGetDebugWithAuditDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.DEBUG);
+        event.setMarker(Markers.AUDIT);
+        assertEquals(discriminator.getDiscriminatingValue(event), DEBUG);
+    }
+
+    @Test
+    public void testGetDebugWithMetricsDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.DEBUG);
+        event.setMarker(Markers.METRICS);
+        assertEquals(discriminator.getDiscriminatingValue(event), DEBUG);
+    }
+
+    @Test
+    public void testGetTraceWithAuditDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.TRACE);
+        event.setMarker(Markers.AUDIT);
+        assertEquals(discriminator.getDiscriminatingValue(event), DEBUG);
+    }
+
+    @Test
+    public void testGetTraceWithMetricsDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.TRACE);
+        event.setMarker(Markers.METRICS);
+        assertEquals(discriminator.getDiscriminatingValue(event), DEBUG);
+    }
+
+    @Test
+    public void testGetErrorWithMarkerDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.ERROR);
+        event.setMarker(MarkerFactory.getMarker("Dummy"));
+        assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
+    }
+
+    @Test
+    public void testGetWarnWithMarkerDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.WARN);
+        event.setMarker(MarkerFactory.getMarker("Dummy"));
+        assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
+    }
+
+    @Test
+    public void testGetDebugWithMarkerDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.DEBUG);
+        event.setMarker(MarkerFactory.getMarker("Dummy"));
+        assertEquals(discriminator.getDiscriminatingValue(event), DEBUG);
+    }
+
+    @Test
+    public void testGetTraceWithMarkerDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.TRACE);
+        event.setMarker(MarkerFactory.getMarker("Dummy"));
+        assertEquals(discriminator.getDiscriminatingValue(event), DEBUG);
+    }
+
+    @Test
+    public void testGetInfoWithMarkerDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.INFO);
+        event.setMarker(MarkerFactory.getMarker("Dummy"));
+        assertEquals(discriminator.getDiscriminatingValue(event), ERROR);
+    }
+
+    @Test
+    public void testGetAuditDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.INFO);
+        event.setMarker(Markers.AUDIT);
+        assertEquals(discriminator.getDiscriminatingValue(event), "Audit");
+    }
+
+    @Test
+    public void testGetMetricsMarkerDiscriminatingValue() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        LoggingEvent event = new LoggingEvent();
+        event.setLevel(Level.INFO);
+        event.setMarker(Markers.METRICS);
+        assertEquals(discriminator.getDiscriminatingValue(event), "Metrics");
+    }
+
+    @Test
+    public void testGetKey() {
+        EventTypeDiscriminator discriminator = new EventTypeDiscriminator();
+        assertEquals("eventType", discriminator.getKey());
+    }
 
 }
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/BaseContextPropagationTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/BaseContextPropagationTest.java
new file mode 100644 (file)
index 0000000..a430de7
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright © 2016-2017 European Support Limited
+ *
+ * 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 org.openecomp.sdc.logging.slf4j;
+
+import org.openecomp.sdc.logging.api.LoggingContext;
+import org.openecomp.sdc.logging.provider.LoggingContextService;
+import org.testng.annotations.DataProvider;
+
+import java.util.concurrent.Callable;
+
+/**
+ * @author EVITALIY
+ * @since 08 Jan 18
+ */
+public abstract class BaseContextPropagationTest {
+
+    // Disable if an old version of ctx implementation is being used.
+    // ctxPropagationFactory should be used when ctx is not propagated to child threads.
+    // See https://jira.qos.ch/browse/LOGBACK-422 and https://jira.qos.ch/browse/LOGBACK-624
+    static final boolean ENABLED = false;
+
+    static final String PROVIDER = "context";
+    static final String KEY = "test-data";
+
+    static final String EXPECT_PROPAGATED_TO_CHILD = "Expected the data to be propagated to the child thread's context";
+    static final String EXPECT_RETAINED_IN_CURRENT = "Expected the data to be retained in this thread";
+    static final String EXPECT_REPLACED_WITH_STORED = "Expected context data to be replaced with stored data";
+    static final String EXPECT_INNER_RUN = "Expected the inner thread to run";
+    static final String EXPECT_OUTER_RUN = "Expected the outer thread to run";
+    static final String EXPECT_NOT_COPIED = "Expected context data not to be copied to this thread";
+    static final String EXPECT_RETAINED_IN_PARENT = "Expected context data to be retained in parent thread";
+    static final String EXPECT_POPULATED = "Expected context data to be populated in this thread";
+    static final String EXPECT_EMPTY = "Expected context data to be empty";
+    static final String EXPECT_REMAIN_EMPTY = "Expected context data to remain empty in this thread";
+    static final String EXPECT_REVERTED_ON_EXCEPTION = "Expected context data to be reverted even in case of exception";
+    static final String EXPECT_EXCEPTION_FROM_INNER = "Expected the inner class to throw exception";
+
+    @DataProvider(name = PROVIDER)
+    public static Object[][] contextServices() {
+        // try both directly call the implementation and get it via the binding
+        return new Object[][] {
+                { new SLF4JLoggingServiceProvider() },
+                { new LoggingContextAdaptor() }
+        };
+    }
+
+    private static class LoggingContextAdaptor implements LoggingContextService {
+
+        @Override
+        public void put(String key, String value) {
+            LoggingContext.put(key, value);
+        }
+
+        @Override
+        public String get(String key) {
+            return LoggingContext.get(key);
+        }
+
+        @Override
+        public void remove(String key) {
+            LoggingContext.remove(key);
+        }
+
+        @Override
+        public void clear() {
+            LoggingContext.clear();
+        }
+
+        @Override
+        public Runnable toRunnable(Runnable runnable) {
+            return LoggingContext.toRunnable(runnable);
+        }
+
+        @Override
+        public <V> Callable<V> toCallable(Callable<V> callable) {
+            return LoggingContext.toCallable(callable);
+        }
+
+        @Override
+        public String toString() {
+            return this.getClass().getName();
+        }
+    }
+}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/CallableContextPropagationTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/CallableContextPropagationTest.java
new file mode 100644 (file)
index 0000000..5fad3f7
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright © 2016-2017 European Support Limited
+ *
+ * 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 org.openecomp.sdc.logging.slf4j;
+
+import org.openecomp.sdc.logging.provider.LoggingContextService;
+import org.testng.annotations.Test;
+
+import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * @author EVITALIY
+ * @since 08 Jan 18
+ */
+public class CallableContextPropagationTest extends BaseContextPropagationTest {
+
+    @Test(enabled = ENABLED, dataProvider = PROVIDER)
+    public void testContextPropagated(LoggingContextService ctx) throws Exception {
+
+        String uuid = UUID.randomUUID().toString();
+        ctx.put(KEY, uuid);
+
+        AtomicBoolean complete = new AtomicBoolean(false);
+
+        // pass the callable to the context service first
+        execute(ctx.toCallable(() -> {
+            assertEquals(ctx.get(KEY), uuid, EXPECT_PROPAGATED_TO_CHILD);
+            complete.set(true);
+            return null;
+        }));
+
+        assertEquals(ctx.get(KEY), uuid, EXPECT_RETAINED_IN_CURRENT);
+        assertTrue(complete.get(), EXPECT_INNER_RUN);
+    }
+
+    @Test(enabled = ENABLED, dataProvider = PROVIDER)
+    public void testContextReplacement(LoggingContextService ctx) throws Exception {
+
+        String innerRandom = UUID.randomUUID().toString();
+        ctx.put(KEY, innerRandom);
+
+        AtomicBoolean innerComplete = new AtomicBoolean(false);
+
+        // should run with the context of main thread
+        Callable inner = ctx.toCallable(() -> {
+            assertEquals(ctx.get(KEY), innerRandom, EXPECT_PROPAGATED_TO_CHILD);
+            innerComplete.set(true);
+            return null;
+        });
+
+        // pushes its own context, but the inner must run with its own context
+        AtomicBoolean outerComplete = new AtomicBoolean(false);
+        execute(() -> {
+            String outerUuid = UUID.randomUUID().toString();
+            ctx.put(KEY, outerUuid);
+            inner.call();
+            assertEquals(ctx.get(KEY), outerUuid, EXPECT_REPLACED_WITH_STORED);
+            outerComplete.set(true);
+            return null;
+        });
+
+        assertEquals(ctx.get(KEY), innerRandom, EXPECT_RETAINED_IN_CURRENT);
+        assertTrue(outerComplete.get(), EXPECT_OUTER_RUN);
+        assertTrue(innerComplete.get(), EXPECT_INNER_RUN);
+    }
+
+    @Test(enabled = ENABLED, dataProvider = PROVIDER)
+    public void testContextRemainsEmpty(LoggingContextService ctx) throws Exception {
+
+        ctx.remove(KEY);
+        assertNull(ctx.get(KEY), EXPECT_EMPTY);
+
+        final AtomicBoolean complete = new AtomicBoolean(false);
+        execute(ctx.toCallable(() -> {
+            assertNull(ctx.get(KEY), EXPECT_EMPTY);
+            complete.set(true);
+            return null;
+        }));
+
+        assertNull(ctx.get(KEY), EXPECT_EMPTY);
+        assertTrue(complete.get(), EXPECT_INNER_RUN);
+    }
+
+    @Test(enabled = ENABLED, dataProvider = PROVIDER)
+    public void testContextCleanedUp(LoggingContextService ctx) throws Exception {
+
+        String innerRandom = UUID.randomUUID().toString();
+        ctx.put(KEY, innerRandom);
+
+        AtomicBoolean innerComplete = new AtomicBoolean(false);
+        // should run with the context of main thread
+        Callable inner = ctx.toCallable((() -> {
+            assertEquals(ctx.get(KEY), innerRandom, EXPECT_PROPAGATED_TO_CHILD);
+            innerComplete.set(true);
+            return null;
+        }));
+
+        // pushes its own context, but runs the inner
+        AtomicBoolean outerComplete = new AtomicBoolean(false);
+        execute(() -> {
+            assertNull(ctx.get(KEY), EXPECT_NOT_COPIED);
+            inner.call();
+            assertNull(ctx.get(KEY), EXPECT_REMAIN_EMPTY);
+            outerComplete.set(true);
+            return null;
+        });
+
+        assertEquals(ctx.get(KEY), innerRandom, EXPECT_RETAINED_IN_PARENT);
+        assertTrue(outerComplete.get(), EXPECT_OUTER_RUN);
+        assertTrue(innerComplete.get(), EXPECT_INNER_RUN);
+    }
+
+    @Test(enabled = ENABLED, dataProvider = PROVIDER)
+    public void testCleanupAfterError(LoggingContextService ctx) throws Exception {
+
+        String innerRandom = UUID.randomUUID().toString();
+        ctx.put(KEY, innerRandom);
+
+        // should run with the context of main thread
+        AtomicBoolean innerComplete = new AtomicBoolean(false);
+        Callable inner = ctx.toCallable(() -> {
+            assertEquals(ctx.get(KEY), innerRandom, EXPECT_PROPAGATED_TO_CHILD);
+            innerComplete.set(true);
+            throw new IllegalArgumentException();
+        });
+
+        // pushes its own context, but runs the inner callable
+        AtomicBoolean outerComplete = new AtomicBoolean(false);
+        AtomicBoolean exceptionThrown = new AtomicBoolean(false);
+        execute(() -> {
+
+            String outerUuid = UUID.randomUUID().toString();
+            ctx.put(KEY, outerUuid);
+            assertEquals(ctx.get(KEY), outerUuid, EXPECT_POPULATED);
+
+            try {
+                inner.call();
+            } catch (IllegalArgumentException e) {
+                exceptionThrown.set(true);
+            } finally {
+                assertEquals(ctx.get(KEY), outerUuid, EXPECT_REVERTED_ON_EXCEPTION);
+                outerComplete.set(true);
+            }
+
+            return null;
+        });
+
+        assertEquals(ctx.get(KEY), innerRandom, EXPECT_RETAINED_IN_PARENT);
+        assertTrue(outerComplete.get(), EXPECT_OUTER_RUN);
+        assertTrue(innerComplete.get(), EXPECT_INNER_RUN);
+        assertTrue(exceptionThrown.get(), EXPECT_EXCEPTION_FROM_INNER);
+    }
+
+    private void execute(Callable<Object> callable) throws Exception {
+
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+
+        try {
+            Future<Object> future = executor.submit(callable);
+            future.get(10, TimeUnit.SECONDS);
+        } finally {
+            executor.shutdown();
+        }
+    }
+}
\ No newline at end of file
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/LoggerFactoryTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/LoggerFactoryTest.java
new file mode 100644 (file)
index 0000000..e04af23
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright © 2016-2017 European Support Limited
+ *
+ * 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 org.openecomp.sdc.logging.slf4j;
+
+import org.openecomp.sdc.logging.api.Logger;
+import org.openecomp.sdc.logging.api.LoggerFactory;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/**
+ * @author EVITALIY
+ * @since 08 Jan 18
+ */
+public class LoggerFactoryTest {
+
+    private static final String CLASS_NAME = LoggerFactoryTest.class.getName();
+
+    @Test
+    public void returnSlf4jLoggerWhenGetByClass() {
+        Logger logger = LoggerFactory.getLogger(LoggerFactoryTest.class);
+        assertEquals(logger.getClass(), SLF4JLoggerWrapper.class);
+        assertEquals(logger.getName(), CLASS_NAME);
+    }
+
+    @Test
+    public void returnSlf4jLoggerWhenGetByName() {
+        Logger logger = LoggerFactory.getLogger(CLASS_NAME);
+        assertEquals(logger.getClass(), SLF4JLoggerWrapper.class);
+        assertEquals(logger.getName(), CLASS_NAME);
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void throwNpeWhenGetByNameWithNull() {
+        LoggerFactory.getLogger((String) null);
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void throwNpeWhenGetByClassWithNull() {
+        LoggerFactory.getLogger((Class<LoggerFactoryTest>) null);
+    }
+}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/LoggingContextTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/LoggingContextTest.java
new file mode 100644 (file)
index 0000000..516a27f
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright © 2016-2017 European Support Limited
+ *
+ * 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 org.openecomp.sdc.logging.slf4j;
+
+import org.openecomp.sdc.logging.api.LoggingContext;
+import org.slf4j.MDC;
+import org.testng.annotations.Test;
+
+import java.util.UUID;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+/**
+ * @author evitaliy
+ * @since 12/09/2016.
+ */
+public class LoggingContextTest {
+
+    @Test
+    public void returnMdcWrapperWhenToRunnableCalled() {
+        assertEquals(LoggingContext.toRunnable(() -> {}).getClass(), MDCRunnableWrapper.class);
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void throwNpeWhenToRunnableWithNull() {
+        LoggingContext.toRunnable(null);
+    }
+
+    @Test
+    public void returnMdcWrapperWhenToCallableCalled() {
+        assertEquals(LoggingContext.toCallable(() -> "").getClass(), MDCCallableWrapper.class);
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void throwNpeWhenToCallableWithNull() {
+        LoggingContext.toCallable(null);
+    }
+
+    @Test
+    public void clearContextWhenClearCalled() {
+
+        String random = UUID.randomUUID().toString();
+
+        try {
+            LoggingContext.put(random, random);
+            LoggingContext.clear();
+            assertNull(MDC.get(random));
+            assertNull(LoggingContext.get(random));
+        } finally {
+            MDC.remove(random);
+        }
+    }
+
+    @Test
+    public void returnContextWhenGetCalled() {
+
+        String random = UUID.randomUUID().toString();
+
+        try {
+            LoggingContext.put(random, random);
+            assertEquals(random, MDC.get(random));
+            assertEquals(random, LoggingContext.get(random));
+        } finally {
+            MDC.remove(random);
+        }
+    }
+
+    @Test
+    public void removeContextWhenRemoveCalled() {
+
+        String random = UUID.randomUUID().toString();
+
+        try {
+            LoggingContext.put(random, random);
+            LoggingContext.remove(random);
+            assertNull(MDC.get(random));
+            assertNull(LoggingContext.get(random));
+        } finally {
+            MDC.remove(random);
+        }
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void throwNpeWhenPutWithKeyNull() {
+        LoggingContext.put(null, "---");
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void throwNpeWhenGetWithKeyNull() {
+        LoggingContext.get(null);
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void throwNpeWhenRemoveWithKeyNull() {
+        LoggingContext.remove(null);
+    }
+}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/RunnableContextPropagationTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/RunnableContextPropagationTest.java
new file mode 100644 (file)
index 0000000..a617abd
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright © 2016-2017 European Support Limited
+ *
+ * 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 org.openecomp.sdc.logging.slf4j;
+
+import org.openecomp.sdc.logging.provider.LoggingContextService;
+import org.testng.annotations.Test;
+
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * @author EVITALIY
+ * @since 08 Jan 18
+ */
+public class RunnableContextPropagationTest extends BaseContextPropagationTest {
+
+    @Test(enabled = ENABLED, dataProvider = PROVIDER)
+    public void contextNotCopiedToChildThreadByDefault(LoggingContextService ctx)
+            throws InterruptedException {
+
+        String random = UUID.randomUUID().toString();
+        ctx.put(KEY, random);
+
+        AtomicBoolean complete = new AtomicBoolean(false);
+
+        // create thread right away without copying context
+        Thread thread = new Thread(() -> {
+            assertNull(ctx.get(KEY), "Data unexpectedly copied to a child thread. " +
+                    "Are you using an old version of SLF4J diagnostic context implementation (e.g. logback)?");
+            complete.set(true);
+        });
+
+        thread.start();
+        thread.join();
+
+        assertEquals(ctx.get(KEY), random, EXPECT_RETAINED_IN_CURRENT);
+        assertTrue(complete.get(), EXPECT_INNER_RUN);
+    }
+
+    @Test(enabled = ENABLED, dataProvider = PROVIDER)
+    public void contextCopiedWhenToRunnableCalled(LoggingContextService ctx)
+            throws InterruptedException {
+
+        String uuid = UUID.randomUUID().toString();
+        ctx.put(KEY, uuid);
+
+        AtomicBoolean complete = new AtomicBoolean(false);
+
+        // pass the runnable to the context service first
+        Thread thread = new Thread(ctx.toRunnable(() -> {
+            assertEquals(ctx.get(KEY), uuid, EXPECT_PROPAGATED_TO_CHILD);
+            complete.set(true);
+        }));
+
+        thread.start();
+        thread.join();
+
+        assertEquals(ctx.get(KEY), uuid, EXPECT_RETAINED_IN_CURRENT);
+        assertTrue(complete.get(), EXPECT_INNER_RUN);
+    }
+
+    @Test(enabled = ENABLED, dataProvider = PROVIDER)
+    public void copiedContextRetainedEvenWhenAnotherPushed(LoggingContextService ctx)
+            throws InterruptedException {
+
+        String innerRandom = UUID.randomUUID().toString();
+        ctx.put(KEY, innerRandom);
+
+        AtomicBoolean innerComplete = new AtomicBoolean(false);
+
+        // should run with the context of main thread
+        Runnable inner = ctx.toRunnable(() -> {
+            assertEquals(ctx.get(KEY), innerRandom, EXPECT_PROPAGATED_TO_CHILD);
+            innerComplete.set(true);
+        });
+
+        // pushes its context, but the inner must run with its own context
+        AtomicBoolean outerComplete = new AtomicBoolean(false);
+        Thread outer = new Thread(() -> {
+            String outerUuid = UUID.randomUUID().toString();
+            ctx.put(KEY, outerUuid);
+            inner.run();
+            assertEquals(ctx.get(KEY), outerUuid, EXPECT_REPLACED_WITH_STORED);
+            outerComplete.set(true);
+        });
+
+        outer.start();
+        outer.join();
+
+        assertEquals(ctx.get(KEY), innerRandom, EXPECT_RETAINED_IN_CURRENT);
+        assertTrue(outerComplete.get(), EXPECT_OUTER_RUN);
+        assertTrue(innerComplete.get(), EXPECT_INNER_RUN);
+    }
+
+    @Test(enabled = ENABLED, dataProvider = PROVIDER)
+    public void contextRemainsEmptyWhenParentWasEmpty(LoggingContextService ctx)
+            throws InterruptedException {
+
+        ctx.remove(KEY);
+        assertNull(ctx.get(KEY), EXPECT_EMPTY);
+
+        final AtomicBoolean complete = new AtomicBoolean(false);
+        Runnable runnable = ctx.toRunnable(() -> {
+            assertNull(ctx.get(KEY), EXPECT_EMPTY);
+            complete.set(true);
+        });
+
+        Thread thread = new Thread(runnable);
+        thread.start();
+        thread.join();
+
+        assertNull(ctx.get(KEY), EXPECT_EMPTY);
+        assertTrue(complete.get(), EXPECT_INNER_RUN);
+    }
+
+    @Test(enabled = ENABLED, dataProvider = PROVIDER)
+    public void childThreadCleanedUpAfterRunnableRuns(LoggingContextService ctx)
+            throws Exception {
+
+        String innerRandom = UUID.randomUUID().toString();
+        ctx.put(KEY, innerRandom);
+
+        AtomicBoolean innerComplete = new AtomicBoolean(false);
+        // should run with the context of main thread
+        Runnable inner = ctx.toRunnable(() -> {
+            assertEquals(ctx.get(KEY), innerRandom, EXPECT_PROPAGATED_TO_CHILD);
+            innerComplete.set(true);
+        });
+
+        // pushes its own context, but runs the inner
+        AtomicBoolean outerComplete = new AtomicBoolean(false);
+        Thread outer = new Thread(() -> {
+            assertNull(ctx.get(KEY), EXPECT_NOT_COPIED);
+            inner.run();
+            assertNull(ctx.get(KEY), EXPECT_REMAIN_EMPTY);
+            outerComplete.set(true);
+        });
+
+        outer.start();
+        outer.join();
+
+        assertEquals(ctx.get(KEY), innerRandom, EXPECT_RETAINED_IN_PARENT);
+        assertTrue(outerComplete.get(), EXPECT_OUTER_RUN);
+        assertTrue(innerComplete.get(), EXPECT_INNER_RUN);
+    }
+
+    @Test(enabled = ENABLED, dataProvider = PROVIDER)
+    public void childThreadCleanedUpAfterException(LoggingContextService ctx)
+            throws Exception {
+
+        String innerRandom = UUID.randomUUID().toString();
+        ctx.put(KEY, innerRandom);
+
+        // should run with the context of main thread
+        AtomicBoolean innerComplete = new AtomicBoolean(false);
+        Runnable inner = ctx.toRunnable(() -> {
+            assertEquals(ctx.get(KEY), innerRandom, EXPECT_PROPAGATED_TO_CHILD);
+            innerComplete.set(true);
+            throw new IllegalArgumentException();
+        });
+
+        // pushes its own context, but runs the inner runnable
+        AtomicBoolean outerComplete = new AtomicBoolean(false);
+        AtomicBoolean exceptionThrown = new AtomicBoolean(false);
+        Thread outer = new Thread(() -> {
+
+            String outerUuid = UUID.randomUUID().toString();
+            ctx.put(KEY, outerUuid);
+            assertEquals(ctx.get(KEY), outerUuid, EXPECT_POPULATED);
+
+            try {
+                inner.run();
+            } catch (IllegalArgumentException e) {
+                exceptionThrown.set(true);
+            } finally {
+                assertEquals(ctx.get(KEY), outerUuid, EXPECT_REVERTED_ON_EXCEPTION);
+                outerComplete.set(true);
+            }
+        });
+
+        outer.start();
+        outer.join();
+
+        assertEquals(ctx.get(KEY), innerRandom, EXPECT_RETAINED_IN_PARENT);
+        assertTrue(outerComplete.get(), EXPECT_OUTER_RUN);
+        assertTrue(innerComplete.get(), EXPECT_INNER_RUN);
+        assertTrue(exceptionThrown.get(), EXPECT_EXCEPTION_FROM_INNER);
+    }
+
+
+}
\ No newline at end of file