class WebappListener implements ServletContextListener, ServletRequestListener {

    private static final Logger LOGGER = LoggerFactory.getLogger(WebappListener.class);
    private final Timer startupTimer = Timer.builder("webapp.startup.latency").
                description("web application startup latency").
                register(Metrics.getPrometheusRegistry());

    /**
     * {@inheritDoc}
     */
    @Override
    public void contextInitialized(final ServletContextEvent servletContextEvent) {
        Instant start = Instant.now();

        ServletContext context = servletContextEvent.getServletContext();
        RuntimeEnvironment env = RuntimeEnvironment.getInstance();

        LOGGER.log(Level.INFO, "Starting webapp with version {0} ({1})",
                    new Object[]{Info.getVersion(), Info.getRevision()});

        String configPath = Optional.ofNullable(context.getInitParameter("CONFIGURATION"))
                .orElseThrow(() -> new WebappError("CONFIGURATION parameter missing in the web.xml file"));
        try {
            env.readConfiguration(new File(configPath), CommandTimeoutType.WEBAPP_START);
        } catch (IOException ex) {
            LOGGER.log(Level.WARNING, "Configuration error. Failed to read config file: ", ex);
        }

        String serverInfo = context.getServerInfo();
        LOGGER.log(Level.INFO, "running inside {0}", serverInfo);
        if (serverInfo.startsWith("Apache Tomcat")) {
            int idx;
            if ((idx = serverInfo.indexOf('/')) > 0) {
                String version = serverInfo.substring(idx + 1);
                if (!version.startsWith("10.")) {
                    LOGGER.log(Level.SEVERE, "Unsupported Tomcat version: {0}", version);
                    throw new WebappError("Unsupported Tomcat version");
                }
            }
        }

        /*
         * Create a new instance of authorization framework. If the code above
         * (reading the configuration) failed then the plugin directory is
         * possibly {@code null} causing the framework to allow every request.
         */
        env.setAuthorizationFramework(new AuthorizationFramework(env.getPluginDirectory(), env.getPluginStack()));
        env.getAuthorizationFramework().reload();

        if (env.isWebappCtags() && !env.validateUniversalCtags()) {
            LOGGER.warning("Didn't find Universal Ctags for --webappCtags");
        }

        String pluginDirectory = env.getPluginDirectory();
        if (pluginDirectory != null && env.isAuthorizationWatchdog()) {
            env.getWatchDog().start(new File(pluginDirectory));
        }

        indexCheck(configPath, env);

        env.startExpirationTimer();

        ApiTaskManager.getInstance().setContextPath(context.getContextPath());
        // register API task queues
        ApiTaskManager.getInstance().addPool(ProjectsController.PROJECTS_PATH, 1);
        // Used by ConfigurationController#reloadAuthorization()
        ApiTaskManager.getInstance().addPool("authorization", 1);
        ApiTaskManager.getInstance().addPool(ConfigurationController.PATH, 1);

        startupTimer.record(Duration.between(start, Instant.now()));
    }

    /**
     * Check index(es). If projects are enabled, those which failed the test will be marked as not indexed.
     * @param configPath path to configuration
     * @param env {@link RuntimeEnvironment} instance
     */
    private static void indexCheck(String configPath, RuntimeEnvironment env) {<FILL_FUNCTION_BODY>}

    /**
     * {@inheritDoc}
     */
    @Override
    public void contextDestroyed(final ServletContextEvent servletContextEvent) {
        RuntimeEnvironment env = RuntimeEnvironment.getInstance();
        env.getIndexerParallelizer().bounce();
        env.getWatchDog().stop();
        env.stopExpirationTimer();
        try {
            env.shutdownRevisionExecutor();
            env.shutdownSearchExecutor();
            env.shutdownDirectoryListingExecutor();
        } catch (InterruptedException e) {
            LOGGER.log(Level.WARNING, "Could not shutdown revision executor", e);
        }

        // need to explicitly close the suggester service because it might have scheduled rebuild which could prevent
        // the web application from closing
        SuggesterServiceFactory.getDefault().close();

        // destroy queue(s) of API tasks
        try {
            ApiTaskManager.getInstance().shutdown();
        } catch (InterruptedException e) {
            LOGGER.log(Level.WARNING, "could not shutdown API task manager cleanly", e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void requestInitialized(ServletRequestEvent e) {
        // pass
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void requestDestroyed(ServletRequestEvent e) {
        PageConfig.cleanup(e.getServletRequest());
        SearchHelper sh = (SearchHelper) e.getServletRequest().getAttribute(SearchHelper.REQUEST_ATTR);
        if (sh != null) {
            sh.destroy();
        }

        AnalyzerGuru.returnAnalyzers();
    }
}

        try (IndexCheck indexCheck = new IndexCheck(Configuration.read(new File(configPath)))) {
                indexCheck.check(IndexCheck.IndexCheckMode.VERSION);
        } catch (IOException e) {
            LOGGER.log(Level.WARNING, "could not perform index check", e);
        } catch (IndexCheckException e) {
            if (env.isProjectsEnabled()) {
                LOGGER.log(Level.INFO, "marking projects that failed the index check as not indexed");

                for (Path path : e.getFailedPaths()) {
                    Project project = Project.getProject(path.toFile());
                    if (project != null) {
                        project.setIndexed(false);
                    }
                }
            } else {
                // Fail the deployment. The index check would fail only on legitimate version discrepancy.
                throw new WebappError("index version check failed", e);
            }
        }
    
<no_super_class>