code: /*
   * Copyright © 2014-2017 EntIT Software LLC, a Micro Focus company (L.P.)
   *
   * 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.
   */
  
  package io.cloudslang.engine.node.services;
  
  import com.google.common.collect.ArrayListMultimap;
  import com.google.common.collect.Multimap;
  import io.cloudslang.engine.node.entities.QueueDetails;
  import io.cloudslang.engine.node.entities.WorkerKeepAliveInfo;
  import io.cloudslang.engine.node.entities.WorkerNode;
  import io.cloudslang.engine.node.repositories.WorkerNodeRepository;
  import io.cloudslang.engine.versioning.services.VersionService;
  import io.cloudslang.score.api.nodes.WorkerStatus;
  import org.apache.commons.lang.StringUtils;
  import org.apache.logging.log4j.LogManager;
  import org.apache.logging.log4j.Logger;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.transaction.PlatformTransactionManager;
  import org.springframework.transaction.annotation.Propagation;
  import org.springframework.transaction.annotation.Transactional;
  import org.springframework.transaction.support.TransactionTemplate;
  
  import javax.annotation.PostConstruct;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Date;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import static com.google.common.collect.Maps.newHashMapWithExpectedSize;
  
  
  public class WorkerNodeServiceImpl implements WorkerNodeService {
  
      private static final Logger logger = LogManager.getLogger(WorkerNodeServiceImpl.class);
  
      private static final long MAX_VERSION_GAP_ALLOWED = Long.getLong("max.allowed.version.gap.worker.recovery", 2);
      private static boolean disableMonitoring = Boolean.getBoolean("global.worker.monitoring.disable");
      private static final String MSG_RECOVERY_VERSION_NAME = "MSG_RECOVERY_VERSION";
  
      @Autowired
      private WorkerNodeRepository workerNodeRepository;
  
      @Autowired
      private WorkerLockService workerLockService;
  
      @Autowired
      private VersionService versionService;
  
      @Autowired(required = false)
      private List<LoginListener> loginListeners;
  
      @Autowired
      private QueueConfigurationDataService queueConfigurationDataService;
  
      @Autowired
      private PlatformTransactionManager transactionManager;
  
      @PostConstruct
      void setWorkerMonitoring(){
          TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
  
          if(disableMonitoring) {
              logger.info("Monitoring is disabled,setting busyness status as not available for all workers");
              transactionTemplate.executeWithoutResult(transactionStatus -> {
                  readAllWorkersUuids().stream().forEach(uuid -> updateWorkerBusynessValue(uuid,"NA") );
              });
          }
      }
  //readAllWorkersUuids().stream().forEach(uuid -> updateWorkerBusynessValue(uuid,"NA") );
  
      @Override
      @Transactional
      public String keepAlive(String uuid) {
          WorkerNode worker = readByUUID(uuid);
          worker.setAckTime(new Date());
          String wrv = worker.getWorkerRecoveryVersion();
          long version = versionService.getCurrentVersion(MSG_RECOVERY_VERSION_NAME);
          worker.setAckVersion(version);
          if (!worker.getStatus().equals(WorkerStatus.IN_RECOVERY)) {
              worker.setStatus(WorkerStatus.RUNNING);
          }
          logger.debug("Got keepAlive for Worker with uuid=" + uuid + " and update its ackVersion to " + version);
          return wrv;
      }
  
      @Override
      @Transactional
      @Deprecated
      public WorkerKeepAliveInfo newKeepAlive(String uuid) {
          // Any worker using this method will be considered an older version from engine
  		return newKeepAlive(uuid, true);
      }
  
      @Override
      @Transactional
      public WorkerKeepAliveInfo newKeepAlive(String uuid, boolean versionMismatch) {
          WorkerNode worker = readByUUID(uuid);
          worker.setAckTime(new Date());
          long version = versionService.getCurrentVersion(MSG_RECOVERY_VERSION_NAME);
          worker.setAckVersion(version);
          if (!versionMismatch && !worker.getStatus().equals(WorkerStatus.IN_RECOVERY)) {
              worker.setStatus(WorkerStatus.RUNNING);
          }
          boolean active = worker.isActive();
          logger.debug(
                  "Got keepAlive for Worker with uuid=" + uuid + " and update its ackVersion to " + version + " isActive"
                          + active);
          QueueDetails queueDetails = queueConfigurationDataService.getQueueConfigurations();
  		return new WorkerKeepAliveInfo(worker.getWorkerRecoveryVersion(), active, queueDetails,disableMonitoring);
      }
  
      @Override
      @Transactional
      public void create(String uuid, String password, String hostName, String installDir) {
          WorkerNode worker = new WorkerNode();
          worker.setUuid(uuid);
          worker.setDescription(uuid);
          worker.setHostName(hostName);
          worker.setActive(false);
          worker.setInstallPath(installDir);
          worker.setStatus(WorkerStatus.FAILED);
          worker.setPassword(password);
          worker.setGroups(Arrays.asList(WorkerNode.DEFAULT_WORKER_GROUPS));
          worker.setWorkerBusynessValue("NA");
          workerNodeRepository.save(worker);
          workerLockService.create(uuid);
      }
  
      @Override
      @Transactional
      public void updateWorkerToDeleted(String uuid) {
          WorkerNode worker = readByUUID(uuid);
          if (worker != null) {
              worker.setActive(false);
              worker.setDeleted(true);
              worker.setStatus(WorkerStatus.IN_RECOVERY);
          }
      }
  
      @Override
      @Transactional
      public void updateWorkerToNotDeleted(String uuid) {
          WorkerNode worker = workerNodeRepository.findByUuidAndDeleted(uuid, true);
          if (worker != null) {
              worker.setActive(false);
              worker.setDeleted(false);
              worker.setStatus(WorkerStatus.IN_RECOVERY);
          }
      }
  
      @Override
      @Transactional
      public List<WorkerNode> readAllNotDeletedWorkers() {
          return workerNodeRepository.findByDeletedOrderByIdAsc(false);
      }
  
      @Override
      @Transactional
      public String up(String uuid, String version, String versionId, boolean versionMismatch) {
  
          if (loginListeners != null) {
              for (LoginListener listener : loginListeners) {
                  listener.preLogin(uuid);
              }
          }
          WorkerKeepAliveInfo workerKeepAliveInfo = newKeepAlive(uuid, versionMismatch);
          if (loginListeners != null) {
              for (LoginListener listener : loginListeners) {
                  listener.postLogin(uuid);
              }
          }
  
          updateVersion(uuid, version, versionId);
  
          return workerKeepAliveInfo.getWorkerRecoveryVersion();
      }
  
      @Override
      @Transactional
      @Deprecated
      public String up(String uuid, String version, String versionId) {
  
          if (loginListeners != null) {
              for (LoginListener listener : loginListeners) {
                  listener.preLogin(uuid);
              }
          }
          WorkerKeepAliveInfo workerKeepAliveInfo = newKeepAlive(uuid);
          if (loginListeners != null) {
              for (LoginListener listener : loginListeners) {
                  listener.postLogin(uuid);
              }
          }
  
          updateVersion(uuid, version, versionId);
  
          return workerKeepAliveInfo.getWorkerRecoveryVersion();
      }
  
      @Override
      @Transactional
      @Deprecated //is left here for backward compatibility
      public String up(String uuid) {
  
          if (loginListeners != null) {
              for (LoginListener listener : loginListeners) {
                  listener.preLogin(uuid);
              }
          }
          WorkerKeepAliveInfo workerKeepAliveInfo = newKeepAlive(uuid);
          if (loginListeners != null) {
              for (LoginListener listener : loginListeners) {
                  listener.postLogin(uuid);
              }
          }
          return workerKeepAliveInfo.getWorkerRecoveryVersion();
      }
  
      @Override
      @Transactional(readOnly = true)
      public WorkerNode readByUUID(String uuid) {
          WorkerNode worker = workerNodeRepository.findByUuidAndDeleted(uuid, false);
          if (worker == null) {
              throw new IllegalStateException("no worker was found by the specified UUID:" + uuid);
          }
          return worker;
      }
  
      @Override
      @Transactional(readOnly = true)
      public boolean isActive(String uuid) {
          WorkerNode worker = workerNodeRepository.findByUuidAndDeleted(uuid, false);
          if (worker == null) {
              throw new IllegalStateException("no worker was found by the specified UUID:" + uuid);
          }
          return worker.isActive();
      }
  
      @Override
      @Transactional(readOnly = true)
      public WorkerNode findByUuid(String uuid) {
          WorkerNode worker = workerNodeRepository.findByUuid(uuid);
          if (worker == null) {
              throw new IllegalStateException("no worker was found by the specified UUID:" + uuid);
          }
          return worker;
      }
  
      @Override
      @Transactional(readOnly = true)
      public List<WorkerNode> readAllWorkers() {
          return workerNodeRepository.findAll();
      }
  
      @Override
      @Transactional(readOnly = true)
      public Map<String, Set<String>> readWorkerGroupsMap() {
          List<WorkerNode> all = workerNodeRepository.findAll();
          Map<String, Set<String>> workerGroupsMap = newHashMapWithExpectedSize(all.size());
          for (WorkerNode workerNode : all) {
              workerGroupsMap.put(workerNode.getUuid(), new HashSet<>(workerNode.getGroups()));
          }
          return workerGroupsMap;
      }
  
      @Override
      @Transactional(readOnly = true)
      public List<String> readAllWorkersUuids() {
          List<WorkerNode> workers = workerNodeRepository.findAll();
          List<String> result = new ArrayList<>();
          for (WorkerNode w : workers) {
              result.add(w.getUuid());
          }
          return result;
      }
  
      @Override
      @Transactional
      public void updateVersion(String workerUuid, String version, String versionId) {
          WorkerNode worker = workerNodeRepository.findByUuid(workerUuid);
          if (worker == null) {
              throw new IllegalStateException("No worker was found by the specified UUID:" + workerUuid);
          }
          worker.setVersion(version);
          worker.setVersionId(versionId);
      }
  
      @Override
      @Transactional
      public void updateMigratedPassword(String workerUuid, String encodedPassword) {
          WorkerNode worker = workerNodeRepository.findByUuid(workerUuid);
          if (worker == null) {
              throw new IllegalStateException("No worker was found by the specified UUID:" + workerUuid);
          }
          if (StringUtils.isEmpty(encodedPassword)) {
              throw new IllegalStateException("Invalid encoded password provided for UUID:" + workerUuid);
          }
          if (!StringUtils.equals(worker.getMigratedPassword(), encodedPassword)) {
              worker.setMigratedPassword(encodedPassword);
          }
      }
  
      @Override
      @Transactional
      public void updateQueueSyncByUuid(String workerUuid, boolean isQueueSync) {
          WorkerNode worker = workerNodeRepository.findByUuid(workerUuid);
          if (worker == null) {
              throw new IllegalStateException("No worker was found by the specified UUID:" + workerUuid);
          }
          worker.setQueueSync(isQueueSync);
      }
  
      @Override
      @Transactional
      public void updateQueueSync(boolean isQueueSync) {
          List<WorkerNode> workers = workerNodeRepository.findAll();
          for (WorkerNode w : workers) {
              w.setQueueSync(isQueueSync);
          }
      }
  
      @Override
      @Transactional(readOnly = true)
      public List<String> readNonRespondingWorkers() {
          long systemVersion = versionService.getCurrentVersion(MSG_RECOVERY_VERSION_NAME);
          long minVersionAllowed = Math.max(systemVersion - MAX_VERSION_GAP_ALLOWED, 0);
          return workerNodeRepository.findNonRespondingWorkers(minVersionAllowed, WorkerStatus.RECOVERED);
      }
  
      @Override
      @Transactional(readOnly = true)
      public List<WorkerNode> readWorkersByActivation(boolean isActive) {
          return workerNodeRepository.findByActiveAndDeleted(isActive, false);
      }
  
      @Override
      @Transactional
      public void activate(String uuid) {
          WorkerNode worker = readByUUID(uuid);
          worker.setActive(true);
      }
  
      @Override
      @Transactional
      public void deactivate(String uuid) {
          WorkerNode worker = readByUUID(uuid);
          worker.setActive(false);
      }
  
      @Override
      @Transactional
      public void updateEnvironmentParams(String uuid, String os, String jvm, String dotNetVersion) {
          WorkerNode worker = readByUUID(uuid);
          worker.setOs(os);
          worker.setJvm(jvm);
          worker.setDotNetVersion(dotNetVersion);
      }
  
      @Override
      @Transactional
      public void updateStatus(String uuid, WorkerStatus status) {
          WorkerNode worker = workerNodeRepository.findByUuid(uuid);
          if (worker == null) {
              throw new IllegalStateException("no worker was found by the specified UUID:" + uuid);
          }
          worker.setStatus(status);
      }
  
      @Override
      @Transactional
      public void updateWorkerBusynessValue(String uuid, String workerBusynessValue) {
          WorkerNode worker = workerNodeRepository.findByUuid(uuid);
          if (worker == null) {
              throw new IllegalStateException("no worker was found by the specified UUID:" + uuid);
          }
          worker.setWorkerBusynessValue(workerBusynessValue);
      }
  
      @Override
      @Transactional(propagation = Propagation.REQUIRES_NEW)
      public void updateStatusInSeparateTransaction(String uuid, WorkerStatus status) {
          WorkerNode worker = workerNodeRepository.findByUuid(uuid);
          if (worker == null) {
              throw new IllegalStateException("no worker was found by the specified UUID:" + uuid);
          }
          worker.setStatus(status);
      }
  
      @Override
      @Transactional
      public void migratePassword(String uuid, String password) {
          WorkerNode workerNode = workerNodeRepository.findByUuid(uuid);
          if (workerNode == null) {
              throw new IllegalStateException("no worker was found by the specified UUID:" + uuid);
          }
          if (StringUtils.isNotEmpty(workerNode.getMigratedPassword())) {
              throw new IllegalStateException(
                      "the migration password has already been changed for the specified UUID:" + uuid);
          }
          workerNode.setMigratedPassword(password);
      }
  
      @Override
      @Transactional(readOnly = true)
      public List<String> readAllWorkerGroups() {
          return workerNodeRepository.findGroups();
      }
  
      @Override
      @Transactional(readOnly = true)
      public List<String> readWorkerGroups(String uuid) {
          WorkerNode node = readByUUID(uuid);
          ArrayList<String> res = new ArrayList<>();
          res.addAll(node.getGroups());
          return res;
      }
  
      @Override
      @Transactional
      public void updateWorkerGroups(String uuid, String... groupNames) {
          WorkerNode worker = readByUUID(uuid);
  
          Set<String> groupSet = groupNames != null ? new HashSet<>(Arrays.asList(groupNames)) : new HashSet<String>();
          List<String> groups = new ArrayList<>();
          groupSet.remove(null);
          groups.addAll(groupSet);
  
          worker.setGroups(groups);
      }
  
      @Override
      @Transactional(readOnly = true)
      public Multimap<String, String> readGroupWorkersMapActiveAndRunningAndVersion(String versionId) {
          Multimap<String, String> result = ArrayListMultimap.create();
          List<WorkerNode> workers;
          workers = workerNodeRepository
                  .findByActiveAndStatusAndDeletedAndVersionId(true, WorkerStatus.RUNNING, false, versionId);
          for (WorkerNode worker : workers) {
              for (String groupName : worker.getGroups()) {
                  result.put(groupName, worker.getUuid());
              }
          }
          return result;
      }
  
      @Override
      @Transactional
      public void addGroupToWorker(String workerUuid, String group) {
  
          if (group == null) {
              return;
          }
  
          WorkerNode worker = readByUUID(workerUuid);
  
          if (!worker.getGroups().contains(group)) {
              List<String> groups = new ArrayList<>(worker.getGroups());
              groups.add(group);
              worker.setGroups(groups);
          }
      }
  
      @Override
      @Transactional
      public void removeGroupFromWorker(String workerUuid, String group) {
          WorkerNode worker = readByUUID(workerUuid);
          List<String> groups = new ArrayList<>(worker.getGroups());
          groups.remove(group);
          if (groups.size() == 0) {
              throw new IllegalStateException("Can't leave worker without any group !");
          }
          worker.setGroups(groups);
      }
  
      @Override
      @Transactional(readOnly = true)
      public List<String> readWorkerGroups(List<String> groups) {
          return workerNodeRepository.findGroups(groups);
      }
  
      @Override
      @Transactional
      public void updateBulkNumber(String workerUuid, String bulkNumber) {
          WorkerNode worker = readByUUID(workerUuid);
          worker.setBulkNumber(bulkNumber);
      }
  
      @Override
      @Transactional
      public void updateWRV(String workerUuid, String wrv) {
          WorkerNode worker = workerNodeRepository.findByUuid(workerUuid);
          worker.setWorkerRecoveryVersion(wrv);
      }
  
  }
  