Logging refactoring and global context 95/38195/1
authorvempo <vitaliy.emporopulo@amdocs.com>
Fri, 23 Mar 2018 20:10:30 +0000 (23:10 +0300)
committervempo <vitaliy.emporopulo@amdocs.com>
Fri, 23 Mar 2018 20:10:30 +0000 (23:10 +0300)
Implemented global context (instance ID, host address).
Refactored the logging context API for clarity and
separation of concerns. Added unit tests. Fixed Javadoc.

Change-Id: I6e29b7b3613aebf23112dc017490ad2828e0fb91
Issue-ID: SDC-772
Signed-off-by: vempo <vitaliy.emporopulo@amdocs.com>
26 files changed:
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/AuditData.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/ContextData.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/LoggingContext.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/spi/LoggingContextService.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/api/ContextDataTest.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/LoggingContextTest.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/pom.xml
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/GlobalLoggingContext.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/BaseMDCCopyingWrapper.java [deleted file]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/Context.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/ContextField.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/ContextProvider.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/GlobalContextProvider.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
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/MDCDelegate.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
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/RequestContextProvider.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
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/BaseContextPropagationTest.java [deleted file]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/CallableContextPropagationTest.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/ContextPropagationTestHelper.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/ContextTest.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/GlobalContextProviderTest.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
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/RequestContextProviderTest.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

