package de.is24.infrastructure.gridfs.http.metadata.scheduling;
import de.is24.infrastructure.gridfs.http.domain.RepoEntry;
import de.is24.infrastructure.gridfs.http.metadata.MetadataService;
import de.is24.infrastructure.gridfs.http.metadata.RepoEntriesRepository;
import de.is24.infrastructure.gridfs.http.mongo.MongoPrimaryDetector;
import de.is24.infrastructure.gridfs.http.utils.MDCHelper;
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.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import static de.is24.infrastructure.gridfs.http.domain.RepoType.SCHEDULED;
import static java.util.stream.Collectors.toSet;
@ManagedResource
@Service
public class RepoMetadataScheduler {
private static final Logger LOG = LoggerFactory.getLogger(RepoMetadataScheduler.class);
private final RepoEntriesRepository repo;
private final MetadataService metadataService;
private final MongoPrimaryDetector primaryDetector;
private final ScheduledExecutorService scheduledExecutorService;
private final int delayInSec;
private Map<String, RepoMetadataGeneratorJob> repoJobs;
private Object semaphore = new Object();
@Autowired
public RepoMetadataScheduler(RepoEntriesRepository repo,
MetadataService metadataService,
MongoPrimaryDetector primaryDetector,
ScheduledExecutorService scheduledExecutorService,
@Value("${scheduler.delay:10}") int delayInSec) {
this.repo = repo;
this.metadataService = metadataService;
this.primaryDetector = primaryDetector;
this.scheduledExecutorService = scheduledExecutorService;
this.delayInSec = delayInSec;
this.repoJobs = new ConcurrentHashMap<>();
}
@Scheduled(cron = "${scheduler.update.cron:*/30 * * * * *}")
public void update() {
LOG.debug("Checking for updates in scheduled repository definitions.");
new MDCHelper(this.getClass()).run(() -> {
try {
Set<String> repoNamesToSchedule = repo.findByType(SCHEDULED)
.stream()
.map(RepoEntry::getName)
.collect(toSet());
repoNamesToSchedule.forEach(this::ensureRunningRepoJob);
removeJobsNotFoundInDb(repoNamesToSchedule);
} catch (Exception e) {
LOG.error("while updating scheduled repo jobs", e);
}
});
}
private void removeJobsNotFoundInDb(Set<String> repoNamesToSchedule) {
repoJobs.keySet().stream().filter(repoName -> !repoNamesToSchedule.contains(repoName)).forEach(this::removeRepoJob);
}
public void removeRepoJob(String repoName) {
synchronized (semaphore) {
RepoMetadataGeneratorJob removed = repoJobs.remove(repoName);
if (removed != null) {
removed.deactivate();
}
}
LOG.info("Removed scheduling job for repository: {}", repoName);
}
public void ensureRunningRepoJob(String name) {
synchronized (semaphore) {
removeJobIfBroken(name);
if (!isJobPresent(name)) {
repoJobs.put(name,
new RepoMetadataGeneratorJob(name, metadataService, primaryDetector, scheduledExecutorService, delayInSec));
LOG.info("Added scheduling job for repository: {}", name);
}
}
}
// expects caller to synchronize on semaphore
private void removeJobIfBroken(String name) {
RepoMetadataGeneratorJob repoMetadataGeneratorJob = repoJobs.get(name);
if ((repoMetadataGeneratorJob != null) && repoMetadataGeneratorJob.isNotScheduledAnyLonger()) {
LOG.info("will remove not any longer scheduled repo metadata generator job for {}", name);
removeRepoJob(name);
}
}
private boolean isJobPresent(String name) {
return repoJobs.get(name) != null;
}
@ManagedOperation
public Map<String, RepoMetadataGeneratorJob> getRepoJobs() {
// we do not want external Changes to interfere with our synchronizations, so we do not allow modifications.
// And we rely on ConcurrentHashMap to handle concurrent reads.
return Collections.unmodifiableMap(repoJobs);
}
}