package de.is24.infrastructure.gridfs.http.metadata; import de.is24.infrastructure.gridfs.http.domain.RepoEntry; import de.is24.infrastructure.gridfs.http.domain.RepoType; import de.is24.infrastructure.gridfs.http.domain.YumEntry; import de.is24.infrastructure.gridfs.http.gridfs.StorageService; import de.is24.infrastructure.gridfs.http.jaxb.Data; import de.is24.infrastructure.gridfs.http.metadata.generation.DbGenerator; import de.is24.infrastructure.gridfs.http.metadata.generation.FileListsGenerator; import de.is24.infrastructure.gridfs.http.metadata.generation.OtherDbGenerator; import de.is24.infrastructure.gridfs.http.metadata.generation.PrimaryDbGenerator; import de.is24.infrastructure.gridfs.http.metadata.generation.RepoMdGenerator; import de.is24.infrastructure.gridfs.http.repos.RepoCleaner; import de.is24.infrastructure.gridfs.http.repos.RepoService; import de.is24.infrastructure.gridfs.http.storage.FileStorageService; import de.is24.util.monitoring.InApplicationMonitor; import de.is24.util.monitoring.spring.TimeMeasurement; 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.data.mongodb.tx.MongoTx; import org.springframework.jmx.export.annotation.ManagedOperation; import org.springframework.jmx.export.annotation.ManagedResource; import org.springframework.stereotype.Service; import java.io.File; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Date; import java.util.List; import static java.io.File.createTempFile; import static java.util.Arrays.asList; import static org.springframework.util.ObjectUtils.nullSafeEquals; @ManagedResource @Service @TimeMeasurement public class MetadataService { private static final Logger LOG = LoggerFactory.getLogger(MetadataService.class); private static final String METADATA_FILE_PATTERN = "/repodata/.*sqlite.bz2"; private static final String METADATA_SERVICE = "MetadataService."; private static final String METADATA_SERVICE_MARK_FOR_DELETE = METADATA_SERVICE + "markForDelete."; private static final String METADATA_SERVICE_FIND_ENTRIES = METADATA_SERVICE + "findEntries."; private static final String METADATA_SERVICE_CREATE_DB = METADATA_SERVICE + "createDb."; private static final String METADATA_SERVICE_STORE_DB = METADATA_SERVICE + "storeDb."; private static final String METADATA_SERVICE_GENERATE_REPOMDXML = METADATA_SERVICE + "generateRepomdXml."; private final StorageService storageService; private final RepoMdGenerator repoMdGenerator; private final YumEntriesRepository entriesRepository; private final RepoService repoService; private final RepoCleaner repoCleaner; private final YumEntriesHashCalculator entriesHashCalculator; private final InApplicationMonitor inApplicationMonitor; private final FileStorageService fileStorageService; private File tmpDir; //only for cglib proxy public MetadataService() { storageService = null; repoMdGenerator = null; entriesRepository = null; repoService = null; repoCleaner = null; entriesHashCalculator = null; inApplicationMonitor = null; fileStorageService = null; } @Autowired public MetadataService(StorageService gridFs, FileStorageService fileStorageService, YumEntriesRepository entriesRepository, RepoMdGenerator repoMdGenerator, RepoService repoService, RepoCleaner repoCleaner, YumEntriesHashCalculator entriesHashCalculator, InApplicationMonitor inApplicationMonitor) { this.storageService = gridFs; this.fileStorageService = fileStorageService; this.entriesRepository = entriesRepository; this.repoMdGenerator = repoMdGenerator; this.repoService = repoService; this.repoCleaner = repoCleaner; this.entriesHashCalculator = entriesHashCalculator; this.inApplicationMonitor = inApplicationMonitor; } @ManagedOperation(description = "generate metadata if its necessary") @MongoTx public void generateYumMetadataIfNecessary(String reponame) throws IOException, SQLException { generateYumMetadata(reponame, false); } @ManagedOperation(description = "always generate and cleanup repo") @MongoTx public void generateYumMetadata(String reponame) throws IOException, SQLException { generateYumMetadata(reponame, true); } @ManagedOperation(description = "only generate metadata without cleanup, but always") @MongoTx public void doYumMetadataGenerationOnly(String reponame) throws IOException, SQLException { doYumMetadataGenerationOnlyInternal(reponame, entriesHashCalculator.hashForRepo(reponame)); } private void generateYumMetadata(String reponame, boolean allwaysGenerate) throws IOException, SQLException { final RepoEntry repoEntry = repoService.ensureEntry(reponame, RepoType.STATIC, RepoType.SCHEDULED); String calculatedHash = entriesHashCalculator.hashForRepo(repoEntry.getName()); if (allwaysGenerate || needsMetadataUpdate(repoEntry, calculatedHash)) { if (repoCleaner.cleanup(reponame)) { calculatedHash = entriesHashCalculator.hashForRepo(repoEntry.getName()); } doYumMetadataGenerationOnlyInternal(reponame, calculatedHash); } else { LOG.debug("Generation for repository {} skipped. Because no update available.", reponame); } } private boolean needsMetadataUpdate(final RepoEntry repoEntry, final String calculatedHash) { final String savedHash = repoEntry.getHashOfEntries(); return !nullSafeEquals(calculatedHash, savedHash); } private void doYumMetadataGenerationOnlyInternal(final String reponame, final String calculatedHashOfEntries) throws IOException, SQLException { LOG.info("Generating metadata for {} started ..", reponame); long start = System.currentTimeMillis(); long current; fileStorageService.markForDeletionByFilenameRegex(reponame + METADATA_FILE_PATTERN); current = System.currentTimeMillis(); inApplicationMonitor.addTimerMeasurement(METADATA_SERVICE_MARK_FOR_DELETE + reponame, start, current); start = current; Date startTime = new Date(); List<YumEntry> entries = entriesRepository.findByRepo(reponame); inApplicationMonitor.addTimerMeasurement(METADATA_SERVICE_FIND_ENTRIES + reponame, start, System.currentTimeMillis()); List<Data> dbData = new ArrayList<>(); for (DbGenerator dbGenerator : asList(new PrimaryDbGenerator(), new FileListsGenerator(), new OtherDbGenerator())) { LOG.info("Generate {}-DB for {}", dbGenerator.getName(), reponame); Data data = saveDb(dbGenerator, reponame, entries); data.setType(dbGenerator.getName() + "_db"); dbData.add(data); } start = System.currentTimeMillis(); repoMdGenerator.generateRepoMdXml(reponame, dbData); inApplicationMonitor.addTimerMeasurement(METADATA_SERVICE_GENERATE_REPOMDXML + reponame, start, System.currentTimeMillis()); repoService.updateLastMetadataGeneration(reponame, startTime, calculatedHashOfEntries); LOG.info("Generating metadata for {} finished.", reponame); } private Data saveDb(DbGenerator dbGenerator, String reponame, List<YumEntry> entries) throws IOException, SQLException { long start = System.currentTimeMillis(); long current; File tempDbFile = createTempFile(reponame + "-" + dbGenerator.getName(), ".sqlite", tmpDir); try { dbGenerator.createDb(tempDbFile, entries); current = System.currentTimeMillis(); inApplicationMonitor.addTimerMeasurement(METADATA_SERVICE_CREATE_DB + dbGenerator.getName() + "." + reponame, start, current); start = current; Data data = storageService.storeRepodataDbBz2(reponame, tempDbFile, dbGenerator.getName()); data.setType(dbGenerator.getName() + "_db"); inApplicationMonitor.addTimerMeasurement(METADATA_SERVICE_STORE_DB + dbGenerator.getName() + "." + reponame, start, System.currentTimeMillis()); return data; } finally { tempDbFile.delete(); } } @Value("${metadata.tmp.dir:@null}") public void setTmpDir(File tmpDir) { this.tmpDir = tmpDir; if (tmpDir != null) { tmpDir.mkdirs(); } } }