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

io.zahori.server.service.SelenoidService Maven / Gradle / Ivy

There is a newer version: 0.1.20
Show newest version
package io.zahori.server.service;

/*-
 * #%L
 * zahori-server
 * $Id:$
 * $HeadURL:$
 * %%
 * Copyright (C) 2021 PANEL SISTEMAS INFORMATICOS,S.L
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 * #L%
 */

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import io.zahori.server.model.CaseExecution;
import io.zahori.server.model.Execution;
import io.zahori.server.model.selenoid.SelenoidSession;
import io.zahori.server.model.selenoid.SelenoidStatus;
import io.zahori.server.repository.CaseExecutionsRepository;

/**
 * The type Selenoid service.
 */
@Service
public class SelenoidService {

    private static final Logger LOG = LoggerFactory.getLogger(SelenoidService.class);

    @Value("${ZAHORI_SELENOID_UI_INTERNAL_HOST}")
    private String selenoidUiHost;

    @Value("${ZAHORI_SELENOID_UI_INTERNAL_PORT}")
    private String selenoidUiPort;

    private CaseExecutionsRepository caseExecutionsRepository;

    /**
     * Instantiates a new Selenoid service.
     *
     * @param caseExecutionsRepository the case executions repository
     */
    @Autowired
    public SelenoidService(CaseExecutionsRepository caseExecutionsRepository) {
        this.caseExecutionsRepository = caseExecutionsRepository;
    }

    /**
     * Watch status.
     *
     * @param execution the execution
     */
    @Async
    public void watchStatus(Execution execution) {
        LOG.info("Watching selenoid sessions for execution {}", execution.getExecutionId());

        Map caseExecutions = getMap(execution);
        LOG.info("######## caseExecutions map: " + caseExecutions.toString());

        // Timeout cuando aún queda algún caso que no se ha llegado ha ejecutar
        int maxTimeoutRetries = 20;
        int timeoutRetry = 0;
        int waitSeconds = 1;
        Set casesExecuted = new HashSet<>();
        List caseExecutionsToUpdate;

        while (true) {
            if (casesExecuted.size() == execution.getCasesExecutions().size()) {
                LOG.info("All cases executed");
                return;
            }

            if (timeoutRetry > maxTimeoutRetries) {
                LOG.info("Timeout retries reached, finish watching selenoid");
                return;
            }

            SelenoidStatus status = getSelenoidStatus();

            if (status.getSessions().isEmpty() && casesExecuted.size() < execution.getCasesExecutions().size()) {
                LOG.info("No sessions and there are still cases pending to be executed");
                wait(waitSeconds);
                timeoutRetry++;
                continue;
            }

            caseExecutionsToUpdate = new ArrayList<>();
            for (SelenoidSession session : status.getSessions().values()) {
                Long caseExecutionIdInSession = Long.valueOf(session.getCaps().getName());
                LOG.info("######## caseExecutionIdInSession: " + caseExecutionIdInSession);

                // Validate that executionId in this session is from a case in this execution
                if (!caseExecutions.containsKey(caseExecutionIdInSession)) {
                    continue;
                }

                // If not running previously -> set selenoid sessionId and browser version
                if (!casesExecuted.contains(caseExecutionIdInSession)) {
                    LOG.info("CaseExecution '{}' is running", caseExecutionIdInSession);

                    CaseExecution caseExecution = caseExecutions.get(caseExecutionIdInSession);
                    caseExecution.setSelenoidId(session.getId());
                    caseExecution.setBrowserVersion(session.getCaps().getVersion());

                    caseExecutionsToUpdate.add(caseExecution);
                    casesExecuted.add(caseExecutionIdInSession);
                }
            }

            // Update caseExecutions that changed its state
            caseExecutionsRepository.saveAll(caseExecutionsToUpdate);
            caseExecutionsToUpdate.clear();

            if (casesExecuted.size() < execution.getCasesExecutions().size() || !casesExecuted.isEmpty()) {
                LOG.debug("There are still cases running or pending to be executed");
                wait(waitSeconds);
            }
        }
    }

    private SelenoidStatus getSelenoidStatus() {
        String selenoidUiUrl = "http://" + selenoidUiHost + ":" + selenoidUiPort + "/status";
        LOG.info("get selenoid status: {}", selenoidUiUrl);
        HttpHeaders headers = getHeaders();

        HttpEntity> requestEntity = new HttpEntity<>(null, headers);

        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity response = restTemplate.exchange(selenoidUiUrl, HttpMethod.GET, requestEntity, SelenoidStatus.class);

        printStatus(response.getStatusCodeValue());

        SelenoidStatus status = response.getBody();

        LOG.info(status.toString());
        return status;
    }

    private HttpHeaders getHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.set(HttpHeaders.ACCEPT, "*/*");
        return headers;
    }

    private void printStatus(int statusCode) {
        LOG.info("--> status: {}", statusCode);
    }

    private Map getMap(Execution execution) {
        Map map = new HashMap<>();
        if (execution != null && execution.getCasesExecutions() != null) {
            for (CaseExecution caseExecution : execution.getCasesExecutions()) {
                map.put(caseExecution.getCaseExecutionId(), caseExecution);
            }
        }
        return map;
    }

    private void wait(int seconds) {
        LOG.info("Waiting {} seconds", seconds);
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
            LOG.error("Error executing sleep for {} seconds", seconds);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy