All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.ddemin.envrouter.base.EnvsLocksController Maven / Gradle / Ivy

package com.github.ddemin.envrouter.base;

import static com.github.ddemin.envrouter.base.EnvironmentLock.LockStatus.FAILURE_NO_AVAILABLE;
import static com.github.ddemin.envrouter.base.EnvironmentLock.LockStatus.FAILURE_NO_ENTITY_FOR_AVAILABLE_ENVS;
import static com.github.ddemin.envrouter.base.EnvironmentLock.LockStatus.FAILURE_NO_TARGET_ENTITIES;
import static com.github.ddemin.envrouter.base.EnvironmentLock.LockStatus.FAILURE_TIMEOUT;
import static com.github.ddemin.envrouter.base.EnvironmentLock.LockStatus.FAILURE_UNDEFINED_ENV;
import static com.github.ddemin.envrouter.base.EnvironmentLock.LockStatus.SUCCESS_HARD_LOCKED;
import static com.github.ddemin.envrouter.base.EnvironmentLock.LockStatus.SUCCESS_LOCKED;
import static java.lang.String.format;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.core.Is.is;

import com.github.ddemin.envrouter.RouterConfig;
import com.github.ddemin.envrouter.RouterConfig.RouterConfigKeys;
import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import lombok.Getter;
import lombok.NonNull;
import lombok.Synchronized;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.awaitility.core.ConditionTimeoutException;

/**
 * Created by Dmitrii Demin on 18.09.2017.
 */
@Slf4j
@Getter
@UtilityClass
public class EnvsLocksController {

  private static Map testDatasLockMap;

  static {
    reinit();
  }

  /**
   * Creates controller that handle availability of environments for usage in demo threads. ENVS_DIRECTORY and
   * ENV_THREADS_MAX from RouterConfig will be used.
   */
  public static void reinit() {
    log.debug("Create lock controller...");
    testDatasLockMap = new TreeMap<>((o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName()));
    testDatasLockMap.putAll(
        Maps.asMap(
            EnvironmentsUtils.getAllEnvironments(),
            e -> RouterConfig.ENV_THREADS_MAX
        )
    );
    if (testDatasLockMap.isEmpty()) {
      throw new IllegalStateException(
          "No any environments were found and initiated, check property: "
              + RouterConfigKeys.ENVS_DIRECTORY_KEY
      );
    }
  }

  /**
   * Returns set of all environments provided to this controller.
   *
   * @return set of environments
   */
  public static Set getAll() {
    return testDatasLockMap.keySet();
  }

  /**
   * Get environment from map by name.
   *
   * @param envName environment name
   * @return environment object
   */
  public static Environment getByName(@NonNull String envName) {
    return getAll().stream()
        .filter(it -> it.getName().equalsIgnoreCase(envName))
        .findFirst()
        .orElse(null);
  }

  /**
   * Check availability of environment for usage in demo thread.
   *
   * @param env environment
   * @return possibility to use this environment in one more demo thread
   */
  @Synchronized
  public static boolean isAvailable(@NonNull Environment env) {
    return testDatasLockMap.get(env) > 0;
  }

  /**
   * Check availability of environment for usage in demo thread.
   *
   * @param envName name of environment
   * @return possibility to use this environment in one more demo thread
   */
  @Synchronized
  public static boolean isAvailable(@NonNull String envName) {
    Environment td = getByName(envName);
    return td != null && isAvailable(td);
  }

  /**
   * Returns set of all environments available for usage in demo threads.
   *
   * @return set of all environments that can be used in one more demo thread
   */
  @Synchronized
  public static Set getAllAvailable() {
    return testDatasLockMap.entrySet().stream()
        .map(Entry::getKey)
        .filter(EnvsLocksController::isAvailable)
        .collect(Collectors.toCollection(LinkedHashSet::new));
  }

  /**
   * Try to hard-lock environment. All slots will be used!
   *
   * @param env environment for locking
   * @return success of environment lock
   */
  @Synchronized
  public static boolean hardLock(@NonNull Environment env) {
    if (testDatasLockMap.get(env) < RouterConfig.ENV_THREADS_MAX) {
      return false;
    }
    log.debug("Hard-lock of environment: {}", env);
    testDatasLockMap.put(env, testDatasLockMap.get(env) * -1);
    return true;
  }

  /**
   * Try to lock environment.
   *
   * @param env environment for locking
   * @return success of environment lock
   */
  @Synchronized
  public static boolean lock(@NonNull Environment env) {
    if (!isAvailable(env)) {
      return false;
    }
    log.debug("Lock environment: {}", env);
    testDatasLockMap.put(env, testDatasLockMap.get(env) - 1);
    return true;
  }

