class TrustAnchorValidationService {

    public void validate(long trustAnchorId) {
        Optional<TrustAnchor> maybeTrustAnchor = storage.readTx(tx -> trustAnchors.get(tx, Key.of(trustAnchorId)));
        if (!maybeTrustAnchor.isPresent()) {
            log.error("Trust anchor {} doesn't exist.", trustAnchorId);
            return;
        }

        TrustAnchor trustAnchor = maybeTrustAnchor.get();
        log.info("trust anchor {} located at {} with subject public key info {}", trustAnchor.getName(), trustAnchor.getLocations(), trustAnchor.getSubjectPublicKeyInfo());

        TrustAnchorValidationRun validationRun = storage.readTx(tx -> {
            final Ref<TrustAnchor> trustAnchorRef = trustAnchors.makeRef(tx, Key.of(trustAnchorId));
            return new TrustAnchorValidationRun(trustAnchorRef, trustAnchor.getLocations().get(0));
        });

        final ValidationLocation trustAnchorValidationLocation = new ValidationLocation(validationRun.getTrustAnchorCertificateURI());
        ValidationResult validationResult = newValidationResult(trustAnchorValidationLocation);

        boolean updatedTrustAnchor = false;
        try {
            final Optional<Tuple2<URI, byte[]>> maybeTrustAnchorCertificate = fetchPreferredTrustAnchorCertificate(trustAnchor, validationResult);

            if (maybeTrustAnchorCertificate.isPresent()) {
                final Tuple2<URI, byte[]> res = maybeTrustAnchorCertificate.get();
                updatedTrustAnchor = readTrustAnchorFromLocation(res.v2, trustAnchor, res.v1, validationResult);
            } else {
                validationResult.error(
                        ErrorCodes.TRUST_ANCHOR_FETCH,
                        "any location",
                        String.format("None of the locations (%s) could be loaded.", Joiner.on(", ").join(trustAnchor.getLocations())));
                validationRun.setFailed();
            }

            if (validationResult.hasFailures()) {
                log.warn("Validation result for the TA {} has failures: {}", trustAnchor.getName(),
                        validationResult.getFailuresForAllLocations());
            }

            if (trustAnchor.getRsyncPrefetchUri() != null) {
                storage.writeTx0(tx -> {
                    final Ref<TrustAnchor> trustAnchorRef = trustAnchors.makeRef(tx, trustAnchor.key());
                    rpkiRepositories.register(tx, trustAnchorRef,
                            trustAnchor.getRsyncPrefetchUri(), RpkiRepository.Type.RSYNC_PREFETCH);
                });
            }

            validationRun.completeWith(validationResult);
            if (!validatedAtLeastOnce.contains(trustAnchor.getId()) || updatedTrustAnchor) {
                if (updatedTrustAnchor) {
                    storage.writeTx0(tx -> trustAnchors.update(tx, trustAnchor));
                }
                final Set<TrustAnchor> affectedTrustAnchors = Sets.newHashSet(trustAnchor);
                if (trustAnchor.getRsyncPrefetchUri() != null) {
                    storage.readTx(tx ->
                            rpkiRepositories.findByURI(tx, trustAnchor.getRsyncPrefetchUri()))
                            .ifPresent(r ->
                                    affectedTrustAnchors.addAll(repositoryValidationService.prefetchRepository(r)));
                }
                affectedTrustAnchors.forEach(validationScheduler::triggerCertificateTreeValidation);
            }
        } catch (CommandExecutionException | IOException e) {
            log.error("validation run for trust anchor {} failed", trustAnchor, e);
            validationRun.addCheck(new ValidationCheck(validationRun.getTrustAnchorCertificateURI(), ValidationCheck.Status.ERROR, ErrorCodes.UNHANDLED_EXCEPTION, e.toString()));
            validationRun.setFailed();
        } finally {
            validatedAtLeastOnce.add(trustAnchor.getId());
            storage.writeTx0(tx -> validationRuns.add(tx, validationRun));
        }
    }

    @Autowired public  TrustAnchorValidationService(
        TrustAnchors trustAnchors,
        RpkiRepositories rpkiRepositories,
        ValidationRuns validationRuns,
        ValidationScheduler validationScheduler,
        RpkiRepositoryValidationService repositoryValidationService,
        Storage storage,
        TrustAnchorMetricsService trustAnchorMetricsService,
        TrustAnchorRetrievalService trustAnchorRetrievalService);

}