com.redhat.lightblue.migrator.Controller Maven / Gradle / Ivy
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