  /**
   * Release environment.
   *
   * @param env environment for release
   */
  @Synchronized
  public static void release(@NonNull Environment env) {
    log.debug("Release environment: {}", env);
    int envFreeThreads = testDatasLockMap.get(env);
    if (Math.abs(envFreeThreads) < RouterConfig.ENV_THREADS_MAX) {
      if (envFreeThreads < 0) {
        log.debug("Release after hard-lock: {}", env);
        testDatasLockMap.put(env, envFreeThreads * -1);
      } else {
        testDatasLockMap.put(env, envFreeThreads + 1);
      }
    } else {
      testDatasLockMap.put(env, RouterConfig.ENV_THREADS_MAX);
    }
  }

  /**
   * Reset locking of environment.
   *
   * @param env environment for reset
   */
  @Synchronized
  public static void resetLock(@NonNull Environment env) {
    log.debug("Reset locking of environment: {}", env);
    testDatasLockMap.put(env, RouterConfig.ENV_THREADS_MAX);
  }

  /**
   * Release all environments.
   */
  @Synchronized
  public static void releaseAll() {
    testDatasLockMap.keySet().forEach(EnvsLocksController::release);
  }

  /**
   * Reset locking of all environments.
   */
  @Synchronized
  public static void resetLockingOfAll() {
    testDatasLockMap.keySet().forEach(EnvsLocksController::resetLock);
  }

  /**
   * Find untested entity and try to lock appropriate environment for it.
   *
   * @param envQueues queues with untested entities
   * @return environment-for-entity lock with some status
   */
  public static  EnvironmentLock findUntestedEntityAndLockEnv(
      @NonNull TestEntitiesQueues envQueues
  ) {
    EnvironmentLock rez;
    try {
      rez = await()
          .pollInSameThread()
          .timeout(RouterConfig.LOCK_TIMEOUT_MS, TimeUnit.MILLISECONDS)
          .pollInterval(500, TimeUnit.MILLISECONDS)
          .until(
              () -> findEntityAndLockEnv(envQueues),
              hasProperty(
                  "lockStatus",
                  not(
                      anyOf(
                          is(FAILURE_NO_AVAILABLE),
                          is(FAILURE_NO_ENTITY_FOR_AVAILABLE_ENVS)
                      )
                  )
              )
          );
    } catch (ConditionTimeoutException timeoutEx) {
      rez = new EnvironmentLock<>(FAILURE_TIMEOUT);
      List untestedEntities = envQueues.getQueuesMap().values().stream()
          .flatMap(Collection::stream)
          .collect(Collectors.toList());
      rez.setStatusMessage(
          format(
              "Tests routing timeout occurred (%d ms). Untested entities:%n%s",
              RouterConfig.LOCK_TIMEOUT_MS,
              Joiner.on(System.lineSeparator()).join(untestedEntities))
      );
    }
    return rez;
  }

  @Synchronized
  private static   EnvironmentLock findEntityAndLockEnv(
      @NonNull TestEntitiesQueues queues
  ) {
    List>> queuesForUndefEnvs = queues.getQueuesForUndefinedEnvs(getAll());
    Set availableEnvs = getAllAvailable();
    T entityForTest;

    if (queues.entitiesInAllQueues() <= 0) {
      log.error("No any entities in queues");
      return new EnvironmentLock<>(FAILURE_NO_TARGET_ENTITIES);
    } else if (queuesForUndefEnvs.size() > 0) {
      T entity = queuesForUndefEnvs.get(0).getValue().poll();
      log.warn("Entity for undefined env was found: {}", entity);
      return new EnvironmentLock<>(null, entity, FAILURE_UNDEFINED_ENV, "");
    } else if (availableEnvs.size() <= 0) {
      log.info("No any available environments");
      return new EnvironmentLock<>(FAILURE_NO_AVAILABLE);
    } else {
      log.trace("Search untested entities for available environments...");
      for (Environment env : availableEnvs) {
        entityForTest = queues.pollEntityFor(env.getName());
        if (entityForTest == null) {
          continue;
        } else if (entityForTest.isRequiresHardLock() && hardLock(env)) {
          log.info(
              "Untested entity was found: {} and environment was HARD-locked: {}",
              entityForTest,
              env
          );
          return new EnvironmentLock<>(env, entityForTest, SUCCESS_HARD_LOCKED, "");
        } else if (!entityForTest.isRequiresHardLock() && lock(env)) {
          log.info(
              "Untested entity was found: {} and environment was locked: {}",
              entityForTest,
              env
          );
          return new EnvironmentLock<>(env, entityForTest, SUCCESS_LOCKED, "");
        } else {
          queues.add(entityForTest);
        }
      }
    }

    return new EnvironmentLock<>(FAILURE_NO_ENTITY_FOR_AVAILABLE_ENVS);
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy