content: // Licensed to the Apache Software Foundation (ASF) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The ASF licenses this file
  // to you under the Apache License, Version 2.0 (the
  // "License"); you may not use this file except in compliance
  // with the License.  You may obtain a copy of the License at
  //
  //   http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing,
  // software distributed under the License is distributed on an
  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, either express or implied.  See the License for the
  // specific language governing permissions and limitations
  // under the License.
  package com.cloud.hypervisor.vmware.util;
  
  import org.apache.commons.lang3.StringUtils;
  import org.apache.cloudstack.managed.context.ManagedContextTimerTask;
  import org.apache.logging.log4j.Logger;
  import org.apache.logging.log4j.LogManager;
  import org.joda.time.Duration;
  
  import java.util.ArrayList;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Queue;
  import java.util.Timer;
  import java.util.TimerTask;
  import java.util.concurrent.ConcurrentHashMap;
  import java.util.concurrent.ConcurrentLinkedQueue;
  import java.util.concurrent.ConcurrentMap;
  
  public class VmwareContextPool {
      protected Logger logger = LogManager.getLogger(getClass());
  
      private static final Duration DEFAULT_CHECK_INTERVAL = Duration.millis(10000L);
      private static final int DEFAULT_IDLE_QUEUE_LENGTH = 128;
  
      private final ConcurrentMap<String, Queue<VmwareContext>> _pool;
      private int _maxIdleQueueLength = DEFAULT_IDLE_QUEUE_LENGTH;
      private Duration _idleCheckInterval = DEFAULT_CHECK_INTERVAL;
  
      private Timer _timer = new Timer();
  
      public VmwareContextPool() {
          this(DEFAULT_IDLE_QUEUE_LENGTH, DEFAULT_CHECK_INTERVAL);
      }
  
      public VmwareContextPool(int maxIdleQueueLength, Duration idleCheckInterval) {
          _pool = new ConcurrentHashMap<String, Queue<VmwareContext>>();
  
          _maxIdleQueueLength = maxIdleQueueLength;
          _idleCheckInterval = idleCheckInterval;
  
          _timer.scheduleAtFixedRate(getTimerTask(), _idleCheckInterval.getMillis(), _idleCheckInterval.getMillis());
      }
  
      public VmwareContext getContext(final String vCenterAddress, final String vCenterUserName) {
          final String poolKey = composePoolKey(vCenterAddress, vCenterUserName).intern();
          if (StringUtils.isEmpty(poolKey)) {
              return null;
          }
          synchronized (poolKey) {
              final Queue<VmwareContext> ctxList = _pool.get(poolKey);
              if (ctxList != null && !ctxList.isEmpty()) {
                  final VmwareContext context = ctxList.remove();
                  if (context != null) {
                      context.setPoolInfo(this, poolKey);
                  }
                  if (logger.isTraceEnabled()) {
                      logger.trace("Return a VmwareContext from the idle pool: " + poolKey + ". current pool size: " + ctxList.size() + ", outstanding count: " +
                              VmwareContext.getOutstandingContextCount());
                  }
                  return context;
              }
              return null;
          }
      }
  
      public void registerContext(final VmwareContext context) {
          assert (context.getPool() == this);
          assert (context.getPoolKey() != null);
  
          final String poolKey = context.getPoolKey().intern();
          synchronized (poolKey) {
              Queue<VmwareContext> ctxQueue = _pool.get(poolKey);
  
              if (ctxQueue == null) {
                  ctxQueue = new ConcurrentLinkedQueue<>();
                  _pool.put(poolKey, ctxQueue);
              }
  
              if (ctxQueue.size() >= _maxIdleQueueLength) {
                  final VmwareContext oldestContext = ctxQueue.remove();
                  if (oldestContext != null) {
                      try {
                          oldestContext.close();
                      } catch (Throwable t) {
                          logger.error("Unexpected exception caught while trying to purge oldest VmwareContext", t);
                      }
                  }
              }
              context.clearStockObjects();
              ctxQueue.add(context);
  
              if (logger.isTraceEnabled()) {
                  logger.trace("Recycle VmwareContext into idle pool: " + context.getPoolKey() + ", current idle pool size: " + ctxQueue.size() + ", outstanding count: "
                          + VmwareContext.getOutstandingContextCount());
              }
          }
      }
  
      public void unregisterContext(final VmwareContext context) {
          assert (context != null);
          final String poolKey = context.getPoolKey().intern();
          final Queue<VmwareContext> ctxList = _pool.get(poolKey);
          synchronized (poolKey) {
              if (StringUtils.isNotEmpty(poolKey) && ctxList != null && ctxList.contains(context)) {
                  ctxList.remove(context);
              }
          }
      }
  
      private TimerTask getTimerTask() {
          return new ManagedContextTimerTask() {
              @Override
              protected void runInContext() {
                  try {
                      doKeepAlive();
                  } catch (Throwable e) {
                      logger.error("Unexpected exception", e);
                  }
              }
          };
      }
  
      private void doKeepAlive() {
          final List<VmwareContext> closableCtxList = new ArrayList<>();
          for (final Queue<VmwareContext> ctxQueue : _pool.values()) {
              for (Iterator<VmwareContext> iterator = ctxQueue.iterator(); iterator.hasNext();) {
                  final VmwareContext context = iterator.next();
                  if (context == null) {
                      iterator.remove();
                      continue;
                  }
                  try {
                      context.idleCheck();
                  } catch (Throwable e) {
                      logger.warn("Exception caught during VmwareContext idle check, close and discard the context", e);
                      closableCtxList.add(context);
                      iterator.remove();
                  }
              }
          }
          for (final VmwareContext context : closableCtxList) {
              context.close();
          }
      }
  
      public static String composePoolKey(final String vCenterAddress, final String vCenterUserName) {
          assert (vCenterUserName != null);
          assert (vCenterAddress != null);
          return vCenterUserName + "@" + vCenterAddress;
      }
  }
  