2  * ============LICENSE_START=======================================================
 
   4  * ================================================================================
 
   5  * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
 
   6  * ================================================================================
 
   7  * Licensed under the Apache License, Version 2.0 (the "License");
 
   8  * you may not use this file except in compliance with the License.
 
   9  * You may obtain a copy of the License at
 
  11  *      http://www.apache.org/licenses/LICENSE-2.0
 
  13  * Unless required by applicable law or agreed to in writing, software
 
  14  * distributed under the License is distributed on an "AS IS" BASIS,
 
  15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
  16  * See the License for the specific language governing permissions and
 
  17  * limitations under the License.
 
  18  * ============LICENSE_END=========================================================
 
  21 package org.onap.policy.drools.apps.controlloop.feature.trans;
 
  23 import com.google.common.cache.CacheBuilder;
 
  24 import com.google.common.cache.CacheLoader;
 
  25 import com.google.common.cache.LoadingCache;
 
  26 import com.google.common.cache.RemovalListener;
 
  27 import com.google.common.cache.RemovalNotification;
 
  29 import java.time.Instant;
 
  30 import java.time.ZonedDateTime;
 
  31 import java.util.ArrayList;
 
  32 import java.util.List;
 
  33 import java.util.Properties;
 
  34 import java.util.UUID;
 
  35 import java.util.concurrent.TimeUnit;
 
  37 import org.onap.policy.controlloop.ControlLoopOperation;
 
  38 import org.onap.policy.controlloop.VirtualControlLoopNotification;
 
  39 import org.onap.policy.drools.persistence.SystemPersistence;
 
  40 import org.onap.policy.drools.system.PolicyController;
 
  41 import org.onap.policy.drools.utils.logging.MDCTransaction;
 
  42 import org.slf4j.Logger;
 
  43 import org.slf4j.LoggerFactory;
 
  46  * Control Loop Metrics Tracker.
 
  48 public interface ControlLoopMetrics {
 
  51      * Singleton manager object.
 
  53     ControlLoopMetrics manager = new CacheBasedControlLoopMetricsManager();
 
  56      * Gets all transaction identifiers being monitored.
 
  58      * @return transaction id list
 
  60     List<UUID> getTransactionIds();
 
  63      * Gets all detailed transactions.
 
  65      * @return list of transactions
 
  67     List<VirtualControlLoopNotification> getTransactions();
 
  70      * Track controller's notification events.
 
  72      * @param controller policy controller sending out notification
 
  73      * @param notification notification
 
  75     void transactionEvent(PolicyController controller, VirtualControlLoopNotification notification);
 
  78      * Gets an in-progress transaction.
 
  80      * @param requestId request ID
 
  81      * @return in progress notification
 
  83     VirtualControlLoopNotification getTransaction(UUID requestId);
 
  86      * Removes an in-progress transaction.
 
  88      * @param requestId request ID
 
  90     void removeTransaction(UUID requestId);
 
 104     long getCacheOccupancy();
 
 109      * @param cacheSize cache size
 
 111     void setMaxCacheSize(long cacheSize);
 
 114      * Cached transaction expiration timeout in seconds.
 
 116      * @return transaction timeout in seconds
 
 118     long getTransactionTimeout();
 
 121      * Sets transaction timeout in seconds.
 
 123      * @param transactionTimeout transaction timeout in seconds
 
 125     void setTransactionTimeout(long transactionTimeout);
 
 130      * @param cacheSize new cache size
 
 131      * @param transactionTimeout new transaction timeout in seconds
 
 133     void resetCache(long cacheSize, long transactionTimeout);
 
 136      * Refresh underlying transaction management.
 
 143  * Control Loop Metrics Tracker Implementation.
 
 145 class CacheBasedControlLoopMetricsManager implements ControlLoopMetrics {
 
 147     private static final Logger logger = LoggerFactory.getLogger(CacheBasedControlLoopMetricsManager.class);
 
 149     private LoadingCache<UUID, VirtualControlLoopNotification> cache;
 
 150     private long cacheSize = ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_DEFAULT;
 
 152     private long transactionTimeout = ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_DEFAULT;
 
 154     public CacheBasedControlLoopMetricsManager() {
 
 156         Properties properties =
 
 157                 SystemPersistence.manager.getProperties(ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME);
 
 163                     Long.parseLong(properties.getProperty(ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_PROPERTY,
 
 164                             "" + ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_DEFAULT));
 
 165         } catch (Exception e) {
 
 166             logger.warn("{}:{} property cannot be accessed", ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME,
 
 167                     ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_PROPERTY, e);
 
 170         /* transaction timeout */
 
 173             this.transactionTimeout = Long
 
 174                     .parseLong(properties.getProperty(ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_PROPERTY,
 
 175                             "" + ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_DEFAULT));
 
 176         } catch (Exception e) {
 
 177             logger.warn("{}:{} property cannot be accessed", ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME,
 
 178                     ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_PROPERTY, e);
 
 181         resetCache(this.cacheSize, this.transactionTimeout);
 
 185     public void resetCache(long cacheSize, long transactionTimeout) {
 
 186         this.cacheSize = cacheSize;
 
 187         this.transactionTimeout = transactionTimeout;
 
 189         CacheLoader<UUID, VirtualControlLoopNotification> loader =
 
 190                 new CacheLoader<UUID, VirtualControlLoopNotification>() {
 
 193             public VirtualControlLoopNotification load(UUID key) throws Exception {
 
 198         RemovalListener<UUID, VirtualControlLoopNotification> listener =
 
 199                 new RemovalListener<UUID, VirtualControlLoopNotification>() {
 
 201             public void onRemoval(RemovalNotification<UUID, VirtualControlLoopNotification> notification) {
 
 202                 if (notification.wasEvicted()) {
 
 203                     evicted(notification.getValue());
 
 205                     logger.info("REMOVAL: {} because of {}", notification.getValue().getRequestId(),
 
 206                                     notification.getCause().name());
 
 211         synchronized (this) {
 
 212             if (this.cache != null) {
 
 213                 this.cache.cleanUp();
 
 214                 this.cache.invalidateAll();
 
 217             this.cache = CacheBuilder.newBuilder().maximumSize(this.cacheSize)
 
 218                     .expireAfterWrite(transactionTimeout, TimeUnit.SECONDS).removalListener(listener).build(loader);
 
 223     public void refresh() {
 
 224         this.cache.cleanUp();
 
 228     public List<UUID> getTransactionIds() {
 
 229         return new ArrayList<>(this.cache.asMap().keySet());
 
 233     public List<VirtualControlLoopNotification> getTransactions() {
 
 234         return new ArrayList<>(this.cache.asMap().values());
 
 238     public void transactionEvent(PolicyController controller, VirtualControlLoopNotification notification) {
 
 239         if (notification == null || notification.getRequestId() == null || notification.getNotification() == null) {
 
 240             logger.warn("Invalid notification: {}", notification);
 
 244         if (notification.getNotificationTime() == null) {
 
 245             notification.setNotificationTime(ZonedDateTime.now());
 
 248         notification.setFrom(notification.getFrom() + ":" + controller.getName());
 
 250         switch (notification.getNotification()) {
 
 255                 endTransaction(notification);
 
 259             case OPERATION_SUCCESS:
 
 260             case OPERATION_FAILURE:
 
 261                 /* any other value is an in progress transaction */
 
 262                 inProgressTransaction(notification);
 
 266                 logger.warn("unexpected notification type {} in notification {}",
 
 267                         notification.getNotification().toString(), notification);
 
 273     public VirtualControlLoopNotification getTransaction(UUID requestId) {
 
 274         return cache.getIfPresent(requestId);
 
 278     public void removeTransaction(UUID requestId) {
 
 279         cache.invalidate(requestId);
 
 283      * Tracks an in progress control loop transaction.
 
 285      * @param notification control loop notification
 
 287     protected void inProgressTransaction(VirtualControlLoopNotification notification) {
 
 288         if (cache.getIfPresent(notification.getRequestId()) == null) {
 
 289             cache.put(notification.getRequestId(), notification);
 
 292         this.metric(notification);
 
 296      * End of a control loop transaction.
 
 298      * @param notification control loop notification
 
 300     protected void endTransaction(VirtualControlLoopNotification notification) {
 
 301         ZonedDateTime startTime;
 
 302         VirtualControlLoopNotification startNotification = cache.getIfPresent(notification.getRequestId());
 
 303         if (startNotification != null) {
 
 304             startTime = startNotification.getNotificationTime();
 
 306             startTime = notification.getNotificationTime();
 
 309         this.transaction(notification, startTime);
 
 310         if (startNotification != null) {
 
 311             cache.invalidate(startNotification);
 
 315     protected void evicted(VirtualControlLoopNotification notification) {
 
 317                 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
 
 318                 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget())
 
 319                 .setStartTime(notification.getNotificationTime().toInstant()).setEndTime(Instant.now())
 
 320                 .setResponseDescription("EVICTED").setStatusCode(false).metric().resetTransaction();
 
 324     public long getCacheSize() {
 
 325         return this.cacheSize;
 
 329     public void setMaxCacheSize(long cacheSize) {
 
 330         this.cacheSize = cacheSize;
 
 334     public long getTransactionTimeout() {
 
 335         return this.transactionTimeout;
 
 339     public void setTransactionTimeout(long transactionTimeout) {
 
 340         this.transactionTimeout = transactionTimeout;
 
 344     public long getCacheOccupancy() {
 
 345         return this.cache.size();
 
 348     protected void metric(VirtualControlLoopNotification notification) {
 
 349         MDCTransaction trans = MDCTransaction
 
 350                 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
 
 351                 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget());
 
 353         List<ControlLoopOperation> operations = notification.getHistory();
 
 354         switch (notification.getNotification()) {
 
 356                 trans.setStatusCode(true);
 
 357                 trans.metric().resetTransaction();
 
 360                 trans.setStatusCode(true);
 
 361                 if (!operations.isEmpty()) {
 
 362                     ControlLoopOperation operation = operations.get(operations.size() - 1);
 
 363                     trans.setTargetEntity(operation.getTarget());
 
 364                     trans.setTargetServiceName(operation.getActor());
 
 366                 trans.metric().resetTransaction();
 
 368             case OPERATION_SUCCESS:
 
 369                 trans.setStatusCode(true);
 
 370                 operation(trans, operations);
 
 371                 trans.transaction().resetTransaction();
 
 373             case OPERATION_FAILURE:
 
 374                 trans.setStatusCode(false);
 
 375                 operation(trans, operations);
 
 376                 trans.transaction().resetTransaction();
 
 380                 logger.warn("unexpected notification type {} in notification {}",
 
 381                         notification.getNotification().toString(), notification);
 
 386     protected void operation(MDCTransaction trans, List<ControlLoopOperation> operations) {
 
 387         if (!operations.isEmpty()) {
 
 388             ControlLoopOperation operation = operations.get(operations.size() - 1);
 
 390             if (operation.getTarget() != null) {
 
 391                 trans.setTargetEntity(operation.getTarget());
 
 394             if (operation.getActor() != null) {
 
 395                 trans.setTargetServiceName(operation.getActor());
 
 398             if (operation.getMessage() != null) {
 
 399                 trans.setResponseDescription(operation.getMessage());
 
 402             trans.setInvocationId(operation.getSubRequestId());
 
 404             if (operation.getOutcome() != null) {
 
 405                 trans.setResponseCode(operation.getOutcome());
 
 408             if (operation.getStart() != null) {
 
 409                 trans.setStartTime(operation.getStart());
 
 412             if (operation.getEnd() != null) {
 
 413                 trans.setEndTime(operation.getEnd());
 
 418     protected void transaction(VirtualControlLoopNotification notification, ZonedDateTime startTime) {
 
 419         MDCTransaction trans = MDCTransaction
 
 420                 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
 
 421                 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget())
 
 422                 .setStartTime(startTime.toInstant()).setEndTime(notification.getNotificationTime().toInstant())
 
 423                 .setResponseDescription(notification.getMessage());
 
 425         switch (notification.getNotification()) {
 
 427                 trans.setStatusCode(true);
 
 430                 trans.setStatusCode(true);
 
 433                 trans.setStatusCode(false);
 
 436                 trans.setStatusCode(false);
 
 440                 logger.warn("unexpected notification type {} in notification {}",
 
 441                         notification.getNotification(), notification);
 
 445         trans.transaction().resetTransaction();
 
 449     public String toString() {
 
 450         final StringBuilder sb = new StringBuilder();
 
 451         sb.append("CacheBasedControlLoopMetricsManager{");
 
 452         sb.append("cacheSize=").append(cacheSize);
 
 453         sb.append(", transactionTimeout=").append(transactionTimeout);
 
 455         return sb.toString();