diff --git a/compose/compose.yml b/compose/compose.yml index a45f9677d..06f7d5fd4 100644 --- a/compose/compose.yml +++ b/compose/compose.yml @@ -67,7 +67,7 @@ services: environment: JAVA_OPTS: "${JAVA_OPTS_CONFIG}" SPRING_PROFILES_ACTIVE: "${CONFIG_SERVER_DEFAULT_PROFILES}" - restart: unless-stopped + # restart: unless-stopped volumes: # override with the local copy to test config changes during development - config:/etc/geoserver diff --git a/src/apps/base-images/jre/Dockerfile b/src/apps/base-images/jre/Dockerfile index f9b747bcb..addc775d4 100644 --- a/src/apps/base-images/jre/Dockerfile +++ b/src/apps/base-images/jre/Dockerfile @@ -2,7 +2,20 @@ FROM eclipse-temurin:21-jre LABEL maintainer="GeoServer PSC " -ENV JAVA_TOOL_OPTIONS= +# default JVM parameters https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html +# to add JVM parameters use the JAVA_OPTS env variable preferrably +ENV DEFAULT_JAVA_TOOL_OPTIONS="\ +--add-opens=java.base/java.lang=ALL-UNNAMED \ +--add-opens=java.base/java.util=ALL-UNNAMED \ +--add-opens=java.base/java.lang.reflect=ALL-UNNAMED \ +--add-opens=java.base/java.text=ALL-UNNAMED \ +--add-opens=java.desktop/java.awt.font=ALL-UNNAMED \ +--add-opens=java.desktop/sun.awt.image=ALL-UNNAMED \ +--add-opens=java.desktop/sun.java2d.pipe=ALL-UNNAMED \ +--add-opens=java.naming/com.sun.jndi.ldap=ALL-UNNAMED \ +-Djava.awt.headless=true" + +ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS}" ENV JAVA_OPTS= # Install the system CA certificates for the JVM :wqnow that we're root diff --git a/src/apps/base-images/spring-boot/Dockerfile b/src/apps/base-images/spring-boot/Dockerfile index bbffd5b53..83b5a20bf 100644 --- a/src/apps/base-images/spring-boot/Dockerfile +++ b/src/apps/base-images/spring-boot/Dockerfile @@ -18,20 +18,6 @@ RUN mkdir -p /opt/app/bin WORKDIR /opt/app/bin -# default JVM parameters https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html -# to add JVM parameters use the JAVA_OPTS env variable preferrably -ENV JAVA_TOOL_OPTIONS="\ ---add-opens=java.base/java.lang=ALL-UNNAMED \ ---add-opens=java.base/java.util=ALL-UNNAMED \ ---add-opens=java.base/java.lang.reflect=ALL-UNNAMED \ ---add-opens=java.base/java.text=ALL-UNNAMED \ ---add-opens=java.desktop/java.awt.font=ALL-UNNAMED \ ---add-opens=java.desktop/sun.awt.image=ALL-UNNAMED \ ---add-opens=java.desktop/sun.java2d.pipe=ALL-UNNAMED \ ---add-opens=java.naming/com.sun.jndi.ldap=ALL-UNNAMED \ --Djava.awt.headless=true" - -ENV JAVA_OPTS= EXPOSE 8080 EXPOSE 8081 diff --git a/src/apps/geoserver/gwc/Dockerfile b/src/apps/geoserver/gwc/Dockerfile index 35f9293be..4cb66258d 100644 --- a/src/apps/geoserver/gwc/Dockerfile +++ b/src/apps/geoserver/gwc/Dockerfile @@ -20,3 +20,15 @@ COPY --from=builder spring-boot-loader/ ./ #see https://github.com/moby/moby/issues/37965 RUN true COPY --from=builder application/ ./ + +# Execute the CDS training run +RUN mkdir /tmp/tmpdatadir +RUN java \ +-XX:ArchiveClassesAtExit=application.jsa \ +-Dspring.context.exit=onRefreshed \ +-Dspring.profiles.active=standalone,datadir,offline \ +-Dgeosever.backend.data-directory.location=/tmp/tmpdatadir \ +org.springframework.boot.loader.JarLauncher +RUN rm -rf /tmp/* + +ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa" diff --git a/src/apps/geoserver/restconfig/Dockerfile b/src/apps/geoserver/restconfig/Dockerfile index 35f9293be..4cb66258d 100644 --- a/src/apps/geoserver/restconfig/Dockerfile +++ b/src/apps/geoserver/restconfig/Dockerfile @@ -20,3 +20,15 @@ COPY --from=builder spring-boot-loader/ ./ #see https://github.com/moby/moby/issues/37965 RUN true COPY --from=builder application/ ./ + +# Execute the CDS training run +RUN mkdir /tmp/tmpdatadir +RUN java \ +-XX:ArchiveClassesAtExit=application.jsa \ +-Dspring.context.exit=onRefreshed \ +-Dspring.profiles.active=standalone,datadir,offline \ +-Dgeosever.backend.data-directory.location=/tmp/tmpdatadir \ +org.springframework.boot.loader.JarLauncher +RUN rm -rf /tmp/* + +ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa" diff --git a/src/apps/geoserver/wcs/Dockerfile b/src/apps/geoserver/wcs/Dockerfile index 35f9293be..4cb66258d 100644 --- a/src/apps/geoserver/wcs/Dockerfile +++ b/src/apps/geoserver/wcs/Dockerfile @@ -20,3 +20,15 @@ COPY --from=builder spring-boot-loader/ ./ #see https://github.com/moby/moby/issues/37965 RUN true COPY --from=builder application/ ./ + +# Execute the CDS training run +RUN mkdir /tmp/tmpdatadir +RUN java \ +-XX:ArchiveClassesAtExit=application.jsa \ +-Dspring.context.exit=onRefreshed \ +-Dspring.profiles.active=standalone,datadir,offline \ +-Dgeosever.backend.data-directory.location=/tmp/tmpdatadir \ +org.springframework.boot.loader.JarLauncher +RUN rm -rf /tmp/* + +ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa" diff --git a/src/apps/geoserver/webui/Dockerfile b/src/apps/geoserver/webui/Dockerfile index 35f9293be..4cb66258d 100644 --- a/src/apps/geoserver/webui/Dockerfile +++ b/src/apps/geoserver/webui/Dockerfile @@ -20,3 +20,15 @@ COPY --from=builder spring-boot-loader/ ./ #see https://github.com/moby/moby/issues/37965 RUN true COPY --from=builder application/ ./ + +# Execute the CDS training run +RUN mkdir /tmp/tmpdatadir +RUN java \ +-XX:ArchiveClassesAtExit=application.jsa \ +-Dspring.context.exit=onRefreshed \ +-Dspring.profiles.active=standalone,datadir,offline \ +-Dgeosever.backend.data-directory.location=/tmp/tmpdatadir \ +org.springframework.boot.loader.JarLauncher +RUN rm -rf /tmp/* + +ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa" diff --git a/src/apps/geoserver/wfs/Dockerfile b/src/apps/geoserver/wfs/Dockerfile index 35f9293be..4cb66258d 100644 --- a/src/apps/geoserver/wfs/Dockerfile +++ b/src/apps/geoserver/wfs/Dockerfile @@ -20,3 +20,15 @@ COPY --from=builder spring-boot-loader/ ./ #see https://github.com/moby/moby/issues/37965 RUN true COPY --from=builder application/ ./ + +# Execute the CDS training run +RUN mkdir /tmp/tmpdatadir +RUN java \ +-XX:ArchiveClassesAtExit=application.jsa \ +-Dspring.context.exit=onRefreshed \ +-Dspring.profiles.active=standalone,datadir,offline \ +-Dgeosever.backend.data-directory.location=/tmp/tmpdatadir \ +org.springframework.boot.loader.JarLauncher +RUN rm -rf /tmp/* + +ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa" diff --git a/src/apps/geoserver/wms/Dockerfile b/src/apps/geoserver/wms/Dockerfile index 35f9293be..4cb66258d 100644 --- a/src/apps/geoserver/wms/Dockerfile +++ b/src/apps/geoserver/wms/Dockerfile @@ -20,3 +20,15 @@ COPY --from=builder spring-boot-loader/ ./ #see https://github.com/moby/moby/issues/37965 RUN true COPY --from=builder application/ ./ + +# Execute the CDS training run +RUN mkdir /tmp/tmpdatadir +RUN java \ +-XX:ArchiveClassesAtExit=application.jsa \ +-Dspring.context.exit=onRefreshed \ +-Dspring.profiles.active=standalone,datadir,offline \ +-Dgeosever.backend.data-directory.location=/tmp/tmpdatadir \ +org.springframework.boot.loader.JarLauncher +RUN rm -rf /tmp/* + +ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa" diff --git a/src/apps/geoserver/wps/Dockerfile b/src/apps/geoserver/wps/Dockerfile index 35f9293be..4cb66258d 100644 --- a/src/apps/geoserver/wps/Dockerfile +++ b/src/apps/geoserver/wps/Dockerfile @@ -20,3 +20,15 @@ COPY --from=builder spring-boot-loader/ ./ #see https://github.com/moby/moby/issues/37965 RUN true COPY --from=builder application/ ./ + +# Execute the CDS training run +RUN mkdir /tmp/tmpdatadir +RUN java \ +-XX:ArchiveClassesAtExit=application.jsa \ +-Dspring.context.exit=onRefreshed \ +-Dspring.profiles.active=standalone,datadir,offline \ +-Dgeosever.backend.data-directory.location=/tmp/tmpdatadir \ +org.springframework.boot.loader.JarLauncher +RUN rm -rf /tmp/* + +ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa" diff --git a/src/apps/infrastructure/admin/Dockerfile b/src/apps/infrastructure/admin/Dockerfile index bb8b79e07..8c40e34c1 100644 --- a/src/apps/infrastructure/admin/Dockerfile +++ b/src/apps/infrastructure/admin/Dockerfile @@ -28,3 +28,12 @@ HEALTHCHECK \ CMD curl -f -s -o /dev/null localhost:8080/actuator/health || exit 1 CMD exec env USER_ID="$(id -u)" USER_GID="$(id -g)" java $JAVA_OPTS org.springframework.boot.loader.JarLauncher + +# Execute the CDS training run +RUN java -XX:ArchiveClassesAtExit=application.jsa \ +-Dspring.context.exit=onRefreshed \ +-Dspring.profiles.active=standalone,offline \ +org.springframework.boot.loader.JarLauncher +RUN rm -rf /tmp/* + +ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa" diff --git a/src/apps/infrastructure/config/Dockerfile b/src/apps/infrastructure/config/Dockerfile index 6a5fadd53..80956b47b 100644 --- a/src/apps/infrastructure/config/Dockerfile +++ b/src/apps/infrastructure/config/Dockerfile @@ -21,7 +21,7 @@ RUN true COPY --from=builder application/ ./ # Either 'git' or 'native'. -ENV SPRING_PROFILES_ACTIVE=native +ENV SPRING_PROFILES_ACTIVE=native,standalone # 'native' profile config, use the default config embedded in the Docker image. # Feel free to override with a mounted volume ENV CONFIG_NATIVE_PATH=/etc/geoserver @@ -36,4 +36,8 @@ ENV CONFIG_GIT_BASEDIR: /tmp/git_config # avoid stack trace due to jgit not being able of creating a .config dir at $HOME ENV XDG_CONFIG_HOME: /tmp +# Execute the CDS training run +RUN java -XX:ArchiveClassesAtExit=application.jsa -Dspring.context.exit=onRefreshed org.springframework.boot.loader.JarLauncher +RUN rm -rf /tmp/* +ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa" diff --git a/src/apps/infrastructure/discovery/Dockerfile b/src/apps/infrastructure/discovery/Dockerfile index 7a3d9206f..64efd18c7 100644 --- a/src/apps/infrastructure/discovery/Dockerfile +++ b/src/apps/infrastructure/discovery/Dockerfile @@ -19,3 +19,12 @@ COPY --from=builder spring-boot-loader/ ./ #see https://github.com/moby/moby/issues/37965 RUN true COPY --from=builder application/ ./ + +# Execute the CDS training run +RUN java -XX:ArchiveClassesAtExit=application.jsa \ +-Dspring.context.exit=onRefreshed \ +-Dspring.profiles.active=standalone \ +org.springframework.boot.loader.JarLauncher +RUN rm -rf /tmp/* + +ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa" diff --git a/src/apps/infrastructure/gateway/Dockerfile b/src/apps/infrastructure/gateway/Dockerfile index 7a3d9206f..64efd18c7 100644 --- a/src/apps/infrastructure/gateway/Dockerfile +++ b/src/apps/infrastructure/gateway/Dockerfile @@ -19,3 +19,12 @@ COPY --from=builder spring-boot-loader/ ./ #see https://github.com/moby/moby/issues/37965 RUN true COPY --from=builder application/ ./ + +# Execute the CDS training run +RUN java -XX:ArchiveClassesAtExit=application.jsa \ +-Dspring.context.exit=onRefreshed \ +-Dspring.profiles.active=standalone \ +org.springframework.boot.loader.JarLauncher +RUN rm -rf /tmp/* + +ENV JAVA_TOOL_OPTIONS="${DEFAULT_JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa" diff --git a/src/starters/spring-boot/src/main/java/org/geoserver/cloud/app/ExitOnApplicationEventAutoConfiguration.java b/src/starters/spring-boot/src/main/java/org/geoserver/cloud/app/ExitOnApplicationEventAutoConfiguration.java new file mode 100644 index 000000000..989882ea6 --- /dev/null +++ b/src/starters/spring-boot/src/main/java/org/geoserver/cloud/app/ExitOnApplicationEventAutoConfiguration.java @@ -0,0 +1,122 @@ +/* + * (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the + * GPL 2.0 license, available at the root application directory. + */ +package org.geoserver.cloud.app; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.event.ApplicationContextEvent; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.core.env.Environment; + +/** + * Allows to pass a JVM argument to exit the application upon specific {@link + * ApplicationContextEvent application events}, mostly useful to start up an application during the + * Docker image build process to create the AppCDS archive. + * + *

