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

com.redhat.lightblue.migrator.Controller Maven / Gradle / Ivy

There is a newer version: 2.56.1
Show newest version
package com.redhat.lightblue.migrator;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.redhat.lightblue.client.LightblueClient;
import com.redhat.lightblue.client.LightblueException;
import com.redhat.lightblue.client.Projection;
import com.redhat.lightblue.client.Query;
import com.redhat.lightblue.client.request.data.DataFindRequest;

/**
 * This is the main thread. It runs until it is interrupted. Periodically it
 * reads migration configurations, and creates MigratorController and
 * ConsistencyChecker threads. All those threads are derived from
 * AbstractController, and have their own thread groups.
 */
public class Controller extends Thread {

    private static final Logger LOGGER = LoggerFactory.getLogger(Controller.class);

    private final MainConfiguration cfg;
    private final LightblueClient lightblueClient;
    private final Map migrationMap = new HashMap<>();
    private final ThreadMonitor threadMonitor;
    private boolean stopped=false;

    public static class MigrationProcess {
        public MigrationConfiguration cfg;
        public MigratorController mig;
        public AbstractController ccc;

        public MigrationProcess(MigrationConfiguration cfg,
                                MigratorController mig,
                                AbstractController ccc) {
            this.cfg = cfg;
            this.mig = mig;
            this.ccc = ccc;
        }
    }

    public Controller(MainConfiguration cfg) {
        this.cfg = cfg;
        lightblueClient = getLightblueClient();
        Long tt = cfg.getThreadTimeout();
        if (tt == null) {
            threadMonitor = new ThreadMonitor();
        } else {
            threadMonitor = new ThreadMonitor(tt);
        }
        threadMonitor.start();
    }

    public void setStopped() {
        this.stopped=true;
        interrupt();
    }

    public Map getMigrationProcesses() {
        return migrationMap;
    }

    public MainConfiguration getMainConfiguration() {
        return cfg;
    }

    public ThreadMonitor getThreadMonitor() {
        return threadMonitor;
    }

    /**
     * Read configurations from the database whose name matches this instance
     * name
     */
    public MigrationConfiguration[] getMigrationConfigurations()
            throws IOException, LightblueException {
        DataFindRequest findRequest = new DataFindRequest("migrationConfiguration", null);
        findRequest.where(Query.withValue("consistencyCheckerName", Query.eq, cfg.getName()));
        findRequest.select(Projection.includeFieldRecursively("*"));
        LOGGER.debug("Loading configuration:{}", findRequest.getBody());
        return lightblueClient.data(findRequest, MigrationConfiguration[].class);
    }

    /**
     * Read a configuration from the database whose name matches the the given
     * configuration name
     */
    public MigrationConfiguration getMigrationConfiguration(String configurationName)
            throws IOException, LightblueException {
        DataFindRequest findRequest = new DataFindRequest("migrationConfiguration", null);
        findRequest.where(Query.and(
                Query.withValue("configurationName", Query.eq, configurationName),
                Query.withValue("consistencyCheckerName", Query.eq, cfg.getName()))
        );
        findRequest.select(Projection.includeFieldRecursively("*"));
        LOGGER.debug("Loading configuration:{}", findRequest.getBody());
        return lightblueClient.data(findRequest, MigrationConfiguration.class);
    }

    /**
     * Load migration configuration based on its id
     */
    public MigrationConfiguration loadMigrationConfiguration(String migrationConfigurationId)
            throws IOException, LightblueException {
        DataFindRequest findRequest = new DataFindRequest("migrationConfiguration", null);
        findRequest.where(Query.withValue("_id", Query.eq, migrationConfigurationId));
        findRequest.select(Projection.includeFieldRecursively("*"));
        LOGGER.debug("Loading configuration");
        return lightblueClient.data(findRequest, MigrationConfiguration.class);
    }

    public LightblueClient getLightblueClient() {
        LOGGER.debug("Getting client, config={}", cfg.getClientConfig());
        return cfg.getLightblueClient();
    }

    private boolean shouldHaveConsistencyChecker(MigrationConfiguration cfg) {
        return cfg.getPeriod() != null && cfg.getPeriod().trim().length() > 0;
    }

