Показывать статическую HTML-страницу во время инициализации контекста Spring

#spring #spring-boot #spring-mvc #embedded-jetty

#spring #spring-boot #spring-mvc #встроенный-причал

Вопрос:

Загрузка Spring 2.3.7, встроенная упаковка Jetty, JSP и WAR. Я хочу показать свою статическую HTML-страницу во время инициализации контекста Spring. Она должна быть видна при запуске приложения и до обновления контекста Spring. Я попытался использовать это руководство в качестве примера https://www.nurkiewicz.com/2015/09/displaying-progress-of-spring.html но это не работает. Мне нужно запустить встроенный jetty непосредственно при инициализации jetty. Но spring boot запускает встроенный jetty только при обновлении контекста. Как мне это сделать?

Ответ №1:

Я создал класс прогрева Jetty warpper:

 public final class WarmupServer {

    private final String contextPath;
    private final String displayName;
    private final DefaultApplicationArguments arguments;

    private final String[] welcomeFiles;
    private final Resource baseResource;

    private Server server;

    public WarmupServer(String contextPath,
                        String displayName,
                        Resource baseResource,
                        String[] welcomeFiles,
                        String... runArgs) {
        this.contextPath = StringUtils.defaultIfBlank(contextPath, "/");
        this.displayName = StringUtils.defaultIfBlank(displayName, "Warmup");
        this.baseResource = ObjectUtils.defaultIfNull(baseResource, Resource.newClassPathResource("/static"));
        this.welcomeFiles = ArrayUtils.isEmpty(welcomeFiles) ? new String[]{"html/warmup.html"} : welcomeFiles;
        this.arguments = new DefaultApplicationArguments(ArrayUtils.nullToEmpty(runArgs));
    }

    public Server start() throws Exception {
        if (server != null amp;amp; server.isStarted()) {
            throw new IllegalStateException("Server already started");
        }
        server = new Server();
        server.setHandler(buildServletHandler());

        final String configPath = parseArg(OPT_CONFIG);
        if (StringUtils.isBlank(configPath)) {
            throw new RuntimeException(OPT_CONFIG   " argument is not set");
        }
        final Config config = ConfigUtils.parseFile(new File(configPath), DEFAULT_CONFIG_FILE_NAME);

        configureHttpConnector(config);
        configureHttpsConnector(config);

        server.start();
        return server;
    }

    public void registerWarmupServerStopLifecycle(ConfigurableApplicationContext context) {
        context.getBeanFactory()
                .registerSingleton(WarmupStopLifecycle.class.getSimpleName(), new WarmupStopLifecycle(server));
    }

    private ServletContextHandler buildServletHandler() {
        final ServletContextHandler handler = new ServletContextHandler(ServletContextHandler.SESSIONS);
        handler.addServlet(DefaultServlet.class, "/");
        handler.setDisplayName(displayName);
        handler.setContextPath(contextPath);
        handler.setWelcomeFiles(welcomeFiles);
        handler.setBaseResource(baseResource);
        return handler;
    }

    private void configureHttpConnector(Config config) {
        final int httpPort = NumberUtils.toInt(parseArg(OPT_HTTP_PORT), config.getInt(OPT_HTTP_PORT));
        final ServerConnector connector = new ServerConnector(server);
        connector.setPort(httpPort);
        server.addConnector(connector);
    }

    private void configureHttpsConnector(Config config) {
        final int httpsPort = NumberUtils.toInt(
                parseArg(OPT_HTTPS_PORT), config.getInt(OPT_HTTPS_PORT));
        final String keyStorePath = StringUtils.defaultIfBlank(
                parseArg(OPT_KEYSTORE_FILE), config.getString(OPT_KEYSTORE_FILE));

        final boolean sslEnabled = StringUtils.isNotBlank(keyStorePath)
                amp;amp; Files.isReadable(Paths.get(keyStorePath));

        if (sslEnabled) {
            final HttpConfiguration configuration = new HttpConfiguration();
            configuration.setSecurePort(httpsPort);
            configuration.setSecureScheme(HTTPS_SCHEME);

            final ServerConnector httpsConnector = new HttpsConnector()
                    .createConnector(server, configuration, config.getConfig(JETTY_HTTPS_CONFIG), httpsPort);
            server.addConnector(httpsConnector);
        }
    }

    private String parseArg(String optionName) {
        final List<String> values = arguments.getOptionValues(optionName);
        return CollectionUtils.isEmpty(values) ? StringUtils.EMPTY : values.get(0);
    }

      
    public static WarmupServer start(String contextPath,
                                     String displayName,
                                     Resource baseResource,
                                     String[] welcomeFiles,
                                     String... runArgs) throws Exception {
        final WarmupServer server = new WarmupServer(contextPath, displayName, baseResource, welcomeFiles, runArgs);
        server.start();
        return server;
    }

}
 

Эта оболочка анализирует аргументы командной строки и создает обработчик Jetty и соединители HTTP и (или) HTTPS, используя предоставленные аргументы командной строки.

И класс реализации жизненного цикла simple Spring:

 @RequiredArgsConstructor
class WarmupStopLifecycle implements SmartLifecycle {

    private static final Logger logger = LogManager.getFormatterLogger();

    private final Server warmupServer;
    private volatile boolean isRunning;

    @Override
    public void start() {
        try {
            warmupServer.stop();
            isRunning = true;
        } catch (Exception e) {
            logger.error("Failed to stop warmup server", e);
            throw new RuntimeException("Failed to stop warmup server", e);
        }
    }

    @Override
    public void stop() {
    }

    @Override
    public boolean isRunning() {
        return isRunning;
    }

    /**
     * Returns phase of this lifecycle.
     * A phase MUST be before the Spring web server starts.
     * See {@code org.springframework.boot.web.servlet.context.WebServerStartStopLifecycle} phase.
     */
    @Override
    public int getPhase() {
        return Integer.MAX_VALUE - 2;
    }
}
 

Итак, использование этого:

 @SpringBootApplication
public class SpringApplication {

    public static void main(String[] args) throws Exception {
        final WarmupServer warmupServer = WarmupServer.start(
                "/my_context_path", "My Warmup server handler", args);

        new SpringApplicationBuilder()
                .sources(SpringApplication.class)
                .initializers(warmupServer::registerWarmupServerStopLifecycle)
                .run(args);
    }
}
 

WarmupServer запускается сразу после запуска приложения и будет остановлен перед запуском веб-сервера Spring.