Usage: run the application with {@code -Dspring.context.exit=}, where {@code } + * is one of + * + *

+ * + *

Note Spring Boot 3.2 supports {@code spring.context.exit=onRefresh} as of this + * commit, and when we migrate from Spring Boot 2.7 to 3.2+ this will not be necessary most + * probably, although we've added additional events because some applications may fail to start + * without all the machinery in place at different stages. Nonetheless, the new {@code offline} + * embedded spring profile should allow them all to start without spring cloud bus, ACL, etc. + * + * @since 1.9.0 + */ +@AutoConfiguration +@ConditionalOnProperty("spring.context.exit") +@Slf4j +public class ExitOnApplicationEventAutoConfiguration { + + public enum ExitOn { + /** + * The {@link SpringApplication} is starting up and the {@link ApplicationContext} is fully + * prepared but not refreshed. The bean definitions will be loaded and the {@link + * Environment} is ready for use at this stage. + * + * @see ApplicationPreparedEvent + */ + onPrepared, + /** + * {@code ApplicationContext} gets initialized or refreshed + * + * @see ContextRefreshedEvent + */ + onRefreshed, + /** + * {@code ApplicationContext} has been refreshed but before any {@link ApplicationRunner + * application} and {@link CommandLineRunner command line} runners have been called. + * + * @see ApplicationStartedEvent + */ + onStarted, + /** + * Published as late as conceivably possible to indicate that the application is ready to + * service requests. The source of the event is the {@link SpringApplication} itself, but + * beware all initialization steps will have been completed by then. + * + * @see ApplicationReadyEvent + */ + onReady + } + + @Autowired private ApplicationContext appContext; + + @Value("${spring.context.exit}") + ExitOn exitOn; + + @EventListener(ApplicationPreparedEvent.class) + void exitOnApplicationPreparedEvent(ApplicationPreparedEvent event) { + exit(ExitOn.onStarted, event.getApplicationContext()); + } + + @EventListener(ContextRefreshedEvent.class) + void exitOnContextRefreshedEvent(ContextRefreshedEvent event) { + exit(ExitOn.onRefreshed, event.getApplicationContext()); + } + + @EventListener(ApplicationStartedEvent.class) + void exitOnApplicationStartedEvent(ApplicationStartedEvent event) { + exit(ExitOn.onStarted, event.getApplicationContext()); + } + + @EventListener(ApplicationReadyEvent.class) + void exitOnApplicationReadyEvent(ApplicationReadyEvent event) { + exit(ExitOn.onStarted, event.getApplicationContext()); + } + + private void exit(ExitOn ifGiven, ApplicationContext applicationContext) { + if (this.exitOn == ifGiven && applicationContext == this.appContext) { + log.warn("Exiting application, spring.context.exit={}", ifGiven); + try { + ((ConfigurableApplicationContext) applicationContext).close(); + } finally { + System.exit(0); + } + } + } +} diff --git a/src/starters/spring-boot/src/main/resources/META-INF/spring.factories b/src/starters/spring-boot/src/main/resources/META-INF/spring.factories index 7e12ee77a..abc412472 100644 --- a/src/starters/spring-boot/src/main/resources/META-INF/spring.factories +++ b/src/starters/spring-boot/src/main/resources/META-INF/spring.factories @@ -1,3 +1,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.geoserver.cloud.app.StartupLoggerAutoConfiguration,\ -org.geoserver.cloud.app.ServiceIdFilterAutoConfiguration \ No newline at end of file +org.geoserver.cloud.app.ServiceIdFilterAutoConfiguration,\ +org.geoserver.cloud.app.ExitOnApplicationEventAutoConfiguration \ No newline at end of file diff --git a/src/starters/spring-boot/src/main/resources/gs_cloud_bootstrap_profiles.yml b/src/starters/spring-boot/src/main/resources/gs_cloud_bootstrap_profiles.yml index f846e8328..86594bf7a 100644 --- a/src/starters/spring-boot/src/main/resources/gs_cloud_bootstrap_profiles.yml +++ b/src/starters/spring-boot/src/main/resources/gs_cloud_bootstrap_profiles.yml @@ -116,6 +116,17 @@ eureka: defaultZone: ${eureka.server.url} healthcheck: enabled: false # must only be set to true in application.yml, not bootstrap + +--- +spring.config.activate.on-profile: offline +spring: + cloud.config.enabled: false + cloud.config.discovery.enabled: false + cloud.discovery.enabled: false + cloud.bus.enabled: false + eureka.client.enabled: false + +geoserver.acl.enabled: false --- spring.config.activate.on-profile: local # Profile used for local development, so an app launched from the IDE can participate in the cluster.