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

com.northernwall.hadrian.schedule.ScheduleRunner Maven / Gradle / Ivy

/*
 * Copyright 2016 Richard Thurston.
 *
 * 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 com.northernwall.hadrian.schedule;

import com.cronutils.model.Cron;
import com.cronutils.model.CronType;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.model.time.ExecutionTime;
import com.cronutils.parser.CronParser;
import com.northernwall.hadrian.db.DataAccess;
import com.northernwall.hadrian.domain.Module;
import com.northernwall.hadrian.domain.ModuleType;
import com.northernwall.hadrian.domain.Service;
import com.northernwall.hadrian.handlers.utility.HealthWriter;
import com.northernwall.hadrian.messaging.MessagingCoodinator;
import com.northernwall.hadrian.workItem.helper.SmokeTestHelper;
import java.io.IOException;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import org.dshops.metrics.MetricRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author Richard
 */
public class ScheduleRunner implements Runnable {
    private final static Logger LOGGER = LoggerFactory.getLogger(ScheduleRunner.class);
    private final static CronParser CRON_PARSER = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.UNIX));
    private final static String METRICS_CRON = "15 4 * * *";
    
    public static Cron parseCron(String cronExpression) {
        return CRON_PARSER.parse(cronExpression);
    }

    private final int group;
    private final DataAccess dataAccess;
    private final MetricRegistry metricRegistry;
    private final Leader leader;
    private final SmokeTestHelper smokeTestHelper;
    private final MessagingCoodinator messagingCoodinator;
    private final ScheduledExecutorService scheduledExecutorService;
    private ZonedDateTime prevChecked;

    public ScheduleRunner(int group, DataAccess dataAccess, MetricRegistry metricRegistry, Leader leader, SmokeTestHelper smokeTestHelper, MessagingCoodinator messagingCoodinator, ScheduledExecutorService scheduledExecutorService) {
        this.group = group;
        this.dataAccess = dataAccess;
        this.metricRegistry = metricRegistry;
        this.leader = leader;
        this.smokeTestHelper = smokeTestHelper;
        this.messagingCoodinator = messagingCoodinator;
        this.scheduledExecutorService = scheduledExecutorService;
        this.prevChecked = ZonedDateTime.now();
    }

    @Override
    public void run() {
        ZonedDateTime now = ZonedDateTime.now();
        
        boolean doMetrics = checkCron(METRICS_CRON, now);
        
        try {
            if (leader.isLeader(group)) {
                processGroup(now, doMetrics);
            }
        } catch (Exception e) {
            LOGGER.error("Exception during running group {}, {}", group, e.getMessage());
        } finally {
            prevChecked = now;
        }
    }

    private void processGroup(ZonedDateTime now, boolean doMetrics) {
        int serviceCount = 0;
        int smokeTestCount = 0;
        List services = dataAccess.getActiveServices();
        for (Service service : services) {
            int serviceGroup = Math.abs(service.getServiceId().hashCode() % Scheduler.GROUP_COUNT);
            if (serviceGroup == group) {
                serviceCount++;
                List modules = dataAccess.getModules(service.getServiceId());
                if (modules != null && !modules.isEmpty()) {
                    LOGGER.info("Processing {} with {} modules", service.getServiceName(), modules.size());
                    for (Module module : modules) {
                        LOGGER.info("Processing {} {} with type {}", module.getModuleName(), service.getServiceName(), module.getModuleType());
                        if (module.getModuleType() == ModuleType.Deployable) {
                            String smokeTestCron = module.getSmokeTestCron();
                            String smokeTestUrl = module.getSmokeTestUrl();
                            LOGGER.info("Processing {} {} with '{}' {}", module.getModuleName(), service.getServiceName(), smokeTestCron, smokeTestUrl);
                            if (smokeTestUrl != null
                                    && !smokeTestUrl.isEmpty()
                                    && smokeTestCron != null
                                    && !smokeTestCron.isEmpty()
                                    && checkCron(smokeTestCron, now)) {
                                smokeTestCount++;
                                scheduledExecutorService.submit(new SmokeTestRunner(service, module, group, dataAccess, smokeTestHelper, messagingCoodinator));
                            }
                        }
                    }
                } else {
                    LOGGER.info("Processing {} with no modules", service.getServiceName());
                }
                if (doMetrics) {
                    scheduledExecutorService.submit(new MetricsRunner(service, group, dataAccess, metricRegistry));
                }
            }
        }
        LOGGER.info("Run schedule for group {}, service count {}, smoke test count {}", group, serviceCount, smokeTestCount);
    }

    private boolean checkCron(String cronExpression, ZonedDateTime now) {
        try {
            if (cronExpression == null || cronExpression.isEmpty()) {
                return false;
            }
            Cron cron = parseCron(cronExpression);
            ExecutionTime executionTime = ExecutionTime.forCron(cron);
            ZonedDateTime last = executionTime.lastExecution(now);
            boolean b1 = last.isAfter(prevChecked);
            boolean b2 = last.isBefore(now);
            LOGGER.info("cron={} now={} last={} prevCheck={} b1={} b2={}", cronExpression, now.toString(), last.toString(), prevChecked.toString(), b1, b2);
            return (b1 && b2);
            //return (last.isAfter(lastChecked) && last.isBefore(now));
        } catch (Exception e) {
            LOGGER.error("Check cron '{}' failed, {}", cronExpression, e.getMessage());
            return false;
        }
    }
    
    public void getHealth(HealthWriter writer) throws IOException {
        if (leader.isLeader(group)) {
            writer.addStringLine("Schedule Runner " + group, "Leader");
        } else {
            writer.addStringLine("Schedule Runner " + group, "Not leader");
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy