class VulnerabilityAnalysisTask implements Subscriber {

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

    /**
     * {@inheritDoc}
     */
    @Override
    public void inform(final Event e) {
        if (e instanceof VulnerabilityAnalysisEvent event) {
            if (event.getComponents() != null && event.getComponents().size() > 0) {
                final List<Component> components = new ArrayList<>();
                try (final QueryManager qm = new QueryManager()) {
                    for (final Component c : event.getComponents()) {
                        // Ensures the current component (and related objects such as Project) are attached to the
                        // current persistence manager. This may cause duplicate projects to be created and other
                        // unexpected behavior.
                        components.add(qm.getObjectByUuid(Component.class, c.getUuid()));
                    }
                    analyzeComponents(qm, components, e);
                }
            }
        } else if (e instanceof PortfolioVulnerabilityAnalysisEvent event) {
            LOGGER.info("Analyzing portfolio");
            try (final QueryManager qm = new QueryManager()) {
                final List<UUID> projectUuids = qm.getAllProjects(true)
                        .stream()
                        .map(Project::getUuid)
                        .collect(Collectors.toList());
                for (final UUID projectUuid : projectUuids) {
                    final Project project = qm.getObjectByUuid(Project.class, projectUuid);
                    if (project == null) continue;
                    final List<Component> components = qm.getAllComponents(project);
                    LOGGER.info("Analyzing " + components.size() + " components in project: " + project.getUuid());
                    analyzeComponents(qm, components, e);
                    performPolicyEvaluation(project, components);
                    LOGGER.info("Completed scheduled analysis of " + components.size() + " components in project: " + project.getUuid());
                }
            }
            LOGGER.info("Portfolio analysis complete");
        }
    }

    private void analyzeComponents(final QueryManager qm, final List<Component> components, final Event event) {<FILL_FUNCTION_BODY>}

    private void performPolicyEvaluation(Project project, List<Component> components) {
        // Evaluate the components against applicable policies via the PolicyEngine.
        final PolicyEngine pe = new PolicyEngine();
        pe.evaluate(components);
        if (project != null) {
            Event.dispatch(new ProjectMetricsUpdateEvent(project.getUuid()));
        }
    }

    private void inspectComponentReadiness(final Component component, final ScanTask scanTask, final List<Component> candidates) {
        if (scanTask.isCapable(component)) {
            if (scanTask.getClass().isAssignableFrom(CacheableScanTask.class)) {
                final CacheableScanTask cacheableScanTask = (CacheableScanTask) scanTask;
                if (cacheableScanTask.shouldAnalyze(component.getPurl())) {
                    candidates.add(component);
                } else {
                    cacheableScanTask.applyAnalysisFromCache(component);
                }
            } else {
                candidates.add(component);
            }
        }
    }

    private void performAnalysis(final Subscriber scanTask, final VulnerabilityAnalysisEvent event,
                                 final AnalyzerIdentity analyzerIdentity, final Event eventType) {
        Instant start = Instant.now();
        event.setVulnerabilityAnalysisLevel(VulnerabilityAnalysisLevel.PERIODIC_ANALYSIS);
        if (eventType instanceof VulnerabilityAnalysisEvent) {
            event.setVulnerabilityAnalysisLevel(VulnerabilityAnalysisLevel.BOM_UPLOAD_ANALYSIS);
        }
        if (CollectionUtils.isNotEmpty(event.getComponents())) {
            // Clear the transient cache result for each component.
            // Each analyzer will have its own result. Therefore, we do not want to mix them.
            event.getComponents().forEach(c -> c.setCacheResult(null));
            try {
                scanTask.inform(event);
            } catch (Exception ex) {
                LOGGER.error("An unexpected error occurred performing a vulnerability analysis task", ex);
            }
            // Clear the transient cache result for each component.
            // Each analyzer will have its own result. Therefore, we do not want to mix them.
            event.getComponents().forEach(c -> c.setCacheResult(null));
            Instant end = Instant.now();
            Duration timeElapsed = Duration.between(start, end);
            LOGGER.debug("Time taken by perform analysis task by " + analyzerIdentity.name() + " : " + timeElapsed.toMillis() + " milliseconds");
        }
    }
}

        /*
          When this task is processing events that specify the components to scan,
          separate them out into 'candidates' so that we can fire off multiple events
          in hopes of perform parallel analysis using different analyzers.
        */
        final InternalAnalysisTask internalAnalysisTask = new InternalAnalysisTask();
        final OssIndexAnalysisTask ossIndexAnalysisTask = new OssIndexAnalysisTask();
        final VulnDbAnalysisTask vulnDbAnalysisTask = new VulnDbAnalysisTask();
        final SnykAnalysisTask snykAnalysisTask = new SnykAnalysisTask();
        final TrivyAnalysisTask trivyAnalysisTask = new TrivyAnalysisTask();
        final List<Component> internalCandidates = new ArrayList<>();
        final List<Component> ossIndexCandidates = new ArrayList<>();
        final List<Component> vulnDbCandidates = new ArrayList<>();
        final List<Component> snykCandidates = new ArrayList<>();
        final List<Component> trivyCandidates = new ArrayList<>();
        for (final Component component : components) {
            inspectComponentReadiness(component, internalAnalysisTask, internalCandidates);
            inspectComponentReadiness(component, ossIndexAnalysisTask, ossIndexCandidates);
            inspectComponentReadiness(component, vulnDbAnalysisTask, vulnDbCandidates);
            inspectComponentReadiness(component, snykAnalysisTask, snykCandidates);
            inspectComponentReadiness(component, trivyAnalysisTask, trivyCandidates);
        }

        qm.detach(components);

        // Do not call individual async events when processing a known list of components.
        // Call each analyzer task sequentially and catch any exceptions as to prevent one analyzer
        // from interrupting the successful execution of all analyzers.
        performAnalysis(internalAnalysisTask, new InternalAnalysisEvent(internalCandidates), internalAnalysisTask.getAnalyzerIdentity(), event);
        performAnalysis(ossIndexAnalysisTask, new OssIndexAnalysisEvent(ossIndexCandidates), ossIndexAnalysisTask.getAnalyzerIdentity(), event);
        performAnalysis(snykAnalysisTask, new SnykAnalysisEvent(snykCandidates), snykAnalysisTask.getAnalyzerIdentity(), event);
        performAnalysis(trivyAnalysisTask, new TrivyAnalysisEvent(trivyCandidates), trivyAnalysisTask.getAnalyzerIdentity(), event);
        performAnalysis(vulnDbAnalysisTask, new VulnDbAnalysisEvent(vulnDbCandidates), vulnDbAnalysisTask.getAnalyzerIdentity(), event);
    
<no_super_class>