transaction logging through a feature 75/37975/2
authorJorge Hernandez <jh1730@att.com>
Fri, 23 Mar 2018 06:14:12 +0000 (01:14 -0500)
committerJorge Hernandez <jh1730@att.com>
Fri, 23 Mar 2018 17:40:02 +0000 (17:40 +0000)
This is the first of 2 code inspections.

This review contains though most of the work.
It keeps tracks of all in progress transactions
by intercepting all notifications.   From that
appropriate metrics can be obtained and logged.

The actual logging of the metrics and enablement
of the feature at install time will come in a
subsequent review.

Change-Id: Ie5c8adfbdeae72758da7da8a1b4133cd4818c6fb
Issue-ID: POLICY-533
Signed-off-by: Jorge Hernandez <jh1730@att.com>
controlloop/common/feature-controlloop-trans/pom.xml [new file with mode: 0644]
controlloop/common/feature-controlloop-trans/src/assembly/assemble_zip.xml [new file with mode: 0644]
controlloop/common/feature-controlloop-trans/src/main/feature/config/feature-controlloop-trans.properties [new file with mode: 0644]
controlloop/common/feature-controlloop-trans/src/main/java/org/onap/policy/drools/apps/controlloop/feature/trans/ControlLoopMetrics.java [new file with mode: 0644]
controlloop/common/feature-controlloop-trans/src/main/java/org/onap/policy/drools/apps/controlloop/feature/trans/ControlLoopMetricsFeature.java [new file with mode: 0644]
controlloop/common/feature-controlloop-trans/src/main/resources/META-INF/services/org.onap.policy.drools.features.PolicyControllerFeatureAPI [new file with mode: 0644]
controlloop/common/feature-controlloop-trans/src/test/java/org/onap/policy/drools/apps/controlloop/feature/trans/ControlLoopMetricsFeatureTest.java [new file with mode: 0644]
controlloop/common/feature-controlloop-trans/src/test/resources/feature-controlloop-trans.properties [new file with mode: 0644]
controlloop/common/feature-controlloop-trans/src/test/resources/metrics-controller.properties [new file with mode: 0644]
controlloop/common/pom.xml

