class InternalComponentIdentificationTask implements Subscriber {

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

    @Override
    public void inform(final Event e) {
        if (e instanceof InternalComponentIdentificationEvent) {
            LOGGER.info("Starting internal component identification");
            final Instant startTime = Instant.now();
            try {
                analyze();
            } catch (Exception ex) {
                LOGGER.error("An unexpected error occurred while identifying internal components", ex);
            }
            LOGGER.info("Internal component identification completed in "
                    + DateFormatUtils.format(Duration.between(startTime, Instant.now()).toMillis(), "mm:ss:SS"));
        }
    }

    private void analyze() throws Exception {<FILL_FUNCTION_BODY>}

    /**
     * Efficiently page through all components using keyset pagination.
     *
     * @param pm     The {@link PersistenceManager} to use
     * @param lastId ID of the last {@link Component} in the previous result set, or {@code null} if this is the first invocation
     * @return A {@link List} representing a page of up to {@code 500} {@link Component}s
     * @throws Exception When closing the query failed
     * @see <a href="https://use-the-index-luke.com/no-offset">Keyset pagination</a>
     */
    private List<Component> fetchNextComponentsPage(final PersistenceManager pm, final Long lastId) throws Exception {
        try (final Query<Component> query = pm.newQuery(Component.class)) {
            if (lastId != null) {
                query.setFilter("id < :lastId");
                query.setParameters(lastId);
            }
            query.setOrdering("id DESC");
            query.setRange(0, 500);
            query.getFetchPlan().setGroup(Component.FetchGroup.INTERNAL_IDENTIFICATION.name());
            return List.copyOf(query.executeList());
        }
    }

}

        try (final var qm = new QueryManager()) {
            final PersistenceManager pm = qm.getPersistenceManager();

            // Disable the DataNucleus L2 cache for this persistence manager.
            // The cache will hold references to the queried objects, preventing them
            // from being garbage collected. This is not required the case of this task.
            pm.setProperty(PropertyNames.PROPERTY_CACHE_L2_TYPE, "none");

            final var internalComponentIdentifier = new InternalComponentIdentifier();
            List<Component> components = fetchNextComponentsPage(pm, null);
            while (!components.isEmpty()) {
                for (final Component component : components) {
                    String coordinates = component.getName();
                    if (StringUtils.isNotBlank(component.getGroup())) {
                        coordinates = component.getGroup() + ":" + coordinates;
                    }

                    final boolean internal = internalComponentIdentifier.isInternal(component);
                    if (internal) {
                        LOGGER.debug("Component " + coordinates + " (" + component.getUuid() + ") was identified to be internal");
                    }

                    if (component.isInternal() != internal) {
                        if (internal) {
                            LOGGER.info("Component " + coordinates + " (" + component.getUuid()
                                    + ") was identified to be internal. It was previously not an internal component.");
                        } else {
                            LOGGER.info("Component " + coordinates + " (" + component.getUuid()
                                    + ") was previously identified as internal. It is no longer identified as internal.");
                        }
                    }

                    if (component.isInternal() != internal) {
                        final Transaction trx = pm.currentTransaction();
                        try {
                            trx.begin();
                            component.setInternal(internal);
                            trx.commit();
                        } finally {
                            if (trx.isActive()) {
                                trx.rollback();
                            }
                        }
                    }
                }

                final long lastId = components.get(components.size() - 1).getId();
                components = fetchNextComponentsPage(pm, lastId);
            }
        }
    
<no_super_class>