SLF4J adapter (in 'common') + call graph demo 69/46569/1
authorLuke Parker <lparker@amdocs.com>
Tue, 8 May 2018 09:32:41 +0000 (19:32 +1000)
committerLuke Parker <lparker@amdocs.com>
Tue, 8 May 2018 09:40:52 +0000 (19:40 +1000)
Issue-ID: LOG-115
Change-Id: I0ba28c64b1aaeb571094409704f4f00313afdf36
Signed-off-by: Luke Parker <lparker@amdocs.com>
38 files changed:
reference/slf4j-reference/README.md [new file with mode: 0644]
reference/slf4j-reference/pom.xml [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/common/ONAPLogAdapter.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/common/ONAPLogConstants.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/common/package-info.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/SLF4JRefApplication.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/bean/AbstractBean.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/bean/Request.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/bean/Response.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/bean/package-info.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/AbstractComponent.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/alpha/ComponentAlpha.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/alpha/package-info.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/beta/ComponentBeta.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/beta/package-info.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/delta/ComponentDelta.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/delta/package-info.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/gamma/ComponentGamma.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/gamma/package-info.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/package-info.java [new file with mode: 0644]
reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/package-info.java [new file with mode: 0644]
reference/slf4j-reference/src/main/resources/logback.xml [new file with mode: 0644]
reference/slf4j-reference/src/main/webapp/WEB-INF/web.xml [new file with mode: 0644]
reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/CallGraphTest.java [new file with mode: 0644]
reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/analysis/CallGraphAnalyzer.java [new file with mode: 0644]
reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/analysis/CallGraphReportWriter.java [new file with mode: 0644]
reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/analysis/LogEntry.java [new file with mode: 0644]
reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/analysis/LogEntryTest.java [new file with mode: 0644]
reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/common/ONAPLogAdapterTest.java [new file with mode: 0644]
reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/common/ONAPLogConstantsTest.java [new file with mode: 0644]
reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/bean/RequestTest.java [new file with mode: 0644]
reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/bean/ResponseTest.java [new file with mode: 0644]
reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/component/AbstractComponentTest.java [new file with mode: 0644]
reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/component/alpha/ComponentAlphaTest.java [new file with mode: 0644]
reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/component/beta/ComponentBetaTest.java [new file with mode: 0644]
reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/component/delta/ComponentDeltaTest.java [new file with mode: 0644]
reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/component/gamma/ComponentGammaTest.java [new file with mode: 0644]
reference/slf4j-reference/src/test/java/testng.xml [new file with mode: 0644]

diff --git a/reference/slf4j-reference/README.md b/reference/slf4j-reference/README.md
new file mode 100644 (file)
index 0000000..4434964
--- /dev/null
@@ -0,0 +1,50 @@
+# README - slf4j-reference
+
+This project gives an example of ONAP-compliant logging using SLF4J logging.
+
+## Adapter
+
+In ```org.onap.logging.ref.slf4j.common```, there are TWO classes:
+1. ```org.onap.logging.ref.slf4j.common.ONAPLogConstants```, providing declarations of standard ONAP Markers, MDCs and HTTP headers.
+2. ```org.onap.logging.ref.slf4j.common.ONAPLogAdapter```, providing a lightweight, compliant implementation of the ONAP logging spec.
+
+The adapter provides:
+1. A loosely-coupled SLF4j logging wrapper:
+ * To be used for logging ONAP ```entry```, ```exit``` and ```invoke``` behavior.
+ * Devolving all *application* logging to the component, via the regular SLF4J ```Logger``` facade.
+2. Customization options:
+ * *Cheap*, by way of bean properties. This is suited to most Use Cases.
+ * *Sophisticated*:
+    * By OPTIONALLY implementing one of a number of adapters:
+      * ```RequestAdapter``` to read incoming headers.
+      * ```ServiceDescriptor``` for reporting attributes of the current service.
+      * ```ResponseDescriptor``` for reporting outcomes.
+      * ```RequestBuilder``` for setting southbound request headers.
+    * By OPTIONALLY overriding methods like ```ONAPLogAdapter#setMDCs(RequestAdapter)```.
+
+Note that:
+* The adapter implementation uses static inner classes in order to fit in a single source file. This was an objective. 
+
+## WAR
+
+Building produces a simple (spring-boot](https://projects.spring.io/spring-boot/) example WAR, which can be launched from this directory with:
+
+```bash
+$ java -war target/*war
+```
+
+The WAR publishes four web services:
+1. ```services/alpha```
+2. ```services/beta```
+3. ```services/gamma```
+4. ```services/delta```
+
+... each of which can invoke the others.
+
+The purpose of this WAR is to demonstrate minimalist ONAP-compliant logging for web components, but a secondary purpose is to demonstrate that the call graph can be generated for a (mostly) representative set of interacting REST services.
+
+## Tests
+
+Tests for:
+1. Code in the (potentially) reusable ``common`` package.
+2. Validating that emitted logs can be used to generate an unambiguous call graph.
diff --git a/reference/slf4j-reference/pom.xml b/reference/slf4j-reference/pom.xml
new file mode 100644 (file)
index 0000000..16caa92
--- /dev/null
@@ -0,0 +1,141 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <!--
+    <parent>
+      <groupId>org.onap.logging-analytics</groupId>
+      <artifactId>logging-reference</artifactId>
+      <version>1.2.0-SNAPSHOT</version>
+      <relativePath>..</relativePath>
+    </parent>
+    -->
+
+    <groupId>org.onap.logging-analytics</groupId>
+    <artifactId>slf4j-reference</artifactId>
+    <packaging>war</packaging>
+    <name>logging-demo</name>
+    <version>1.2.0-SNAPSHOT</version>
+
+    <properties>
+        <springframework.boot.version>1.5.10.RELEASE</springframework.boot.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.mashape.unirest</groupId>
+            <artifactId>unirest-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-test</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-all</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${springframework.boot.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.commons</groupId>
+                <artifactId>commons-lang3</artifactId>
+                <version>3.4</version>
+            </dependency>
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-api</artifactId>
+                <version>1.7.25</version>
+            </dependency>
+            <dependency>
+                <groupId>ch.qos.logback</groupId>
+                <artifactId>logback-classic</artifactId>
+                <version>1.2.3</version>
+            </dependency>
+            <dependency>
+                <groupId>com.mashape.unirest</groupId>
+                <artifactId>unirest-java</artifactId>
+                <version>1.4.9</version>
+            </dependency>
+            <dependency>
+                <groupId>org.testng</groupId>
+                <artifactId>testng</artifactId>
+                <version>6.8.5</version>
+            </dependency>
+            <dependency>
+                <groupId>org.hamcrest</groupId>
+                <artifactId>hamcrest-all</artifactId>
+                <version>1.3</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-test</artifactId>
+                <version>5.0.5.RELEASE</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-war-plugin</artifactId>
+                <configuration/>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.7.0</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${springframework.boot.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/common/ONAPLogAdapter.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/common/ONAPLogAdapter.java
new file mode 100644 (file)
index 0000000..898cfb3
--- /dev/null
@@ -0,0 +1,611 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.common;
+
+import java.time.LocalDateTime;
+import java.util.UUID;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.slf4j.Logger;
+import org.slf4j.MDC;
+import org.slf4j.Marker;
+import org.slf4j.event.Level;
+
+/**
+ * Extensible adapter for cheaply meeting ONAP logging obligations using
+ * an SLF4J facade.
+ *
+ * <p>This can be used with any SLF4J-compatible logging provider, with
+ * appropriate provider configuration.</p>
+ *
+ * <p>The basics are that:
+ * <ul>
+ *     <li>{@link #entering} sets all MDCs.</li>
+ *     <li>{@link #exiting} unsets all MDCs *and* logs response information.</li>
+ *     <li>{@link #invoke} logs and returns a UUID to passed during invocation,
+ *     and optionally sets these for you on your downstream request by way of
+ *     an adapter.</li>
+ *     <li>Call {@link #getServiceDescriptor()} and its setters to set service-related MDCs.</li>
+ *     <li>Call {@link #getResponseDescriptor()} and its setters to set response-related MDCs.</li>
+ * </ul>
+ * </p>
+ *
+ * <p>Minimal usage is:
+ * <ol>
+ *     <li>#entering(RequestAdapter)</li>
+ *     <li>#invoke, #invoke, ...</li>
+ *     <li>#getResponse + setters (or #setResponse)</li>
+ *     <li>#exiting</li>
+ * </ol>
+ * </p>
+ *
+ * <p> ... if you're happy for service information to be automatically derived as follows:
+ * <ul>
+ *     <li><tt>ServiceName</tt> - from <tt>HttpServletRequest#getRequestURI()</tt></li>
+ *     <li><tt>InstanceUUID</tt> - classloader-scope UUID.</li>
+ * </ul>
+ * </p>
+ *
+ * <p>... and if those defaults don't suit, then you can override using properties on
+ * {@link #getServiceDescriptor()}, or by injecting your own adapter using
+ * {@link #setServiceDescriptor(ServiceDescriptor)}, or by overriding
+ * a <tt>protected</tt> methods like{@link #setEnteringMDCs}.</p>
+ *
+ * <p>For everything else:
+ * <ul>
+ *     <li>The underlying SLF4J {@link Logger} can be retrieved using {@link #unwrap}.
+ *     Use this or create your own using the usual SLF4J factor.</li>
+ *     <li>Set whatever MDCs you like.</li>
+ *     <li>Log whatever else you like.</li>
+ * </ul>
+ * </p>
+ */
+public class ONAPLogAdapter {
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Constants.
+    //
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    /** String constant for messages <tt>ENTERING</tt>, <tt>EXITING</tt>, etc. */
+    private static final String EMPTY_MESSAGE = "";
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Fields.
+    //
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    /** Automatic UUID, overrideable per adapter or per invocation. */
+    private static UUID sInstanceUUID = UUID.randomUUID();
+
+    /** Logger delegate. */
+    private Logger mLogger;
+
+    /** Overrideable descriptor for the service doing the logging. */
+    private ServiceDescriptor mServiceDescriptor = new ServiceDescriptor();
+
+    /** Overrideable descriptor for the response returned by the service doing the logging. */
+    private ResponseDescriptor mResponseDescriptor = new ResponseDescriptor();
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Constructors.
+    //
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Construct adapter.
+     *
+     * @param logger non-null logger.
+     */
+    public ONAPLogAdapter(final Logger logger) {
+        this.mLogger = checkNotNull(logger);
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Public methods.
+    //
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Get logger.
+     *
+     * @return unwrapped logger.
+     */
+    public Logger unwrap() {
+        return this.mLogger;
+    }
+
+    /**
+     * Report <tt>ENTERING</tt> marker.
+     *
+     * @param request non-null incoming request (wrapper).
+     * @return this.
+     */
+    public ONAPLogAdapter entering(final RequestAdapter request) {
+
+        checkNotNull(request);
+
+        // Default the service name.
+
+        this.setEnteringMDCs(request);
+        this.mLogger.info(ONAPLogConstants.Markers.ENTRY, EMPTY_MESSAGE);
+
+        return this;
+    }
+
+    /**
+     * Report <tt>ENTERING</tt> marker.
+     *
+     * @param request non-null incoming request.
+     * @return this.
+     */
+    public ONAPLogAdapter entering(final HttpServletRequest request) {
+        return this.entering(new HttpServletRequestAdapter(checkNotNull(request)));
+    }
+
+    /**
+     * Report <tt>EXITING</tt> marker.
+     *
+     * @return this.
+     */
+    public ONAPLogAdapter exiting() {
+        try {
+            this.mResponseDescriptor.setMDCs();
+            this.mLogger.info(ONAPLogConstants.Markers.EXIT, EMPTY_MESSAGE);
+        }
+        finally {
+            MDC.clear();
+        }
+        return this;
+    }
+
+    /**
+     * Report pending invocation with <tt>INVOKE</tt> marker.
+     *
+     * <p>If you call this variant, then YOU are assuming responsibility for
+     * setting the requisite ONAP headers.</p>
+     *
+     * @param sync whether synchronous.
+     * @return invocation ID to be passed with invocation.
+     */
+    public UUID invoke(final ONAPLogConstants.InvocationMode sync) {
+
+        final UUID invocationID = UUID.randomUUID();
+
+        // Derive SYNC/ASYNC marker.
+
+        final Marker marker = (sync == null) ? ONAPLogConstants.Markers.INVOKE : sync.getMarker();
+
+        // Log INVOKE*, with the invocationID as the message body.
+        // (We didn't really want this kind of behavior in the standard,
+        // but is it worse than new, single-message MDC?)
+
+        this.mLogger.info(marker, "{}", invocationID);
+        return invocationID;
+    }
+
+    /**
+     * Report pending invocation with <tt>INVOKE</tt> marker,
+     * setting standard ONAP logging headers automatically.
+     *
+     * @param builder request builder, for setting headers.
+     * @param sync whether synchronous, nullable.
+     * @return invocation ID to be passed with invocation.
+     */
+    public UUID invoke(final RequestBuilder builder,
+                       final ONAPLogConstants.InvocationMode sync) {
+
+        // Sync can be defaulted. Builder cannot.
+
+        checkNotNull(builder);
+
+        // Log INVOKE, and retain invocation ID for header + return.
+
+        final UUID invocationID = this.invoke(sync);
+
+        // Set standard HTTP headers on (southbound request) builder.
+
+        builder.setHeader(ONAPLogConstants.Headers.REQUEST_ID,
+                defaultToEmpty(MDC.get(ONAPLogConstants.MDCs.REQUEST_ID)));
+        builder.setHeader(ONAPLogConstants.Headers.INVOCATION_ID,
+                defaultToEmpty(invocationID));
+        builder.setHeader(ONAPLogConstants.Headers.PARTNER_NAME,
+                defaultToEmpty(MDC.get(ONAPLogConstants.MDCs.PARTNER_NAME)));
+
+        return invocationID;
+    }
+
+    /**
+     * Report vanilla <tt>INVOKE</tt> marker.
+     *
+     * @param builder builder for downstream requests, if you want the
+     *                standard ONAP headers to be added automatically.
+     * @return invocation ID to be passed with invocation.
+     */
+    public UUID invoke(final RequestBuilder builder) {
+        return this.invoke(builder, (ONAPLogConstants.InvocationMode)null);
+    }
+
+    /**
+     * Get descriptor, for overriding service details.
+     * @return non-null descriptor.
+     */
+    public ServiceDescriptor getServiceDescriptor() {
+        return checkNotNull(this.mServiceDescriptor);
+    }
+
+    /**
+     * Override {@link ServiceDescriptor}.
+     * @param d non-null override.
+     * @return this.
+     */
+    public ONAPLogAdapter setServiceDescriptor(final ServiceDescriptor d) {
+        this.mServiceDescriptor = checkNotNull(d);
+        return this;
+    }
+
+    /**
+     * Get descriptor, for setting response details.
+     * @return non-null descriptor.
+     */
+    public ResponseDescriptor getResponseDescriptor() {
+        return checkNotNull(this.mResponseDescriptor);
+    }
+
+    /**
+     * Override {@link ResponseDescriptor}.
+     * @param d non-null override.
+     * @return this.
+     */
+    public ONAPLogAdapter setResponseDescriptor(final ResponseDescriptor d) {
+        this.mResponseDescriptor = checkNotNull(d);
+        return this;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Protected methods.
+    //
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Set MDCs that persist for the duration of an invocation.
+     *
+     * <p>It would be better to roll this into {@link #entering}, like
+     * with {@link #exiting}. Then it would be easier to do, but it
+     * would mean more work. </p>
+     *
+     * @param request incoming HTTP request.
+     * @return this.
+     */
+    protected ONAPLogAdapter setEnteringMDCs(final RequestAdapter<?> request) {
+
+        // Extract MDC values from standard HTTP headers.
+
+        final String requestID = defaultToUUID(request.getHeader(ONAPLogConstants.Headers.REQUEST_ID));
+        final String invocationID = defaultToUUID(request.getHeader(ONAPLogConstants.Headers.INVOCATION_ID));
+        final String partnerName = defaultToEmpty(request.getHeader(ONAPLogConstants.Headers.PARTNER_NAME));
+
+        // Set standard MDCs. Override this entire method if you want to set
+        // others, OR set them BEFORE or AFTER the invocation of #entering,
+        // depending on where you need them to appear, OR extend the
+        // ServiceDescriptor to add them.
+
+        MDC.put(ONAPLogConstants.MDCs.ENTRY_TIMESTAMP, LocalDateTime.now().toString());
+        MDC.put(ONAPLogConstants.MDCs.REQUEST_ID, requestID);
+        MDC.put(ONAPLogConstants.MDCs.INVOCATION_ID, invocationID);
+        MDC.put(ONAPLogConstants.MDCs.PARTNER_NAME, partnerName);
+        MDC.put(ONAPLogConstants.MDCs.CLIENT_IP_ADDRESS, defaultToEmpty(request.getClientAddress()));
+        MDC.put(ONAPLogConstants.MDCs.SERVER_FQDN, defaultToEmpty(request.getServerAddress()));
+
+        // Delegate to the service adapter, for service-related DMCs.
+
+        this.mServiceDescriptor.setMDCs();
+
+        // Default the service name to the requestURI, in the event that
+        // no value has been provided.
+
+        if (MDC.get(ONAPLogConstants.MDCs.SERVICE_NAME) == null) {
+            MDC.put(ONAPLogConstants.MDCs.SERVICE_NAME, request.getRequestURI());
+        }
+
+        return this;
+    }
+
+    /**
+     * Dependency-free nullcheck.
+     *
+     * @param in to be checked.
+     * @param <T> argument (and return) type.
+     * @return input arg.
+     */
+    protected static <T> T checkNotNull(final T in) {
+        if (in == null) {
+            throw new NullPointerException();
+        }
+        return in;
+    }
+
+    /**
+     * Dependency-free string default.
+     *
+     * @param in to be filtered.
+     * @return input string or null.
+     */
+    protected static String defaultToEmpty(final Object in) {
+        if (in == null) {
+            return "";
+        }
+        return in.toString();
+    }
+
+    /**
+     * Dependency-free string default.
+     *
+     * @param in to be filtered.
+     * @return input string or null.
+     */
+    protected static String defaultToUUID(final String in) {
+        if (in == null) {
+            return UUID.randomUUID().toString();
+        }
+        return in;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Inner classes.
+    //
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Extensible descriptor for reporting service details.
+     *
+     * <p>In most cases extension isn't required. </p>
+     */
+    public static class ServiceDescriptor {
+
+        /** <tt>ServiceName</tt>. */
+        protected String mName;
+
+        /** <tt>InstanceUUID</tt>. */
+        protected String mUUID = sInstanceUUID.toString();
+
+        /**
+         * Set name.
+         * @param name <tt>ServiceName</tt>.
+         * @return this.
+         */
+        public ServiceDescriptor setServiceName(final String name) {
+            this.mName = name;
+            return this;
+        }
+
+        /**
+         * Set name.
+         * @param uuid <tt>InstanceUUID</tt>.
+         * @return this.
+         */
+        public ServiceDescriptor setServiceUUID(final String uuid) {
+            this.mUUID = uuid;
+            return this;
+        }
+
+        /**
+         * Set MDCs. Once set they remain set until everything is cleared.
+         */
+        protected void setMDCs() {
+            MDC.put(ONAPLogConstants.MDCs.SERVICE_NAME, defaultToEmpty(this.mName));
+            MDC.put(ONAPLogConstants.MDCs.INSTANCE_UUID, defaultToEmpty(this.mUUID));
+        }
+    }
+
+    /**
+     * Response is different in that response MDCs are normally only
+     * reported once, for a single log message. (But there's no method
+     * for clearing them, because this is only expected to be called
+     * during <tt>#exiting</tt>.)
+     */
+    public static class ResponseDescriptor {
+
+        /** Response errorcode. */
+        protected String mCode;
+
+        /** Response description. */
+        protected String mDescription;
+
+        /** Response severity. */
+        protected Level mSeverity;
+
+        /** Response status, of {<tt>COMPLETED</tt>, <tt>ERROR</tt>}. */
+        protected ONAPLogConstants.ResponseStatus mStatus;
+
+        /**
+         * Setter.
+         *
+         * @param code response (error) code.
+         * @return this.
+         */
+        public ResponseDescriptor setResponseCode(final String code) {
+            this.mCode = code;
+            return this;
+        }
+
+        /**
+         * Setter.
+         *
+         * @param description response description.
+         * @return this.
+         */
+        public ResponseDescriptor setResponseDescription(final String description) {
+            this.mDescription = description;
+            return this;
+        }
+
+        /**
+         * Setter.
+         *
+         * @param severity response outcome severity.
+         * @return this.
+         */
+        public ResponseDescriptor setResponseSeverity(final Level severity) {
+            this.mSeverity = severity;
+            return this;
+        }
+
+        /**
+         * Setter.
+         *
+         * @param status response overall status.
+         * @return this.
+         */
+        public ResponseDescriptor setResponseStatus(final ONAPLogConstants.ResponseStatus status) {
+            this.mStatus = status;
+            return this;
+        }
+
+        /**
+         * Overrideable method to set MDCs based on property values.
+         */
+        protected void setMDCs() {
+            MDC.put(ONAPLogConstants.MDCs.RESPONSE_CODE, defaultToEmpty(this.mCode));
+            MDC.put(ONAPLogConstants.MDCs.RESPONSE_DESCRIPTION, defaultToEmpty(this.mDescription));
+            MDC.put(ONAPLogConstants.MDCs.RESPONSE_SEVERITY, defaultToEmpty(this.mSeverity));
+            MDC.put(ONAPLogConstants.MDCs.RESPONSE_STATUS, defaultToEmpty(this.mStatus));
+        }
+    }
+
+    /**
+     * Adapter for reading information from an incoming HTTP request.
+     *
+     * <p>Incoming is generally easy, because in most cases you'll be able to
+     * get your hands on the <tt>HttpServletRequest</tt>.</p>
+     *
+     * <p>Perhaps should be generalized to refer to constants instead of
+     * requiring the implementation of specific methods.</p>
+     *
+     * @param <T> type, for chaining.
+     */
+    public interface RequestAdapter<T extends RequestAdapter> {
+
+        /**
+         * Get header by name.
+         * @param name header name.
+         * @return header value, or null.
+         */
+        String getHeader(String name);
+
+        /**
+         * Get client address.
+         * @return address, if available.
+         */
+        String getClientAddress();
+
+        /**
+         * Get server address.
+         * @return address, if available.
+         */
+        String getServerAddress();
+
+        /**
+         * Get default service name, from service URI.
+         * @return service name default.
+         */
+        String getRequestURI();
+    }
+
+    /**
+     * Default {@link RequestBuilder} impl for {@link HttpServletRequest}, which
+     * will should available for most incoming REST requests.
+     */
+    public static class HttpServletRequestAdapter implements RequestAdapter<HttpServletRequestAdapter> {
+
+        /** Wrapped HTTP request. */
+        private final HttpServletRequest mRequest;
+
+        /**
+         * Construct adapter for HTTP request.
+         * @param request to be wrapped;
+         */
+        public HttpServletRequestAdapter(final HttpServletRequest request) {
+            this.mRequest = checkNotNull(request);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String getHeader(final String name) {
+            return this.mRequest.getHeader(name);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String getClientAddress() {
+            return this.mRequest.getRemoteAddr();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String getServerAddress() {
+            return this.mRequest.getServerName();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String getRequestURI() {
+            return this.mRequest.getRequestURI();
+        }
+    }
+
+    /**
+     * Header builder, which (unlike {@link RequestAdapter} will tend to
+     * vary a lot from caller to caller, since they each get to choose their
+     * own REST (or HTTP, or whatever) client APIs.
+     *
+     * <p>No default implementation, because there's no HTTP client that's
+     * sufficiently ubiquitous to warrant incurring a mandatory dependency.</p>
+     *
+     * @param <T> type, for chaining.
+     */
+    public interface RequestBuilder<T extends RequestBuilder> {
+
+        /**
+         * Set HTTP header.
+         * @param name header name.
+         * @param value header value.
+         * @return this.
+         */
+        T setHeader(String name, String value);
+    }
+}
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/common/ONAPLogConstants.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/common/ONAPLogConstants.java
new file mode 100644 (file)
index 0000000..0274f80
--- /dev/null
@@ -0,0 +1,228 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.common;
+
+import org.slf4j.Marker;
+import org.slf4j.MarkerFactory;
+
+/**
+ * Constants for standard ONAP headers, MDCs, etc.
+ *
+ * <p>See <tt>package-info.java</tt>.</p>
+ */
+public final class ONAPLogConstants {
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Constructors.
+    //
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Hide and forbid construction.
+     */
+    private ONAPLogConstants() {
+        throw new UnsupportedOperationException();
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Inner classes.
+    //
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Marker constants.
+     */
+    public static final class Markers {
+
+        /** Marker reporting invocation. */
+        public static final Marker INVOKE = MarkerFactory.getMarker("INVOKE");
+
+        /** Marker reporting synchronous invocation. */
+        public static final Marker INVOKE_SYNCHRONOUS = build("INVOKE", "SYNCHRONOUS");
+
+        /** Marker reporting asynchronous invocation. */
+        public static final Marker INVOKE_ASYNCHRONOUS = build("INVOKE", "ASYNCHRONOUS");
+
+        /** Marker reporting entry into a component. */
+        public static final Marker ENTRY = MarkerFactory.getMarker("ENTRY");
+
+        /** Marker reporting exit from a component. */
+        public static final Marker EXIT = MarkerFactory.getMarker("EXIT");
+
+        /**
+         * Build nested, detached marker.
+         * @param m1 top token.
+         * @param m2 sub-token.
+         * @return detached Marker.
+         */
+        private static Marker build(final String m1, final String m2) {
+            final Marker marker = MarkerFactory.getDetachedMarker(m1);
+            marker.add(MarkerFactory.getDetachedMarker(m2));
+            return marker;
+        }
+
+        /**
+         * Hide and forbid construction.
+         */
+        private Markers() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    /**
+     * MDC name constants.
+     */
+    public static final class MDCs {
+
+        /** MDC correlating messages for a logical transaction. */
+        public static final String REQUEST_ID = "RequestID";
+
+        /** MDC correlating messages for an invocation. */
+        public static final String INVOCATION_ID = "InvocationID";
+
+        /** MDC recording current service. */
+        public static final String SERVICE_NAME = "ServiceName";
+
+        /** MDC recording calling service. */
+        public static final String PARTNER_NAME = "PartnerName";
+
+        /** MDC recording current service instance. */
+        public static final String INSTANCE_UUID = "InstanceUUID";
+
+        /** MDC recording caller address. */
+        public static final String CLIENT_IP_ADDRESS = "ClientIPAddress";
+
+        /** MDC recording server address. */
+        public static final String SERVER_FQDN = "ServerFQDN";
+
+        /** MDC recording timestamp at the start of the current invocation. */
+        public static final String ENTRY_TIMESTAMP = "EntryTimestamp";
+
+        /** MDC reporting outcome code. */
+        public static final String RESPONSE_CODE = "ResponseCode";
+
+        /** MDC reporting outcome description. */
+        public static final String RESPONSE_DESCRIPTION = "ResponseDescription";
+
+        /** MDC reporting outcome error level. */
+        public static final String RESPONSE_SEVERITY = "Severity";
+
+        /** MDC reporting outcome error level. */
+        public static final String RESPONSE_STATUS = "StatusCode";
+
+        /**
+         * Hide and forbid construction.
+         */
+        private MDCs() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    /**
+     * Header name constants.
+     */
+    public static final class Headers {
+
+        /** HTTP <tt>X-ONAP-RequestID</tt> header. */
+        public static final String REQUEST_ID = "X-ONAP-RequestID";
+
+        /** HTTP <tt>X-ONAP-InvocationID</tt> header. */
+        public static final String INVOCATION_ID = "X-ONAP-InvocationID";
+
+        /** HTTP <tt>X-ONAP-PartnerName</tt> header. */
+        public static final String PARTNER_NAME = "X-ONAP-PartnerName";
+
+        /**
+         * Hide and forbid construction.
+         */
+        private Headers() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Enums.
+    //
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Response success or not, for setting <tt>StatusCode</tt>.
+     */
+    public enum ResponseStatus {
+
+        /** Success. */
+        COMPLETED,
+
+        /** Not. */
+        ERROR,
+    }
+
+    /**
+     * Synchronous or asynchronous execution, for setting invocation marker.
+     */
+    public enum InvocationMode {
+
+        /** Synchronous, blocking. */
+        SYNCHRONOUS("SYNCHRONOUS", Markers.INVOKE_SYNCHRONOUS),
+
+        /** Asynchronous, non-blocking. */
+        ASYNCHRONOUS("", Markers.INVOKE_ASYNCHRONOUS);
+
+        /** Enum value. */
+        private String mString;
+
+        /** Corresponding marker. */
+        private Marker mMarker;
+
+        /**
+         * Construct enum.
+         *
+         * @param s enum value.
+         * @param m corresponding Marker.
+         */
+        InvocationMode(final String s, final Marker m) {
+            this.mString = s;
+            this.mMarker = m;
+        }
+
+        /**
+         * Get Marker for enum.
+         *
+         * @return Marker.
+         */
+        public Marker getMarker() {
+            return this.mMarker;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            return this.mString;
+        }
+    }
+
+}
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/common/package-info.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/common/package-info.java
new file mode 100644 (file)
index 0000000..d5c4375
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.common;
+
+/**
+ * <p>Code in here has potential application outside this reference
+ * example, and accordingly:
+ * <ul>
+ *     <li>Packaged in <tt>common</tt>.</li>
+ *     <li>Has minimal dependencies.</li>
+ * </ul>
+ * </p>
+ */
\ No newline at end of file
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/SLF4JRefApplication.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/SLF4JRefApplication.java
new file mode 100644 (file)
index 0000000..ae6aa92
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * Spring launcher, for testing invocations via REST.
+ */
+@SpringBootApplication
+public class SLF4JRefApplication {
+
+    /**
+     * Launch from CLI.
+     * @param args command-line args.
+     * @throws Exception launch error.
+     */
+    public static void main(final String[] args) throws Exception {
+        System.getProperties().setProperty("SLF4J_OUTPUT_DIRECTORY", ".");
+        SpringApplication.run(SLF4JRefApplication.class, args);
+    }
+}
\ No newline at end of file
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/bean/AbstractBean.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/bean/AbstractBean.java
new file mode 100644 (file)
index 0000000..6c6060d
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.bean;
+
+import org.json.JSONObject;
+
+/**
+ * Base class for {@link Request} and {@link Response} beans, since
+ * they're almost the same thing.
+ */
+public abstract class AbstractBean {
+
+    /** Bean property. */
+    private String mService;
+
+    /** Bean property. */
+    private String mCode;
+
+    /** Bean property. */
+    private String mSeverity;
+
+    /**
+     * Getter.
+     * @return bean property.
+     */
+    public String getService() {
+        return this.mService;
+    }
+
+    /**
+     * Setter.
+     * @param service bean property.
+     */
+    public void setService(final String service) {
+        this.mService = service;
+    }
+
+    /**
+     * Getter.
+     * @return bean property.
+     */
+    public String getCode() {
+        return this.mCode;
+    }
+
+    /**
+     * Setter.
+     * @param code bean property.
+     */
+    public void setCode(final String code) {
+        this.mCode = code;
+    }
+
+    /**
+     * Getter.
+     * @return bean property.
+     */
+    public String getSeverity() {
+        return this.mSeverity;
+    }
+
+    /**
+     * Setter.
+     * @param severity bean property.
+     */
+    public void setSeverity(final String severity) {
+        this.mSeverity = severity;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        return new JSONObject(this).toString(4);
+    }
+}
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/bean/Request.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/bean/Request.java
new file mode 100644 (file)
index 0000000..5318333
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.bean;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+/**
+ * Test class, describing a request to be executed.
+ */
+public class Request extends AbstractBean {
+
+    /** Subrequests to be executed. */
+    private final List<Request> mRequests = new ArrayList<>();
+
+    /**
+     * Get subrequests.
+     * @return subrequests.
+     */
+    public List<Request> getRequests() {
+        return mRequests;
+    }
+
+    /**
+     * Parse from serialized form.
+     * @param in JSON.
+     * @return parsed.
+     */
+    public static Request fromJSON(final JSONObject in) {
+        final Request request = new Request();
+        request.setService(in.optString("service"));
+        request.setCode(in.optString("code"));
+        request.setSeverity(in.optString("severity"));
+        final JSONArray requests = in.optJSONArray("requests");
+        if (requests != null) {
+            for (int i = 0 ; i < requests.length() ; i++) {
+                request.getRequests().add(Request.fromJSON(requests.getJSONObject(i)));
+            }
+        }
+        return request;
+    }
+}
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/bean/Response.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/bean/Response.java
new file mode 100644 (file)
index 0000000..b0c6a4a
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.bean;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+/**
+ * Test class, describing an outcome that should be reported.
+ */
+public class Response extends  AbstractBean {
+
+    /** Delegate responses. */
+    private final List<Response> mResponses = new ArrayList<>();
+
+    /**
+     * Get delegate responses.
+     * @return responses.
+     */
+    public List<Response> getResponses() {
+        return mResponses;
+    }
+
+    /**
+     * Parse from serialized form.
+     * @param in JSON.
+     * @return parsed.
+     */
+    public static Response fromJSON(final JSONObject in) {
+        final Response request = new Response();
+        request.setCode(in.optString("code"));
+        request.setSeverity(in.optString("severity"));
+        final JSONArray responses = in.optJSONArray("responses");
+        if (responses != null) {
+            for (int i = 0 ; i < responses.length() ; i++) {
+                request.getResponses().add(Response.fromJSON(responses.getJSONObject(i)));
+            }
+        }
+        return request;
+    }
+}
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/bean/package-info.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/bean/package-info.java
new file mode 100644 (file)
index 0000000..2364f9f
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.bean;
+
+/**
+ * Simple request/responses beans for testcases.
+ */
\ No newline at end of file
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/AbstractComponent.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/AbstractComponent.java
new file mode 100644 (file)
index 0000000..236345f
--- /dev/null
@@ -0,0 +1,278 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.component;
+
+import java.util.Locale;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.servlet.http.HttpServletRequest;
+
+import com.mashape.unirest.http.HttpResponse;
+import com.mashape.unirest.http.JsonNode;
+import com.mashape.unirest.http.Unirest;
+import com.mashape.unirest.http.exceptions.UnirestException;
+import org.apache.commons.lang3.StringUtils;
+import org.json.JSONObject;
+import org.onap.logging.ref.slf4j.common.ONAPLogAdapter;
+import org.onap.logging.ref.slf4j.common.ONAPLogConstants;
+import org.onap.logging.ref.slf4j.demo.bean.Request;
+import org.onap.logging.ref.slf4j.demo.bean.Response;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+import org.springframework.http.MediaType;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Base class for <tt>Alpha</tt>, <tt>Beta</tt> and <tt>Gamma</tt>
+ * and <tt>Delta</tt> controllers, implementing all the actual logic.
+ *
+ * <p>(The subclasses provide nothing but identifiers to allow them
+ * to be distinguished from one another, for the purposes of addressing
+ * requests and generating the call graph from their logger output.)</p>
+ */
+@RestController
+public abstract class AbstractComponent {
+
+    /**
+     * Test switch, routing invocations between components in-process,
+     * rather than via REST over HTTP.
+     */
+    private static boolean sInProcess;
+
+    /**
+     * Get service identifier, used to derive {@link #getServiceName()},
+     * <tt>PartnerName</tt>, etc.
+     * @return <tt>alpha</tt>, <tt>beta</tt>, <tt>gamma</tt>.
+     */
+    protected abstract String getId();
+
+    /**
+     * Get component UUID.
+     * @return globally unique ID string.
+     */
+    protected abstract String getInstanceUUID();
+
+    /**
+     * Execute REST request.
+     * @param request request data.
+     * @param http HTTP request.
+     * @return response data.
+     * @throws UnirestException REST error.
+     */
+    @RequestMapping(value = "/invoke",
+            method = RequestMethod.POST,
+            consumes = MediaType.APPLICATION_JSON_VALUE,
+            produces = MediaType.APPLICATION_JSON_VALUE)
+    public Response execute(final Request request,
+                            final HttpServletRequest http) throws UnirestException {
+
+        final ONAPLogAdapter adapter = new ONAPLogAdapter(this.getLogger());
+
+        try {
+
+            adapter.entering(new ONAPLogAdapter.HttpServletRequestAdapter(http));
+
+            final Response response = new Response();
+            response.setService(request.getService());
+            final String code = StringUtils.defaultString(request.getCode(), "OK").toUpperCase();
+            response.setCode(this.getId() + "." + code);
+            response.setSeverity(StringUtils.defaultString(request.getSeverity(), "INFO"));
+
+            for (final Request target : request.getRequests()) {
+                final Response targetResponse = this.executeDelegate(target, http, adapter);
+                response.getResponses().add(targetResponse);
+            }
+
+            return response;
+        }
+        finally {
+            adapter.exiting();
+        }
+    }
+
+    /**
+     * Set in-process mode, for unit testing.
+     */
+    static void setInProcess() {
+        sInProcess = true;
+    }
+
+    /**
+     * Execute request.
+     * @param request to be executed.
+     * @param http incoming HTTP request.
+     * @param logger logging adapter.
+     * @return response
+     */
+    private Response executeDelegate(final Request request,
+                                     final HttpServletRequest http,
+                                     final ONAPLogAdapter logger) {
+
+
+        notNull(request);
+        notNull(http);
+
+        // Downstream call.
+
+        try {
+
+            if (sInProcess) {
+                return this.executeInProcess(request, logger);
+            }
+
+            return this.executeREST(request, http, logger);
+        }
+        catch (final UnirestException | ReflectiveOperationException e) {
+            logger.unwrap().error("Execute error", e);
+            final Response response = new Response();
+            response.setCode((this.getServiceName() + ".INVOKE_ERROR").toUpperCase(Locale.getDefault()));
+            response.setSeverity("ERROR");
+            return response;
+        }
+    }
+
+    /**
+     * Execute invocation over REST.
+     * @param request mock request to be executed.
+     * @param http HTTP request, used (only) to address the outgoing request.
+     * @param logger logger adapter.
+     * @return invocation response.
+     * @throws UnirestException REST error.
+     */
+    private Response executeREST(final Request request,
+                                 final HttpServletRequest http,
+                                 final ONAPLogAdapter logger) throws UnirestException {
+        // Otherwise via REST.
+
+        logger.unwrap().info("Sending:\n{}", request);
+        final StringBuilder url = new StringBuilder();
+        url.append(http.getProtocol()).append("://");
+        url.append(http.getServerName()).append(':');
+        url.append(http.getServerPort()).append("/services/").append(request.getService());
+
+        final UUID invocationID = logger.invoke(ONAPLogConstants.InvocationMode.SYNCHRONOUS);
+        final HttpResponse<JsonNode> response =
+                Unirest.post(url.toString())
+                        .header(ONAPLogConstants.Headers.REQUEST_ID, MDC.get(ONAPLogConstants.MDCs.REQUEST_ID))
+                        .header(ONAPLogConstants.Headers.INVOCATION_ID, invocationID.toString())
+                        .header(ONAPLogConstants.Headers.PARTNER_NAME, this.getServiceName())
+                        .header("Accept", MediaType.APPLICATION_JSON_VALUE)
+                        .header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
+                        .body(request)
+                        .asJson();
+
+        // Parse response.
+
+        final JSONObject responseJSON = response.getBody().getObject();
+        logger.unwrap().info("Received:\n{}", responseJSON);
+        return Response.fromJSON(responseJSON);
+    }
+
+    /**
+     * Execute request in-process.
+     * @param request mock request to be executed.
+     * @param logger logger adapter.
+     * @return invocation response.
+     * @throws ReflectiveOperationException error loading target class.
+     * @throws UnirestException REST error.
+     */
+    private Response executeInProcess(final Request request,
+                                      final ONAPLogAdapter logger) throws ReflectiveOperationException, UnirestException {
+
+        logger.unwrap().info("Executing in-process:\n{}", request);
+
+        // Derive the name of the delegate class.
+
+        final String delegateClass
+                = AbstractComponent.class.getPackage().getName() + "." + request.getService()
+                + ".Component" + request.getService().substring(0, 1).toUpperCase()
+                + request.getService().substring(1);
+        logger.unwrap().info("Invoking in-process [{}].", delegateClass);
+        final AbstractComponent component = (AbstractComponent)Class.forName(delegateClass).newInstance();
+
+        // Using Spring mock since we're not *actually* going over HTTP.
+
+        final MockHttpServletRequest mock = new MockHttpServletRequest();
+
+        // Generate INVOCATION_ID, and set MDCs aside for safekeeping.
+        // (This is because when mocking, everything happens in the same thread.)
+
+        final UUID invocationID = logger.invoke(ONAPLogConstants.InvocationMode.SYNCHRONOUS);
+        final String requestID = MDC.get(ONAPLogConstants.MDCs.REQUEST_ID);
+        final Map<String, String> safekeeping = MDC.getCopyOfContextMap();
+
+        // Set headers.
+
+        mock.addHeader(ONAPLogConstants.Headers.REQUEST_ID, StringUtils.defaultString(requestID));
+        mock.addHeader(ONAPLogConstants.Headers.INVOCATION_ID, invocationID.toString());
+        mock.addHeader(ONAPLogConstants.Headers.PARTNER_NAME, this.getServiceName());
+
+        try {
+
+            MDC.clear();
+
+            // Execute.
+
+            return component.execute(request, mock);
+        }
+        finally {
+
+            // Restore MDCs.
+
+            safekeeping.forEach((k, v) -> MDC.put(k, v));
+        }
+    }
+
+    /**
+     * Ensure non-nullness.
+     * @param in to be checked.
+     * @param <T> type.
+     * @return input value, not null.
+     */
+    private static <T> T notNull(final T in) {
+        if (in == null) {
+            throw new AssertionError("");
+        }
+        return in;
+    }
+
+    /**
+     * Get service name, with default.
+     * @return service name, suitable for logging as MDC.
+     */
+    private String getServiceName() {
+        return "service." + StringUtils.defaultString(this.getId(), "unnamed");
+    }
+
+    /**
+     * Get logger instance.
+     * @return logger.
+     */
+    private Logger getLogger() {
+        return LoggerFactory.getLogger(this.getClass());
+    }
+}
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/alpha/ComponentAlpha.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/alpha/ComponentAlpha.java
new file mode 100644 (file)
index 0000000..d5cf182
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.component.alpha;
+
+import java.util.UUID;
+
+import org.onap.logging.ref.slf4j.demo.component.AbstractComponent;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * Discrete service, identical to the others but with its own identifiers.
+ */
+@RequestMapping("/services/alpha")
+public class ComponentAlpha extends AbstractComponent {
+
+    /** Component instance UUID constant. */
+    private static final String INSTANCE_UUID = UUID.randomUUID().toString();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected String getId() {
+        return "alpha";
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected String getInstanceUUID() {
+        return INSTANCE_UUID;
+    }
+}
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/alpha/package-info.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/alpha/package-info.java
new file mode 100644 (file)
index 0000000..4fa0fbc
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.component.alpha;
+
+/**
+ * Example service.
+ */
\ No newline at end of file
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/beta/ComponentBeta.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/beta/ComponentBeta.java
new file mode 100644 (file)
index 0000000..9a4e6eb
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.component.beta;
+
+import java.util.UUID;
+
+import org.onap.logging.ref.slf4j.demo.component.AbstractComponent;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * Discrete service, identical to the others but with its own identifiers.
+ */
+@RequestMapping("/services/beta")
+public class ComponentBeta extends AbstractComponent {
+
+    /** Component instance UUID constant. */
+    private static final String INSTANCE_UUID = UUID.randomUUID().toString();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected String getId() {
+        return "beta";
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected String getInstanceUUID() {
+        return INSTANCE_UUID;
+    }
+}
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/beta/package-info.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/beta/package-info.java
new file mode 100644 (file)
index 0000000..68d7f74
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.component.beta;
+
+/**
+ * Example service.
+ */
\ No newline at end of file
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/delta/ComponentDelta.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/delta/ComponentDelta.java
new file mode 100644 (file)
index 0000000..d63d80a
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.component.delta;
+
+import java.util.UUID;
+
+import org.onap.logging.ref.slf4j.demo.component.AbstractComponent;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * Discrete service, identical to the others but with its own identifiers.
+ */
+@RequestMapping("/services/delta")
+public class ComponentDelta extends AbstractComponent {
+
+    /** Component instance UUID constant. */
+    private static final String INSTANCE_UUID = UUID.randomUUID().toString();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected String getId() {
+        return "delta";
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected String getInstanceUUID() {
+        return INSTANCE_UUID;
+    }
+}
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/delta/package-info.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/delta/package-info.java
new file mode 100644 (file)
index 0000000..4b02ab4
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.component.delta;
+
+/**
+ * Example service.
+ */
\ No newline at end of file
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/gamma/ComponentGamma.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/gamma/ComponentGamma.java
new file mode 100644 (file)
index 0000000..9294743
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.component.gamma;
+
+import java.util.UUID;
+
+import org.onap.logging.ref.slf4j.demo.component.AbstractComponent;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * Discrete service, identical to the others but with its own identifiers.
+ */
+@RequestMapping("/services/gamma")
+public class ComponentGamma extends AbstractComponent {
+
+    /** Component instance UUID constant. */
+    private static final String INSTANCE_UUID = UUID.randomUUID().toString();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected String getId() {
+        return "gamma";
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected String getInstanceUUID() {
+        return INSTANCE_UUID;
+    }
+}
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/gamma/package-info.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/gamma/package-info.java
new file mode 100644 (file)
index 0000000..7f5e30a
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.component.gamma;
+
+/**
+ * Example service.
+ */
\ No newline at end of file
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/package-info.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/component/package-info.java
new file mode 100644 (file)
index 0000000..c650948
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.component;
+
+/**
+ * Components that demonstrate usage and participate in the test.
+ */
\ No newline at end of file
diff --git a/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/package-info.java b/reference/slf4j-reference/src/main/java/org/onap/logging/ref/slf4j/demo/package-info.java
new file mode 100644 (file)
index 0000000..feb5e85
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo;
+
+/**
+ * Minimal, spring-boot refernce example.
+ *
+ * <p>Code that exists for testing can have whatever dependencies
+ * it likes, but take care with the contents of <tt>common</tt>,
+ * since it may be useful elsewhere.</p>
+ */
\ No newline at end of file
diff --git a/reference/slf4j-reference/src/main/resources/logback.xml b/reference/slf4j-reference/src/main/resources/logback.xml
new file mode 100644 (file)
index 0000000..554d712
--- /dev/null
@@ -0,0 +1,35 @@
+<configuration>
+
+    <property name="p_tim" value="%d{&quot;yyyy-MM-dd'T'HH:mm:ss.SSSXXX&quot;, UTC}"/>
+    <property name="p_lvl" value="%level"/>
+    <property name="p_log" value="%logger"/>
+    <property name="p_mdc" value="%replace(%replace(%mdc){'\t','\\\\t'}){'\n', '\\\\n'}"/>
+    <property name="p_msg" value="%replace(%replace(%msg){'\t', '\\\\t'}){'\n','\\\\n'}"/>
+    <property name="p_exc" value="%replace(%replace(%rootException){'\t', '\\\\t'}){'\n','\\\\n'}"/>
+    <property name="p_mak" value="%replace(%replace(%marker){'\t', '\\\\t'}){'\n','\\\\n'}"/>
+    <property name="p_thr" value="%thread"/>
+    <property name="pattern" value="%nopexception${p_tim}\t${p_thr}\t${p_lvl}\t${p_log}\t${p_mdc}\t${p_msg}\t${p_exc}\t${p_mak}\t%n"/>
+
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>${pattern}</pattern>
+        </encoder>
+    </appender>
+
+    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+        <file>${SLF4J_OUTPUT_DIRECTORY}/output.log</file>
+        <encoder>
+            <pattern>${pattern}</pattern>
+        </encoder>
+    </appender>
+
+    <logger level="debug" name="org.onap.logging.ref.slf4j" additivity="false">
+        <appender-ref ref="STDOUT" />
+        <appender-ref ref="FILE" />
+    </logger>
+
+    <root level="error">
+        <appender-ref ref="STDOUT" />
+    </root>
+
+</configuration>
diff --git a/reference/slf4j-reference/src/main/webapp/WEB-INF/web.xml b/reference/slf4j-reference/src/main/webapp/WEB-INF/web.xml
new file mode 100644 (file)
index 0000000..91a632c
--- /dev/null
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="slf4j-reference" version="3.0"/>
diff --git a/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/CallGraphTest.java b/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/CallGraphTest.java
new file mode 100644 (file)
index 0000000..3d123e0
--- /dev/null
@@ -0,0 +1,207 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.nio.file.Files;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.onap.logging.ref.slf4j.analysis.CallGraphAnalyzer;
+import org.onap.logging.ref.slf4j.analysis.CallGraphReportWriter;
+import org.onap.logging.ref.slf4j.analysis.LogEntry;
+import org.onap.logging.ref.slf4j.demo.bean.Request;
+import org.onap.logging.ref.slf4j.demo.bean.Response;
+import org.onap.logging.ref.slf4j.demo.component.AbstractComponentTest;
+import org.onap.logging.ref.slf4j.demo.component.alpha.ComponentAlpha;
+import org.slf4j.LoggerFactory;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.testng.Assert;
+import org.testng.annotations.AfterSuite;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+/**
+ * Simple verification that we can easily get a call graph out of
+ * some calls to logging via <tt>ONAPLogAdapter</tt>.
+ */
+public class CallGraphTest {
+
+    /** Temporary directory into which logfiles are written. */
+    private static File sDir;
+
+    @BeforeSuite
+    public static void setUp() throws Exception {
+        AbstractComponentTest.setInProcess();
+        sDir = Files.createTempDirectory(CallGraphTest.class.getName()).toFile();
+        System.getProperties().setProperty("SLF4J_OUTPUT_DIRECTORY", sDir.getAbsolutePath());
+        LoggerFactory.getLogger(CallGraphTest.class).info("Starting.");
+    }
+
+    @AfterSuite
+    public static void tearDown() throws Exception {
+        LoggerFactory.getLogger(CallGraphTest.class).info("Ending.");
+        Thread.sleep(1000L);
+        if (sDir != null) {
+            System.err.println("Should be deleting [" + sDir.getAbsolutePath() + "]...");
+        }
+    }
+
+    @Test(enabled = false)
+    public void testSimple() throws Exception {
+
+        final HttpServletRequest mock = new MockHttpServletRequest();
+        final ComponentAlpha a = new ComponentAlpha();
+        final Request request = new Request();
+        final Response response = a.execute(request, mock);
+        assertThat(response.getResponses().size(), is(0));
+    }
+
+    /**
+     * A more complex (interesting) example of generating a call graph.
+     * @throws Exception test failure.
+     */
+    @Test
+    public void testComplex() throws Exception {
+
+        Assert.assertNotNull(sDir);
+
+        // Fan out some requests between test components.
+
+        final Request a = new Request();
+        a.setService("alpha");
+
+        final Request b = new Request();
+        b.setService("beta");
+
+        final Request ac = new Request();
+        ac.setService("gamma");
+
+        final Request ad = new Request();
+        ad.setService("delta");
+
+        final Request bc1 = new Request();
+        bc1.setService("gamma");
+
+        final Request bc2 = new Request();
+        bc2.setService("gamma");
+
+        a.getRequests().add(b);
+        a.getRequests().add(ac);
+        a.getRequests().add(ad);
+        b.getRequests().add(bc1);
+        b.getRequests().add(bc2);
+
+        // Deeper.
+
+        final Request xb = new Request();
+        xb.setService("beta");
+
+        final Request xg = new Request();
+        xg.setService("gamma");
+
+        final Request xd = new Request();
+        xd.setService("delta");
+
+        a.getRequests().add(xb);
+        xb.getRequests().add(xg);
+        xg.getRequests().add(xd);
+
+        // Execute.
+
+        final HttpServletRequest mock = new MockHttpServletRequest();
+        final ComponentAlpha component = new ComponentAlpha();
+        final Response response = component.execute(a, mock);
+        System.err.println(response);
+
+        assertThat(response.getResponses().size(), is(4));
+
+        Thread.sleep(1000L);
+
+        // Find logfile.
+
+        File log = null;
+        for (final File candidate : sDir.listFiles()) {
+            if (candidate.getName().endsWith(".log")) {
+                log = candidate;
+                break;
+            }
+        }
+
+        Assert.assertNotNull(log);
+
+        System.err.println("READING LOGFILE: " + log.getAbsolutePath());
+
+        final CallGraphAnalyzer analyzer = new CallGraphAnalyzer();
+        try (final BufferedReader reader = new BufferedReader(new FileReader(log))) {
+            while (true) {
+
+                final String line = reader.readLine();
+                if (line == null) {
+                    break;
+                }
+
+                final LogEntry entry = new LogEntry(line);
+                analyzer.add(entry);
+            }
+        }
+
+        //
+        // Debug during dev, but annoying the rest of the time.
+        //
+        // System.err.println("--------------------------------------------------");
+        // for (final LogEntry e : analyzer.getEntries()) {
+        //     System.err.println(e.toShortString());
+        // }
+        // System.err.println("--------------------------------------------------");
+
+        final CallGraphReportWriter writer = new CallGraphReportWriter(analyzer);
+        final String shortReport = writer.getShortReport();
+        final String longReport = writer.getLongReport();
+
+        // Dump long report.
+
+        System.out.println("----\nGraph:\n\n" + longReport + "\n----");
+
+        // Validate short report.
+
+        assertThat("Alpha\n" +
+                "    Beta\n" +
+                "        Gamma\n" +
+                "        Gamma\n" +
+                "    Gamma\n" +
+                "    Delta\n" +
+                "    Beta\n" +
+                "        Gamma\n" +
+                "            Delta\n",
+                is(shortReport));
+
+        // Ensure output reaches System.xxx.
+
+        Thread.sleep(1000L);
+    }
+}
\ No newline at end of file
diff --git a/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/analysis/CallGraphAnalyzer.java b/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/analysis/CallGraphAnalyzer.java
new file mode 100644 (file)
index 0000000..208b3e1
--- /dev/null
@@ -0,0 +1,114 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.analysis;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Crude analyzer for log messages, to build a simple
+ * representation of the call graph.
+ */
+public class CallGraphAnalyzer {
+
+    /** Messages of interest. */
+    private List<LogEntry> mEntries = new ArrayList<>();
+
+    /**
+     * Capture entry if it's interesting.
+     * @param entry candidate.
+     * @return this.
+     */
+    public CallGraphAnalyzer add(final LogEntry entry) {
+
+        if (entry.getLogger().contains("ONAPLogAdapterTest")) {
+            return this;
+        }
+
+        if (StringUtils.isNotBlank(entry.getMarkers())) {
+            this.mEntries.add(entry);
+        }
+
+        return this;
+    }
+
+    /**
+     * Get all captured entries, for diagnostics only.
+     * @return entries.
+     */
+    public List<LogEntry> getEntries() {
+        return this.mEntries;
+    }
+
+    /**
+     * Find the entry point into the call graph through the various components.
+     * @return entry point or (failure) null.
+     */
+    public LogEntry findEntryPoint() {
+        for (final LogEntry e : this.mEntries) {
+            if (e.getLogger().endsWith("ComponentAlpha")) {
+                if ("ENTRY".equals(e.getMarkers())) {
+                    if (StringUtils.isBlank(e.getPartnerName())) {
+                        return e;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Find entries for where a component invokes others.
+     * @param parent parent ENTRY (not actually the entry where it's doing the invoking).
+     * @return components invoked by this one.
+     */
+    public List<LogEntry> findInvokes(final LogEntry parent) {
+        final List<LogEntry> invokes = new ArrayList<>();
+        for (final LogEntry e : this.mEntries) {
+            if (StringUtils.equals(parent.getInvocationID(), e.getInvocationID())) {
+                final String invokingID = e.getInvokingID();
+                if (StringUtils.isNotBlank(invokingID)) {
+                    invokes.add(e);
+                }
+            }
+        }
+        return invokes;
+    }
+
+    /**
+     * Find a specific invocation.
+     * @param invoke invocation record.
+     * @return invocation ENTRY, or (failure) null if not found.
+     */
+    public LogEntry findInvocation(final LogEntry invoke) {
+        for (final LogEntry e : this.mEntries) {
+            if ("ENTRY".equals(e.getMarkers())) {
+                if (StringUtils.equals(invoke.getInvokingID(), e.getInvocationID())) {
+                    return e;
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/analysis/CallGraphReportWriter.java b/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/analysis/CallGraphReportWriter.java
new file mode 100644 (file)
index 0000000..ee67b35
--- /dev/null
@@ -0,0 +1,111 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.analysis;
+
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.testng.Assert;
+
+/**
+ * A simple, recursive text-only report writer for the call graph.
+ */
+public class CallGraphReportWriter {
+
+    /** The analyzer which does the work. */
+    final CallGraphAnalyzer mAnalyzer;
+
+    /** Short report, for validation. */
+    final StringBuilder mShortReport = new StringBuilder();
+
+    /** Longer report, for human eyes. */
+    final StringBuilder mLongReport = new StringBuilder();
+
+    /**
+     * Construct writer.
+     * @param analyzer initialized analyzer.
+     */
+    public CallGraphReportWriter(final CallGraphAnalyzer analyzer) {
+
+        this.mAnalyzer = analyzer;
+
+        Assert.assertTrue(analyzer.getEntries().size() > 0);
+        final LogEntry e0 = analyzer.findEntryPoint();
+        Assert.assertNotNull(e0);
+
+        this.mLongReport.append(e0.toShortString()).append("\n");
+        this.mShortReport.append(StringUtils.substringAfter(e0.getLogger(), ".Component")).append("\n");
+
+        this.report(e0, 1);
+
+    }
+
+    /**
+     * Recursively analyze.
+     * @param invoker entry point.
+     * @param depth recursive depth, for handbrake.
+     */
+    private void report(final LogEntry invoker, final int depth) {
+
+        if (depth > 100) {
+            throw new AssertionError("Recursion ad infinitum");
+        }
+
+        final List<LogEntry> invokes0 = this.mAnalyzer.findInvokes(invoker);
+        for (final LogEntry invoke0 : invokes0) {
+
+            final LogEntry invoked0 = this.mAnalyzer.findInvocation(invoke0);
+
+            Assert.assertNotNull(invoked0);
+
+            final String indent = StringUtils.repeat(' ', depth * 4);
+            this.mLongReport.append(indent).append(invoked0.toShortString()).append('\n');
+            this.mShortReport.append(indent).append(StringUtils.substringAfter(invoked0.getLogger(), ".Component")).append('\n');
+
+            report(invoked0, depth + 1);
+        }
+    }
+
+    /**
+     * Get report.
+     * @return short report, for validation.
+     */
+    public String getShortReport() {
+        return this.mShortReport.toString();
+    }
+
+    /**
+     * Get report.
+     * @return long report, for printing out.
+     */
+    public String getLongReport() {
+        return this.mLongReport.toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        return this.getLongReport();
+    }
+}
diff --git a/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/analysis/LogEntry.java b/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/analysis/LogEntry.java
new file mode 100644 (file)
index 0000000..b9bd48f
--- /dev/null
@@ -0,0 +1,238 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.analysis;
+
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.slf4j.event.Level;
+
+/**
+ * Test class for reading a logentry during analysis.
+ */
+public class LogEntry {
+
+    /** Property. */
+    private final Date mTimestamp;
+
+    /** Property. */
+    private final String mThread;
+
+    /** Property. */
+    private final Level mLevel;
+
+    /** Property. */
+    private final String mLogger;
+
+    /** Property. */
+    private final String mMessage;
+
+    /** Property. */
+    private final String mException;
+
+    /** Property. */
+    private final Map<String, String> mMDCs;
+
+    /** Property. */
+    private final String mMarkers;
+
+    /**
+     * Construct from log line.
+     * @param line to be parsed.
+     */
+    public LogEntry(final String line) {
+
+        final String [] tokens = line.split("\t", -1);
+        if (tokens.length < 8) {
+            throw new IllegalArgumentException("Unsupported line (expected 8+ tokens, got "
+                    + tokens.length + "): " + line);
+        }
+
+        int index = 0;
+
+        this.mTimestamp = DatatypeConverter.parseDateTime(tokens[index++]).getTime();
+        this.mThread = tokens[index++];
+        this.mLevel = Level.valueOf(tokens[index++].trim());
+        this.mLogger = tokens[index++];
+
+        this.mMDCs = parseMDCs(tokens[index++]);
+        this.mMessage = tokens[index++];
+        this.mException = tokens[index++];
+        this.mMarkers = tokens[index++];
+    }
+
+    /**
+     * Parse serialized MDCs.
+     * @param mdc serialized DMC map.
+     * @return parsed.
+     */
+    static Map<String, String> parseMDCs(final String mdc) {
+
+        final Map<String, String> mdcs = new HashMap<>();
+        for (final String token : mdc.split(",")) {
+            final String[] mdcTokens = token.split("=");
+            if (mdcTokens.length == 2) {
+                mdcs.put(StringUtils.trim(mdcTokens[0]), StringUtils.trim(mdcTokens[1]));
+            }
+        }
+        return Collections.unmodifiableMap(mdcs);
+    }
+
+    /**
+     * Getter.
+     * @return property.
+     */
+    public Date getTimestamp() {
+        return this.mTimestamp;
+    }
+
+    /**
+     * Getter.
+     * @return property.
+     */
+    public String getThread() {
+        return this.mThread;
+    }
+
+    /**
+     * Getter.
+     * @return property.
+     */
+    public Level getLevel() {
+        return this.mLevel;
+    }
+
+    /**
+     * Getter.
+     * @return property.
+     */
+    public String getLogger() {
+        return this.mLogger;
+    }
+
+    /**
+     * Getter.
+     * @return property.
+     */
+    public String getMessage() {
+        return this.mMessage;
+    }
+
+    /**
+     * Getter.
+     * @return property.
+     */
+    public String getException() {
+        return this.mException;
+    }
+
+    /**
+     * Getter.
+     * @return property.
+     */
+    public Map<String, String> getMDCs() {
+        return this.mMDCs;
+    }
+
+    /**
+     * Getter.
+     * @return property.
+     */
+    public String getMarkers() {
+        return this.mMarkers;
+    }
+
+    /**
+     * Getter.
+     * @return property.
+     */
+    public String getRequestID() {
+        return this.getMDCs().get("RequestID");
+    }
+
+    /**
+     * Getter.
+     * @return property.
+     */
+    public String getInvocationID() {
+        return this.getMDCs().get("InvocationID");
+    }
+
+    /**
+     * Getter.
+     * @return property.
+     */
+    public String getPartnerName() {
+        return this.getMDCs().get("PartnerName");
+    }
+
+    /**
+     * Getter.
+     * @return property.
+     */
+    public String getInvokingID() {
+        if (StringUtils.defaultString(this.getMarkers()).startsWith("INVOKE")) {
+            return this.getMessage();
+        }
+        return null;
+    }
+
+    /**
+     * Getter.
+     * @return property.
+     */
+    public String toShortString() {
+        final StringBuilder buf = new StringBuilder();
+        buf.append("LogEntry(markers=").append(StringUtils.defaultString(this.getMarkers()));
+        buf.append(", logger=").append(this.getLogger().substring(1 + this.getLogger().lastIndexOf(".")));
+        if (StringUtils.isNotBlank(this.getRequestID())) {
+            buf.append(", requestID=[...]").append(StringUtils.right(this.getRequestID(), 8));
+        }
+        if (StringUtils.isNotBlank(this.getInvocationID())) {
+            buf.append(", invocationID=[...]").append(StringUtils.right(this.getInvocationID(), 8));
+        }
+        if (StringUtils.isNotBlank(this.getInvokingID())) {
+            buf.append(", invokingID=[...]").append(StringUtils.right(this.getInvokingID(), 8));
+        }
+
+        final Calendar c = Calendar.getInstance();
+        c.setTime(this.getTimestamp());
+
+        buf.append(", timestamp=").append(DatatypeConverter.printDateTime(c));
+        return buf.append(")").toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        return ToStringBuilder.reflectionToString(this);
+    }
+}
diff --git a/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/analysis/LogEntryTest.java b/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/analysis/LogEntryTest.java
new file mode 100644 (file)
index 0000000..63ead27
--- /dev/null
@@ -0,0 +1,71 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.analysis;
+
+import java.util.Map;
+
+import org.slf4j.event.Level;
+import org.testng.annotations.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+
+public class LogEntryTest {
+
+    @Test
+    public void testLogEntry() {
+
+        final String eg = "2018-05-07T16:45:53.056Z\tpool-1-thread-1\tINFO"
+            + "\torg.onap.logging.ref.slf4j.component.gamma.ComponentGamma\tInstanceUUID=fa8dd337-6991-4535-a069-ca552466d972,"
+            + " RequestID=46161759-1b92-40a4-a408-800e0d62dd9e, ServiceName=service.alpha, EntryTimestamp=2018-05-08T02:45:53.056,"
+            + " InvocationID=aac8fec9-498c-42a2-936b-38f5c0f5ca82, PartnerName=service.beta, ClientIPAddress=127.0.0.1,"
+            + " ServerFQDN=localhost\t\t\tENTRY\t\n";
+
+        final LogEntry parsed = new LogEntry(eg);
+        assertThat(parsed.getTimestamp(), notNullValue());
+        assertThat(parsed.getThread(), is("pool-1-thread-1"));
+        assertThat(parsed.getLevel(), is(Level.INFO));
+        assertThat(parsed.getLogger(), is("org.onap.logging.ref.slf4j.component.gamma.ComponentGamma"));
+        assertThat(parsed.getMDCs().get("ServiceName"), is("service.alpha"));
+        assertThat(parsed.getMDCs().get("PartnerName"), is("service.beta"));
+        assertThat(parsed.getMessage(), is(""));
+        assertThat(parsed.getMarkers(), is("ENTRY"));
+        assertThat(parsed.getException(), is(""));
+
+    }
+
+    @Test
+    public void testParseMDCsEmpty() {
+        final Map<String, String> map = LogEntry.parseMDCs("");
+        assertThat(map.size(), is(0));
+    }
+
+    @Test
+    public void testParseMDCs() {
+        final Map<String, String> map = LogEntry.parseMDCs("A=B, C=D , D = F ");
+        assertThat(map.get("A"), is("B"));
+        assertThat(map.get("C"), is("D"));
+        assertThat(map.get("D"), is("F"));
+        assertThat(map.size(), is(3));
+    }
+}
diff --git a/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/common/ONAPLogAdapterTest.java b/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/common/ONAPLogAdapterTest.java
new file mode 100644 (file)
index 0000000..60ee60f
--- /dev/null
@@ -0,0 +1,266 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.common;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+import org.slf4j.event.Level;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+
+/**
+ * Tests for {@link ONAPLogAdapter}.
+ */
+public class ONAPLogAdapterTest {
+
+    /**
+     * Ensure that MDCs are cleared after each testcase.
+     */
+    @AfterMethod
+    public void resetMDCs() {
+        MDC.clear();
+    }
+
+    /**
+     * Test nullcheck.
+     */
+    @Test
+    public void testCheckNotNull() {
+
+        ONAPLogAdapter.checkNotNull("");
+
+        try {
+            ONAPLogAdapter.checkNotNull(null);
+            Assert.fail("Should throw NullPointerException");
+        }
+        catch (final NullPointerException e) {
+
+        }
+    }
+
+    /**
+     * Test defaulting of nulls.
+     */
+    @Test
+    public void testDefaultToEmpty() {
+        assertThat(ONAPLogAdapter.defaultToEmpty("123"), is("123"));
+        assertThat(ONAPLogAdapter.defaultToEmpty(Integer.valueOf(1984)), is("1984"));
+        assertThat(ONAPLogAdapter.defaultToEmpty(null), is(""));
+    }
+
+    /**
+     * Test defaulting of nulls.
+     */
+    @Test
+    public void testDefaultToUUID() {
+        assertThat(ONAPLogAdapter.defaultToUUID("123"), is("123"));
+        UUID.fromString(ONAPLogAdapter.defaultToUUID(null));
+    }
+
+    /**
+     * Test ENTERING.
+     */
+    @Test
+    public void testEntering() {
+
+        final Logger logger = LoggerFactory.getLogger(this.getClass());
+        final ONAPLogAdapter adapter = new ONAPLogAdapter(logger);
+        final MockHttpServletRequest http = new MockHttpServletRequest();
+        http.setRequestURI("uri123");
+        http.setServerName("local123");
+        http.setRemoteAddr("remote123");
+        http.addHeader("X-ONAP-RequestID", "request123");
+        http.addHeader("X-ONAP-InvocationID", "invocation123");
+        http.addHeader("X-ONAP-PartnerName", "partner123");
+
+        try {
+            adapter.getServiceDescriptor().setServiceName("uri123");
+            adapter.entering(http);
+            final Map<String, String> mdcs = MDC.getCopyOfContextMap();
+            assertThat(mdcs.get("RequestID"), is("request123"));
+            assertThat(mdcs.get("InvocationID"), is("invocation123"));
+            assertThat(mdcs.get("PartnerName"), is("partner123"));
+            assertThat(mdcs.get("ServiceName"), is("uri123"));
+            assertThat(mdcs.get("ServerFQDN"), is("local123"));
+            assertThat(mdcs.get("ClientIPAddress"), is("remote123"));
+        }
+        finally {
+            MDC.clear();
+        }
+    }
+
+    /**
+     * Test EXITING.
+     */
+    @Test
+    public void testExiting() {
+
+        final Logger logger = LoggerFactory.getLogger(this.getClass());
+        final ONAPLogAdapter adapter = new ONAPLogAdapter(logger);
+
+        try {
+            MDC.put("somekey", "somevalue");
+            assertThat(MDC.get("somekey"), is("somevalue"));
+            adapter.exiting();
+            assertThat(MDC.get("somekey"), nullValue());
+        }
+        finally {
+            MDC.clear();
+        }
+    }
+
+    /**
+     * Test INVOKE.
+     */
+    @Test
+    public void testInvokeSyncAsyncNull() {
+
+        final Logger logger = LoggerFactory.getLogger(this.getClass());
+        final ONAPLogAdapter adapter = new ONAPLogAdapter(logger);
+
+        final UUID syncUUID = adapter.invoke(ONAPLogConstants.InvocationMode.SYNCHRONOUS);
+        assertThat(syncUUID, notNullValue());
+
+        final UUID asyncUUID = adapter.invoke(ONAPLogConstants.InvocationMode.SYNCHRONOUS);
+        assertThat(asyncUUID, notNullValue());
+
+        final UUID agnosticUUID = adapter.invoke((ONAPLogConstants.InvocationMode)null);
+        assertThat(agnosticUUID, notNullValue());
+
+    }
+
+    /**
+     * Test INVOKE, with RequestAdapter.
+     */
+    @Test
+    public void testInvokeWithAdapter() throws Exception {
+
+        final Logger logger = LoggerFactory.getLogger(this.getClass());
+        final ONAPLogAdapter adapter = new ONAPLogAdapter(logger);
+
+        final Map<String, String> headers = new HashMap<>();
+        final ONAPLogAdapter.RequestBuilder builder = new ONAPLogAdapter.RequestBuilder<ONAPLogAdapter.RequestBuilder>() {
+            @Override
+            public ONAPLogAdapter.RequestBuilder setHeader(final String name, final String value) {
+                headers.put(name, value);
+                return this;
+            }
+        };
+
+        try {
+            final UUID uuid = adapter.invoke(builder, ONAPLogConstants.InvocationMode.SYNCHRONOUS);
+            assertThat(uuid, notNullValue());
+            assertThat(headers.get(ONAPLogConstants.Headers.INVOCATION_ID), is(uuid.toString()));
+            assertThat(headers.containsKey(ONAPLogConstants.Headers.PARTNER_NAME), is(true));
+            assertThat(headers.containsKey(ONAPLogConstants.Headers.REQUEST_ID), is(true));
+        }
+        finally {
+            MDC.clear();
+        }
+    }
+
+    /**
+     * Exercise the contract, for a caller that's happy to have their
+     * service name automatically derived. (This validates nothing
+     * and achieves nothing; it's just to provide an example of minimal usage).
+     */
+    @Test
+    public void testContract() {
+
+        // Note no wrapper around HttpServletRequest, which will work for
+        // most invocations (since they come via HTTP), but otherwise
+        // can implement your own RequestAdapter.
+
+        final Logger logger = LoggerFactory.getLogger(this.getClass());
+        final ONAPLogAdapter adapter = new ONAPLogAdapter(logger);
+        final MockHttpServletRequest http = new MockHttpServletRequest();
+
+        // Immediately log ENTERING marker, with global MDCs.
+
+        adapter.entering(http);
+        try {
+
+            // Generate (and log) an invocationID, then use it to
+            // invoke another component.
+
+            final RESTClient client = new RESTClient();             // implements ONAPLogAdapter.RequestBuilder<RESTClient>.
+            adapter.invoke(client, ONAPLogConstants.InvocationMode.SYNCHRONOUS);
+            final RESTRequest request = null;                       // TODO: build real request.
+            final RESTResponse response = client.execute(request);  // TODO: handle real response.
+
+            // Set response details prior to #exiting.
+            // (Obviously there'd be errorhandling, etc. IRL).
+
+            adapter.getResponseDescriptor()
+                    .setResponseCode((String)null)
+                    .setResponseSeverity(Level.INFO)
+                    .setResponseStatus(ONAPLogConstants.ResponseStatus.COMPLETED);
+        }
+        finally {
+
+            // Return, logging EXIT marker, with response MDCs.
+
+            adapter.exiting();
+        }
+    }
+
+    /**
+     * Dummy class, for example code.
+     */
+    static class RESTClient implements ONAPLogAdapter.RequestBuilder<RESTClient> {
+
+        @Override
+        public RESTClient setHeader(final String name, final String value) {
+            return null;
+        }
+
+        RESTResponse execute(RESTRequest request) {
+            return null;
+        }
+    }
+
+    /**
+     * Dummy class, for example code.
+     */
+    static class RESTRequest {
+
+    }
+
+    /**
+     * Dummy class, for example code.
+     */
+    static class RESTResponse {
+
+    }
+}
diff --git a/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/common/ONAPLogConstantsTest.java b/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/common/ONAPLogConstantsTest.java
new file mode 100644 (file)
index 0000000..aa0f7ae
--- /dev/null
@@ -0,0 +1,86 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.common;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+
+/**
+ * Tests for {@link ONAPLogConstants}.
+ */
+public class ONAPLogConstantsTest {
+
+    @Test
+    public void testConstructor() throws Exception {
+        try {
+            ONAPLogConstants.class.getDeclaredConstructors()[0].newInstance();
+            Assert.fail("Should fail for hidden constructor.");
+        }
+        catch (final IllegalAccessException e) {
+
+        }
+    }
+
+    @Test
+    public void testConstructorUnsupported() throws Exception {
+        try {
+            Constructor<?> c = ONAPLogConstants.class.getDeclaredConstructors()[0];
+            c.setAccessible(true);
+            c.newInstance();
+            Assert.fail("Should fail for hidden constructor.");
+        }
+        catch (final InvocationTargetException e) {
+            assertThat(e.getCause(), instanceOf(UnsupportedOperationException.class));
+        }
+    }
+
+    @Test
+    public void testHeaders() {
+        assertThat(ONAPLogConstants.Headers.REQUEST_ID.toString(), is("X-ONAP-RequestID"));
+        assertThat(ONAPLogConstants.Headers.INVOCATION_ID.toString(), is("X-ONAP-InvocationID"));
+        assertThat(ONAPLogConstants.Headers.PARTNER_NAME.toString(), is("X-ONAP-PartnerName"));
+    }
+
+    @Test
+    public void testMarkers() {
+        assertThat(ONAPLogConstants.Markers.ENTRY.toString(), is("ENTRY"));
+        assertThat(ONAPLogConstants.Markers.EXIT.toString(), is("EXIT"));
+        assertThat(ONAPLogConstants.Markers.INVOKE.toString(), is("INVOKE"));
+        assertThat(ONAPLogConstants.Markers.INVOKE_ASYNCHRONOUS.toString(), is("INVOKE [ ASYNCHRONOUS ]"));
+        assertThat(ONAPLogConstants.Markers.INVOKE_SYNCHRONOUS.toString(), is("INVOKE [ SYNCHRONOUS ]"));
+    }
+
+    @Test
+    public void testInvocationMode() {
+        assertThat(ONAPLogConstants.InvocationMode.SYNCHRONOUS.getMarker(),
+                is(ONAPLogConstants.Markers.INVOKE_SYNCHRONOUS));
+        assertThat(ONAPLogConstants.InvocationMode.ASYNCHRONOUS.getMarker(),
+                is(ONAPLogConstants.Markers.INVOKE_ASYNCHRONOUS));
+    }
+}
diff --git a/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/bean/RequestTest.java b/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/bean/RequestTest.java
new file mode 100644 (file)
index 0000000..6c622a7
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.bean;
+
+import org.json.JSONObject;
+import org.testng.annotations.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+public class RequestTest {
+
+    @Test
+    public void testRoundtrip() {
+
+        final Request in = new Request();
+        in.setCode("code0");
+        in.setService("service0");
+        in.setSeverity("severity0");
+
+        final Request childA = new Request();
+        childA.setCode("codeA");
+        childA.setService("serviceA");
+        childA.setSeverity("severityA");
+
+        final Request childB = new Request();
+        childB.setCode("codeB");
+        childB.setService("serviceB");
+        childB.setSeverity("severityB");
+
+        in.getRequests().add(childA);
+        in.getRequests().add(childB);
+
+        System.out.println(in.toString());
+        System.out.println(new JSONObject(in.toString()).toString());
+
+        final Request out = Request.fromJSON(new JSONObject(in.toString()));
+        assertThat(out.getCode(), is(in.getCode()));
+        assertThat(out.getService(), is(in.getService()));
+        assertThat(out.getSeverity(), is(in.getSeverity()));
+        assertThat(out.getRequests().size(), is(2));
+        assertThat(out.getRequests().get(0).getCode(), is("codeA"));
+        assertThat(out.getRequests().get(1).getCode(), is("codeB"));
+    }
+}
diff --git a/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/bean/ResponseTest.java b/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/bean/ResponseTest.java
new file mode 100644 (file)
index 0000000..63cc68e
--- /dev/null
@@ -0,0 +1,61 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.bean;
+
+import org.json.JSONObject;
+import org.testng.annotations.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+public class ResponseTest {
+
+    @Test
+    public void testRoundtrip() {
+
+        final Response in = new Response();
+        in.setCode("code0");
+        in.setSeverity("severity0");
+
+        final Response childA = new Response();
+        childA.setCode("codeA");
+        childA.setSeverity("severityA");
+
+        final Response childB = new Response();
+        childB.setCode("codeB");
+        childB.setSeverity("severityB");
+
+        in.getResponses().add(childA);
+        in.getResponses().add(childB);
+
+        System.out.println(in.toString());
+        System.out.println(new JSONObject(in.toString()).toString());
+
+        final Response out = Response.fromJSON(new JSONObject(in.toString()));
+        assertThat(out.getCode(), is(in.getCode()));
+        assertThat(out.getSeverity(), is(in.getSeverity()));
+        assertThat(out.getResponses().size(), is(2));
+        assertThat(out.getResponses().get(0).getCode(), is("codeA"));
+        assertThat(out.getResponses().get(1).getCode(), is("codeB"));
+
+    }
+}
diff --git a/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/component/AbstractComponentTest.java b/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/component/AbstractComponentTest.java
new file mode 100644 (file)
index 0000000..5275853
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.component;
+
+public class AbstractComponentTest {
+
+    public static void setInProcess() {
+        AbstractComponent.setInProcess();
+    }
+}
diff --git a/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/component/alpha/ComponentAlphaTest.java b/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/component/alpha/ComponentAlphaTest.java
new file mode 100644 (file)
index 0000000..50cd80a
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.component.alpha;
+
+import java.util.UUID;
+
+import org.testng.annotations.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+/**
+ * Tests for {@link ComponentAlpha}.
+ */
+public class ComponentAlphaTest {
+
+    @Test
+    public void testGetId() {
+        assertThat(new ComponentAlpha().getId(), is("alpha"));
+    }
+
+    @Test
+    public void testGetInstanceUUID() {
+        UUID.fromString(new ComponentAlpha().getInstanceUUID());
+    }
+}
diff --git a/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/component/beta/ComponentBetaTest.java b/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/component/beta/ComponentBetaTest.java
new file mode 100644 (file)
index 0000000..20f4d72
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.component.beta;
+
+import java.util.UUID;
+
+import org.testng.annotations.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+/**
+ * Tests for {@link ComponentBeta}.
+ */
+public class ComponentBetaTest {
+
+    @Test
+    public void testGetId() {
+        assertThat(new ComponentBeta().getId(), is("beta"));
+    }
+
+    @Test
+    public void testGetInstanceUUID() {
+        UUID.fromString(new ComponentBeta().getInstanceUUID());
+    }
+}
diff --git a/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/component/delta/ComponentDeltaTest.java b/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/component/delta/ComponentDeltaTest.java
new file mode 100644 (file)
index 0000000..53829a8
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.component.delta;
+
+import java.util.UUID;
+
+import org.testng.annotations.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+/**
+ * Tests for {@link ComponentDelta}.
+ */
+public class ComponentDeltaTest {
+
+    @Test
+    public void testGetId() {
+        assertThat(new ComponentDelta().getId(), is("delta"));
+    }
+
+    @Test
+    public void testGetInstanceUUID() {
+        UUID.fromString(new ComponentDelta().getInstanceUUID());
+    }
+}
diff --git a/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/component/gamma/ComponentGammaTest.java b/reference/slf4j-reference/src/test/java/org/onap/logging/ref/slf4j/demo/component/gamma/ComponentGammaTest.java
new file mode 100644 (file)
index 0000000..24222a6
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.logging
+ * ================================================================================
+ * Copyright © 2018 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.logging.ref.slf4j.demo.component.gamma;
+
+import java.util.UUID;
+
+import org.testng.annotations.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+/**
+ * Tests for {@link ComponentGamma}.
+ */
+public class ComponentGammaTest {
+
+    @Test
+    public void testGetId() {
+        assertThat(new ComponentGamma().getId(), is("gamma"));
+    }
+
+    @Test
+    public void testGetInstanceUUID() {
+        UUID.fromString(new ComponentGamma().getInstanceUUID());
+    }
+}
diff --git a/reference/slf4j-reference/src/test/java/testng.xml b/reference/slf4j-reference/src/test/java/testng.xml
new file mode 100644 (file)
index 0000000..7b31c26
--- /dev/null
@@ -0,0 +1,8 @@
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="org.onap.logging.ref.slf4j" verbose="9" thread-count="1" parallel="methods">
+    <test name="all" thread-count="1" enabled="true">
+        <packages>
+            <package name="org.onap.logging.ref.slf4j.*"/>
+        </packages>
+    </test>
+</suite>
\ No newline at end of file