<%#
 Copyright 2013-2025 the original author or authors from the JHipster project.

 This file is part of the JHipster project, see https://www.jhipster.tech/
 for more information.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

      https://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
-%>
package <%= packageName %>.config;

<%_ if (reactive) { _%>
import com.fasterxml.jackson.databind.ObjectMapper;
<%_ } _%>
<%_ if (!skipClient && reactive || devDatabaseTypeH2Any) { _%>
import tech.jhipster.config.JHipsterConstants;
<%_ } _%>
import tech.jhipster.config.JHipsterProperties;
<%_ if (devDatabaseTypeH2Any) { _%>
import tech.jhipster.config.ApplicationProperties;
import tech.jhipster.config.h2.H2ConfigurationHelper;
<%_ } _%>
<%_ if (!skipClient && reactive) { _%>
import tech.jhipster.web.filter.<% if (reactive) { %>reactive.<% } %>CachingHttpHeadersFilter;
<%_ } _%>
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
<%_ if (!reactive) { _%>
import org.springframework.boot.web.server.*;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
<%_ } _%>
<%_ if (reactive && !skipClient) { _%>
import org.springframework.boot.autoconfigure.web.reactive.ResourceHandlerRegistrationCustomizer;
<%_ } _%>
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
<%_ if (reactive) { _%>
    <%_ if (!skipClient) { _%>
import org.springframework.context.annotation.Profile;
    <%_ } _%>
import org.springframework.core.annotation.Order;
<%_ } _%>
<%_ if (reactive && !databaseTypeNo) { _%>
import org.springframework.data.web.ReactivePageableHandlerMethodArgumentResolver;
import org.springframework.data.web.ReactiveSortHandlerMethodArgumentResolver;
<%_ } _%>
<%_ if (!reactive || (devDatabaseTypeH2Any && reactive)) { _%>
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
<%_ } _%>
import org.springframework.util.CollectionUtils;
import org.springframework.web.cors.CorsConfiguration;
<%_ if (!reactive) { _%>
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
<%_ } _%>
<%_ if (reactive) { _%>
import org.springframework.web.cors.reactive.CorsConfigurationSource;;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
import org.springframework.web.server.WebExceptionHandler;
import <%= packageName %>.web.rest.errors.ExceptionTranslator;
import tech.jhipster.web.rest.errors.ReactiveWebExceptionHandler;
<%_ } _%>
<%_ if (!reactive) { _%>

import jakarta.servlet.*;
    <%_ if (!skipClient) { _%>
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;

import static java.net.URLDecoder.decode;
    <%_ } _%>
<%_ } _%>
<%_ if (reactive && !skipClient) { _%>

import java.util.concurrent.TimeUnit;
<%_ } _%>

/**
 * Configuration of web application with Servlet 3.0 APIs.
 */
@Configuration
public class WebConfigurer implements <% if (!reactive) { %>ServletContextInitializer<% if (!skipClient) { %>, WebServerFactoryCustomizer<WebServerFactory><% } %><% } %><% if (reactive) { %>WebFluxConfigurer<% } %> {

    private static final Logger LOG = LoggerFactory.getLogger(WebConfigurer.class);

<%_ if (!reactive) { _%>
    private final Environment env;

<%_ } _%>
    private final JHipsterProperties jHipsterProperties;