    private AbstractController getConsistencyCheckerController(MigrationConfiguration cfg) {
        AbstractController ccc = null;
        try {
            if (shouldHaveConsistencyChecker(cfg)) {
                if (cfg.getConsistencyCheckerControllerClass() != null
                        && cfg.getConsistencyCheckerControllerClass().length() > 0) {
                    ccc = (AbstractController) Class.forName(cfg.getConsistencyCheckerControllerClass()).
                            getConstructor(Controller.class, MigrationConfiguration.class).newInstance(this, cfg);
                } else {
                    ccc = new ConsistencyCheckerController(this, cfg);
                }
            }
        } catch (Exception e) {
            LOGGER.error("Cannot create consistency checker controller for {}:{}", cfg.getConfigurationName(), e);
        }
        return ccc;
    }

    /**
     * Creates controller threads for migrators and consistency checkers based
     * on the configuration loaded from the db.
     *
     * For each configuration item, a migrator controller thread is created and
     * started.
     *
     * Once created, each thread manages its own lifecycle. If the corresponding
     * configuration is removed, thread terminates, or it is modified, thread
     * behaves accordingly.
     */
    public void createControllers(MigrationConfiguration[] configurations) throws Exception {
        for (MigrationConfiguration cfg : configurations) {
            MigrationProcess process = migrationMap.get(cfg.get_id());
            if (process == null) {
                LOGGER.debug("Creating a controller thread for configuration {}: {}", cfg.get_id(), cfg.getConfigurationName());
                MigratorController c = new MigratorController(this, cfg);
                if (c instanceof MonitoredThread) {
                    ((MonitoredThread) c).registerThreadMonitor(threadMonitor);
                }
                AbstractController ccc = getConsistencyCheckerController(cfg);;
                if (ccc instanceof MonitoredThread) {
                    ((MonitoredThread) ccc).registerThreadMonitor(threadMonitor);
                }
                migrationMap.put(cfg.get_id(), new MigrationProcess(cfg, c, ccc));
                c.start();
                if (ccc != null) {
                    ccc.start();
                }
            } else {
                healthcheck(cfg);
            }
        }
    }

    public void healthcheck(MigrationConfiguration cfg) {
        // Healthcheck
        MigrationProcess process = migrationMap.get(cfg.get_id());
        if (process != null) {
            process.cfg = cfg;
            if (!process.mig.isAlive()) {
                LOGGER.error("Migrator thread for {} is not alive, recreating", cfg.getConfigurationName());
                process.mig = new MigratorController(this, cfg);
                if (process.mig instanceof MonitoredThread) {
                    ((MonitoredThread) process.mig).registerThreadMonitor(threadMonitor);
                }
                process.mig.start();
            }
            if (shouldHaveConsistencyChecker(cfg)) {
                if (process.ccc == null || (process.ccc != null && !process.ccc.isAlive())) {
                    LOGGER.error("Consistency checker for {} is not alive, recreating", cfg.getConfigurationName());
                    process.ccc = getConsistencyCheckerController(cfg);
                    if (process.ccc != null) {
                        if (process.ccc instanceof MonitoredThread) {
                            ((MonitoredThread) process.ccc).registerThreadMonitor(threadMonitor);
                        }
                        process.ccc.start();
                    }
                }
            }
        }
    }

    @Override
    public void run() {
        LOGGER.debug("Starting controller");        
        Breakpoint.checkpoint("Controller:start");
        CleanupThread cleanup = new CleanupThread(this);
        if (cfg.getThreadTimeout() != null) {
            cleanup.setPeriod(cfg.getThreadTimeout() * 4);
        }
        cleanup.start();
        while(!stopped) {
            try {
                Breakpoint.checkpoint("Controller:loadconfig");
                MigrationConfiguration[] cfg=getMigrationConfigurations();
                createControllers(cfg);
                Breakpoint.checkpoint("Controller:createconfig");
                Thread.sleep(30000);
            } catch (Throwable e) {
                LOGGER.error("Error during configuration load:"+e);
            }
        }
        for(MigrationProcess p:migrationMap.values()) {
            p.mig.setStopped();
            if(p.ccc!=null) {
                p.ccc.setStopped();
            }
        }
        Breakpoint.checkpoint("Controller:end");
        cleanup.interrupt();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy