class ApiKeyHandler implements MiddlewareHandler {
    static final Logger logger = LoggerFactory.getLogger(ApiKeyHandler.class);
    static final String API_KEY_MISMATCH = "ERR10075";
    static ApiKeyConfig config;

    private volatile HttpHandler next;

    public ApiKeyHandler() {
        if(logger.isTraceEnabled()) logger.trace("ApiKeyHandler is loaded.");
        config = ApiKeyConfig.load();
    }

    /**
     * This is a constructor for test cases only. Please don't use it.
     * @param cfg BasicAuthConfig
     */
    @Deprecated
    public ApiKeyHandler(ApiKeyConfig cfg) {
        config = cfg;
        if(logger.isInfoEnabled()) logger.info("ApiKeyHandler is loaded.");
    }

    @Override
    public HttpHandler getNext() {
        return next;
    }

    @Override
    public MiddlewareHandler setNext(HttpHandler next) {
        Handlers.handlerNotNull(next);
        this.next = next;
        return this;
    }

    @Override
    public boolean isEnabled() {
        return config.isEnabled();
    }

    @Override
    public void register() {
        // As apiKeys are in the config file, we need to mask them.
        List<String> masks = new ArrayList<>();
        // if hashEnabled, there is no need to mask in the first place.
        if(!config.hashEnabled) {
            masks.add("apiKey");
        }
        ModuleRegistry.registerModule(ApiKeyConfig.CONFIG_NAME, ApiKeyHandler.class.getName(), Config.getNoneDecryptedInstance().getJsonMapConfigNoCache(ApiKeyConfig.CONFIG_NAME), masks);
    }

    @Override
    public void reload() {
        config.reload();
        List<String> masks = new ArrayList<>();
        if(!config.hashEnabled) {
            masks.add("apiKey");
        }
        ModuleRegistry.registerModule(ApiKeyConfig.CONFIG_NAME, ApiKeyHandler.class.getName(), Config.getNoneDecryptedInstance().getJsonMapConfigNoCache(ApiKeyConfig.CONFIG_NAME), masks);
        if(logger.isInfoEnabled()) logger.info("ApiKeyHandler is reloaded.");
    }

    @Override
    public void handleRequest(HttpServerExchange exchange) throws Exception {
        if(logger.isDebugEnabled()) logger.debug("ApiKeyHandler.handleRequest starts.");
        String requestPath = exchange.getRequestPath();
        if(handleApiKey(exchange, requestPath)) {
            if(logger.isDebugEnabled()) logger.debug("ApiKeyHandler.handleRequest ends.");
            // only goes to the next handler the APIKEY verification is passed successfully.
            Handler.next(exchange, next);
        }
    }

    public boolean handleApiKey(HttpServerExchange exchange, String requestPath) {<FILL_FUNCTION_BODY>}
}

        if(logger.isTraceEnabled()) logger.trace("requestPath = " + requestPath);
        if (config.getPathPrefixAuths() != null) {
            boolean matched = false;
            boolean found = false;
            // iterate all the ApiKey entries to find if any of them matches the request path.
            for(ApiKey apiKey: config.getPathPrefixAuths()) {
                if(requestPath.startsWith(apiKey.getPathPrefix())) {
                    found = true;
                    // found the matched prefix, validate the apiKey by getting the header and compare.
                    String k = exchange.getRequestHeaders().getFirst(apiKey.getHeaderName());
                    if(config.hashEnabled) {
                        // hash the apiKey and compare with the one in the config.
                        try {
                            matched = HashUtil.validatePassword(k.toCharArray(), apiKey.getApiKey());
                            if(matched) {
                                if (logger.isTraceEnabled()) logger.trace("Found valid apiKey with prefix = " + apiKey.getPathPrefix() + " headerName = " + apiKey.getHeaderName());
                                break;
                            }
                        } catch (Exception e) {
                            // there is no way to get here as the validatePassword will not throw any exception.
                            logger.error("Exception:", e);
                        }
                    } else {
                        // if not hash enabled, then compare the apiKey directly.
                        if(apiKey.getApiKey().equals(k)) {
                            if (logger.isTraceEnabled()) logger.trace("Found matched apiKey with prefix = " + apiKey.getPathPrefix() + " headerName = " + apiKey.getHeaderName());
                            matched = true;
                            break;
                        }
                    }
                }
            }
            if(!found) {
                // the request path is no in the configuration, consider pass and go to the next handler.
                return true;
            }
            if(!matched) {
                // at this moment, if not matched, then return an error message.
                logger.error("Could not find matched APIKEY for request path " + requestPath);
                setExchangeStatus(exchange, API_KEY_MISMATCH, requestPath);
                if(logger.isDebugEnabled()) logger.debug("ApiKeyHandler.handleRequest ends with an error.");
                exchange.endExchange();
                return false;
            }
        }
        return true;
    
<no_super_class>