diff --git a/controlloop/common/feature-controlloop-trans/pom.xml b/controlloop/common/feature-controlloop-trans/pom.xml
new file mode 100644 (file)
index 0000000..3f34ff6
--- /dev/null
@@ -0,0 +1,124 @@
+<!--
+  ============LICENSE_START=======================================================
+  ONAP
+  ================================================================================
+  Copyright (C) 2018 AT&T Intellectual Property. 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=========================================================
+  -->
+
+<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.policy.drools-applications.controlloop.common</groupId>
+        <artifactId>common</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>feature-controlloop-trans</artifactId>
+
+    <description>
+        Loadable PDP-D feature module to track control loop transactions
+    </description>
+
+    <properties>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <version>2.6</version>
+                <executions>
+                    <execution>
+                        <id>zipfile</id>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <phase>package</phase>
+                        <configuration>
+                            <attach>true</attach>
+                            <finalName>${project.artifactId}-${project.version}</finalName>
+                            <descriptors>
+                                <descriptor>src/assembly/assemble_zip.xml</descriptor>
+                            </descriptors>
+                            <appendAssemblyId>false</appendAssemblyId>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <version>2.8</version>
+                <executions>
+                    <execution>
+                        <id>copy-dependencies</id>
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+                        <phase>prepare-package</phase>
+                        <configuration>
+                            <outputDirectory>${project.build.directory}/assembly/lib</outputDirectory>
+                            <overWriteReleases>false</overWriteReleases>
+                            <overWriteSnapshots>true</overWriteSnapshots>
+                            <overWriteIfNewer>true</overWriteIfNewer>
+                            <useRepositoryLayout>false</useRepositoryLayout>
+                            <addParentPoms>false</addParentPoms>
+                            <copyPom>false</copyPom>
+                            <includeScope>runtime</includeScope>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onap.policy.drools-applications.controlloop.common.model-impl</groupId>
+            <artifactId>events</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.policy.drools-pdp</groupId>
+            <artifactId>policy-management</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.policy.drools-pdp</groupId>
+            <artifactId>policy-utils</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>19.0</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/controlloop/common/feature-controlloop-trans/src/assembly/assemble_zip.xml b/controlloop/common/feature-controlloop-trans/src/assembly/assemble_zip.xml
new file mode 100644 (file)
index 0000000..095c8f1
--- /dev/null
@@ -0,0 +1,75 @@
+<!--
+  ============LICENSE_START=======================================================
+  ONAP
+  ================================================================================
+  Copyright (C) 2018 AT&T Intellectual Property. 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=========================================================
+  -->
+
+<!-- Defines how we build the .zip file which is our distribution. -->
+
+<assembly
+       xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+       <id>feature-controlloop-trans-package</id>
+       <formats>
+               <format>zip</format>
+       </formats>
+
+       <includeBaseDirectory>false</includeBaseDirectory>
+
+       <fileSets>
+               <fileSet>
+                       <directory>target</directory>
+                       <outputDirectory>lib/feature</outputDirectory>
+                       <includes>
+                               <include>feature-controlloop-trans-${project.version}.jar</include>
+                       </includes>
+               </fileSet>
+               <fileSet>
+                       <directory>target/assembly/lib</directory>
+                       <outputDirectory>lib/dependencies</outputDirectory>
+                       <includes>
+                               <include>*.jar</include>
+                       </includes>
+               </fileSet>
+               <fileSet>
+                       <directory>src/main/feature/config</directory>
+                       <outputDirectory>config</outputDirectory>
+                       <fileMode>0644</fileMode>
+                       <excludes/>
+               </fileSet>
+               <fileSet>
+                       <directory>src/main/feature/bin</directory>
+                       <outputDirectory>bin</outputDirectory>
+                       <fileMode>0744</fileMode>
+                       <excludes/>
+               </fileSet>
+               <fileSet>
+                       <directory>src/main/feature/db</directory>
+                       <outputDirectory>db</outputDirectory>
+                       <fileMode>0744</fileMode>
+                       <excludes/>
+               </fileSet>
+               <fileSet>
+                       <directory>src/main/feature/install</directory>
+                       <outputDirectory>install</outputDirectory>
+                       <fileMode>0744</fileMode>
+                       <excludes/>
+               </fileSet>
+       </fileSets>
+
+</assembly>
diff --git a/controlloop/common/feature-controlloop-trans/src/main/feature/config/feature-controlloop-trans.properties b/controlloop/common/feature-controlloop-trans/src/main/feature/config/feature-controlloop-trans.properties
new file mode 100644 (file)
index 0000000..5d350d5
--- /dev/null
@@ -0,0 +1,2 @@
+controlloop.cache.transactions.size=500
+controllop.cache.transactions.timeout.seconds=3600
diff --git a/controlloop/common/feature-controlloop-trans/src/main/java/org/onap/policy/drools/apps/controlloop/feature/trans/ControlLoopMetrics.java b/controlloop/common/feature-controlloop-trans/src/main/java/org/onap/policy/drools/apps/controlloop/feature/trans/ControlLoopMetrics.java
new file mode 100644 (file)
index 0000000..9306514
--- /dev/null
@@ -0,0 +1,351 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. 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.policy.drools.apps.controlloop.feature.trans;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.cache.RemovalListener;
+import com.google.common.cache.RemovalNotification;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import org.onap.policy.controlloop.VirtualControlLoopNotification;
+import org.onap.policy.drools.persistence.SystemPersistence;
+import org.onap.policy.drools.system.PolicyController;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Control Loop Metrics Tracker
+ */
+public interface ControlLoopMetrics {
+
+    /**
+     * gets all transaction identifiers being monitored
+     *
+     * @return transaction id list
+     */
+    List<UUID> getTransactionIds();
+
+    /**
+     * gets all detailed transactions
+     *
+     * @return list of transactions
+     */
+    List<VirtualControlLoopNotification> getTransactions();
+
+    /**
+     * track controller's notification events
+     *
+     * @param controller policy controller sending out notification
+     * @param notification notification
+     */
+    void transactionEvent(PolicyController controller, VirtualControlLoopNotification notification);
+
+    /**
+     * gets an in-progress transaction
+     *
+     * @param requestId request ID
+     * @return in progress notification
+     */
+    VirtualControlLoopNotification getTransaction(UUID requestId);
+
+    /**
+     * removes an in-progress transaction
+     *
+     * @param requestId request ID
+     * @return in progress notification
+     */
+    void removeTransaction(UUID requestId);
+
+    /**
+     * get cache size
+     *
+     * @return cache size
+     */
+    long getCacheSize();
+
+    /**
+     * get cache size
+     *
+     * @return cache size
+     */
+    long getCacheOccupancy();
+
+    /**
+     * sets cache size
+     *
+     * @param cacheSize cache size
+     */
+    void setMaxCacheSize(long cacheSize);
+
+    /**
+     * cached transaction expiration timeout in seconds
+     *
+     * @return transaction timeout in seconds
+     */
+    long getTransactionTimeout();
+
+    /**
+     * sets transaction timeout in seconds
+     *
+     * @param transactionTimeout transaction timeout in seconds
+     */
+    void setTransactionTimeout(long transactionTimeout);
+
+    /**
+     * reset cache
+     *
+     * @param cacheSize new cache size
+     * @param transactionTimeout new transaction timeout in seconds
+     */
+    void resetCache(long cacheSize, long transactionTimeout);
+
+    /**
+     * refresh underlying transaction management
+     */
+    void refresh();
+
+    /**
+     * singleton manager object
+     */
+    ControlLoopMetrics manager = new CacheBasedControlLoopMetricsManager();
+}
+
+/**
+ * Control Loop Metrics Tracker Implementation
+ */
+class CacheBasedControlLoopMetricsManager implements ControlLoopMetrics {
+
+    private static final Logger logger = LoggerFactory.getLogger(CacheBasedControlLoopMetricsManager.class);
+
+    private LoadingCache<UUID, VirtualControlLoopNotification> cache;
+    private long cacheSize = ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_DEFAULT;
+
+    private long transactionTimeout = ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_DEFAULT;
+
+    public CacheBasedControlLoopMetricsManager() {
+
+        Properties properties =
+            SystemPersistence.manager.getProperties(ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME);
+
+        /* cache size */
+
+        try {
+            this.cacheSize =
+                Long.parseLong(properties.getProperty(ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_PROPERTY,
+                    "" + ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_DEFAULT));
+        } catch (Exception e) {
+            logger.warn("{}:{} property cannot be accessed", ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME,
+                ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_PROPERTY, e);
+        }
+
+        /* transaction timeout */
+
+        try {
+            this.transactionTimeout =
+                Long.parseLong(properties.getProperty(ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_PROPERTY,
+                    "" + ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_DEFAULT));
+        } catch (Exception e) {
+            logger.warn("{}:{} property cannot be accessed", ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME,
+                ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_PROPERTY, e);
+        }
+
+        resetCache(this.cacheSize, this.transactionTimeout);
+    }
+
+    @Override
+    public void resetCache(long cacheSize, long transactionTimeout) {
+        this.cacheSize = cacheSize;
+        this.transactionTimeout = transactionTimeout;
+
+        CacheLoader<UUID, VirtualControlLoopNotification> loader = new CacheLoader<UUID, VirtualControlLoopNotification>() {
+
+            @Override
+            public VirtualControlLoopNotification load(UUID key) throws Exception {
+                return null;
+            }
+        };
+
+        RemovalListener<UUID, VirtualControlLoopNotification> listener = new RemovalListener<UUID, VirtualControlLoopNotification>() {
+            @Override
+            public void onRemoval(RemovalNotification<UUID, VirtualControlLoopNotification> notification) {
+                if (notification.wasEvicted()) {
+                    evicted(notification.getValue());
+                } else {
+                    logger.info("REMOVAL: {}->{} from {} because of {}", notification.getValue().getFrom(),
+                        notification.getValue(), notification.getCause().name());
+                }
+            }
+        };
+
+        synchronized (this) {
+            if (this.cache != null) {
+                this.cache.cleanUp();
+                this.cache.invalidateAll();
+            }
+
+            this.cache = CacheBuilder.newBuilder().
+                maximumSize(this.cacheSize).expireAfterWrite(transactionTimeout, TimeUnit.SECONDS).
+                removalListener(listener).build(loader);
+        }
+    }
+
+    @Override
+    public void refresh() {
+        this.cache.cleanUp();
+    }
+
+    @Override
+    public List<UUID> getTransactionIds() {
+        return new ArrayList<>(this.cache.asMap().keySet());
+    }
+
+    @Override
+    public List<VirtualControlLoopNotification> getTransactions() {
+        return new ArrayList<>(this.cache.asMap().values());
+    }
+
+    @Override
+    public void transactionEvent(PolicyController controller, VirtualControlLoopNotification notification) {
+        if (notification == null || notification.getRequestID() == null || notification.getNotification() == null) {
+            logger.warn("Invalid notification: {}", notification);
+            return;
+        }
+
+        if (notification.getNotificationTime() == null) {
+            notification.setNotificationTime(ZonedDateTime.now());
+        }
+
+        notification.setFrom(notification.getFrom() + ":" + controller.getName());
+
+        this.metric(notification);
+
+        switch (notification.getNotification()) {
+            case REJECTED:
+            case FINAL_FAILURE:
+            case FINAL_SUCCESS:
+            case FINAL_OPENLOOP:
+                endTransaction(notification);
+                break;
+            default:
+                /* any other value is an in progress transaction */
+                inProgressTransaction(notification);
+                break;
+        }
+    }
+
+    @Override
+    public VirtualControlLoopNotification getTransaction(UUID requestId) {
+        return cache.getIfPresent(requestId);
+    }
+
+    @Override
+    public void removeTransaction(UUID requestId) {
+        cache.invalidate(requestId);
+    }
+
+    /**
+     * tracks an in progress control loop transaction
+     *
+     * @param notification control loop notification
+     */
+    protected void inProgressTransaction(VirtualControlLoopNotification notification) {
+        if (cache.getIfPresent(notification.getRequestID()) == null) {
+            cache.put(notification.getRequestID(), notification);
+        }
+    }
+
+    /**
+     * end of a control loop transaction
+     *
+     * @param notification control loop notification
+     */
+    protected void endTransaction(VirtualControlLoopNotification notification) {
+        ZonedDateTime startTime;
+        VirtualControlLoopNotification startNotification = cache.getIfPresent(notification.getRequestID());
+        if (startNotification != null) {
+            startTime = startNotification.getNotificationTime();
+        } else {
+            startTime = notification.getNotificationTime();
+        }
+
+        this.transaction(notification, startTime);
+
+        cache.invalidate(startNotification);
+    }
+
+    protected void evicted(VirtualControlLoopNotification notification) {
+        transaction(notification, ZonedDateTime.now());
+    }
+
+    @Override
+    public long getCacheSize() {
+        return this.cacheSize;
+    }
+
+    @Override
+    public void setMaxCacheSize(long cacheSize) {
+        this.cacheSize = cacheSize;
+    }
+
+    @Override
+    public long getTransactionTimeout() {
+        return this.transactionTimeout;
+    }
+
+    @Override
+    public void setTransactionTimeout(long transactionTimeout) {
+        this.transactionTimeout = transactionTimeout;
+    }
+
+    @Override
+    public long getCacheOccupancy() {
+        return this.cache.size();
+    }
+
+    protected void metric(VirtualControlLoopNotification notification) {
+        // TODO: next review
+        // set up MDC
+        // logger.info(LoggerUtil.METRIC_LOG_MARKER, "METRIC:{}", notification);
+    }
+
+    protected void transaction(VirtualControlLoopNotification notification, ZonedDateTime startTime) {
+        // TODO: next review
+        // set up MDC
+        // Duration.between(notification.getNotificationTime(), ZonedDateTime.now()).toMillis())
+        // logger.info(LoggerUtil.TRANSACTION_LOG_MARKER, "TRANSACTION:{}->{} {} ms.", notification.getRequestID(), notification,
+        //  durationMs);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuffer sb = new StringBuffer("CacheBasedControlLoopMetricsManager{");
+        sb.append("cacheSize=").append(cacheSize);
+        sb.append(", transactionTimeout=").append(transactionTimeout);
+        sb.append('}');
+        return sb.toString();
+    }
+}
diff --git a/controlloop/common/feature-controlloop-trans/src/main/java/org/onap/policy/drools/apps/controlloop/feature/trans/ControlLoopMetricsFeature.java b/controlloop/common/feature-controlloop-trans/src/main/java/org/onap/policy/drools/apps/controlloop/feature/trans/ControlLoopMetricsFeature.java
new file mode 100644 (file)
index 0000000..29630e3
--- /dev/null
@@ -0,0 +1,90 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. 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.policy.drools.apps.controlloop.feature.trans;
+
+import org.onap.policy.controlloop.VirtualControlLoopNotification;
+import org.onap.policy.drools.event.comm.Topic.CommInfrastructure;
+import org.onap.policy.drools.features.PolicyControllerFeatureAPI;
+import org.onap.policy.drools.system.PolicyController;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Feature that tracks Transactions by observing Notification Patterns.
+ */
+public class ControlLoopMetricsFeature implements PolicyControllerFeatureAPI {
+
+    /**
+     * Feature Sequence Priority
+     */
+    public final static int FEATURE_SEQUENCE_PRIORITY = 100000;
+
+    /**
+     * Properties Configuration Name
+     */
+    public static final String CONFIGURATION_PROPERTIES_NAME = "feature-controlloop-trans";
+
+    /**
+     * maximum number of transaction cache entries
+     */
+    public static final String CL_CACHE_TRANS_SIZE_PROPERTY = "controlloop.cache.transactions.size";
+    public static final int CL_CACHE_TRANS_SIZE_DEFAULT = 100;
+
+    /**
+     * transaction timeout in minutes
+     */
+    public static final String CL_CACHE_TRANS_TIMEOUT_SECONDS_PROPERTY = "controllop.cache.transactions.timeout.seconds";
+    public static final long CL_CACHE_TRANS_TIMEOUT_SECONDS_DEFAULT = 1L * 60 * 60;
+
+    @Override
+    public boolean afterShutdown(PolicyController controller) {
+        return false;
+    }
+
+    /**
+     * Logger
+     */
+    private static Logger logger = LoggerFactory.getLogger(ControlLoopMetricsFeature.class);
+
+    /**
+     * Intercept Control Loop Notifications
+     *
+     * @param controller - controller
+     * @param protocol - protocol
+     * @param topic - topic
+     * @param event - event object
+     * @return
+     */
+    @Override
+    public boolean beforeDeliver(PolicyController controller, CommInfrastructure protocol, String topic, Object event) {
+        if (event instanceof VirtualControlLoopNotification)
+            ControlLoopMetrics.manager.transactionEvent(controller, (VirtualControlLoopNotification) event);
+
+        /* do not take ownership */
+        return false;
+    }
+
+    @Override
+    public int getSequenceNumber() {
+        return FEATURE_SEQUENCE_PRIORITY;
+    }
+
+}
diff --git a/controlloop/common/feature-controlloop-trans/src/main/resources/META-INF/services/org.onap.policy.drools.features.PolicyControllerFeatureAPI b/controlloop/common/feature-controlloop-trans/src/main/resources/META-INF/services/org.onap.policy.drools.features.PolicyControllerFeatureAPI
new file mode 100644 (file)
index 0000000..1e9fde6
--- /dev/null
@@ -0,0 +1 @@
+org.onap.policy.drools.apps.controlloop.feature.trans.ControlLoopMetricsFeature
diff --git a/controlloop/common/feature-controlloop-trans/src/test/java/org/onap/policy/drools/apps/controlloop/feature/trans/ControlLoopMetricsFeatureTest.java b/controlloop/common/feature-controlloop-trans/src/test/java/org/onap/policy/drools/apps/controlloop/feature/trans/ControlLoopMetricsFeatureTest.java
new file mode 100644 (file)
index 0000000..6e3db3f
--- /dev/null
@@ -0,0 +1,181 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. 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.policy.drools.apps.controlloop.feature.trans;
+
+import java.nio.file.Path;
+import java.util.UUID;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.policy.controlloop.ControlLoopNotificationType;
+import org.onap.policy.controlloop.VirtualControlLoopNotification;
+import org.onap.policy.drools.event.comm.Topic.CommInfrastructure;
+import org.onap.policy.drools.persistence.SystemPersistence;
+import org.onap.policy.drools.system.PolicyController;
+import org.onap.policy.drools.system.PolicyEngine;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * ControlLoopMetrics Tests
+ */
+public class ControlLoopMetricsFeatureTest {
+
+    private static final Path configPath = SystemPersistence.manager.getConfigurationPath();
+    private static PolicyController testController;
+
+    @BeforeClass
+    public static void setUp() {
+        SystemPersistence.manager.setConfigurationDir("src/test/resources");
+        testController =
+            PolicyEngine.manager.createPolicyController
+                ("metrics", SystemPersistence.manager.getControllerProperties("metrics"));
+    }
+
+    @AfterClass
+    public static void tearDown() {
+        SystemPersistence.manager.setConfigurationDir(configPath.toString());
+    }
+
+    @Test
+    public void cacheDefaults() {
+        assertTrue(ControlLoopMetrics.manager.getCacheSize() == 3);
+        assertTrue(ControlLoopMetrics.manager.getTransactionTimeout() == 10);
+        assertTrue(ControlLoopMetrics.manager.getCacheOccupancy() == 0);
+    }
+
+    @Test
+    public void invalidNotifications() {
+        ControlLoopMetricsFeature feature = new ControlLoopMetricsFeature();
+        VirtualControlLoopNotification notification = new VirtualControlLoopNotification();
+        feature.beforeDeliver(testController, CommInfrastructure.DMAAP, "POLICY-CL-MGT", notification);
+        this.cacheDefaults();
+
+        UUID requestId = UUID.randomUUID();
+        notification.setRequestID(requestId);
+
+        feature.beforeDeliver(testController, CommInfrastructure.DMAAP, "POLICY-CL-MGT", notification);
+        assertNull(ControlLoopMetrics.manager.getTransaction(requestId));
+        this.cacheDefaults();
+    }
+
+    @Test
+    public void validActiveNotification() {
+        ControlLoopMetricsFeature feature = new ControlLoopMetricsFeature();
+        VirtualControlLoopNotification notification = new VirtualControlLoopNotification();
+        UUID requestId = UUID.randomUUID();
+        notification.setRequestID(requestId);
+        notification.setNotification(ControlLoopNotificationType.ACTIVE);
+
+        feature.beforeDeliver(testController, CommInfrastructure.DMAAP, "POLICY-CL-MGT", notification);
+        assertNotNull(ControlLoopMetrics.manager.getTransaction(requestId));
+        assertTrue(ControlLoopMetrics.manager.getTransaction(requestId).getFrom().contains(testController.getName()));
+        assertNotNull(ControlLoopMetrics.manager.getTransaction(requestId).getNotificationTime());
+        assertTrue(ControlLoopMetrics.manager.getCacheOccupancy() == 1);
+
+        /* let the entries expire */
+        try {
+            Thread.sleep((ControlLoopMetrics.manager.getTransactionTimeout()+5)*1000L);
+        } catch (InterruptedException e) {
+            /* nothing to do */
+        }
+
+        assertNull(ControlLoopMetrics.manager.getTransaction(requestId));
+        this.cacheDefaults();
+    }
+
+    @Test
+    public void reset() {
+        VirtualControlLoopNotification notification = this.generateNotification();
+        new ControlLoopMetricsFeature().beforeDeliver(testController, CommInfrastructure.DMAAP, "POLICY-CL-MGT", notification);
+
+        assertNotNull(ControlLoopMetrics.manager.getTransaction(notification.getRequestID()));
+
+        ControlLoopMetrics.manager.resetCache(ControlLoopMetrics.manager.getCacheSize(), ControlLoopMetrics.manager.getTransactionTimeout());
+        assertNull(ControlLoopMetrics.manager.getTransaction(notification.getRequestID()));
+        this.cacheDefaults();
+    }
+
+    @Test
+    public void removeTransaction() {
+        VirtualControlLoopNotification notification = this.generateNotification();
+        assertNull(ControlLoopMetrics.manager.getTransaction(notification.getRequestID()));
+        ControlLoopMetrics.manager.removeTransaction(notification.getRequestID());
+
+        ControlLoopMetrics.manager.transactionEvent(testController, notification);
+        assertNotNull(ControlLoopMetrics.manager.getTransaction(notification.getRequestID()));
+        ControlLoopMetrics.manager.removeTransaction(notification.getRequestID());
+        assertNull(ControlLoopMetrics.manager.getTransaction(notification.getRequestID()));
+    }
+
+    @Test
+    public void eviction() {
+        ControlLoopMetricsFeature feature = new ControlLoopMetricsFeature();
+        for (int i=0; i < ControlLoopMetrics.manager.getCacheSize(); i++) {
+            VirtualControlLoopNotification notification = generateNotification();
+            feature.beforeDeliver(testController, CommInfrastructure.DMAAP, "POLICY-CL-MGT", notification);
+            assertNotNull(ControlLoopMetrics.manager.getTransaction(notification.getRequestID()));
+        }
+
+        assertTrue(ControlLoopMetrics.manager.getCacheOccupancy() == ControlLoopMetrics.manager.getCacheOccupancy());
+
+        VirtualControlLoopNotification overflowNotification = generateNotification();
+        feature.beforeDeliver(testController, CommInfrastructure.DMAAP, "POLICY-CL-MGT", overflowNotification);
+        assertTrue(ControlLoopMetrics.manager.getCacheOccupancy() == ControlLoopMetrics.manager.getCacheOccupancy());
+        assertNotNull(ControlLoopMetrics.manager.getTransaction(overflowNotification.getRequestID()));
+        assertTrue(ControlLoopMetrics.manager.getTransactionIds().size() == ControlLoopMetrics.manager.getCacheSize());
+        assertTrue(ControlLoopMetrics.manager.getCacheOccupancy() == ControlLoopMetrics.manager.getCacheSize());
+        assertFalse(ControlLoopMetrics.manager.getTransactionIds().isEmpty());
+        assertFalse(ControlLoopMetrics.manager.getTransactions().isEmpty());
+
+        /* let the entries expire */
+        try {
+            Thread.sleep((ControlLoopMetrics.manager.getTransactionTimeout()+5)*1000L);
+        } catch (InterruptedException e) {
+            /* nothing to do */
+        }
+
+        ControlLoopMetrics.manager.refresh();
+        assertTrue(ControlLoopMetrics.manager.getTransactionIds().size() == ControlLoopMetrics.manager.getCacheOccupancy());
+        assertFalse(ControlLoopMetrics.manager.getCacheOccupancy() == ControlLoopMetrics.manager.getCacheSize());
+        assertTrue(ControlLoopMetrics.manager.getTransactionIds().isEmpty());
+        assertTrue(ControlLoopMetrics.manager.getTransactions().isEmpty());
+
+        this.cacheDefaults();
+    }
+
+    private VirtualControlLoopNotification generateNotification() {
+        VirtualControlLoopNotification notification = new VirtualControlLoopNotification();
+        UUID requestId = UUID.randomUUID();
+        notification.setRequestID(requestId);
+        notification.setNotification(ControlLoopNotificationType.ACTIVE);
+        return notification;
+    }
+
+    @Test
+    public void getSequenceNumber() {
+        ControlLoopMetricsFeature feature = new ControlLoopMetricsFeature();
+        assertTrue(feature.getSequenceNumber() == ControlLoopMetricsFeature.FEATURE_SEQUENCE_PRIORITY);
+    }
+}
\ No newline at end of file
diff --git a/controlloop/common/feature-controlloop-trans/src/test/resources/feature-controlloop-trans.properties b/controlloop/common/feature-controlloop-trans/src/test/resources/feature-controlloop-trans.properties
new file mode 100644 (file)
index 0000000..80bdc2a
--- /dev/null
@@ -0,0 +1,2 @@
+controlloop.cache.transactions.size=3
+controllop.cache.transactions.timeout.seconds=10
\ No newline at end of file
diff --git a/controlloop/common/feature-controlloop-trans/src/test/resources/metrics-controller.properties b/controlloop/common/feature-controlloop-trans/src/test/resources/metrics-controller.properties
new file mode 100644 (file)
index 0000000..9612315
--- /dev/null
@@ -0,0 +1 @@
+controller.name=metrics
index 582183d..4c322e2 100644 (file)
@@ -40,6 +40,7 @@
     <module>policy-yaml</module>
     <module>simulators</module>
     <module>feature-controlloop-utils</module>
+    <module>feature-controlloop-trans</module>
     <module>msb</module>
   </modules>