    public WebConfigurer(<% if (!reactive || devDatabaseTypeH2Any) { %>Environment env, <% } %>JHipsterProperties jHipsterProperties) {
<%_ if (!reactive) { _%>
        this.env = env;
<%_ } _%>
        this.jHipsterProperties = jHipsterProperties;
<%_ if (devDatabaseTypeH2Any && reactive) { _%>
        if (h2ConsoleIsEnabled(env)) {
            try {
                LOG.info("Initialize H2 console");
                H2ConfigurationHelper.initH2Console();
            } catch (Exception e) {
                // Console may already be running on another app or after a refresh.
                e.printStackTrace();
            };
        }
<%_ } _%>
    }
<%_ if (!reactive) { _%>

    @Override
    public void onStartup(ServletContext servletContext) {
        if (env.getActiveProfiles().length != 0) {
            LOG.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles());
        }

  <%_ if (devDatabaseTypeH2Any) { _%>
        if (h2ConsoleIsEnabled(env)) {
            initH2Console(servletContext);
        }
  <%_ } _%>
        LOG.info("Web application fully configured");
    }
  <%_ if (!skipClient) { _%>

    /**
     * Customize the Servlet engine: Mime types, the document root, the cache.
     */
    @Override
    public void customize(WebServerFactory server) {
        // When running in an IDE or with <% if (buildToolGradle) { %>./gradlew bootRun<% } else { %>./mvnw spring-boot:run<% } %>, set location of the static web assets.
        setLocationForStaticAssets(server);
    }

    private void setLocationForStaticAssets(WebServerFactory server) {
        if (server instanceof ConfigurableServletWebServerFactory servletWebServer) {
            File root;
            String prefixPath = resolvePathPrefix();
            root = Path.of(prefixPath + "<%= clientDistDir %>").toFile();
            if (root.exists() && root.isDirectory()) {
                servletWebServer.setDocumentRoot(root);
            }
        }
    }

    /**
     * Resolve path prefix to static resources.
     */
    private String resolvePathPrefix() {
        String fullExecutablePath = decode(this.getClass().getResource("").getPath(), StandardCharsets.UTF_8);
        String rootPath = Path.of(".").toUri().normalize().getPath();
        String extractedPath = fullExecutablePath.replace(rootPath, "");
        int extractionEndIndex = extractedPath.indexOf("<%= temporaryDir %>");
        if (extractionEndIndex <= 0) {
            return "";
        }
        return extractedPath.substring(0, extractionEndIndex);
    }
  <%_ } _%>
<%_ } _%>

    @Bean
    public <% if (reactive) { %>CorsConfigurationSource corsConfigurationSource()<% } else { %>CorsFilter corsFilter()<% } %> {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = jHipsterProperties.getCors();
        if (!CollectionUtils.isEmpty(config.getAllowedOrigins()) || !CollectionUtils.isEmpty(config.getAllowedOriginPatterns())) {
            LOG.debug("Registering CORS filter");
            source.registerCorsConfiguration("/api/**", config);
            source.registerCorsConfiguration("/management/**", config);
            source.registerCorsConfiguration("/v3/api-docs", config);
            source.registerCorsConfiguration("/swagger-ui/**", config);
<%_ if (applicationTypeGateway) { _%>
            source.registerCorsConfiguration("/*/api/**", config);
            source.registerCorsConfiguration("/services/*/api/**", config);
            source.registerCorsConfiguration("/*/management/**", config);
<%_ } _%>
        }
        return <% if (reactive) { %>source<% } else { %>new CorsFilter(source)<% } %>;
    }
<%_ if (reactive) { _%>

  <%_ if (!databaseTypeNo) { _%>
    // TODO: remove when this is supported in spring-boot
    @Bean
    HandlerMethodArgumentResolver reactivePageableHandlerMethodArgumentResolver() {
        return new ReactivePageableHandlerMethodArgumentResolver();
    }

    // TODO: remove when this is supported in spring-boot
    @Bean
    HandlerMethodArgumentResolver reactiveSortHandlerMethodArgumentResolver() {
        return new ReactiveSortHandlerMethodArgumentResolver();
    }
  <%_ } _%>

    @Bean
    @Order(-2) // The handler must have precedence over WebFluxResponseStatusExceptionHandler and Spring Boot's ErrorWebExceptionHandler
    public WebExceptionHandler problemExceptionHandler(ObjectMapper mapper, ExceptionTranslator problemHandling) {
        return new ReactiveWebExceptionHandler(problemHandling, mapper);
    }

  <%_ if (!skipClient) { _%>

    @Bean
    ResourceHandlerRegistrationCustomizer registrationCustomizer() {
        // Disable built-in cache control to use our custom filter instead
        return registration -> registration.setCacheControl(null);
    }

    @Bean
    @Profile(JHipsterConstants.SPRING_PROFILE_PRODUCTION)
    public CachingHttpHeadersFilter cachingHttpHeadersFilter() {
        // Use a cache filter that only match selected paths
        return new CachingHttpHeadersFilter(TimeUnit.DAYS.toMillis(jHipsterProperties.getHttp().getCache().getTimeToLiveInDays()));
    }
  <%_ } _%>
<%_ } _%>
<%_ if (devDatabaseTypeH2Any) { _%>

    private boolean h2ConsoleIsEnabled(Environment env) {
        return env.acceptsProfiles(Profiles.of(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) && "true".equals(env.getProperty("spring.h2.console.enabled"));
    }
  <%_ if (!reactive) { _%>

    /**
     * Initializes H2 console.
     */
    private void initH2Console(ServletContext servletContext) {
        LOG.info("Initialize H2 console");
        H2ConfigurationHelper.initH2Console(servletContext);
    }
  <%_ } _%>
<%_ } _%>
}