index fdef45a..ecf795b 100644 (file)
@@ -27,8 +27,9 @@ package org.openecomp.sdc.logging.api;
  */
 public class AuditData {
 
-    // A concrete implementation interface was chosen over an interface to enable to gracefully
-    // add new fields without affecting client code
+    // A concrete implementation interface was chosen over an interface to enable to gracefully add new fields without
+    // affecting client code. Also, the builder can be implemented differently if needed, without affecting client code.
+    // For instance, it may delegate the population and instantiation to a service provider.
 
     private final long startTime;
     private final long endTime;
@@ -37,7 +38,7 @@ public class AuditData {
     private final String responseDescription;
     private final String clientIpAddress;
 
-    AuditData(final AuditDataBuilder builder) {
+    private AuditData(final AuditDataBuilder builder) {
         this.startTime = builder.startTime;
         this.endTime = builder.endTime;
         this.statusCode = builder.statusCode;
@@ -47,7 +48,7 @@ public class AuditData {
     }
 
     /**
-     * Begin timestamp of an API invocation
+     * Begin timestamp of an API invocation.
      *
      * @return timestamp
      */
@@ -56,7 +57,7 @@ public class AuditData {
     }
 
     /**
-     * End timestamp of an API invocation
+     * End timestamp of an API invocation.
      *
      * @return timestamp
      */
@@ -65,7 +66,7 @@ public class AuditData {
     }
 
     /**
-     * Result status of an API invocation
+     * Result status of an API invocation.
      *
      * @return protocol and application agnostic status code
      */
@@ -74,7 +75,7 @@ public class AuditData {
     }
 
     /**
-     * Application/protocol specific response status of an API invocation
+     * Application/protocol specific response status of an API invocation.
      *
      * @return response code
      */
@@ -83,7 +84,7 @@ public class AuditData {
     }
 
     /**
-     * Application/protocol specific response in a human-friendly way
+     * Application/protocol specific response in a human-friendly way.
      *
      * @return human-friendly response description
      */
@@ -92,7 +93,7 @@ public class AuditData {
     }
 
     /**
-     * IP address of the invoking client when available
+     * IP address of the invoking client when available.
      *
      * @return IP address
      */
@@ -102,15 +103,18 @@ public class AuditData {
 
     @Override
     public String toString() {
-        return "AuditData{startTime=" + startTime + ", endTime=" + endTime + ", statusCode=" + statusCode +
-            ", responseCode='" + responseCode + ", responseDescription=" + responseDescription + ", clientIpAddress="
-            + clientIpAddress + '}';
+        return "AuditData{startTime=" + startTime + ", endTime=" + endTime + ", statusCode=" + statusCode
+                + ", responseCode=" + responseCode + ", responseDescription=" + responseDescription
+                + ", clientIpAddress=" + clientIpAddress + '}';
     }
 
     public static AuditDataBuilder builder() {
         return new AuditDataBuilder();
     }
 
+    /**
+     * Fluent API for building audit data.
+     */
     public static class AuditDataBuilder {
 
         private long startTime;
@@ -123,7 +127,7 @@ public class AuditData {
         AuditDataBuilder() { /* package-private default constructor to hide the public one */ }
 
         /**
-         * Begin timestamp of an activity being audited
+         * Begin timestamp of an activity being audited.
          *
          * @param startTime local timestamp, usually received from {@link System#currentTimeMillis()}
          * @return this builder for fluent API
@@ -134,7 +138,7 @@ public class AuditData {
         }
 
         /**
-         * End timestamp of an activity being audited
+         * End timestamp of an activity being audited.
          *
          * @param endTime local timestamp, usually received from {@link System#currentTimeMillis()}
          * @return this builder for fluent API
@@ -191,7 +195,7 @@ public class AuditData {
         }
 
         /**
-         * Create an instance of {@link AuditData}
+         * Create an instance of {@link AuditData}.
          *
          * @return a populated instance of audit data
          */
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/ContextData.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/ContextData.java
new file mode 100644 (file)
index 0000000..ea777d5
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright © 2016-2018 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;
+
+/**
+ * Builder to populate logging context data. This includes only data known to an application, and not otherwise
+ * available to the logging framework.
+ *
+ * @author evitaliy
+ * @since Mar 22, 2018
+ */
+public class ContextData {
+
+    private final String requestId;
+    private final String serviceName;
+    private final String partnerName;
+
+    private ContextData(final ContextDataBuilder builder) {
+        this.requestId = builder.requestId;
+        this.serviceName = builder.serviceName;
+        this.partnerName = builder.partnerName;
+    }
+
+    /**
+     * Uniques request ID received from a calling peer, or created.
+     *
+     * @return unique identifier of a request
+     */
+    public String getRequestId() {
+        return requestId;
+    }
+
+    /**
+     * Service, in the context of which logs will be written.
+     *
+     * @return a string that identifies an exposed service
+     */
+    public String getServiceName() {
+        return serviceName;
+    }
+
+    /**
+     * Identifies a peer (if any).
+     *
+     * @return identification of a calling partner
+     */
+    public String getPartnerName() {
+        return partnerName;
+    }
+
+    @Override
+    public String toString() {
+        return "ContextData{responseCode=" + requestId + ", responseDescription=" + serviceName
+                + ", clientIpAddress=" + partnerName + '}';
+    }
+
+    public static ContextDataBuilder builder() {
+        return new ContextDataBuilder();
+    }
+
+    /**
+     * Fluent API for building context data.
+     */
+    public static class ContextDataBuilder {
+
+        private String requestId;
+        private String serviceName;
+        private String partnerName;
+
+        ContextDataBuilder() { /* package-private default constructor to hide the public one */ }
+
+        /**
+         * Unique request ID, most likely propagated via an HTTP header.
+         *
+         * @param requestId generated or propagated request ID.
+         * @return this builder for fluent API
+         */
+        public ContextDataBuilder requestId(final String requestId) {
+            this.requestId = requestId;
+            return this;
+        }
+
+        /**
+         * Name of a invoked API, by which it can be identified in the application.
+         *
+         * @param serviceName human-friendly service identifier
+         * @return this builder for fluent API
+         */
+        public ContextDataBuilder serviceName(final String serviceName) {
+            this.serviceName = serviceName;
+            return this;
+        }
+
+        /**
+         * Identifier of a peer calling a service {@link #serviceName(String)}).
+         *
+         * @param partnerName an string that is received from a calling peer and can identify it
+         * @return this builder for fluent API
+         */
+        public ContextDataBuilder partnerName(final String partnerName) {
+            this.partnerName = partnerName;
+            return this;
+        }
+
+        /**
+         * Create an instance of {@link ContextData}.
+         *
+         * @return a populated instance of audit data
+         */
+        public ContextData build() {
+            return new ContextData(this);
+        }
+    }
+}
index 879d9cf..2dc4afd 100644 (file)
 
 package org.openecomp.sdc.logging.api;
 
-import org.openecomp.sdc.logging.spi.LoggingContextService;
-
 import java.util.Objects;
 import java.util.concurrent.Callable;
+import org.openecomp.sdc.logging.spi.LoggingContextService;
 
 /**
  * <p>Factory to hide a concrete, framework-specific implementation of diagnostic context.</p>
@@ -44,16 +43,8 @@ public class LoggingContext {
         // prevent instantiation
     }
 
-    public static void putRequestId(String requestId) {
-        SERVICE.putRequestId(requestId);
-    }
-
-    public static void putServiceName(String serviceName) {
-        SERVICE.putServiceName(serviceName);
-    }
-
-    public static void putPartnerName(String partnerName) {
-        SERVICE.putPartnerName(partnerName);
+    public static void put(ContextData contextData) {
+        SERVICE.put(contextData);
     }
 
     public static void clear() {
@@ -71,18 +62,8 @@ public class LoggingContext {
     private static class NoOpLoggingContextService implements LoggingContextService {
 
         @Override
-        public void putRequestId(String requestId) {
-            Objects.requireNonNull(requestId, "Request ID cannot be null");
-        }
-
-        @Override
-        public void putServiceName(String serviceName) {
-            Objects.requireNonNull(serviceName, "Service name cannot be null");
-        }
-
-        @Override
-        public void putPartnerName(String partnerName) {
-            Objects.requireNonNull(partnerName, "Partner name cannot be null");
+        public void put(ContextData contextData) {
+            Objects.requireNonNull(contextData, "Context data cannot be null");
         }
 
         @Override
index 07b93c1..b90ce63 100644 (file)
@@ -17,6 +17,7 @@
 package org.openecomp.sdc.logging.spi;
 
 import java.util.concurrent.Callable;
+import org.openecomp.sdc.logging.api.ContextData;
 
 /**
  * Should be used to implement a framework-specific mechanism of managing a per-thread diagnostic context (for instance
@@ -33,14 +34,13 @@ import java.util.concurrent.Callable;
 
 public interface LoggingContextService {
 
-    void putRequestId(String requestId);
-
-    void putServiceName(String serviceName);
-
-    void putPartnerName(String partnerName);
+    /**
+     * Put logging data on the context.
+     */
+    void put(ContextData contextData);
 
     /**
-     * Clear logging thread context
+     * Clear logging thread context.
      */
     void clear();
 
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/api/ContextDataTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/api/ContextDataTest.java
new file mode 100644 (file)
index 0000000..8a173b4
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2016-2018 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 static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+import org.testng.annotations.Test;
+
+/**
+ * Unit-testing context data builder.
+ *
+ * @author evitaliy
+ * @since 04 Mar 18
+ */
+public class ContextDataTest {
+
+    @Test
+    public void allPropertiesReadWhenPopulated() {
+
+        final String serviceName = "running-service";
+        final String partnerName = "remote-partner";
+        final String requestId = "123412341234";
+
+        ContextData data = ContextData.builder()
+            .serviceName(serviceName).partnerName(partnerName).requestId(requestId).build();
+
+        assertEquals(data.getRequestId(), requestId);
+        assertEquals(data.getServiceName(), serviceName);
+        assertEquals(data.getPartnerName(), partnerName);
+    }
+
+    @Test
+    public void allPropertiesEmptyWhenUnpopulated() {
+        ContextData data = ContextData.builder().build();
+        assertNull(data.getRequestId());
+        assertNull(data.getServiceName());
+        assertNull(data.getPartnerName());
+    }
+}
\ No newline at end of file
index bfc53a3..3e7bbe1 100644 (file)
@@ -24,7 +24,9 @@ import java.util.concurrent.Callable;
 import org.testng.annotations.Test;
 
 /**
- * @author EVITALIY
+ * Unit-testing default context service implementation.
+ *
+ * @author evitaliy
  * @since 08 Jan 18
  */
 public class LoggingContextTest {
@@ -39,18 +41,8 @@ public class LoggingContextTest {
     }
 
     @Test(expectedExceptions = NullPointerException.class)
-    public void throwNpeWhenPartnerNameIsNull() {
-        LoggingContext.putPartnerName(null);
-    }
-
-    @Test(expectedExceptions = NullPointerException.class)
-    public void throwNpeWhenServiceNameIsNull() {
-        LoggingContext.putServiceName(null);
-    }
-
-    @Test(expectedExceptions = NullPointerException.class)
-    public void throwNpeWhenRequestIdIsNull() {
-        LoggingContext.putRequestId(null);
+    public void throwNpeWhenContextIsNull() {
+        LoggingContext.put(null);
     }
 
     @Test
index 2bbd0d2..14f54b0 100644 (file)
         <dependency>
             <groupId>ch.qos.logback</groupId>
             <artifactId>logback-classic</artifactId>
-            <version>${logback.version}</version>
+            <!--
+                Use latest version for proper testing of context propagation.
+                Will not affect the application because the scope is "provided".
+            -->
+            <version>1.2.3</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -41,7 +45,7 @@
             <version>${servlet.version}</version>
             <scope>provided</scope>
         </dependency>
-               
+
                <!-- for testing -->
         <dependency>
             <groupId>org.testng</groupId>
index 95dc52c..a708ed6 100644 (file)
 
 package org.openecomp.sdc.logging;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
-import java.nio.charset.StandardCharsets;
-import java.util.Properties;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.prefs.BackingStoreException;
@@ -38,45 +33,20 @@ import java.util.prefs.Preferences;
 @SuppressWarnings({"UseOfSystemOutOrSystemErr", "CallToPrintStackTrace", "squid:S106", "squid:S1148"})
 public class GlobalLoggingContext {
 
-    private static final String APPLICATION_ID_KEY = "ApplicationId";
-
-    private static final String CONFIGURATION_RESOURCE = "META-INF/logging/logger.properties";
-
-    @SuppressWarnings("squid:S1075")
-    private static final String ID_PREFERENCES_PATH = "/logging/instance/uuid";
-
-    private static final String APP_DISTINGUISHER_KEY = "app.distinguisher";
-
     // should be cashed to avoid low-level call, but with a timeout to account for IP or FQDN changes
-    private static final HostAddressCache HOST_ADDRESS = new HostAddressCache();
-
-    private static final String DISTINGUISHER;
+    private static final HostAddressCache HOST_ADDRESS_CACHE = new HostAddressCache();
 
-    private static final String APPLICATION_ID;
+    @SuppressWarnings("squid:S1075")
+    private static final String INSTANCE_UUID_PREFERENCES_PATH = "/logging/instance/uuid";
 
     private static final String INSTANCE_ID;
 
     static {
-        APPLICATION_ID = System.getProperty(APPLICATION_ID_KEY);
-        DISTINGUISHER = readDistinguisher();
         INSTANCE_ID = readInstanceId();
     }
 
-    private GlobalLoggingContext() { /* prevent instantiation */ }
-
-    public static String getApplicationId() {
-        return APPLICATION_ID;
-    }
-
-    /**
-     * A distinguisher to allow separation of logs created by applications running with the same configuration, but
-     * different class-loaders. For instance, when multiple web application are running in the same container and their
-     * logger configuration is passed at the JVM level.
-     *
-     * @return application distinguisher defined in a properties file
-     */
-    public static String getDistinguisher() {
-        return DISTINGUISHER;
+    private GlobalLoggingContext() {
+        // prevent instantiation
     }
 
     /**
@@ -96,25 +66,22 @@ public class GlobalLoggingContext {
      * @return local host address, may be null if could not be read for some reason
      */
     public static InetAddress getHostAddress() {
-        return HOST_ADDRESS.get();
+        return HOST_ADDRESS_CACHE.get();
     }
 
     private static String readInstanceId() {
 
-        String appId = System.getProperty(APPLICATION_ID_KEY);
-        String key = ID_PREFERENCES_PATH + (appId == null ? "" : "/" + appId);
-
         try {
 
-            // By default, this will be ~/.java/.userPrefs/prefs.xml
+            // On Linux, by default this will be ~/.java/.userPrefs/prefs.xml
             final Preferences preferences = Preferences.userRoot();
-            String existingId = preferences.get(key, null);
+            String existingId = preferences.get(INSTANCE_UUID_PREFERENCES_PATH, null);
             if (existingId != null) {
                 return existingId;
             }
 
             String newId = UUID.randomUUID().toString();
-            preferences.put(key, newId);
+            preferences.put(INSTANCE_UUID_PREFERENCES_PATH, newId);
             preferences.flush();
             return newId;
 
@@ -125,35 +92,6 @@ public class GlobalLoggingContext {
         }
     }
 
-    private static String readDistinguisher() {
-
-        try {
-            Properties properties = loadConfiguration();
-            return properties.getProperty(APP_DISTINGUISHER_KEY, "");
-        } catch (IOException e) {
-            e.printStackTrace(); // can't write to a log
-            return "";
-        }
-    }
-
-    private static Properties loadConfiguration() throws IOException {
-
-        Properties properties = new Properties();
-
-        try (InputStream is = Thread.currentThread().getContextClassLoader()
-            .getResourceAsStream(CONFIGURATION_RESOURCE)) {
-
-            if (is == null) {
-                return properties;
-            }
-
-            try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
-                properties.load(reader);
-                return properties;
-            }
-        }
-    }
-
     private static class HostAddressCache {
 
         private static final long REFRESH_TIME = 60000L;
@@ -161,7 +99,7 @@ public class GlobalLoggingContext {
         private final AtomicLong lastUpdated = new AtomicLong(0L);
         private InetAddress hostAddress;
 
-        public InetAddress get() {
+        InetAddress get() {
 
             long current = System.currentTimeMillis();
             if (current - lastUpdated.get() > REFRESH_TIME) {
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
deleted file mode 100644 (file)
index d667ff7..0000000
+++ /dev/null
@@ -1,73 +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.slf4j;
-
-import java.util.EnumMap;
-import java.util.Map;
-import org.openecomp.sdc.logging.slf4j.SLF4JLoggingServiceProvider.ContextField;
-import org.slf4j.MDC;
-
-/**
- * Because we don't know which information should be carried over from MDC, and which shouldn't, copy just the keys that
- * the logging service uses.
- *
- * @author evitaliy
- * @since 08 Jan 2018
- */
-abstract class BaseMDCCopyingWrapper {
-
-    private final Map<ContextField, String> context;
-
-    BaseMDCCopyingWrapper() {
-        this.context = fromMdc();
-    }
-
-    final Map<ContextField, String> replace() {
-        Map<ContextField, String> old = fromMdc();
-        toMdc(this.context);
-        return old;
-    }
-
-    final void revert(Map<ContextField, String> old) {
-        toMdc(old);
-    }
-
-    private Map<ContextField, String> fromMdc() {
-
-        Map<ContextField, String> copy = new EnumMap<>(ContextField.class);
-        for (ContextField k : ContextField.values()) {
-            String v = MDC.get(k.asKey());
-            if (v != null) {
-                copy.put(k, v);
-            }
-        }
-
-        return copy;
-    }
-
-    private static void toMdc(Map<ContextField, String> context) {
-
-        for (ContextField k : ContextField.values()) {
-            String v = context.get(k);
-            if (v != null) {
-                MDC.put(k.asKey(), v);
-            } else {
-                MDC.remove(k.asKey());
-            }
-        }
-    }
-}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/Context.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/Context.java
new file mode 100644 (file)
index 0000000..25784fe
--- /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 java.util.Map;
+
+/**
+ * Does not store a state other than initial context values. Objects of this class may be reused by multiple threads,
+ * therefore they must be stateless to prevent inadvertent exchange of context values between threads.
+ *
+ * @author evitaliy
+ * @since 08 Jan 2018
+ */
+final class Context {
+
+    private final Map<ContextField, String> originalCtx;
+
+    Context() {
+        this.originalCtx = MDCDelegate.copy();
+    }
+
+    /**
+     * Pushes the initial context onto current thread, and returns the existing context. The result cannot be stored as
+     * local state (see the class comments), and must be kept in a local variable to work properly.
+     *
+     * @return previous context values
+     */
+    final Map<ContextField, String> replace() {
+        Map<ContextField, String> old = MDCDelegate.copy();
+        MDCDelegate.replace(this.originalCtx);
+        return old;
+    }
+
+    /**
+     * Pushes an old context onto current thread.
+     *
+     * @param old copy of the old context returned by {@link #replace()}
+     */
+    final void revert(Map<ContextField, String> old) {
+        MDCDelegate.replace(old);
+    }
+}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/ContextField.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/ContextField.java
new file mode 100644 (file)
index 0000000..6aa689b
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2016-2018 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;
+
+/**
+ * MDC fields to work with - populate, clear, copy.
+ *
+ * @author evitaliy
+ * @since 23 Mar 2018
+ */
+enum ContextField {
+
+    REQUEST_ID("RequestId"),
+    SERVICE_NAME("ServiceName"),
+    PARTNER_NAME("PartnerName"),
+    INSTANCE_ID("InstanceId"),
+    SERVER("Server"),
+    SERVER_IP_ADDRESS("ServerIpAddress");
+
+    private final String key;
+
+    ContextField(String key) {
+        this.key = key;
+    }
+
+    public String asKey() {
+        return key;
+    }
+}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/ContextProvider.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/ContextProvider.java
new file mode 100644 (file)
index 0000000..f9a7144
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright © 2016-2018 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;
+
+/**
+ * Abstracts a source of MDC values.
+ *
+ * @author evitaliy
+ * @since 23 Mar 2018
+ */
+interface ContextProvider {
+    Map<ContextField, String> values();
+}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/GlobalContextProvider.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/GlobalContextProvider.java
new file mode 100644 (file)
index 0000000..a415e22
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2016-2018 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.net.InetAddress;
+import java.util.EnumMap;
+import java.util.Map;
+import org.openecomp.sdc.logging.GlobalLoggingContext;
+
+/**
+ * Maps global logging context to corresponding MDC fields.
+ *
+ * @author evitaliy
+ * @since 23 Mar 2018
+ */
+class GlobalContextProvider implements ContextProvider {
+
+    @Override
+    public Map<ContextField, String> values() {
+
+        Map<ContextField, String> values = new EnumMap<>(ContextField.class);
+        values.put(ContextField.INSTANCE_ID, GlobalLoggingContext.getInstanceId());
+
+        InetAddress hostAddress = GlobalLoggingContext.getHostAddress();
+        if (hostAddress != null) {
+            values.put(ContextField.SERVER, hostAddress.getHostName());
+            values.put(ContextField.SERVER_IP_ADDRESS, hostAddress.getHostAddress());
+        }
+
+        return values;
+    }
+}
index 07d0f93..84aa256 100644 (file)
@@ -18,30 +18,32 @@ package org.openecomp.sdc.logging.slf4j;
 
 import java.util.Map;
 import java.util.concurrent.Callable;
-import org.openecomp.sdc.logging.slf4j.SLF4JLoggingServiceProvider.ContextField;
 
 /**
- * @author EVITALIY
+ * Carries MDC values over to a Callable from the instantiating thread to the moment the callable will run.
+ *
+ * @author evitaliy
  * @since 08 Jan 18
  */
-class MDCCallableWrapper<V> extends BaseMDCCopyingWrapper implements Callable<V> {
+class MDCCallableWrapper<V> implements Callable<V> {
+
+    private final Context context = new Context();
 
     private final Callable<V> task;
 
     MDCCallableWrapper(Callable<V> task) {
-        super();
         this.task = task;
     }
 
     @Override
     public V call() throws Exception {
 
-        Map<ContextField, String> oldContext = replace();
+        Map<ContextField, String> oldContext = context.replace();
 
         try {
             return task.call();
         } finally {
-            revert(oldContext);
+            context.revert(oldContext);
         }
     }
 }
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/MDCDelegate.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/MDCDelegate.java
new file mode 100644 (file)
index 0000000..8d719a2
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright © 2016-2018 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.EnumMap;
+import java.util.Map;
+import org.slf4j.MDC;
+
+/**
+ * Because we don't know which information should be carried over from MDC, and which shouldn't, copy just the keys that
+ * the logging service uses.
+ *
+ * @author evitaliy
+ * @since 23 Mar 2018
+ */
+class MDCDelegate {
+
+    private MDCDelegate() {
+        // static methods only, prevent instantiation
+    }
+
+    /**
+     * Get a copy of logging MDC fields.
+     */
+    static Map<ContextField, String> copy() {
+
+        Map<ContextField, String> copy = new EnumMap<>(ContextField.class);
+        for (ContextField k : ContextField.values()) {
+            String v = MDC.get(k.asKey());
+            if (v != null) {
+                copy.put(k, v);
+            }
+        }
+
+        return copy;
+    }
+
+    /**
+     * Entirely replaces the logging MDC context with the content of the argument. Logging keys that are not present in
+     * the input map will be cleared from MDC.
+     */
+    static void replace(Map<ContextField, String> values) {
+
+        for (ContextField key : ContextField.values()) {
+            updateKey(key, values.get(key));
+        }
+    }
+
+    /**
+     * Push data by multiple data providers on MDC.
+     */
+    static void put(ContextProvider... dataProviders) {
+
+        clear();
+
+        for (ContextProvider provider : dataProviders) {
+            push(provider.values());
+        }
+    }
+
+    /**
+     * Updates the logging MDC context with the content of the argument. Logging keys that are not present in the input
+     * map will remain "as is", keys with null values will be cleared from MDC.
+     */
+    private static void push(Map<ContextField, String> values) {
+
+        for (Map.Entry<ContextField, String> entry : values.entrySet()) {
+            updateKey(entry.getKey(), entry.getValue());
+        }
+    }
+
+    private static void updateKey(ContextField key, String value) {
+
+        if (value != null) {
+            MDC.put(key.asKey(), value);
+        } else {
+            MDC.remove(key.asKey());
+        }
+    }
+
+    static void clear() {
+
+        for (ContextField field : ContextField.values()) {
+            MDC.remove(field.asKey());
+        }
+    }
+}
index e1b8f1e..9d93246 100644 (file)
 package org.openecomp.sdc.logging.slf4j;
 
 import java.util.Map;
-import org.openecomp.sdc.logging.slf4j.SLF4JLoggingServiceProvider.ContextField;
 
 /**
- * @author EVITALIY
+ * Carries MDC values over to a Runnable from the instantiating thread to the moment the callable will run.
+ *
+ * @author evitaliy
  * @since 08 Jan 18
  */
-class MDCRunnableWrapper extends BaseMDCCopyingWrapper implements Runnable {
+class MDCRunnableWrapper implements Runnable {
+
+    private final Context context = new Context();
 
     private final Runnable task;
 
     MDCRunnableWrapper(Runnable task) {
-        super();
         this.task = task;
     }
 
     @Override
     public void run() {
 
-        Map<ContextField, String> oldContext = replace();
+        Map<ContextField, String> oldContext = context.replace();
 
         try {
             task.run();
         } finally {
-            revert(oldContext);
+            context.revert(oldContext);
         }
     }
 }
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/RequestContextProvider.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/RequestContextProvider.java
new file mode 100644 (file)
index 0000000..d79771c
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright © 2016-2018 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.EnumMap;
+import java.util.Map;
+import org.openecomp.sdc.logging.api.ContextData;
+
+/**
+ * Maps request data sent to the context service to corresponding MDC fields.
+ *
+ * @author evitaliy
+ * @since 23 Mar 2018
+ */
+class RequestContextProvider implements ContextProvider {
+
+    private final ContextData data;
+
+    RequestContextProvider(ContextData contextData) {
+        this.data = contextData;
+    }
+
+    @Override
+    public Map<ContextField, String> values() {
+
+        Map<ContextField, String> values = new EnumMap<>(ContextField.class);
+
+        putIfNotNull(values, ContextField.REQUEST_ID, data.getRequestId());
+        putIfNotNull(values, ContextField.SERVICE_NAME, data.getServiceName());
+        putIfNotNull(values, ContextField.PARTNER_NAME, data.getPartnerName());
+
+        return values;
+    }
+
+    private void putIfNotNull(Map<ContextField, String> values, ContextField field, String value) {
+
+        if (value != null) {
+            values.put(field, value);
+        }
+    }
+}
index 6f69aae..1a5d6fa 100644 (file)
 
 package org.openecomp.sdc.logging.slf4j;
 
-import static org.openecomp.sdc.logging.slf4j.SLF4JLoggingServiceProvider.ContextField.PARTNER_NAME;
-import static org.openecomp.sdc.logging.slf4j.SLF4JLoggingServiceProvider.ContextField.REQUEST_ID;
-import static org.openecomp.sdc.logging.slf4j.SLF4JLoggingServiceProvider.ContextField.SERVICE_NAME;
-
 import java.util.Objects;
 import java.util.concurrent.Callable;
+import org.openecomp.sdc.logging.api.ContextData;
 import org.openecomp.sdc.logging.api.Logger;
 import org.openecomp.sdc.logging.spi.LoggingServiceProvider;
-import org.slf4j.MDC;
 
 /**
+ * Uses SLF4J as backend for logging service.
+ *
  * @author evitaliy
  * @since 13 Sep 2016
  */
 public class SLF4JLoggingServiceProvider implements LoggingServiceProvider {
 
-    enum ContextField {
-
-        REQUEST_ID("RequestId"),
-        SERVICE_NAME("ServiceName"),
-        PARTNER_NAME("PartnerName");
-
-        private final String key;
-
-        ContextField(String key) {
-            this.key = key;
-        }
-
-        String asKey() {
-            return key;
-        }
-    }
-
     @Override
     public Logger getLogger(String className) {
         Objects.requireNonNull(className, "Name cannot be null");
@@ -62,29 +43,14 @@ public class SLF4JLoggingServiceProvider implements LoggingServiceProvider {
     }
 
     @Override
-    public void putRequestId(String requestId) {
-        put(REQUEST_ID.key, requestId);
-    }
-
-    @Override
-    public void putServiceName(String serviceName) {
-        put(SERVICE_NAME.key, serviceName);
-    }
-
-    @Override
-    public void putPartnerName(String partnerName) {
-        put(PARTNER_NAME.key, partnerName);
+    public void put(ContextData contextData) {
+        Objects.requireNonNull(contextData, "Context data cannot be null");
+        MDCDelegate.put(new RequestContextProvider(contextData), new GlobalContextProvider());
     }
 
     @Override
     public void clear() {
-        for (ContextField s : ContextField.values()) {
-            MDC.remove(s.key);
-        }
-    }
-
-    private void put(String key, String value) {
-        MDC.put(key, Objects.requireNonNull(value, key));
+        MDCDelegate.clear();
     }
 
     @Override
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
deleted file mode 100644 (file)
index 52a794f..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright © 2016-2018 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.spi.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 logback implementation is being used.
-    // Context propagation 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 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 putRequestId(String requestId) {
-            LoggingContext.putRequestId(requestId);
-        }
-
-        @Override
-        public void putServiceName(String serviceName) {
-            LoggingContext.putServiceName(serviceName);
-        }
-
-        @Override
-        public void putPartnerName(String partnerName) {
-            LoggingContext.putPartnerName(partnerName);
-        }
-
-        @Override
-        public void clear() {
-            LoggingContext.clear();
-        }
-
-        @Override
-        public Runnable copyToRunnable(Runnable runnable) {
-            return LoggingContext.copyToRunnable(runnable);
-        }
-
-        @Override
-        public <V> Callable<V> copyToCallable(Callable<V> callable) {
-            return LoggingContext.copyToCallable(callable);
-        }
-
-        @Override
-        public String toString() {
-            return this.getClass().getName();
-        }
-    }
-}
index 58d52bf..897a871 100644 (file)
 
 package org.openecomp.sdc.logging.slf4j;
 
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_EMPTY;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_EXCEPTION_FROM_INNER;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_INNER_RUN;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_NOT_COPIED;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_OUTER_RUN;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_POPULATED;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_PROPAGATED_TO_CHILD;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_REMAIN_EMPTY;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_REPLACED_WITH_STORED;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_RETAINED_IN_CURRENT;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_RETAINED_IN_PARENT;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_REVERTED_ON_EXCEPTION;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.IS_SUITABLE_LOGBACK_VERSION;
 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.assertContextEmpty;
 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.assertContextFields;
 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.putUniqueValues;
@@ -28,24 +41,28 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
-import org.openecomp.sdc.logging.slf4j.SLF4JLoggingServiceProvider.ContextField;
 import org.openecomp.sdc.logging.spi.LoggingContextService;
 import org.testng.annotations.Test;
 
 /**
+ * Tests propagation of logging fields to Callable via the logging service.
+ * 
  * @author evitaliy
  * @since 08 Jan 18
  */
-public class CallableContextPropagationTest extends BaseContextPropagationTest {
+@SuppressWarnings("DefaultAnnotationParam") // see the comment to ENABLED
+public class CallableContextPropagationTest {
 
-    @Test(enabled = ENABLED, dataProvider = PROVIDER)
-    public void testContextPropagated(LoggingContextService ctx) throws Exception {
+    private final LoggingContextService ctxService = new SLF4JLoggingServiceProvider();
 
-        Map<ContextField, String> values = putUniqueValues(ctx);
+    @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
+    public void testContextPropagated() throws Exception {
+
+        Map<ContextField, String> values = putUniqueValues();
         AtomicBoolean complete = new AtomicBoolean(false);
 
         // pass the callable to the context service first
-        execute(ctx.copyToCallable(() -> {
+        execute(ctxService.copyToCallable(() -> {
             assertContextFields(values, EXPECT_PROPAGATED_TO_CHILD);
             complete.set(true);
             return null;
@@ -55,14 +72,14 @@ public class CallableContextPropagationTest extends BaseContextPropagationTest {
         assertTrue(complete.get(), EXPECT_INNER_RUN);
     }
 
-    @Test(enabled = ENABLED, dataProvider = PROVIDER)
-    public void testContextReplacement(LoggingContextService ctx) throws Exception {
+    @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
+    public void testContextReplacement() throws Exception {
 
-        Map<ContextField, String> innerValues = putUniqueValues(ctx);
+        Map<ContextField, String> innerValues = putUniqueValues();
         AtomicBoolean innerComplete = new AtomicBoolean(false);
 
         // should run with the context of main thread
-        Callable inner = ctx.copyToCallable(() -> {
+        Callable inner = ctxService.copyToCallable(() -> {
             assertContextFields(innerValues, EXPECT_PROPAGATED_TO_CHILD);
             innerComplete.set(true);
             return null;
@@ -71,7 +88,7 @@ public class CallableContextPropagationTest extends BaseContextPropagationTest {
         // pushes its own context, but the inner must run with its own context
         AtomicBoolean outerComplete = new AtomicBoolean(false);
         execute(() -> {
-            Map<ContextField, String> outerValues = putUniqueValues(ctx);
+            Map<ContextField, String> outerValues = putUniqueValues();
             inner.call();
             assertContextFields(outerValues, EXPECT_REPLACED_WITH_STORED);
             outerComplete.set(true);
@@ -83,14 +100,14 @@ public class CallableContextPropagationTest extends BaseContextPropagationTest {
         assertTrue(innerComplete.get(), EXPECT_INNER_RUN);
     }
 
-    @Test(enabled = ENABLED, dataProvider = PROVIDER)
-    public void testContextRemainsEmpty(LoggingContextService ctx) throws Exception {
+    @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
+    public void testContextRemainsEmpty() throws Exception {
 
-        ctx.clear();
+        ctxService.clear();
         assertContextEmpty(EXPECT_EMPTY);
 
         final AtomicBoolean complete = new AtomicBoolean(false);
-        execute(ctx.copyToCallable(() -> {
+        execute(ctxService.copyToCallable(() -> {
             assertContextEmpty(EXPECT_EMPTY);
             complete.set(true);
             return null;
@@ -100,14 +117,14 @@ public class CallableContextPropagationTest extends BaseContextPropagationTest {
         assertTrue(complete.get(), EXPECT_INNER_RUN);
     }
 
-    @Test(enabled = ENABLED, dataProvider = PROVIDER)
-    public void testContextCleanedUp(LoggingContextService ctx) throws Exception {
+    @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
+    public void testContextCleanedUp() throws Exception {
 
-        Map<ContextField, String> innerValues = putUniqueValues(ctx);
+        Map<ContextField, String> innerValues = putUniqueValues();
 
         AtomicBoolean innerComplete = new AtomicBoolean(false);
         // should run with the context of main thread
-        Callable inner = ctx.copyToCallable((() -> {
+        Callable inner = ctxService.copyToCallable((() -> {
             assertContextFields(innerValues, EXPECT_PROPAGATED_TO_CHILD);
             innerComplete.set(true);
             return null;
@@ -128,14 +145,14 @@ public class CallableContextPropagationTest extends BaseContextPropagationTest {
         assertTrue(innerComplete.get(), EXPECT_INNER_RUN);
     }
 
-    @Test(enabled = ENABLED, dataProvider = PROVIDER)
-    public void testCleanupAfterError(LoggingContextService ctx) throws Exception {
+    @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
+    public void testCleanupAfterError() throws Exception {
 
-        Map<ContextField, String> innerValues = putUniqueValues(ctx);
+        Map<ContextField, String> innerValues = putUniqueValues();
 
         // should run with the context of main thread
         AtomicBoolean innerComplete = new AtomicBoolean(false);
-        Callable inner = ctx.copyToCallable(() -> {
+        Callable inner = ctxService.copyToCallable(() -> {
             assertContextFields(innerValues, EXPECT_PROPAGATED_TO_CHILD);
             innerComplete.set(true);
             throw new IllegalArgumentException();
@@ -146,7 +163,7 @@ public class CallableContextPropagationTest extends BaseContextPropagationTest {
         AtomicBoolean exceptionThrown = new AtomicBoolean(false);
         execute(() -> {
 
-            Map<ContextField, String> outerValues = putUniqueValues(ctx);
+            Map<ContextField, String> outerValues = putUniqueValues();
             assertContextFields(outerValues, EXPECT_POPULATED);
 
             try {
index 5112d37..fccc6ba 100644 (file)
 package org.openecomp.sdc.logging.slf4j;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
 
-import java.util.Collections;
 import java.util.EnumMap;
 import java.util.Map;
 import java.util.UUID;
-import org.openecomp.sdc.logging.slf4j.SLF4JLoggingServiceProvider.ContextField;
-import org.openecomp.sdc.logging.spi.LoggingContextService;
 import org.slf4j.MDC;
 
-/**
- * @author evitaliy
- * @since 08 Mar 18
- */
 class ContextPropagationTestHelper {
 
-    private static final Map<ContextField, String> EMPTY_CONTEXT =
-        Collections.unmodifiableMap(new EnumMap<>(ContextField.class));
+    // Set to "false" if an old version of logback implementation is being used.
+    // Explicit context propagation should be used when the context 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 IS_SUITABLE_LOGBACK_VERSION = true;
 
-    static Map<ContextField, String> putUniqueValues(LoggingContextService ctx) {
+    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";
 
-        Map<ContextField, String> values = new EnumMap<>(ContextField.class);
+    static Map<ContextField, String> putUniqueValues() {
 
-        String service = UUID.randomUUID().toString();
-        ctx.putServiceName(service);
-        values.put(ContextField.SERVICE_NAME, service);
+        Map<ContextField, String> values = new EnumMap<>(ContextField.class);
 
-        String partner = UUID.randomUUID().toString();
-        ctx.putPartnerName(partner);
-        values.put(ContextField.PARTNER_NAME, partner);
+        String random = UUID.randomUUID().toString();
 
-        String request = UUID.randomUUID().toString();
-        ctx.putRequestId(request);
-        values.put(ContextField.REQUEST_ID, request);
+        for (ContextField key : ContextField.values()) {
+            String value = random + "-" + key.name();
+            values.put(key, value);
+            MDC.put(key.asKey(), value);
+        }
 
         return values;
     }
@@ -62,6 +67,9 @@ class ContextPropagationTestHelper {
     }
 
     static void assertContextEmpty(String error) {
-        assertContextFields(EMPTY_CONTEXT, error);
+
+        for (ContextField key : ContextField.values()) {
+            assertNull(MDC.get(key.asKey()), error);
+        }
     }
 }
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/ContextTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/ContextTest.java
new file mode 100644 (file)
index 0000000..bed5cec
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2016-2018 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 static org.testng.Assert.assertEquals;
+
+import java.util.EnumMap;
+import java.util.Map;
+import org.slf4j.MDC;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+/**
+ * Unit-tests context replacement on MDC.
+ *
+ * @author evitaliy
+ * @since 23 Mar 2018
+ */
+public class ContextTest {
+
+    private static final ContextField FIELD = ContextField.SERVICE_NAME;
+    private static final String KEY = FIELD.asKey();
+    private static final String VALUE = "service-name-value";
+
+    @AfterMethod
+    public void clearMdc() {
+        MDC.clear();
+    }
+
+    @Test
+    public void mdcUpdatedWhenContextReplaced() {
+
+        MDC.put(KEY, VALUE);
+        Context context = new Context();
+        MDC.put(KEY, "modified-" + VALUE);
+
+        context.replace();
+        assertEquals(MDC.get(KEY), VALUE);
+    }
+
+    @Test
+    public void oldValueReturnedWhenContextReplaced() {
+
+        MDC.put(KEY, VALUE);
+        Map<ContextField, String> old = new Context().replace();
+        assertEquals(old.size(), 1);
+        assertEquals(old.get(FIELD), VALUE);
+    }
+
+    @Test
+    public void mdcUpdatedWhenContextReverted() {
+
+        Context context = new Context();
+        Map<ContextField, String> values = new EnumMap<>(ContextField.class);
+        values.put(FIELD, VALUE);
+        context.revert(values);
+        assertEquals(MDC.get(KEY), VALUE);
+    }
+}
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/GlobalContextProviderTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/GlobalContextProviderTest.java
new file mode 100644 (file)
index 0000000..d1e4be3
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2016-2018 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 static org.testng.Assert.assertNotNull;
+
+import java.util.Map;
+import org.testng.annotations.Test;
+
+/**
+ * Tests data supplied by the global logging context.
+ *
+ * @author evitaliy
+ * @since 23 Mar 2018
+ */
+
+public class GlobalContextProviderTest {
+
+    @Test
+    public void providedValuesPopulated() {
+        GlobalContextProvider provider = new GlobalContextProvider();
+        Map<ContextField, String> values = provider.values();
+        assertNotNull(values.get(ContextField.INSTANCE_ID));
+        assertNotNull(values.get(ContextField.SERVER));
+        assertNotNull(values.get(ContextField.SERVER_IP_ADDRESS));
+    }
+}
\ No newline at end of file
index 430d4d4..47386d4 100644 (file)
 
 package org.openecomp.sdc.logging.slf4j;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+import java.util.UUID;
+import org.openecomp.sdc.logging.api.ContextData;
 import org.openecomp.sdc.logging.api.LoggingContext;
-import org.openecomp.sdc.logging.slf4j.SLF4JLoggingServiceProvider.ContextField;
 import org.slf4j.MDC;
+import org.testng.annotations.AfterMethod;
 import org.testng.annotations.Test;
 
-import java.util.UUID;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNull;
-
 /**
+ * Unit-testing logging context service via its facade.
+ *
  * @author evitaliy
  * @since 12 Sep 2016
  */
 public class LoggingContextTest {
 
+    @AfterMethod
+    public void clearMdc() {
+        MDC.clear();
+    }
+
     @Test
     public void returnMdcWrapperWhenToRunnableCalled() {
         assertEquals(LoggingContext.copyToRunnable(() -> {}).getClass(), MDCRunnableWrapper.class);
@@ -57,18 +65,12 @@ public class LoggingContextTest {
 
         String value = UUID.randomUUID().toString();
 
-        try {
-            LoggingContext.putPartnerName(value);
-            LoggingContext.putServiceName(value);
-            LoggingContext.putRequestId(value);
-            LoggingContext.clear();
-
-            for (ContextField field : ContextField.values()) {
-                assertNull(MDC.get(field.asKey()));
-            }
+        ContextData context = ContextData.builder().partnerName(value).requestId(value).serviceName(value).build();
+        LoggingContext.put(context);
+        LoggingContext.clear();
 
-        } finally {
-            MDC.clear();
+        for (ContextField field : ContextField.values()) {
+            assertNull(MDC.get(field.asKey()));
         }
     }
 
@@ -78,68 +80,64 @@ public class LoggingContextTest {
         String randomValue = UUID.randomUUID().toString();
         String randomKey = "Key-" + randomValue;
 
-        try {
-
-            MDC.put(randomKey, randomValue);
-            LoggingContext.clear();
-            assertEquals(MDC.get(randomKey), randomValue);
-
-        } finally {
-            MDC.clear();
-        }
+        MDC.put(randomKey, randomValue);
+        LoggingContext.clear();
+        assertEquals(MDC.get(randomKey), randomValue);
     }
 
     @Test
     public void contextHasServiceNameWhenPut() {
 
         String random = UUID.randomUUID().toString();
-
-        try {
-            LoggingContext.putServiceName(random);
-            assertEquals(random, MDC.get(ContextField.SERVICE_NAME.asKey()));
-        } finally {
-            MDC.clear();
-        }
+        ContextData context = ContextData.builder().serviceName(random).build();
+        LoggingContext.put(context);
+        assertEquals(random, MDC.get(ContextField.SERVICE_NAME.asKey()));
     }
 
     @Test(expectedExceptions = NullPointerException.class)
-    public void throwNpeWhenServiceNameNull() {
-        LoggingContext.putServiceName(null);
+    public void throwNpeWhenContextDataNull() {
+        LoggingContext.put(null);
     }
 
     @Test
     public void contextHasRequestIdWhenPut() {
 
         String random = UUID.randomUUID().toString();
-
-        try {
-            LoggingContext.putRequestId(random);
-            assertEquals(random, MDC.get(ContextField.REQUEST_ID.asKey()));
-        } finally {
-            MDC.clear();
-        }
-    }
-
-    @Test(expectedExceptions = NullPointerException.class)
-    public void throwNpeWhenRequestIdNull() {
-        LoggingContext.putRequestId(null);
+        ContextData context = ContextData.builder().requestId(random).build();
+        LoggingContext.put(context);
+        assertEquals(random, MDC.get(ContextField.REQUEST_ID.asKey()));
     }
 
     @Test
     public void contextHasPartnerNameWhenPut() {
 
         String random = UUID.randomUUID().toString();
+        ContextData context = ContextData.builder().partnerName(random).build();
+        LoggingContext.put(context);
+        assertEquals(random, MDC.get(ContextField.PARTNER_NAME.asKey()));
+    }
 
-        try {
-            LoggingContext.putPartnerName(random);
-            assertEquals(random, MDC.get(ContextField.PARTNER_NAME.asKey()));
-        } finally {
-            MDC.clear();
-        }
+    @Test
+    public void contextHasServerHostWhenPopulated() {
+
+        ContextData context = ContextData.builder().build();
+        LoggingContext.put(context);
+        assertNotNull(MDC.get(ContextField.SERVER.asKey()));
     }
 
-    @Test(expectedExceptions = NullPointerException.class)
-    public void throwNpeWhenPartnerNameNull() {
-        LoggingContext.putPartnerName(null);
+    @Test
+    public void contextHasServerAddressWhenPopulated() {
+
+        ContextData context = ContextData.builder().build();
+        LoggingContext.put(context);
+        assertNotNull(MDC.get(ContextField.SERVER_IP_ADDRESS.asKey()));
+    }
+
+    @Test
+    public void contextHasInstanceIdWhenPopulated() {
+
+        ContextData context = ContextData.builder().build();
+        LoggingContext.put(context);
+        assertNotNull(MDC.get(ContextField.INSTANCE_ID.asKey()));
     }
 }
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/RequestContextProviderTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/RequestContextProviderTest.java
new file mode 100644 (file)
index 0000000..fa7926a
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2016-2018 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 static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import org.openecomp.sdc.logging.api.ContextData;
+import org.testng.annotations.Test;
+
+/**
+ * Unit-test retrieving values from client-provided request data.
+ *
+ * @author evitaliy
+ * @since 23 Mar 2018
+ */
+public class RequestContextProviderTest {
+
+    @Test
+    public void valuesEmptyWhenInputEmpty() {
+        RequestContextProvider provider = new RequestContextProvider(ContextData.builder().build());
+        assertTrue(provider.values().isEmpty());
+    }
+
+    @Test
+    public void serviceNameReturnedWhenSupplied() {
+        final String service = "supplied-service-name";
+        RequestContextProvider provider =
+                new RequestContextProvider(ContextData.builder().serviceName(service).build());
+        assertEquals(provider.values().get(ContextField.SERVICE_NAME), service);
+    }
+
+    @Test
+    public void partnerNameReturnedWhenSupplied() {
+        final String partner = "supplied-partner-name";
+        RequestContextProvider provider =
+                new RequestContextProvider(ContextData.builder().partnerName(partner).build());
+        assertEquals(provider.values().get(ContextField.PARTNER_NAME), partner);
+    }
+
+    @Test
+    public void requestIdReturnedWhenSupplied() {
+        final String request = "supplied-request-id";
+        RequestContextProvider provider =
+                new RequestContextProvider(ContextData.builder().requestId(request).build());
+        assertEquals(provider.values().get(ContextField.REQUEST_ID), request);
+    }
+}
index fcd1a56..6f988c2 100644 (file)
 
 package org.openecomp.sdc.logging.slf4j;
 
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_EMPTY;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_EXCEPTION_FROM_INNER;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_INNER_RUN;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_NOT_COPIED;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_OUTER_RUN;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_POPULATED;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_PROPAGATED_TO_CHILD;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_REMAIN_EMPTY;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_REPLACED_WITH_STORED;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_RETAINED_IN_CURRENT;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_RETAINED_IN_PARENT;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_REVERTED_ON_EXCEPTION;
+import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.IS_SUITABLE_LOGBACK_VERSION;
 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.assertContextEmpty;
 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.assertContextFields;
 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.putUniqueValues;
@@ -23,27 +36,30 @@ import static org.testng.Assert.assertTrue;
 
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
-import org.openecomp.sdc.logging.slf4j.SLF4JLoggingServiceProvider.ContextField;
 import org.openecomp.sdc.logging.spi.LoggingContextService;
 import org.testng.annotations.Test;
 
 /**
+ * Unit-testing logging context propagation to Runnable.
+ *
  * @author evitaliy
  * @since 08 Jan 18
  */
-public class RunnableContextPropagationTest extends BaseContextPropagationTest {
+@SuppressWarnings("DefaultAnnotationParam")
+public class RunnableContextPropagationTest {
+
+    private final LoggingContextService ctxService = new SLF4JLoggingServiceProvider();
 
-    @Test(enabled = ENABLED, dataProvider = PROVIDER)
-    public void contextNotCopiedToChildThreadByDefault(LoggingContextService ctx)
-            throws InterruptedException {
+    @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
+    public void contextNotCopiedToChildThreadByDefault() throws InterruptedException {
 
-        Map<ContextField, String> values = putUniqueValues(ctx);
+        Map<ContextField, String> values = putUniqueValues();
         AtomicBoolean complete = new AtomicBoolean(false);
 
         // create thread right away without copying context
         Thread thread = new Thread(() -> {
-            assertContextEmpty("Data unexpectedly copied to a child thread. " +
-                    "Are you using an old version of SLF4J diagnostic context implementation (e.g. logback)?");
+            assertContextEmpty("Data unexpectedly copied to a child thread. "
+                    "Are you using an old version of SLF4J diagnostic context implementation (e.g. logback)?");
             complete.set(true);
         });
 
@@ -54,15 +70,14 @@ public class RunnableContextPropagationTest extends BaseContextPropagationTest {
         assertTrue(complete.get(), EXPECT_INNER_RUN);
     }
 
-    @Test(enabled = ENABLED, dataProvider = PROVIDER)
-    public void contextCopiedWhenToRunnableCalled(LoggingContextService ctx)
-            throws InterruptedException {
+    @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
+    public void contextCopiedWhenToRunnableCalled() throws InterruptedException {
 
-        Map<ContextField, String> values = putUniqueValues(ctx);
+        Map<ContextField, String> values = putUniqueValues();
         AtomicBoolean complete = new AtomicBoolean(false);
 
         // pass the runnable to the context service first
-        Thread thread = new Thread(ctx.copyToRunnable(() -> {
+        Thread thread = new Thread(ctxService.copyToRunnable(() -> {
             assertContextFields(values, EXPECT_PROPAGATED_TO_CHILD);
             complete.set(true);
         }));
@@ -74,15 +89,14 @@ public class RunnableContextPropagationTest extends BaseContextPropagationTest {
         assertTrue(complete.get(), EXPECT_INNER_RUN);
     }
 
-    @Test(enabled = ENABLED, dataProvider = PROVIDER)
-    public void copiedContextRetainedEvenWhenAnotherPushed(LoggingContextService ctx)
-            throws InterruptedException {
+    @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
+    public void copiedContextRetainedEvenWhenAnotherPushed() throws InterruptedException {
 
-        Map<ContextField, String> innerValues = putUniqueValues(ctx);
+        Map<ContextField, String> innerValues = putUniqueValues();
         AtomicBoolean innerComplete = new AtomicBoolean(false);
 
         // should run with the context of main thread
-        Runnable inner = ctx.copyToRunnable(() -> {
+        Runnable inner = ctxService.copyToRunnable(() -> {
             assertContextFields(innerValues, EXPECT_PROPAGATED_TO_CHILD);
             innerComplete.set(true);
         });
@@ -90,7 +104,7 @@ public class RunnableContextPropagationTest extends BaseContextPropagationTest {
         // pushes its context, but the inner must run with its own context
         AtomicBoolean outerComplete = new AtomicBoolean(false);
         Thread outer = new Thread(() -> {
-            Map<ContextField, String> outerValues = putUniqueValues(ctx);
+            Map<ContextField, String> outerValues = putUniqueValues();
             inner.run();
             assertContextFields(outerValues, EXPECT_REPLACED_WITH_STORED);
             outerComplete.set(true);
@@ -104,15 +118,14 @@ public class RunnableContextPropagationTest extends BaseContextPropagationTest {
         assertTrue(innerComplete.get(), EXPECT_INNER_RUN);
     }
 
-    @Test(enabled = ENABLED, dataProvider = PROVIDER)
-    public void contextRemainsEmptyWhenParentWasEmpty(LoggingContextService ctx)
-            throws InterruptedException {
+    @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
+    public void contextRemainsEmptyWhenParentWasEmpty() throws InterruptedException {
 
-        ctx.clear();
+        ctxService.clear();
         assertContextEmpty(EXPECT_EMPTY);
 
         final AtomicBoolean complete = new AtomicBoolean(false);
-        Runnable runnable = ctx.copyToRunnable(() -> {
+        Runnable runnable = ctxService.copyToRunnable(() -> {
             assertContextEmpty(EXPECT_EMPTY);
             complete.set(true);
         });
@@ -125,14 +138,13 @@ public class RunnableContextPropagationTest extends BaseContextPropagationTest {
         assertTrue(complete.get(), EXPECT_INNER_RUN);
     }
 
-    @Test(enabled = ENABLED, dataProvider = PROVIDER)
-    public void childThreadCleanedUpAfterRunnableRuns(LoggingContextService ctx)
-            throws Exception {
+    @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
+    public void childThreadCleanedUpAfterRunnableRuns() throws Exception {
 
-        Map<ContextField, String> innerValues = putUniqueValues(ctx);
+        Map<ContextField, String> innerValues = putUniqueValues();
         AtomicBoolean innerComplete = new AtomicBoolean(false);
         // should run with the context of main thread
-        Runnable inner = ctx.copyToRunnable(() -> {
+        Runnable inner = ctxService.copyToRunnable(() -> {
             assertContextFields(innerValues, EXPECT_PROPAGATED_TO_CHILD);
             innerComplete.set(true);
         });
@@ -154,15 +166,14 @@ public class RunnableContextPropagationTest extends BaseContextPropagationTest {
         assertTrue(innerComplete.get(), EXPECT_INNER_RUN);
     }
 
-    @Test(enabled = ENABLED, dataProvider = PROVIDER)
-    public void childThreadCleanedUpAfterException(LoggingContextService ctx)
-            throws Exception {
+    @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
+    public void childThreadCleanedUpAfterException() throws Exception {
 
-        Map<ContextField, String> innerValues = putUniqueValues(ctx);
+        Map<ContextField, String> innerValues = putUniqueValues();
 
         // should run with the context of main thread
         AtomicBoolean innerComplete = new AtomicBoolean(false);
-        Runnable inner = ctx.copyToRunnable(() -> {
+        Runnable inner = ctxService.copyToRunnable(() -> {
             assertContextFields(innerValues, EXPECT_PROPAGATED_TO_CHILD);
             innerComplete.set(true);
             throw new IllegalArgumentException();
@@ -173,7 +184,7 @@ public class RunnableContextPropagationTest extends BaseContextPropagationTest {
         AtomicBoolean exceptionThrown = new AtomicBoolean(false);
         Thread outer = new Thread(() -> {
 
-            Map<ContextField, String> outerValues = putUniqueValues(ctx);
+            Map<ContextField, String> outerValues = putUniqueValues();
             assertContextFields(outerValues, EXPECT_POPULATED);
 
             try {
@@ -194,6 +205,4 @@ public class RunnableContextPropagationTest extends BaseContextPropagationTest {
         assertTrue(innerComplete.get(), EXPECT_INNER_RUN);
         assertTrue(exceptionThrown.get(), EXPECT_EXCEPTION_FROM_INNER);
     }
-
-
 }
\ No newline at end of file