From f8880cda86ed98083c6714202c7127546b274cdd Mon Sep 17 00:00:00 2001 From: Daniel Kec Date: Tue, 6 Dec 2022 00:54:58 +0100 Subject: [PATCH 01/16] POC Signed-off-by: Daniel Kec --- .../microprofile/hello-world-implicit/pom.xml | 9 ----- .../security/SecurityCdiExtension.java | 33 ++++++++++++++++++- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/examples/microprofile/hello-world-implicit/pom.xml b/examples/microprofile/hello-world-implicit/pom.xml index 6023fd346ed..aa4cab6d93b 100644 --- a/examples/microprofile/hello-world-implicit/pom.xml +++ b/examples/microprofile/hello-world-implicit/pom.xml @@ -74,15 +74,6 @@ - - org.jboss.jandex - jandex-maven-plugin - - - make-index - - - diff --git a/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityCdiExtension.java b/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityCdiExtension.java index ffa6623a3ad..206f1df42bc 100644 --- a/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityCdiExtension.java +++ b/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityCdiExtension.java @@ -19,6 +19,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.atomic.AtomicReference; +import java.util.logging.Level; import java.util.logging.Logger; import io.helidon.common.context.Contexts; @@ -43,6 +44,10 @@ import jakarta.enterprise.inject.spi.BeanManager; import jakarta.enterprise.inject.spi.BeforeBeanDiscovery; import jakarta.enterprise.inject.spi.Extension; +import jdk.crac.CheckpointException; +import jdk.crac.Context; +import jdk.crac.Resource; +import jdk.crac.RestoreException; import static jakarta.interceptor.Interceptor.Priority.LIBRARY_BEFORE; import static jakarta.interceptor.Interceptor.Priority.PLATFORM_BEFORE; @@ -50,7 +55,7 @@ /** * Extension to register bean {@link SecurityProducer}. */ -public class SecurityCdiExtension implements Extension { +public class SecurityCdiExtension implements Extension, Resource{ private static final Logger LOGGER = Logger.getLogger(SecurityCdiExtension.class.getName()); private final AtomicReference security = new AtomicReference<>(); @@ -58,6 +63,13 @@ public class SecurityCdiExtension implements Extension { private Security.Builder securityBuilder = Security.builder(); private Config config; + private final CompletableFuture> restored = new CompletableFuture<>(); + + + public SecurityCdiExtension() { + jdk.crac.Core.getGlobalContext().register(this); + } + private void registerBean(@Observes BeforeBeanDiscovery abd) { abd.addAnnotatedType(SecurityProducer.class, "helidon-security-producer") .add(ApplicationScoped.Literal.INSTANCE); @@ -92,6 +104,15 @@ private void registerSecurity(@Observes @Priority(LIBRARY_BEFORE) @Initialized(A securityBuilder.addAuthorizationProvider(AbacProvider.create()); } + try { + jdk.crac.Core.checkpointRestore(); + } catch (RestoreException | CheckpointException e) { + LOGGER.log(Level.INFO, "CRaC snapshot load wasn't successful!", e); + } +// if (Boolean.parseBoolean(System.getProperty("crac", "false"))) + restored.join(); + LOGGER.log(Level.INFO, "CRaC snapshot restored!"); + Security tmpSecurity = securityBuilder.build(); // free it and make sure we fail if somebody wants to update security afterwards securityBuilder = null; @@ -161,4 +182,14 @@ public Security.Builder securityBuilder() { public Optional security() { return Optional.ofNullable(security.get()); } + + @Override + public void beforeCheckpoint(Context context) throws Exception { + + } + + @Override + public void afterRestore(Context context) throws Exception { + restored.complete(context); + } } From e1a05b0a66ca535ed3a081680748be66d72479b8 Mon Sep 17 00:00:00 2001 From: Daniel Kec Date: Tue, 6 Dec 2022 21:45:46 +0100 Subject: [PATCH 02/16] CRaC POC Signed-off-by: Daniel Kec --- .../hello-world-implicit/Dockerfile.crac | 41 ++++++++++++++++ .../hello-world-implicit/README.md | 49 +++++++++++++++++++ .../hello-world-implicit/runOnCRaC.sh | 32 ++++++++++++ .../META-INF/microprofile-config.properties | 1 + .../security/SecurityCdiExtension.java | 32 +----------- .../server/ServerCdiExtension.java | 45 +++++++++++++++-- 6 files changed, 166 insertions(+), 34 deletions(-) create mode 100644 examples/microprofile/hello-world-implicit/Dockerfile.crac create mode 100644 examples/microprofile/hello-world-implicit/runOnCRaC.sh diff --git a/examples/microprofile/hello-world-implicit/Dockerfile.crac b/examples/microprofile/hello-world-implicit/Dockerfile.crac new file mode 100644 index 00000000000..0d8479fcf43 --- /dev/null +++ b/examples/microprofile/hello-world-implicit/Dockerfile.crac @@ -0,0 +1,41 @@ +# +# Copyright (c) 2022 Oracle and/or its affiliates. +# +# 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 +# +# http://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. +# +# +FROM ubuntu:22.04 as ubuntu-crac + +WORKDIR /usr/share + +ENV CRAC_ARTEFACT=openjdk-17-crac+3_linux-x64 +ENV JAVA_HOME=/usr/share/$CRAC_ARTEFACT + +# Install CRaC +RUN apt-get -qq update && apt-get install -y wget \ +&& wget -q https://github.com/CRaC/openjdk-builds/releases/download/17-crac%2B3/$CRAC_ARTEFACT.tar.gz \ +&& tar zxf $CRAC_ARTEFACT.tar.gz \ +&& ln -s $JAVA_HOME/bin/java /bin/ + +FROM ubuntu-crac +WORKDIR /helidon + +ADD target/*.jar . +ADD target/libs libs +ADD runOnCRaC.sh . +RUN chmod +x ./runOnCRaC.sh + +ENTRYPOINT ["./runOnCRaC.sh"] + +EXPOSE 7001 + diff --git a/examples/microprofile/hello-world-implicit/README.md b/examples/microprofile/hello-world-implicit/README.md index 393607f09da..5912619fcdc 100644 --- a/examples/microprofile/hello-world-implicit/README.md +++ b/examples/microprofile/hello-world-implicit/README.md @@ -18,3 +18,52 @@ curl -X GET http://localhost:7001/helloworld curl -X GET http://localhost:7001/helloworld/earth curl -X GET http://localhost:7001/another ``` + +## CRaC + +```bash +docker buildx build -t crac-helloworld . -f Dockerfile.crac +# First time ran, checkpoint is created +docker run -d --privileged -p 7001:7001 --name crac-helloworld crac-helloworld +docker stop crac-helloworld +# Second time starting from checkpoint +docker start crac-helloworld +docker logs crac-helloworld +``` + +``` +=== Creating CRaC checkpoint === +Checking CRIU compatibility(don't forget --privileged): +Warn (criu/kerndat.c:1470): CRIU was built without libnftables support +Looks good. +[Tue Dec 06 20:39:31 GMT 2022] INFO: io.helidon.common.LogConfig doConfigureLogging - Logging at initialization configured using classpath: /logging.properties +[Tue Dec 06 20:39:31 GMT 2022] INFO: org.jboss.weld.bootstrap.WeldStartup - WELD-000900: 4.0.2 (Final) +[Tue Dec 06 20:39:32 GMT 2022] INFO: io.helidon.microprofile.openapi.OpenApiCdiExtension - OpenAPI support could not locate the Jandex index file META-INF/jandex.idx so will build an in-memory index. +This slows your app start-up and, depending on CDI configuration, might omit some type information needed for a complete OpenAPI document. +Consider using the Jandex maven plug-in during your build to create the index and add it to your app. +[Tue Dec 06 20:39:32 GMT 2022] FINE: io.helidon.microprofile.config.ConfigCdiExtension - ConfigCdiExtension instantiated +[Tue Dec 06 20:39:32 GMT 2022] INFO: org.jboss.weld.environment.deployment.discovery.DiscoveryStrategyFactory create - WELD-ENV-000020: Using jandex for bean discovery +[Tue Dec 06 20:39:32 GMT 2022] INFO: org.jboss.weld.bootstrap.WeldStartup startContainer - WELD-000101: Transactional services not available. Injection of @Inject UserTransaction not available. Transactional observers will be invoked synchronously. +[Tue Dec 06 20:39:32 GMT 2022] INFO: org.jboss.weld.event.ExtensionObserverMethodImpl checkRequiredTypeAnnotations - WELD-000411: Observer method [BackedAnnotatedMethod] private io.helidon.microprofile.openapi.OpenApiCdiExtension.processAnnotatedType(@Observes ProcessAnnotatedType) receives events for all annotated types. Consider restricting events using @WithAnnotations or a generic type with bounds. +[Tue Dec 06 20:39:32 GMT 2022] INFO: org.jboss.weld.event.ExtensionObserverMethodImpl checkRequiredTypeAnnotations - WELD-000411: Observer method [BackedAnnotatedMethod] public org.glassfish.jersey.ext.cdi1x.internal.ProcessAllAnnotatedTypes.processAnnotatedType(@Observes ProcessAnnotatedType, BeanManager) receives events for all annotated types. Consider restricting events using @WithAnnotations or a generic type with bounds. +[Tue Dec 06 20:39:32 GMT 2022] WARNING: org.glassfish.jersey.ext.cdi1x.internal.CdiComponentProvider isJerseyOrDependencyType - Class class [I has null package +[Tue Dec 06 20:39:32 GMT 2022] INFO: io.helidon.tracing.tracerresolver.TracerResolverBuilder build - TracerResolver not configured, tracing is disabled +[Tue Dec 06 20:39:32 GMT 2022] INFO: io.helidon.microprofile.security.SecurityCdiExtension registerSecurity - Authentication provider is missing from security configuration, but security extension for microprofile is enabled (requires providers configuration at key security.providers). Security will not have any valid authentication provider +[Tue Dec 06 20:39:32 GMT 2022] INFO: io.helidon.microprofile.security.SecurityCdiExtension registerSecurity - Authorization provider is missing from security configuration, but security extension for microprofile is enabled (requires providers configuration at key security.providers). ABAC provider is configured for authorization. +[Tue Dec 06 20:39:32 GMT 2022] INFO: io.helidon.microprofile.server.ServerCdiExtension addApplication - Registering JAX-RS Application: HelidonMP +CR: Checkpoint ... +./runOnCRaC.sh: line 30: 18 Killed $JAVA_HOME/bin/java -XX:CRaCCheckpointTo=cr -jar ./*.jar +[Tue Dec 06 20:39:33 GMT 2022] INFO: io.helidon.microprofile.server.ServerCdiExtension afterRestore - CRaC snapshot restored! +[Tue Dec 06 20:39:33 GMT 2022] INFO: io.helidon.webserver.NettyWebServer lambda$start$8 - Channel '@default' started: [id: 0x5158f8bb, L:/0.0.0.0:7001] +[Tue Dec 06 20:39:33 GMT 2022] INFO: io.helidon.microprofile.server.ServerCdiExtension startServer - Server started on http://localhost:7001 (and all other host addresses) in 55 milliseconds (since CRaC restore). +[Tue Dec 06 20:39:34 GMT 2022] INFO: io.helidon.common.HelidonFeatures features - Helidon MP 3.0.3-SNAPSHOT features: [CDI, Config, Fault Tolerance, Health, JAX-RS, Metrics, Open API, REST Client, Security, Server, Tracing] +[Tue Dec 06 20:39:41 GMT 2022] INFO: io.helidon.webserver.NettyWebServer lambda$start$6 - Channel '@default' closed: [id: 0x5158f8bb, L:/0.0.0.0:7001] +[Tue Dec 06 20:39:41 GMT 2022] INFO: io.helidon.microprofile.server.ServerCdiExtension doStop - Server stopped in 8 milliseconds. +[Tue Dec 06 20:39:41 GMT 2022] INFO: io.helidon.microprofile.cdi.HelidonContainerImpl$HelidonCdi close - WELD-ENV-002001: Weld SE container e6c58ecb-e5cb-4e0e-83f6-d1ae6b1081d0 shut down +=== Starting directly from CRaC checkpoint === +[Tue Dec 06 20:39:46 GMT 2022] INFO: io.helidon.microprofile.server.ServerCdiExtension afterRestore - CRaC snapshot restored! +[Tue Dec 06 20:39:46 GMT 2022] INFO: io.helidon.webserver.NettyWebServer lambda$start$8 - Channel '@default' started: [id: 0x5158f8bb, L:/0.0.0.0:7001] +[Tue Dec 06 20:39:46 GMT 2022] INFO: io.helidon.microprofile.server.ServerCdiExtension startServer - Server started on http://localhost:7001 (and all other host addresses) in 54 milliseconds (since CRaC restore). +[Tue Dec 06 20:39:46 GMT 2022] INFO: io.helidon.common.HelidonFeatures features - Helidon MP 3.0.3-SNAPSHOT features: [CDI, Config, Fault Tolerance, Health, JAX-RS, Metrics, Open API, REST Client, Security, Server, Tracing] +kec@romulus:~/idp/ora/helidon/helidon3/examples/microprofile/hello-world-implicit$ docker start crac-helloworld +``` \ No newline at end of file diff --git a/examples/microprofile/hello-world-implicit/runOnCRaC.sh b/examples/microprofile/hello-world-implicit/runOnCRaC.sh new file mode 100644 index 00000000000..a4625678838 --- /dev/null +++ b/examples/microprofile/hello-world-implicit/runOnCRaC.sh @@ -0,0 +1,32 @@ +#!/bin/bash -e + +# +# Copyright (c) 2022 Oracle and/or its affiliates. +# +# 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 +# +# http://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. +# +# + +if [ -d "./cr" ]; +then + echo "=== Starting directly from CRaC checkpoint ===" +else + echo "=== Creating CRaC checkpoint ===" + echo "Checking CRIU compatibility(don't forget --privileged):" + $JAVA_HOME/lib/criu check + set +e + $JAVA_HOME/bin/java -XX:CRaCCheckpointTo=cr -jar ./*.jar + set -e +fi +exec $JAVA_HOME/bin/java -XX:CRaCRestoreFrom=cr + diff --git a/examples/microprofile/hello-world-implicit/src/main/resources/META-INF/microprofile-config.properties b/examples/microprofile/hello-world-implicit/src/main/resources/META-INF/microprofile-config.properties index bbae60a1ba5..55832bf20f6 100644 --- a/examples/microprofile/hello-world-implicit/src/main/resources/META-INF/microprofile-config.properties +++ b/examples/microprofile/hello-world-implicit/src/main/resources/META-INF/microprofile-config.properties @@ -20,6 +20,7 @@ app.uri=https://www.example.com app.someInt=147 app.ints=12,12,32,12,44 +server.host=0.0.0.0 server.port=7001 # Enable the optional MicroProfile Metrics REST.request metrics diff --git a/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityCdiExtension.java b/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityCdiExtension.java index 206f1df42bc..975772568b4 100644 --- a/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityCdiExtension.java +++ b/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityCdiExtension.java @@ -44,10 +44,6 @@ import jakarta.enterprise.inject.spi.BeanManager; import jakarta.enterprise.inject.spi.BeforeBeanDiscovery; import jakarta.enterprise.inject.spi.Extension; -import jdk.crac.CheckpointException; -import jdk.crac.Context; -import jdk.crac.Resource; -import jdk.crac.RestoreException; import static jakarta.interceptor.Interceptor.Priority.LIBRARY_BEFORE; import static jakarta.interceptor.Interceptor.Priority.PLATFORM_BEFORE; @@ -55,7 +51,7 @@ /** * Extension to register bean {@link SecurityProducer}. */ -public class SecurityCdiExtension implements Extension, Resource{ +public class SecurityCdiExtension implements Extension { private static final Logger LOGGER = Logger.getLogger(SecurityCdiExtension.class.getName()); private final AtomicReference security = new AtomicReference<>(); @@ -63,13 +59,6 @@ public class SecurityCdiExtension implements Extension, Resource{ private Security.Builder securityBuilder = Security.builder(); private Config config; - private final CompletableFuture> restored = new CompletableFuture<>(); - - - public SecurityCdiExtension() { - jdk.crac.Core.getGlobalContext().register(this); - } - private void registerBean(@Observes BeforeBeanDiscovery abd) { abd.addAnnotatedType(SecurityProducer.class, "helidon-security-producer") .add(ApplicationScoped.Literal.INSTANCE); @@ -104,15 +93,6 @@ private void registerSecurity(@Observes @Priority(LIBRARY_BEFORE) @Initialized(A securityBuilder.addAuthorizationProvider(AbacProvider.create()); } - try { - jdk.crac.Core.checkpointRestore(); - } catch (RestoreException | CheckpointException e) { - LOGGER.log(Level.INFO, "CRaC snapshot load wasn't successful!", e); - } -// if (Boolean.parseBoolean(System.getProperty("crac", "false"))) - restored.join(); - LOGGER.log(Level.INFO, "CRaC snapshot restored!"); - Security tmpSecurity = securityBuilder.build(); // free it and make sure we fail if somebody wants to update security afterwards securityBuilder = null; @@ -182,14 +162,4 @@ public Security.Builder securityBuilder() { public Optional security() { return Optional.ofNullable(security.get()); } - - @Override - public void beforeCheckpoint(Context context) throws Exception { - - } - - @Override - public void afterRestore(Context context) throws Exception { - restored.complete(context); - } } diff --git a/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java b/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java index 23f4cfd0652..9273cd7b57f 100644 --- a/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java +++ b/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; @@ -69,6 +70,9 @@ import jakarta.enterprise.inject.spi.ProcessProducerMethod; import jakarta.ws.rs.core.Application; import jakarta.ws.rs.ext.ParamConverterProvider; +import jdk.crac.CheckpointException; +import jdk.crac.Resource; +import jdk.crac.RestoreException; import org.eclipse.microprofile.config.ConfigProvider; import org.glassfish.jersey.internal.inject.Bindings; import org.glassfish.jersey.internal.inject.InjectionManager; @@ -81,7 +85,7 @@ /** * Extension to handle web server configuration and lifecycle. */ -public class ServerCdiExtension implements Extension { +public class ServerCdiExtension implements Extension, Resource { private static final Logger LOGGER = Logger.getLogger(ServerCdiExtension.class.getName()); private static final Logger STARTUP_LOGGER = Logger.getLogger("io.helidon.microprofile.startup.server"); private static final AtomicBoolean IN_PROGRESS_OR_RUNNING = new AtomicBoolean(); @@ -111,6 +115,12 @@ public class ServerCdiExtension implements Extension { = Collections.synchronizedMap(new IdentityHashMap<>()); private final Set routingsWithKPIMetrics = new HashSet<>(); + private long crac_restore_time = -1; + private final CompletableFuture> restored = new CompletableFuture<>(); + + public ServerCdiExtension() { + jdk.crac.Core.getGlobalContext().register(this); + } private void prepareRuntime(@Observes @RuntimeStart Config config) { serverBuilder.config(config.get("server")); @@ -198,7 +208,18 @@ private void startServer(@Observes @Priority(PLATFORM_AFTER + 100) @Initialized( webserver = serverBuilder.build(); try { - webserver.start().toCompletableFuture().get(); + jdk.crac.Core.checkpointRestore(); + } catch (RestoreException e) { + LOGGER.log(Level.INFO, "CRaC restore wasn't successful!", e); + restored.complete(null); + } catch (CheckpointException e) { + LOGGER.log(Level.INFO, "CRaC checkpoint creation wasn't successful!", e); + restored.complete(null); + } + restored.join(); + + try { + webserver.start().await(); started = true; } catch (Exception e) { throw new DeploymentException("Failed to start webserver", e); @@ -212,9 +233,14 @@ private void startServer(@Observes @Priority(PLATFORM_AFTER + 100) @Initialized( String host = "0.0.0.0".equals(listenHost) ? "localhost" : listenHost; String note = "0.0.0.0".equals(listenHost) ? " (and all other host addresses)" : ""; + + String startupTimeReport = crac_restore_time == -1 + ? " in " + initializationElapsedTime + " milliseconds (since JVM startup). " + : " in " + (System.currentTimeMillis() - crac_restore_time) + " milliseconds (since CRaC restore)."; + LOGGER.info(() -> "Server started on " + protocol + "://" + host + ":" + port - + note + " in " + initializationElapsedTime + " milliseconds (since JVM startup)."); + + note + startupTimeReport); // this is not needed at runtime, collect garbage serverBuilder = null; @@ -591,4 +617,17 @@ public void basePath(String basePath) { void listenHost(String listenHost) { this.listenHost = listenHost; } + + @Override + public void beforeCheckpoint(jdk.crac.Context context) throws Exception { + + } + + @Override + public void afterRestore(jdk.crac.Context context) throws Exception { + crac_restore_time = System.currentTimeMillis(); + LOGGER.log(Level.INFO, "CRaC snapshot restored!"); + restored.complete(context); + } } + From 00df07c6241c6688bd6a1aabc6a796196146655b Mon Sep 17 00:00:00 2001 From: Daniel Kec Date: Thu, 8 Dec 2022 17:05:44 +0100 Subject: [PATCH 03/16] Workaround for: Error (criu/cr-dump.c:203): 18 has rseq but kernel lacks get_rseq_conf feature Signed-off-by: Daniel Kec --- dependencies/pom.xml | 6 ++++ .../hello-world-implicit/Dockerfile.crac | 4 +-- .../hello-world-implicit/README.md | 3 +- .../hello-world-implicit/runOnCRaC.sh | 6 ++++ microprofile/server/pom.xml | 4 +++ .../server/ServerCdiExtension.java | 30 +++++++++++-------- .../server/src/main/java/module-info.java | 1 + 7 files changed, 38 insertions(+), 16 deletions(-) diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 6d3cd86a4bb..eb6f5f611dd 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -58,6 +58,7 @@ 2.9.0 1.49.2 31.1-jre + 0.1.3 2.1.212 1.3 6.1.4.Final @@ -823,6 +824,11 @@ jakarta.validation-api ${version.lib.jakarta.validation-api} + + io.github.crac + org-crac + ${version.lib.crac} + com.h2database h2 diff --git a/examples/microprofile/hello-world-implicit/Dockerfile.crac b/examples/microprofile/hello-world-implicit/Dockerfile.crac index 0d8479fcf43..39d48ec60fb 100644 --- a/examples/microprofile/hello-world-implicit/Dockerfile.crac +++ b/examples/microprofile/hello-world-implicit/Dockerfile.crac @@ -22,9 +22,9 @@ ENV CRAC_ARTEFACT=openjdk-17-crac+3_linux-x64 ENV JAVA_HOME=/usr/share/$CRAC_ARTEFACT # Install CRaC -RUN apt-get -qq update && apt-get install -y wget \ +RUN apt-get -qq update && apt-get install -y wget sudo \ && wget -q https://github.com/CRaC/openjdk-builds/releases/download/17-crac%2B3/$CRAC_ARTEFACT.tar.gz \ -&& tar zxf $CRAC_ARTEFACT.tar.gz \ +&& sudo tar zxf $CRAC_ARTEFACT.tar.gz \ && ln -s $JAVA_HOME/bin/java /bin/ FROM ubuntu-crac diff --git a/examples/microprofile/hello-world-implicit/README.md b/examples/microprofile/hello-world-implicit/README.md index 5912619fcdc..21f5b0c47cb 100644 --- a/examples/microprofile/hello-world-implicit/README.md +++ b/examples/microprofile/hello-world-implicit/README.md @@ -22,7 +22,8 @@ curl -X GET http://localhost:7001/another ## CRaC ```bash -docker buildx build -t crac-helloworld . -f Dockerfile.crac +mvn clean package +docker build -t crac-helloworld . -f Dockerfile.crac # First time ran, checkpoint is created docker run -d --privileged -p 7001:7001 --name crac-helloworld crac-helloworld docker stop crac-helloworld diff --git a/examples/microprofile/hello-world-implicit/runOnCRaC.sh b/examples/microprofile/hello-world-implicit/runOnCRaC.sh index a4625678838..3b1663b339a 100644 --- a/examples/microprofile/hello-world-implicit/runOnCRaC.sh +++ b/examples/microprofile/hello-world-implicit/runOnCRaC.sh @@ -17,9 +17,15 @@ # # +# Workaround for https://github.com/checkpoint-restore/criu/issues/1696 +# see https://github.com/checkpoint-restore/criu/pull/1706 +export GLIBC_TUNABLES=glibc.pthread.rseq=0 + + if [ -d "./cr" ]; then echo "=== Starting directly from CRaC checkpoint ===" + #cat ./cr/dump4.log else echo "=== Creating CRaC checkpoint ===" echo "Checking CRIU compatibility(don't forget --privileged):" diff --git a/microprofile/server/pom.xml b/microprofile/server/pom.xml index 3da6307de9a..2f2773e2941 100644 --- a/microprofile/server/pom.xml +++ b/microprofile/server/pom.xml @@ -44,6 +44,10 @@ provided true + + io.github.crac + org-crac + io.helidon.microprofile.cdi helidon-microprofile-cdi diff --git a/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java b/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java index 9273cd7b57f..38d8a4b6d55 100644 --- a/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java +++ b/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java @@ -70,9 +70,10 @@ import jakarta.enterprise.inject.spi.ProcessProducerMethod; import jakarta.ws.rs.core.Application; import jakarta.ws.rs.ext.ParamConverterProvider; -import jdk.crac.CheckpointException; -import jdk.crac.Resource; -import jdk.crac.RestoreException; +import org.crac.CheckpointException; +import org.crac.Core; +import org.crac.Resource; +import org.crac.RestoreException; import org.eclipse.microprofile.config.ConfigProvider; import org.glassfish.jersey.internal.inject.Bindings; import org.glassfish.jersey.internal.inject.InjectionManager; @@ -116,10 +117,10 @@ public class ServerCdiExtension implements Extension, Resource { private final Set routingsWithKPIMetrics = new HashSet<>(); private long crac_restore_time = -1; - private final CompletableFuture> restored = new CompletableFuture<>(); + private final CompletableFuture> restored = new CompletableFuture<>(); public ServerCdiExtension() { - jdk.crac.Core.getGlobalContext().register(this); + Core.getGlobalContext().register(this); } private void prepareRuntime(@Observes @RuntimeStart Config config) { @@ -208,15 +209,16 @@ private void startServer(@Observes @Priority(PLATFORM_AFTER + 100) @Initialized( webserver = serverBuilder.build(); try { - jdk.crac.Core.checkpointRestore(); + Core.checkpointRestore(); } catch (RestoreException e) { LOGGER.log(Level.INFO, "CRaC restore wasn't successful!", e); - restored.complete(null); +// restored.complete(null); } catch (CheckpointException e) { LOGGER.log(Level.INFO, "CRaC checkpoint creation wasn't successful!", e); - restored.complete(null); +// restored.complete(null); + System.exit(0); } - restored.join(); +// restored.join(); try { webserver.start().await(); @@ -619,15 +621,17 @@ void listenHost(String listenHost) { } @Override - public void beforeCheckpoint(jdk.crac.Context context) throws Exception { - + public void beforeCheckpoint(org.crac.Context context) throws Exception { + LOGGER.log(Level.INFO, "Creating CRaC snapshot after " + + ManagementFactory.getRuntimeMXBean().getUptime() + + "ms of runtime."); } @Override - public void afterRestore(jdk.crac.Context context) throws Exception { + public void afterRestore(org.crac.Context context) throws Exception { crac_restore_time = System.currentTimeMillis(); LOGGER.log(Level.INFO, "CRaC snapshot restored!"); - restored.complete(context); +// restored.complete(context); } } diff --git a/microprofile/server/src/main/java/module-info.java b/microprofile/server/src/main/java/module-info.java index 9b14ebc009b..5379f10cfe6 100644 --- a/microprofile/server/src/main/java/module-info.java +++ b/microprofile/server/src/main/java/module-info.java @@ -42,6 +42,7 @@ requires java.management; requires microprofile.config.api; requires static io.helidon.config.metadata; + requires org.crac; exports io.helidon.microprofile.server; From dc26e26a4abca6708477aa8d3d6f35d7818a0df5 Mon Sep 17 00:00:00 2001 From: Daniel Kec Date: Fri, 9 Dec 2022 10:44:28 +0100 Subject: [PATCH 04/16] Cleanup Signed-off-by: Daniel Kec --- .../Dockerfile.buildtime_crac} | 22 +--- examples/crac/Dockerfile.native_image | 17 +++ .../Dockerfile.runtime_crac} | 11 +- examples/crac/README.md | 28 +++++ examples/crac/pom.xml | 79 ++++++++++++ examples/crac/runtimeCRaC.sh | 43 +++++++ .../helloworld/implicit/AnotherResource.java | 107 +++++++++++++++++ .../implicit/HelloWorldResource.java | 113 ++++++++++++++++++ .../implicit/cdi/LoggerQualifier.java | 34 ++++++ .../helloworld/implicit/cdi/RequestId.java | 33 +++++ .../implicit/cdi/RequestIdProducer.java | 27 +++++ .../implicit/cdi/ResourceProducer.java | 54 +++++++++ .../helloworld/implicit/cdi/package-info.java | 20 ++++ .../helloworld/implicit/package-info.java | 20 ++++ .../src/main/resources/META-INF/beans.xml | 25 ++++ .../META-INF/microprofile-config.properties | 27 +++++ .../src/main/resources/logging.properties | 20 ++++ .../implicit/ImplicitHelloWorldTest.java | 59 +++++++++ .../hello-world-implicit/README.md | 50 -------- .../microprofile/hello-world-implicit/pom.xml | 9 ++ .../META-INF/microprofile-config.properties | 1 - .../security/SecurityCdiExtension.java | 1 - .../server/ServerCdiExtension.java | 22 ++-- 23 files changed, 732 insertions(+), 90 deletions(-) rename examples/{microprofile/hello-world-implicit/runOnCRaC.sh => crac/Dockerfile.buildtime_crac} (51%) create mode 100644 examples/crac/Dockerfile.native_image rename examples/{microprofile/hello-world-implicit/Dockerfile.crac => crac/Dockerfile.runtime_crac} (84%) create mode 100644 examples/crac/README.md create mode 100644 examples/crac/pom.xml create mode 100644 examples/crac/runtimeCRaC.sh create mode 100644 examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/AnotherResource.java create mode 100644 examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/HelloWorldResource.java create mode 100644 examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/LoggerQualifier.java create mode 100644 examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/RequestId.java create mode 100644 examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/RequestIdProducer.java create mode 100644 examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/ResourceProducer.java create mode 100644 examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/package-info.java create mode 100644 examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/package-info.java create mode 100644 examples/crac/src/main/resources/META-INF/beans.xml create mode 100644 examples/crac/src/main/resources/META-INF/microprofile-config.properties create mode 100644 examples/crac/src/main/resources/logging.properties create mode 100644 examples/crac/src/test/java/io/helidon/microprofile/example/helloworld/implicit/ImplicitHelloWorldTest.java diff --git a/examples/microprofile/hello-world-implicit/runOnCRaC.sh b/examples/crac/Dockerfile.buildtime_crac similarity index 51% rename from examples/microprofile/hello-world-implicit/runOnCRaC.sh rename to examples/crac/Dockerfile.buildtime_crac index 3b1663b339a..673744ac637 100644 --- a/examples/microprofile/hello-world-implicit/runOnCRaC.sh +++ b/examples/crac/Dockerfile.buildtime_crac @@ -1,5 +1,3 @@ -#!/bin/bash -e - # # Copyright (c) 2022 Oracle and/or its affiliates. # @@ -15,24 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# - -# Workaround for https://github.com/checkpoint-restore/criu/issues/1696 -# see https://github.com/checkpoint-restore/criu/pull/1706 -export GLIBC_TUNABLES=glibc.pthread.rseq=0 - -if [ -d "./cr" ]; -then - echo "=== Starting directly from CRaC checkpoint ===" - #cat ./cr/dump4.log -else - echo "=== Creating CRaC checkpoint ===" - echo "Checking CRIU compatibility(don't forget --privileged):" - $JAVA_HOME/lib/criu check - set +e - $JAVA_HOME/bin/java -XX:CRaCCheckpointTo=cr -jar ./*.jar - set -e -fi -exec $JAVA_HOME/bin/java -XX:CRaCRestoreFrom=cr +# TODO: docker buildx with priviledged access? - https://docs.docker.com/engine/reference/commandline/buildx_build/#allow \ No newline at end of file diff --git a/examples/crac/Dockerfile.native_image b/examples/crac/Dockerfile.native_image new file mode 100644 index 00000000000..fb147df90a4 --- /dev/null +++ b/examples/crac/Dockerfile.native_image @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022 Oracle and/or its affiliates. +# +# 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 +# +# http://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. +# + +# TODO: native image for comparison \ No newline at end of file diff --git a/examples/microprofile/hello-world-implicit/Dockerfile.crac b/examples/crac/Dockerfile.runtime_crac similarity index 84% rename from examples/microprofile/hello-world-implicit/Dockerfile.crac rename to examples/crac/Dockerfile.runtime_crac index 39d48ec60fb..9d6aedf4a93 100644 --- a/examples/microprofile/hello-world-implicit/Dockerfile.crac +++ b/examples/crac/Dockerfile.runtime_crac @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# FROM ubuntu:22.04 as ubuntu-crac WORKDIR /usr/share @@ -22,9 +21,9 @@ ENV CRAC_ARTEFACT=openjdk-17-crac+3_linux-x64 ENV JAVA_HOME=/usr/share/$CRAC_ARTEFACT # Install CRaC -RUN apt-get -qq update && apt-get install -y wget sudo \ +RUN apt-get -qq update && apt-get install -y wget \ && wget -q https://github.com/CRaC/openjdk-builds/releases/download/17-crac%2B3/$CRAC_ARTEFACT.tar.gz \ -&& sudo tar zxf $CRAC_ARTEFACT.tar.gz \ +&& tar zxf $CRAC_ARTEFACT.tar.gz \ && ln -s $JAVA_HOME/bin/java /bin/ FROM ubuntu-crac @@ -32,10 +31,10 @@ WORKDIR /helidon ADD target/*.jar . ADD target/libs libs -ADD runOnCRaC.sh . -RUN chmod +x ./runOnCRaC.sh +ADD runtimeCRaC.sh . +RUN chmod +x ./runtimeCRaC.sh -ENTRYPOINT ["./runOnCRaC.sh"] +ENTRYPOINT ["./runtimeCRaC.sh"] EXPOSE 7001 diff --git a/examples/crac/README.md b/examples/crac/README.md new file mode 100644 index 00000000000..58ec664a057 --- /dev/null +++ b/examples/crac/README.md @@ -0,0 +1,28 @@ +# Helidon MP on CRaC +[Coordinated Restore at Checkpoint](https://wiki.openjdk.org/display/crac) + + +## Runtime CRaC +Standard docker build doesn't support privileged access to the host machine kernel, +therefore CRaC checkpoint needs to be created in runtime. + +```bash +mvn clean package +docker build -t crac-runtime-helloworld . -f Dockerfile.runtime_crac +# First time ran, checkpoint is created, stop with Ctrl-C +docker run --privileged -p 7001:7001 --name crac-runtime-helloworld crac-runtime-helloworld +# Second time starting from checkpoint, stop with Ctrl-C +docker start -i crac-runtime-helloworld +``` + +## Buildtime CRaC +Docker buildx ... + +[//]: # (TODO docker buildx with privileged access?) + +### Exercise the app +``` +curl -X GET http://localhost:7001/helloworld +curl -X GET http://localhost:7001/helloworld/earth +curl -X GET http://localhost:7001/another +``` \ No newline at end of file diff --git a/examples/crac/pom.xml b/examples/crac/pom.xml new file mode 100644 index 00000000000..aa4cab6d93b --- /dev/null +++ b/examples/crac/pom.xml @@ -0,0 +1,79 @@ + + + + + 4.0.0 + + io.helidon.applications + helidon-mp + 3.0.3-SNAPSHOT + ../../../applications/mp/pom.xml + + io.helidon.examples.microprofile + helidon-examples-microprofile-hello-world-implicit + Helidon Microprofile Examples Implicit Hello World + + + Microprofile example with implicit bootstrapping (cdi.Main(new String[0]) + + + + + io.helidon.microprofile.bundles + helidon-microprofile + + + org.jboss + jandex + runtime + true + + + io.helidon.microprofile.tests + helidon-microprofile-tests-junit5 + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-all + test + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-libs + + + + + + diff --git a/examples/crac/runtimeCRaC.sh b/examples/crac/runtimeCRaC.sh new file mode 100644 index 00000000000..9c6e84af7e2 --- /dev/null +++ b/examples/crac/runtimeCRaC.sh @@ -0,0 +1,43 @@ +#!/bin/bash -e + +# +# Copyright (c) 2022 Oracle and/or its affiliates. +# +# 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 +# +# http://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. +# + +if [ ! -d "./cr" ]; +then + echo "==== Creating CRaC checkpoint ====" + echo "=== Checking CRIU compatibility(don't forget --privileged) ===" + $JAVA_HOME/lib/criu check + + echo "=== Checking glibc version ===" + # glibc version higher than 2.34.9000-29 are known to have problems with rseq + # on some kernels, workaround GLIBC_TUNABLES=glibc.pthread.rseq=0 + ldd --version | grep ldd + # Workaround for https://github.com/checkpoint-restore/criu/issues/1696 + # see https://github.com/checkpoint-restore/criu/pull/1706 + export GLIBC_TUNABLES=glibc.pthread.rseq=0 + + echo "=== Pre-starting Helidon MP app ===" + set +e + $JAVA_HOME/bin/java -XX:CRaCCheckpointTo=cr -jar ./*.jar + set -e + + echo "=== CRaC checkpoint created, checking log dump for errors ===" + cat ./cr/dump*.log | grep "Warn\|Err\|succ" +fi +echo "==== Starting directly from CRaC checkpoint ====" +exec $JAVA_HOME/bin/java -XX:CRaCRestoreFrom=cr + diff --git a/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/AnotherResource.java b/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/AnotherResource.java new file mode 100644 index 00000000000..8807e0e00a4 --- /dev/null +++ b/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/AnotherResource.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018,2020 Oracle and/or its affiliates. + * + * 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 + * + * http://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 io.helidon.microprofile.example.helloworld.implicit; + +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.inject.Provider; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +/** + * Resource showing all possible configuration injections. + */ +@Path("another") +@RequestScoped +public class AnotherResource { + @Inject + @ConfigProperty(name = "app.nonExistent", defaultValue = "145") + private int defaultValue; + + @Inject + @ConfigProperty(name = "app.nonExistent") + private Optional empty; + + @Inject + @ConfigProperty(name = "app.uri") + private Optional full; + + @Inject + @ConfigProperty(name = "app.someInt") + private Provider provider; + + @Inject + @ConfigProperty(name = "app.ints") + private List ints; + + @Inject + @ConfigProperty(name = "app.ints") + private Optional> optionalInts; + + @Inject + @ConfigProperty(name = "app.ints") + private Provider> providedInts; + + @Inject + @ConfigProperty(name = "app.ints") + private int[] intsArray; + + @Inject + @ConfigProperty(name = "app") + private Map detached; + + @Inject + private Config mpConfig; + + @Inject + private io.helidon.config.Config helidonConfig; + + /** + * Get method to validate that all injections worked. + * + * @return data from all fields of this class + */ + @GET + public String get() { + return toString(); + } + + @Override + public String toString() { + return "AnotherResource{" + + "defaultValue=" + defaultValue + + ", empty=" + empty + + ", full=" + full + + ", provider=" + provider + "(" + provider.get() + ")" + + ", ints=" + ints + + ", optionalInts=" + optionalInts + + ", providedInts=" + providedInts + "(" + providedInts.get() + ")" + + ", detached=" + detached + + ", microprofileConfig=" + mpConfig + + ", helidonConfig=" + helidonConfig + + ", intsArray=" + Arrays.toString(intsArray) + + '}'; + } +} diff --git a/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/HelloWorldResource.java b/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/HelloWorldResource.java new file mode 100644 index 00000000000..4fe49e4ca08 --- /dev/null +++ b/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/HelloWorldResource.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * + * 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 + * + * http://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 io.helidon.microprofile.example.helloworld.implicit; + +import java.net.URI; +import java.util.Collections; +import java.util.logging.Logger; + +import io.helidon.config.Config; +import io.helidon.microprofile.example.helloworld.implicit.cdi.LoggerQualifier; +import io.helidon.microprofile.example.helloworld.implicit.cdi.RequestId; +import io.helidon.microprofile.example.helloworld.implicit.cdi.ResourceProducer; + +import jakarta.enterprise.context.RequestScoped; +import jakarta.enterprise.inject.spi.BeanManager; +import jakarta.inject.Inject; +import jakarta.json.Json; +import jakarta.json.JsonBuilderFactory; +import jakarta.json.JsonObject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +/** + * Resource for hello world example. + */ +@Path("helloworld") +@RequestScoped +public class HelloWorldResource { + + private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap()); + + private final Config config; + private final Logger logger; + private final int requestId; + private final String applicationName; + private final URI applicationUri; + private BeanManager beanManager; + + /** + * Using constructor injection for field values. + * + * @param config configuration instance + * @param logger logger (from {@link ResourceProducer} + * @param requestId requestId (from {@link ResourceProducer} + * @param appName name from configuration (app.name) + * @param appUri URI from configuration (app.uri) + * @param beanManager bean manager (injected automatically by CDI) + */ + @Inject + public HelloWorldResource(Config config, + @LoggerQualifier Logger logger, + @RequestId int requestId, + @ConfigProperty(name = "app.name") String appName, + @ConfigProperty(name = "app.uri") URI appUri, + BeanManager beanManager) { + this.config = config; + this.logger = logger; + this.requestId = requestId; + this.applicationName = appName; + this.applicationUri = appUri; + this.beanManager = beanManager; + } + + /** + * Get method for this resource, shows logger and request id. + * + * @return hello world + */ + @GET + @Produces(MediaType.TEXT_PLAIN) + public String message() { + return "Hello World: " + logger + ", request: " + requestId + ", appName: " + applicationName; + } + + /** + * Get method for this resource, returning JSON. + * + * @param name name to add to response + * @return JSON structure with injected fields + */ + @Path("/{name}") + @GET + @Produces(MediaType.APPLICATION_JSON) + public JsonObject getHello(@PathParam("name") String name) { + return JSON.createObjectBuilder() + .add("name", name) + .add("requestId", requestId) + .add("appName", applicationName) + .add("appUri", String.valueOf(applicationUri)) + .add("config", config.get("server.port").asInt().get()) + .add("beanManager", beanManager.toString()) + .add("logger", logger.getName()) + .build(); + } +} diff --git a/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/LoggerQualifier.java b/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/LoggerQualifier.java new file mode 100644 index 00000000000..7de3e86dbb7 --- /dev/null +++ b/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/LoggerQualifier.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * + * 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 + * + * http://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 io.helidon.microprofile.example.helloworld.implicit.cdi; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import jakarta.inject.Qualifier; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Use this qualifier to inject logger instances. + */ +@Qualifier +@Retention(RUNTIME) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE}) +public @interface LoggerQualifier { +} diff --git a/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/RequestId.java b/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/RequestId.java new file mode 100644 index 00000000000..c4e904e6c5e --- /dev/null +++ b/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/RequestId.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * + * 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 + * + * http://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 io.helidon.microprofile.example.helloworld.implicit.cdi; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.inject.Qualifier; + +/** + * Request id qualifier to inject increasing request id. + */ +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE}) +public @interface RequestId { +} diff --git a/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/RequestIdProducer.java b/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/RequestIdProducer.java new file mode 100644 index 00000000000..2eacb8ff66d --- /dev/null +++ b/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/RequestIdProducer.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * + * 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 + * + * http://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 io.helidon.microprofile.example.helloworld.implicit.cdi; + +import jakarta.enterprise.context.ApplicationScoped; + +/** + * Produce an ever increasing request id. + */ +@ApplicationScoped +public class RequestIdProducer { + +} diff --git a/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/ResourceProducer.java b/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/ResourceProducer.java new file mode 100644 index 00000000000..206be30fb38 --- /dev/null +++ b/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/ResourceProducer.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * + * 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 + * + * http://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 io.helidon.microprofile.example.helloworld.implicit.cdi; + +import java.util.concurrent.atomic.AtomicInteger; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; +import jakarta.enterprise.inject.spi.InjectionPoint; + +/** + * Producer for various resources required by this example. + */ +@ApplicationScoped +public class ResourceProducer { + private static final AtomicInteger COUNTER = new AtomicInteger(); + + /** + * Each injection will increase the COUNTER. + * + * @return increased COUNTER value + */ + @Produces + @RequestId + public int produceRequestId() { + return COUNTER.incrementAndGet(); + } + + /** + * Create/get a logger instance for the class that the logger is being injected into. + * + * @param injectionPoint injection point + * @return a logger instance + */ + @Produces + @LoggerQualifier + public java.util.logging.Logger produceLogger(final InjectionPoint injectionPoint) { + return java.util.logging.Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName()); + } +} diff --git a/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/package-info.java b/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/package-info.java new file mode 100644 index 00000000000..3de41659971 --- /dev/null +++ b/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/cdi/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * + * 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 + * + * http://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. + */ + +/** + * CDI classes for example. + */ +package io.helidon.microprofile.example.helloworld.implicit.cdi; diff --git a/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/package-info.java b/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/package-info.java new file mode 100644 index 00000000000..6c8d109a135 --- /dev/null +++ b/examples/crac/src/main/java/io/helidon/microprofile/example/helloworld/implicit/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. + * + * 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 + * + * http://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. + */ + +/** + * Implicit HelloWorld example (starts server without configuration). + */ +package io.helidon.microprofile.example.helloworld.implicit; diff --git a/examples/crac/src/main/resources/META-INF/beans.xml b/examples/crac/src/main/resources/META-INF/beans.xml new file mode 100644 index 00000000000..1b3fbc297cb --- /dev/null +++ b/examples/crac/src/main/resources/META-INF/beans.xml @@ -0,0 +1,25 @@ + + + + diff --git a/examples/crac/src/main/resources/META-INF/microprofile-config.properties b/examples/crac/src/main/resources/META-INF/microprofile-config.properties new file mode 100644 index 00000000000..55832bf20f6 --- /dev/null +++ b/examples/crac/src/main/resources/META-INF/microprofile-config.properties @@ -0,0 +1,27 @@ +# +# Copyright (c) 2020 Oracle and/or its affiliates. +# +# 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 +# +# http://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. +# + +app.name=Hello World Application +app.someInt=147 +app.uri=https://www.example.com +app.someInt=147 +app.ints=12,12,32,12,44 + +server.host=0.0.0.0 +server.port=7001 + +# Enable the optional MicroProfile Metrics REST.request metrics +metrics.rest-request.enabled=true diff --git a/examples/crac/src/main/resources/logging.properties b/examples/crac/src/main/resources/logging.properties new file mode 100644 index 00000000000..2f4a11a6f0c --- /dev/null +++ b/examples/crac/src/main/resources/logging.properties @@ -0,0 +1,20 @@ +# +# Copyright (c) 2022 Oracle and/or its affiliates. +# +# 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 +# +# http://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. +# + +handlers=io.helidon.common.HelidonConsoleHandler +java.util.logging.SimpleFormatter.format=[%1$tc] %4$s: %2$s - %5$s %6$s%n +.level=INFO +io.helidon.microprofile.config.level=FINEST diff --git a/examples/crac/src/test/java/io/helidon/microprofile/example/helloworld/implicit/ImplicitHelloWorldTest.java b/examples/crac/src/test/java/io/helidon/microprofile/example/helloworld/implicit/ImplicitHelloWorldTest.java new file mode 100644 index 00000000000..2e5e7e3b072 --- /dev/null +++ b/examples/crac/src/test/java/io/helidon/microprofile/example/helloworld/implicit/ImplicitHelloWorldTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * + * 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 + * + * http://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 io.helidon.microprofile.example.helloworld.implicit; + +import io.helidon.microprofile.tests.junit5.HelidonTest; + +import jakarta.inject.Inject; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.WebTarget; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertAll; + +/** + * Unit test for {@link HelloWorldResource}. + */ +@HelidonTest +@Disabled("3.0.0-JAKARTA") // OpenAPI: Caused by: java.lang.NoSuchMethodError: + // 'java.util.List org.jboss.jandex.ClassInfo.unsortedFields()' +class ImplicitHelloWorldTest { + private final WebTarget target; + + @Inject + ImplicitHelloWorldTest(WebTarget target) { + this.target = target; + } + @Test + void testJsonResource() { + JsonObject jsonObject = target + .path("/helloworld/unit") + .request() + .get(JsonObject.class); + + assertAll("JSON fields must match expected injection values", + () -> assertThat("Name from request", jsonObject.getString("name"), is("unit")), + () -> assertThat("Request id from CDI provider", jsonObject.getInt("requestId"), is(1)), + () -> assertThat("App name from config", jsonObject.getString("appName"), is("Hello World Application")), + () -> assertThat("Logger name", jsonObject.getString("logger"), is(HelloWorldResource.class.getName())) + ); + + } +} diff --git a/examples/microprofile/hello-world-implicit/README.md b/examples/microprofile/hello-world-implicit/README.md index 21f5b0c47cb..393607f09da 100644 --- a/examples/microprofile/hello-world-implicit/README.md +++ b/examples/microprofile/hello-world-implicit/README.md @@ -18,53 +18,3 @@ curl -X GET http://localhost:7001/helloworld curl -X GET http://localhost:7001/helloworld/earth curl -X GET http://localhost:7001/another ``` - -## CRaC - -```bash -mvn clean package -docker build -t crac-helloworld . -f Dockerfile.crac -# First time ran, checkpoint is created -docker run -d --privileged -p 7001:7001 --name crac-helloworld crac-helloworld -docker stop crac-helloworld -# Second time starting from checkpoint -docker start crac-helloworld -docker logs crac-helloworld -``` - -``` -=== Creating CRaC checkpoint === -Checking CRIU compatibility(don't forget --privileged): -Warn (criu/kerndat.c:1470): CRIU was built without libnftables support -Looks good. -[Tue Dec 06 20:39:31 GMT 2022] INFO: io.helidon.common.LogConfig doConfigureLogging - Logging at initialization configured using classpath: /logging.properties -[Tue Dec 06 20:39:31 GMT 2022] INFO: org.jboss.weld.bootstrap.WeldStartup - WELD-000900: 4.0.2 (Final) -[Tue Dec 06 20:39:32 GMT 2022] INFO: io.helidon.microprofile.openapi.OpenApiCdiExtension - OpenAPI support could not locate the Jandex index file META-INF/jandex.idx so will build an in-memory index. -This slows your app start-up and, depending on CDI configuration, might omit some type information needed for a complete OpenAPI document. -Consider using the Jandex maven plug-in during your build to create the index and add it to your app. -[Tue Dec 06 20:39:32 GMT 2022] FINE: io.helidon.microprofile.config.ConfigCdiExtension - ConfigCdiExtension instantiated -[Tue Dec 06 20:39:32 GMT 2022] INFO: org.jboss.weld.environment.deployment.discovery.DiscoveryStrategyFactory create - WELD-ENV-000020: Using jandex for bean discovery -[Tue Dec 06 20:39:32 GMT 2022] INFO: org.jboss.weld.bootstrap.WeldStartup startContainer - WELD-000101: Transactional services not available. Injection of @Inject UserTransaction not available. Transactional observers will be invoked synchronously. -[Tue Dec 06 20:39:32 GMT 2022] INFO: org.jboss.weld.event.ExtensionObserverMethodImpl checkRequiredTypeAnnotations - WELD-000411: Observer method [BackedAnnotatedMethod] private io.helidon.microprofile.openapi.OpenApiCdiExtension.processAnnotatedType(@Observes ProcessAnnotatedType) receives events for all annotated types. Consider restricting events using @WithAnnotations or a generic type with bounds. -[Tue Dec 06 20:39:32 GMT 2022] INFO: org.jboss.weld.event.ExtensionObserverMethodImpl checkRequiredTypeAnnotations - WELD-000411: Observer method [BackedAnnotatedMethod] public org.glassfish.jersey.ext.cdi1x.internal.ProcessAllAnnotatedTypes.processAnnotatedType(@Observes ProcessAnnotatedType, BeanManager) receives events for all annotated types. Consider restricting events using @WithAnnotations or a generic type with bounds. -[Tue Dec 06 20:39:32 GMT 2022] WARNING: org.glassfish.jersey.ext.cdi1x.internal.CdiComponentProvider isJerseyOrDependencyType - Class class [I has null package -[Tue Dec 06 20:39:32 GMT 2022] INFO: io.helidon.tracing.tracerresolver.TracerResolverBuilder build - TracerResolver not configured, tracing is disabled -[Tue Dec 06 20:39:32 GMT 2022] INFO: io.helidon.microprofile.security.SecurityCdiExtension registerSecurity - Authentication provider is missing from security configuration, but security extension for microprofile is enabled (requires providers configuration at key security.providers). Security will not have any valid authentication provider -[Tue Dec 06 20:39:32 GMT 2022] INFO: io.helidon.microprofile.security.SecurityCdiExtension registerSecurity - Authorization provider is missing from security configuration, but security extension for microprofile is enabled (requires providers configuration at key security.providers). ABAC provider is configured for authorization. -[Tue Dec 06 20:39:32 GMT 2022] INFO: io.helidon.microprofile.server.ServerCdiExtension addApplication - Registering JAX-RS Application: HelidonMP -CR: Checkpoint ... -./runOnCRaC.sh: line 30: 18 Killed $JAVA_HOME/bin/java -XX:CRaCCheckpointTo=cr -jar ./*.jar -[Tue Dec 06 20:39:33 GMT 2022] INFO: io.helidon.microprofile.server.ServerCdiExtension afterRestore - CRaC snapshot restored! -[Tue Dec 06 20:39:33 GMT 2022] INFO: io.helidon.webserver.NettyWebServer lambda$start$8 - Channel '@default' started: [id: 0x5158f8bb, L:/0.0.0.0:7001] -[Tue Dec 06 20:39:33 GMT 2022] INFO: io.helidon.microprofile.server.ServerCdiExtension startServer - Server started on http://localhost:7001 (and all other host addresses) in 55 milliseconds (since CRaC restore). -[Tue Dec 06 20:39:34 GMT 2022] INFO: io.helidon.common.HelidonFeatures features - Helidon MP 3.0.3-SNAPSHOT features: [CDI, Config, Fault Tolerance, Health, JAX-RS, Metrics, Open API, REST Client, Security, Server, Tracing] -[Tue Dec 06 20:39:41 GMT 2022] INFO: io.helidon.webserver.NettyWebServer lambda$start$6 - Channel '@default' closed: [id: 0x5158f8bb, L:/0.0.0.0:7001] -[Tue Dec 06 20:39:41 GMT 2022] INFO: io.helidon.microprofile.server.ServerCdiExtension doStop - Server stopped in 8 milliseconds. -[Tue Dec 06 20:39:41 GMT 2022] INFO: io.helidon.microprofile.cdi.HelidonContainerImpl$HelidonCdi close - WELD-ENV-002001: Weld SE container e6c58ecb-e5cb-4e0e-83f6-d1ae6b1081d0 shut down -=== Starting directly from CRaC checkpoint === -[Tue Dec 06 20:39:46 GMT 2022] INFO: io.helidon.microprofile.server.ServerCdiExtension afterRestore - CRaC snapshot restored! -[Tue Dec 06 20:39:46 GMT 2022] INFO: io.helidon.webserver.NettyWebServer lambda$start$8 - Channel '@default' started: [id: 0x5158f8bb, L:/0.0.0.0:7001] -[Tue Dec 06 20:39:46 GMT 2022] INFO: io.helidon.microprofile.server.ServerCdiExtension startServer - Server started on http://localhost:7001 (and all other host addresses) in 54 milliseconds (since CRaC restore). -[Tue Dec 06 20:39:46 GMT 2022] INFO: io.helidon.common.HelidonFeatures features - Helidon MP 3.0.3-SNAPSHOT features: [CDI, Config, Fault Tolerance, Health, JAX-RS, Metrics, Open API, REST Client, Security, Server, Tracing] -kec@romulus:~/idp/ora/helidon/helidon3/examples/microprofile/hello-world-implicit$ docker start crac-helloworld -``` \ No newline at end of file diff --git a/examples/microprofile/hello-world-implicit/pom.xml b/examples/microprofile/hello-world-implicit/pom.xml index aa4cab6d93b..6023fd346ed 100644 --- a/examples/microprofile/hello-world-implicit/pom.xml +++ b/examples/microprofile/hello-world-implicit/pom.xml @@ -74,6 +74,15 @@ + + org.jboss.jandex + jandex-maven-plugin + + + make-index + + + diff --git a/examples/microprofile/hello-world-implicit/src/main/resources/META-INF/microprofile-config.properties b/examples/microprofile/hello-world-implicit/src/main/resources/META-INF/microprofile-config.properties index 55832bf20f6..bbae60a1ba5 100644 --- a/examples/microprofile/hello-world-implicit/src/main/resources/META-INF/microprofile-config.properties +++ b/examples/microprofile/hello-world-implicit/src/main/resources/META-INF/microprofile-config.properties @@ -20,7 +20,6 @@ app.uri=https://www.example.com app.someInt=147 app.ints=12,12,32,12,44 -server.host=0.0.0.0 server.port=7001 # Enable the optional MicroProfile Metrics REST.request metrics diff --git a/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityCdiExtension.java b/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityCdiExtension.java index 975772568b4..ffa6623a3ad 100644 --- a/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityCdiExtension.java +++ b/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityCdiExtension.java @@ -19,7 +19,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.atomic.AtomicReference; -import java.util.logging.Level; import java.util.logging.Logger; import io.helidon.common.context.Contexts; diff --git a/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java b/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java index 38d8a4b6d55..377e7fa7d92 100644 --- a/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java +++ b/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java @@ -116,9 +116,12 @@ public class ServerCdiExtension implements Extension, Resource { = Collections.synchronizedMap(new IdentityHashMap<>()); private final Set routingsWithKPIMetrics = new HashSet<>(); - private long crac_restore_time = -1; + private long cracRestoreTime = -1; private final CompletableFuture> restored = new CompletableFuture<>(); + /** + * CDI extension to handle web server configuration and lifecycle. + */ public ServerCdiExtension() { Core.getGlobalContext().register(this); } @@ -210,15 +213,13 @@ private void startServer(@Observes @Priority(PLATFORM_AFTER + 100) @Initialized( try { Core.checkpointRestore(); + } catch (UnsupportedOperationException e) { + LOGGER.log(Level.FINEST, "CRaC feature is not available", e); } catch (RestoreException e) { - LOGGER.log(Level.INFO, "CRaC restore wasn't successful!", e); -// restored.complete(null); + LOGGER.log(Level.SEVERE, "CRaC restore wasn't successful!", e); } catch (CheckpointException e) { - LOGGER.log(Level.INFO, "CRaC checkpoint creation wasn't successful!", e); -// restored.complete(null); - System.exit(0); + LOGGER.log(Level.SEVERE, "CRaC checkpoint creation wasn't successful!", e); } -// restored.join(); try { webserver.start().await(); @@ -236,9 +237,9 @@ private void startServer(@Observes @Priority(PLATFORM_AFTER + 100) @Initialized( String note = "0.0.0.0".equals(listenHost) ? " (and all other host addresses)" : ""; - String startupTimeReport = crac_restore_time == -1 + String startupTimeReport = cracRestoreTime == -1 ? " in " + initializationElapsedTime + " milliseconds (since JVM startup). " - : " in " + (System.currentTimeMillis() - crac_restore_time) + " milliseconds (since CRaC restore)."; + : " in " + (System.currentTimeMillis() - cracRestoreTime) + " milliseconds (since CRaC restore)."; LOGGER.info(() -> "Server started on " + protocol + "://" + host + ":" + port @@ -629,9 +630,8 @@ public void beforeCheckpoint(org.crac.Context context) throw @Override public void afterRestore(org.crac.Context context) throws Exception { - crac_restore_time = System.currentTimeMillis(); + cracRestoreTime = System.currentTimeMillis(); LOGGER.log(Level.INFO, "CRaC snapshot restored!"); -// restored.complete(context); } } From f1842911cc5f3d7b83f8b25a17669a556ac39a6b Mon Sep 17 00:00:00 2001 From: Daniel Kec Date: Mon, 12 Dec 2022 17:57:58 +0100 Subject: [PATCH 05/16] Minikube example Signed-off-by: Daniel Kec --- ...ockerfile.runtime_crac => Dockerfile.crac} | 6 +- examples/crac/README.md | 29 ++++++--- examples/crac/app.yaml | 63 +++++++++++++++++++ ...file.buildtime_crac => deploy-minikube.sh} | 14 ++++- examples/crac/runtimeCRaC.sh | 6 +- 5 files changed, 103 insertions(+), 15 deletions(-) rename examples/crac/{Dockerfile.runtime_crac => Dockerfile.crac} (83%) create mode 100644 examples/crac/app.yaml rename examples/crac/{Dockerfile.buildtime_crac => deploy-minikube.sh} (52%) diff --git a/examples/crac/Dockerfile.runtime_crac b/examples/crac/Dockerfile.crac similarity index 83% rename from examples/crac/Dockerfile.runtime_crac rename to examples/crac/Dockerfile.crac index 9d6aedf4a93..9deea271401 100644 --- a/examples/crac/Dockerfile.runtime_crac +++ b/examples/crac/Dockerfile.crac @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -FROM ubuntu:22.04 as ubuntu-crac +FROM debian:stretch-slim as ubuntu-crac WORKDIR /usr/share @@ -21,8 +21,8 @@ ENV CRAC_ARTEFACT=openjdk-17-crac+3_linux-x64 ENV JAVA_HOME=/usr/share/$CRAC_ARTEFACT # Install CRaC -RUN apt-get -qq update && apt-get install -y wget \ -&& wget -q https://github.com/CRaC/openjdk-builds/releases/download/17-crac%2B3/$CRAC_ARTEFACT.tar.gz \ +RUN apt-get -qq update && apt-get install -y wget +RUN wget https://github.com/CRaC/openjdk-builds/releases/download/17-crac%2B3/$CRAC_ARTEFACT.tar.gz \ && tar zxf $CRAC_ARTEFACT.tar.gz \ && ln -s $JAVA_HOME/bin/java /bin/ diff --git a/examples/crac/README.md b/examples/crac/README.md index 58ec664a057..4ee9a02adc0 100644 --- a/examples/crac/README.md +++ b/examples/crac/README.md @@ -8,21 +8,34 @@ therefore CRaC checkpoint needs to be created in runtime. ```bash mvn clean package -docker build -t crac-runtime-helloworld . -f Dockerfile.runtime_crac +docker build -t crac-helloworld . -f Dockerfile.crac # First time ran, checkpoint is created, stop with Ctrl-C -docker run --privileged -p 7001:7001 --name crac-runtime-helloworld crac-runtime-helloworld +docker run --privileged -p 7001:7001 --name crac-helloworld crac-helloworld # Second time starting from checkpoint, stop with Ctrl-C -docker start -i crac-runtime-helloworld +docker start -i crac-helloworld ``` -## Buildtime CRaC -Docker buildx ... - -[//]: # (TODO docker buildx with privileged access?) - ### Exercise the app ``` curl -X GET http://localhost:7001/helloworld curl -X GET http://localhost:7001/helloworld/earth curl -X GET http://localhost:7001/another +``` + +## Kubernetes CRaC + +```shell +minikube start +bash deploy-minikube.sh +curl $(minikube service crac-helloworld -n crac-helloworld --url)/helloworld/earth | jq +``` + +```shell +kubectl get pods +# Check first start +kubectl logs --previous --tail=100 -l app=crac-helloworl +# Check restart +kubectl logs -l app=crac-helloworl +# Scale-up quickly +kubectl scale --replicas=3 deployment/crac-helloworld ``` \ No newline at end of file diff --git a/examples/crac/app.yaml b/examples/crac/app.yaml new file mode 100644 index 00000000000..e924a7a8888 --- /dev/null +++ b/examples/crac/app.yaml @@ -0,0 +1,63 @@ +kind: Service +apiVersion: v1 +metadata: + name: crac-helloworld + labels: + app: crac-helloworld +spec: + type: NodePort + selector: + app: crac-helloworld + ports: + - port: 7001 + targetPort: 7001 + name: http +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: crac-checkpoint +spec: + accessModes: + - ReadWriteOnce + capacity: + storage: 1Gi + hostPath: + path: /data/crac-checkpoint/ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: crac-helloworld + labels: + app: crac-helloworld +spec: + replicas: 1 + selector: + matchLabels: + app: crac-helloworld + template: + metadata: + labels: + app: crac-helloworld + spec: + containers: + - name: crac-helloworld + image: crac-helloworld + imagePullPolicy: IfNotPresent + volumeMounts: + - mountPath: /crac-checkpoint + name: crac-checkpoint + ports: + - containerPort: 7001 + securityContext: + privileged: true + readinessProbe: + tcpSocket: + port: 7001 + initialDelaySeconds: 1 + periodSeconds: 1 + volumes: + - name: crac-checkpoint + hostPath: + path: /crac-checkpoint \ No newline at end of file diff --git a/examples/crac/Dockerfile.buildtime_crac b/examples/crac/deploy-minikube.sh similarity index 52% rename from examples/crac/Dockerfile.buildtime_crac rename to examples/crac/deploy-minikube.sh index 673744ac637..ac8c97be6b8 100644 --- a/examples/crac/Dockerfile.buildtime_crac +++ b/examples/crac/deploy-minikube.sh @@ -1,3 +1,4 @@ +#!/bin/bash -e # # Copyright (c) 2022 Oracle and/or its affiliates. # @@ -13,6 +14,17 @@ # See the License for the specific language governing permissions and # limitations under the License. # +eval $(minikube docker-env) +NAMESPACE=crac-helloworld +mvn package -DskipTests +docker build -t crac-helloworld . -f Dockerfile.crac +# First time ran, checkpoint is created, stop with Ctrl-C +#docker run --privileged -p 7001:7001 --name crac-helloworld crac-helloworld +# Second time starting from checkpoint, stop with Ctrl-C +#docker start -i crac-helloworld -# TODO: docker buildx with priviledged access? - https://docs.docker.com/engine/reference/commandline/buildx_build/#allow \ No newline at end of file +kubectl delete namespace ${NAMESPACE} +kubectl create namespace ${NAMESPACE} +kubectl config set-context --current --namespace=${NAMESPACE} +kubectl apply -f . --namespace ${NAMESPACE} \ No newline at end of file diff --git a/examples/crac/runtimeCRaC.sh b/examples/crac/runtimeCRaC.sh index 9c6e84af7e2..6e79c133973 100644 --- a/examples/crac/runtimeCRaC.sh +++ b/examples/crac/runtimeCRaC.sh @@ -16,7 +16,7 @@ # limitations under the License. # -if [ ! -d "./cr" ]; +if [ ! -d "/crac-checkpoint/cr" ]; then echo "==== Creating CRaC checkpoint ====" echo "=== Checking CRIU compatibility(don't forget --privileged) ===" @@ -32,12 +32,12 @@ then echo "=== Pre-starting Helidon MP app ===" set +e - $JAVA_HOME/bin/java -XX:CRaCCheckpointTo=cr -jar ./*.jar + $JAVA_HOME/bin/java -XX:CRaCCheckpointTo=/crac-checkpoint/cr -jar ./*.jar set -e echo "=== CRaC checkpoint created, checking log dump for errors ===" cat ./cr/dump*.log | grep "Warn\|Err\|succ" fi echo "==== Starting directly from CRaC checkpoint ====" -exec $JAVA_HOME/bin/java -XX:CRaCRestoreFrom=cr +exec $JAVA_HOME/bin/java -XX:CRaCRestoreFrom=/crac-checkpoint/cr From 475e9067ac4b216a9cf08f9fb5ca05feee952404 Mon Sep 17 00:00:00 2001 From: Daniel Kec Date: Mon, 12 Dec 2022 20:03:06 +0100 Subject: [PATCH 06/16] CRaC K8s POC Signed-off-by: Daniel Kec --- examples/crac/Dockerfile.crac | 3 +++ examples/crac/README.md | 10 +++++----- examples/crac/deploy-minikube.sh | 6 ++---- examples/crac/runtimeCRaC.sh | 10 ++++++---- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/examples/crac/Dockerfile.crac b/examples/crac/Dockerfile.crac index 9deea271401..e239f63fb32 100644 --- a/examples/crac/Dockerfile.crac +++ b/examples/crac/Dockerfile.crac @@ -17,8 +17,11 @@ FROM debian:stretch-slim as ubuntu-crac WORKDIR /usr/share +ARG CHECKPOINT_DIR + ENV CRAC_ARTEFACT=openjdk-17-crac+3_linux-x64 ENV JAVA_HOME=/usr/share/$CRAC_ARTEFACT +ENV CR_DIR=${CONT_IMG_VER:-/crac-checkpoint/cr} # Install CRaC RUN apt-get -qq update && apt-get install -y wget diff --git a/examples/crac/README.md b/examples/crac/README.md index 4ee9a02adc0..af5f73e66af 100644 --- a/examples/crac/README.md +++ b/examples/crac/README.md @@ -8,7 +8,7 @@ therefore CRaC checkpoint needs to be created in runtime. ```bash mvn clean package -docker build -t crac-helloworld . -f Dockerfile.crac +docker build --build-arg CR_DIR=~/cr -t crac-helloworld . -f Dockerfile.crac # First time ran, checkpoint is created, stop with Ctrl-C docker run --privileged -p 7001:7001 --name crac-helloworld crac-helloworld # Second time starting from checkpoint, stop with Ctrl-C @@ -32,10 +32,10 @@ curl $(minikube service crac-helloworld -n crac-helloworld --url)/helloworld/ear ```shell kubectl get pods -# Check first start -kubectl logs --previous --tail=100 -l app=crac-helloworl -# Check restart -kubectl logs -l app=crac-helloworl +# Check first start - leghtly checkpoint creation +kubectl logs --previous --tail=100 -l app=crac-helloworld +# Check restart - fast checkpoint restoration +kubectl logs -l app=crac-helloworld # Scale-up quickly kubectl scale --replicas=3 deployment/crac-helloworld ``` \ No newline at end of file diff --git a/examples/crac/deploy-minikube.sh b/examples/crac/deploy-minikube.sh index ac8c97be6b8..78b3fc558c8 100644 --- a/examples/crac/deploy-minikube.sh +++ b/examples/crac/deploy-minikube.sh @@ -19,12 +19,10 @@ NAMESPACE=crac-helloworld mvn package -DskipTests docker build -t crac-helloworld . -f Dockerfile.crac -# First time ran, checkpoint is created, stop with Ctrl-C -#docker run --privileged -p 7001:7001 --name crac-helloworld crac-helloworld -# Second time starting from checkpoint, stop with Ctrl-C -#docker start -i crac-helloworld kubectl delete namespace ${NAMESPACE} +# Cleanup any previous checkpoint +minikube ssh "sudo rm -rf /crac-checkpoint/cr" kubectl create namespace ${NAMESPACE} kubectl config set-context --current --namespace=${NAMESPACE} kubectl apply -f . --namespace ${NAMESPACE} \ No newline at end of file diff --git a/examples/crac/runtimeCRaC.sh b/examples/crac/runtimeCRaC.sh index 6e79c133973..a45b01968c2 100644 --- a/examples/crac/runtimeCRaC.sh +++ b/examples/crac/runtimeCRaC.sh @@ -16,7 +16,7 @@ # limitations under the License. # -if [ ! -d "/crac-checkpoint/cr" ]; +if [ ! -d "$CR_DIR" ]; then echo "==== Creating CRaC checkpoint ====" echo "=== Checking CRIU compatibility(don't forget --privileged) ===" @@ -36,8 +36,10 @@ then set -e echo "=== CRaC checkpoint created, checking log dump for errors ===" - cat ./cr/dump*.log | grep "Warn\|Err\|succ" -fi + cat $CR_DIR/dump*.log | grep "Warn\|Err\|succ" +else echo "==== Starting directly from CRaC checkpoint ====" -exec $JAVA_HOME/bin/java -XX:CRaCRestoreFrom=/crac-checkpoint/cr +exec $JAVA_HOME/bin/java -XX:CRaCRestoreFrom=$CR_DIR +fi + From 5e22b6b63b96d8e65d5d78114c9f2b7499112741 Mon Sep 17 00:00:00 2001 From: Daniel Kec Date: Mon, 12 Dec 2022 23:17:19 +0100 Subject: [PATCH 07/16] CRaC K8s POC Signed-off-by: Daniel Kec --- examples/crac/app.yaml | 1 + examples/crac/pom.xml | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/examples/crac/app.yaml b/examples/crac/app.yaml index e924a7a8888..1f9248324ff 100644 --- a/examples/crac/app.yaml +++ b/examples/crac/app.yaml @@ -51,6 +51,7 @@ spec: ports: - containerPort: 7001 securityContext: + # TODO: be nicer privileged: true readinessProbe: tcpSocket: diff --git a/examples/crac/pom.xml b/examples/crac/pom.xml index aa4cab6d93b..6023fd346ed 100644 --- a/examples/crac/pom.xml +++ b/examples/crac/pom.xml @@ -74,6 +74,15 @@ + + org.jboss.jandex + jandex-maven-plugin + + + make-index + + + From 1415b0d88e3258420b4c6ec483623df823a7ed53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Kr=C3=A1l?= Date: Wed, 7 Dec 2022 10:55:42 +0100 Subject: [PATCH 08/16] Client tracing interceptor no longer clears exception (#5601) Signed-off-by: David Kral --- .../helidon/tracing/jersey/client/ClientTracingInterceptor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing/jersey-client/src/main/java/io/helidon/tracing/jersey/client/ClientTracingInterceptor.java b/tracing/jersey-client/src/main/java/io/helidon/tracing/jersey/client/ClientTracingInterceptor.java index 0f97a43603d..ce60fb6c5f6 100644 --- a/tracing/jersey-client/src/main/java/io/helidon/tracing/jersey/client/ClientTracingInterceptor.java +++ b/tracing/jersey-client/src/main/java/io/helidon/tracing/jersey/client/ClientTracingInterceptor.java @@ -52,7 +52,7 @@ public void onException(ClientRequestContext requestContext, ExceptionContext ex if (spanProperty instanceof Span span) { span.status(Span.Status.ERROR); - span.end(exceptionContext.getThrowables().pop()); + span.end(exceptionContext.getThrowables().peek()); requestContext.removeProperty(SPAN_PROPERTY_NAME); } if (scopeProperty instanceof Scope scope) { From 10e4d9cc7a06c52dabd5faf7667450ba61c74537 Mon Sep 17 00:00:00 2001 From: Daniel Kec Date: Wed, 7 Dec 2022 19:49:47 +0100 Subject: [PATCH 09/16] 5496 flatMapCompletionStage javadoc fix (#5622) Signed-off-by: Daniel Kec Signed-off-by: Daniel Kec --- .../src/main/java/io/helidon/common/reactive/Multi.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/reactive/src/main/java/io/helidon/common/reactive/Multi.java b/common/reactive/src/main/java/io/helidon/common/reactive/Multi.java index b3be57e450d..605fe526625 100644 --- a/common/reactive/src/main/java/io/helidon/common/reactive/Multi.java +++ b/common/reactive/src/main/java/io/helidon/common/reactive/Multi.java @@ -646,8 +646,8 @@ default Multi flatMap(Function output item type From aa2c94e8726084f4612fb73f495008496a68e339 Mon Sep 17 00:00:00 2001 From: Joe DiPol Date: Wed, 7 Dec 2022 15:57:26 -0800 Subject: [PATCH 10/16] Update owasp dependency-check version. Add fp suppressions. Remove old suppressions. (#5626) --- etc/dependency-check-suppression.xml | 104 +++++++-------------------- pom.xml | 2 +- 2 files changed, 28 insertions(+), 78 deletions(-) diff --git a/etc/dependency-check-suppression.xml b/etc/dependency-check-suppression.xml index 834ca2fed0f..21206ab19a1 100644 --- a/etc/dependency-check-suppression.xml +++ b/etc/dependency-check-suppression.xml @@ -215,15 +215,15 @@ CVE-2022-28948 - ^pkg:maven/com\.h2database/h2@.*$ - CVE-2018-14335 + CVE-2022-45868 - - - ^pkg:maven/org\.apache\.logging\.log4j/log4j\-api@.*$ - CVE-2022-33915 - - - - - - ^pkg:maven/org\.junit\.platform/junit\-platform\-commons@.*$ - CVE-2022-31514 - - - - - - ^pkg:maven/io\.zipkin\.zipkin2/zipkin@.*$ - CVE-2022-2393 - - - - ^pkg:maven/io\.zipkin\.reporter2/zipkin\-reporter@.*$ - CVE-2022-2393 - - @@ -308,23 +268,18 @@ ^pkg:maven/org\.eclipse\.microprofile\.jwt/microprofile\-jwt\-auth\-api@.*$ CVE-2022-37422 - - - ^pkg:maven/org\.eclipse\.microprofile\.config/microprofile\-config\-api@.*$ - CVE-2022-37422 - - ^pkg:maven/org\.yaml/snakeyaml@.*$ - CVE-2022-38752 + CVE-2022-1471 - - - ^pkg:maven/com\.graphql\-java/graphql\-java\-extended\-scalars@.*$ - CVE-2022-37734 - - - - - - - ^pkg:maven/com\.graphql\-java/java\-dataloader@.*$ - CVE-2022-37734 - - @@ -411,5 +343,23 @@ CVE-2020-2801 + + + + ^pkg:maven/org\.apache\.commons/commons\-pool2@.*$ + CVE-2021-37533 + + + + ^pkg:maven/org\.apache\.commons/commons\-text@.*$ + CVE-2021-37533 + diff --git a/pom.xml b/pom.xml index effeb53b927..a35ae247ba0 100644 --- a/pom.xml +++ b/pom.xml @@ -116,7 +116,7 @@ 3.0.1 4.4.2.2 1.11.0 - 7.2.1 + 7.4.0 3.0.0-M5 1.1 2.3 From 62e554fcc483e597d78314cdd22068a20e5798f3 Mon Sep 17 00:00:00 2001 From: Andrei Arlou Date: Sun, 11 Dec 2022 22:35:46 +0200 Subject: [PATCH 11/16] Use Hamcrest assertions instead of JUnit in tests/functional/jax-rs-multiple-apps (#1749) (#5634) --- .../functional/multipleapps/MainTest.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/functional/jax-rs-multiple-apps/src/test/java/io/helidon/tests/functional/multipleapps/MainTest.java b/tests/functional/jax-rs-multiple-apps/src/test/java/io/helidon/tests/functional/multipleapps/MainTest.java index 45829866dd1..27bdf22fac8 100644 --- a/tests/functional/jax-rs-multiple-apps/src/test/java/io/helidon/tests/functional/multipleapps/MainTest.java +++ b/tests/functional/jax-rs-multiple-apps/src/test/java/io/helidon/tests/functional/multipleapps/MainTest.java @@ -26,11 +26,11 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItem; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.hasKey; @HelidonTest @Disabled("3.0.0-JAKARTA") @@ -46,13 +46,13 @@ void testHelloWorld1() { .request() .header("who", "World 1") .get(); - assertEquals(response.getStatus(), 200); - assertTrue(response.getHeaders().containsKey("sharedfilter")); - assertTrue(response.getHeaders().containsKey("filter1")); - assertFalse(response.getHeaders().containsKey("filter2")); + assertThat(response.getStatus(), is(200)); + assertThat(response.getHeaders(), hasKey("sharedfilter")); + assertThat(response.getHeaders(), hasKey("filter1")); + assertThat(response.getHeaders(), not(hasKey("filter2"))); JsonObject jsonObject = response.readEntity(JsonObject.class); - assertEquals("Hello World 1!", jsonObject.getString("message"), - "default message"); + assertThat("default message", jsonObject.getString("message"), + is("Hello World 1!")); } @Test @@ -62,14 +62,14 @@ void testHelloWorld2() { .request() .header("who", "World 2") .get(); - assertEquals(response.getStatus(), 200); - assertTrue(response.getHeaders().containsKey("sharedfilter")); - assertTrue(response.getHeaders().containsKey("filter2")); - assertTrue(response.getHeaders().containsKey("filter3")); // MyFeature - assertFalse(response.getHeaders().containsKey("filter1")); + assertThat(response.getStatus(), is(200)); + assertThat(response.getHeaders(), hasKey("sharedfilter")); + assertThat(response.getHeaders(), hasKey("filter2")); + assertThat(response.getHeaders(), hasKey("filter3")); // MyFeature + assertThat(response.getHeaders(), not(hasKey("filter1"))); JsonObject jsonObject = response.readEntity(JsonObject.class); - assertEquals("Hello World 2!", jsonObject.getString("message"), - "default message"); + assertThat("default message", jsonObject.getString("message"), + is("Hello World 2!")); } @Test From 56fc41e25f594df292dd1bb286b9a0551a66b9fb Mon Sep 17 00:00:00 2001 From: Andrii Serkes Date: Sun, 11 Dec 2022 21:35:59 +0100 Subject: [PATCH 12/16] WebServer.Builder media support methods with Supplier variants (#5632) * remove -Ddocker.build=true Signed-off-by: aserkes * add methods that accept a Supplier argument Signed-off-by: aserkes Signed-off-by: aserkes --- .../media/common/MediaContextBuilder.java | 54 ++++++++++++++++++- .../common/ParentingMediaContextBuilder.java | 15 +++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/media/common/src/main/java/io/helidon/media/common/MediaContextBuilder.java b/media/common/src/main/java/io/helidon/media/common/MediaContextBuilder.java index f16b43b4ec0..938405efa09 100644 --- a/media/common/src/main/java/io/helidon/media/common/MediaContextBuilder.java +++ b/media/common/src/main/java/io/helidon/media/common/MediaContextBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Oracle and/or its affiliates. + * Copyright (c) 2020, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ */ package io.helidon.media.common; +import java.util.function.Supplier; + /** * Adds methods to support adding readers, writers and media services to the builder. * @@ -30,6 +32,16 @@ public interface MediaContextBuilder { */ T addMediaSupport(MediaSupport mediaSupport); + /** + * Adds new instance of {@link MediaSupport}. + * + * @param mediaSupportSupplier supplier for media support + * @return updated instance of the builder + */ + default T addMediaSupport(Supplier mediaSupportSupplier) { + return addMediaSupport(mediaSupportSupplier.get()); + } + /** * Registers new reader. * @@ -38,6 +50,16 @@ public interface MediaContextBuilder { */ T addReader(MessageBodyReader reader); + /** + * Registers new reader. + * + * @param readerSupplier supplier for a new reader + * @return updated instance of the builder + */ + default T addReader(Supplier> readerSupplier) { + return addReader(readerSupplier.get()); + } + /** * Registers new stream reader. * @@ -46,6 +68,16 @@ public interface MediaContextBuilder { */ T addStreamReader(MessageBodyStreamReader streamReader); + /** + * Registers new stream reader. + * + * @param streamReaderSupplier supplier for a new stream reader + * @return updated instance of the builder + */ + default T addStreamReader(Supplier> streamReaderSupplier) { + return addStreamReader(streamReaderSupplier.get()); + } + /** * Registers new writer. * @@ -54,6 +86,16 @@ public interface MediaContextBuilder { */ T addWriter(MessageBodyWriter writer); + /** + * Registers new writer. + * + * @param writerSupplier writer + * @return updated instance of the builder + */ + default T addWriter(Supplier> writerSupplier){ + return addWriter(writerSupplier.get()); + } + /** * Registers new stream writer. * @@ -62,4 +104,14 @@ public interface MediaContextBuilder { */ T addStreamWriter(MessageBodyStreamWriter streamWriter); + /** + * Registers new stream writer. + * + * @param streamWriterSupplier stream writer + * @return updated instance of the builder + */ + default T addStreamWriter(Supplier> streamWriterSupplier){ + return addStreamWriter(streamWriterSupplier.get()); + } + } diff --git a/media/common/src/main/java/io/helidon/media/common/ParentingMediaContextBuilder.java b/media/common/src/main/java/io/helidon/media/common/ParentingMediaContextBuilder.java index fcdfeff0c99..5e1cd76fcab 100644 --- a/media/common/src/main/java/io/helidon/media/common/ParentingMediaContextBuilder.java +++ b/media/common/src/main/java/io/helidon/media/common/ParentingMediaContextBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Oracle and/or its affiliates. + * Copyright (c) 2020, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ */ package io.helidon.media.common; +import java.util.function.Supplier; + /** * Builder of {@link MediaContext} that can be parented. * @@ -31,4 +33,15 @@ public interface ParentingMediaContextBuilder { */ T mediaContext(MediaContext mediaContext); + /** + * Sets the {@link MediaContext} parent and overrides the existing one. + * This method discards all previously registered readers and writers via builder. + * + * @param mediaContextSupplier supplier for media context + * @return updated instance of the builder + */ + default T mediaContext(Supplier mediaContextSupplier){ + return mediaContext(mediaContextSupplier.get()); + } + } From f811986dc594841534067a82aef644e1a48653f6 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Mon, 12 Dec 2022 17:39:35 -0600 Subject: [PATCH 13/16] [3.x] Various metrics test improvements to avoid intermittent failures (#5621) --- .../config/testing/MatcherWithRetry.java | 12 ++- integrations/micrometer/cdi/pom.xml | 5 + .../micrometer/cdi/HelloWorldTest.java | 10 +- .../micrometer/cdi/MeteredBeanTest.java | 16 +++- .../helidon/metrics/TestPeriodicExecutor.java | 92 ++++++++++++++----- .../metrics/HelloWorldAsyncResponseTest.java | 2 +- ...WorldAsyncResponseWithRestRequestTest.java | 39 ++++---- ...ldRestEndpointSimpleTimerDisabledTest.java | 12 ++- .../microprofile/metrics/HelloWorldTest.java | 43 +++++---- 9 files changed, 149 insertions(+), 82 deletions(-) diff --git a/config/testing/src/main/java/io/helidon/config/testing/MatcherWithRetry.java b/config/testing/src/main/java/io/helidon/config/testing/MatcherWithRetry.java index b96dd061102..20e948f96a9 100644 --- a/config/testing/src/main/java/io/helidon/config/testing/MatcherWithRetry.java +++ b/config/testing/src/main/java/io/helidon/config/testing/MatcherWithRetry.java @@ -22,6 +22,8 @@ import org.hamcrest.Matcher; import org.hamcrest.StringDescription; +import static org.junit.jupiter.api.Assertions.fail; + /** * Hamcrest matcher capable of configured retries before failing the assertion, plus more generic retry processing. */ @@ -41,10 +43,8 @@ private MatcherWithRetry() { * @param matcher Hamcrest matcher which evaluates the supplied value * @return the supplied value * @param type of the supplied value - * @throws InterruptedException if the sleep is interrupted */ - public static T assertThatWithRetry(String reason, Supplier actualSupplier, Matcher matcher) - throws InterruptedException { + public static T assertThatWithRetry(String reason, Supplier actualSupplier, Matcher matcher) { T actual = null; for (int i = 0; i < RETRY_COUNT; i++) { @@ -52,7 +52,11 @@ public static T assertThatWithRetry(String reason, Supplier actualSupplie if (matcher.matches(actual)) { return actual; } - Thread.sleep(RETRY_DELAY_MS); + try { + Thread.sleep(RETRY_DELAY_MS); + } catch (InterruptedException e) { + fail("Error sleeping during assertThatWithRetry", e); + } } Description description = new StringDescription(); diff --git a/integrations/micrometer/cdi/pom.xml b/integrations/micrometer/cdi/pom.xml index 143978b1b43..38fdb01c1f7 100644 --- a/integrations/micrometer/cdi/pom.xml +++ b/integrations/micrometer/cdi/pom.xml @@ -115,6 +115,11 @@ helidon-microprofile-tests-junit5 test + + io.helidon.config + helidon-config-testing + test + diff --git a/integrations/micrometer/cdi/src/test/java/io/helidon/integrations/micrometer/cdi/HelloWorldTest.java b/integrations/micrometer/cdi/src/test/java/io/helidon/integrations/micrometer/cdi/HelloWorldTest.java index bbfb288db1a..6bcd8718e3e 100644 --- a/integrations/micrometer/cdi/src/test/java/io/helidon/integrations/micrometer/cdi/HelloWorldTest.java +++ b/integrations/micrometer/cdi/src/test/java/io/helidon/integrations/micrometer/cdi/HelloWorldTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,6 +31,8 @@ import jakarta.ws.rs.core.MediaType; import org.junit.jupiter.api.Test; +import static io.helidon.config.testing.MatcherWithRetry.assertThatWithRetry; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; @@ -95,7 +97,7 @@ public void testSlowTimer() { assertThat("Returned from HTTP request", result.get(), is(HelloWorldResource.SLOW_RESPONSE)); Timer slowTimer = registry.timer(HelloWorldResource.SLOW_MESSAGE_TIMER); assertThat("Slow message timer", slowTimer, is(notNullValue())); - assertThat("Slow message timer count", slowTimer.count(), is((long) exp)); + assertThatWithRetry("Slow message timer count", slowTimer::count, is((long) exp)); } @Test @@ -116,7 +118,7 @@ public void testFastFailCounter() { Counter counter = registry.counter(HelloWorldResource.FAST_MESSAGE_COUNTER); assertThat("Failed message counter", counter, is(notNullValue())); - assertThat("Failed message counter count", counter.count(), is((double) exp / 2)); + assertThatWithRetry("Failed message counter count", counter::count, is((double) exp / 2)); } @Test @@ -151,7 +153,7 @@ public void testSlowFailCounter() { Counter counter = registry.counter(HelloWorldResource.SLOW_MESSAGE_FAIL_COUNTER); assertThat("Failed message counter", counter, is(notNullValue())); - assertThat("Failed message counter count", counter.count(), is((double) 3)); + assertThatWithRetry("Failed message counter count", counter::count, is((double) 3)); } void checkMicrometerURL(int iterations) { diff --git a/integrations/micrometer/cdi/src/test/java/io/helidon/integrations/micrometer/cdi/MeteredBeanTest.java b/integrations/micrometer/cdi/src/test/java/io/helidon/integrations/micrometer/cdi/MeteredBeanTest.java index 88d80538485..33e0323671c 100644 --- a/integrations/micrometer/cdi/src/test/java/io/helidon/integrations/micrometer/cdi/MeteredBeanTest.java +++ b/integrations/micrometer/cdi/src/test/java/io/helidon/integrations/micrometer/cdi/MeteredBeanTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,8 @@ import jakarta.inject.Inject; import org.junit.jupiter.api.Test; +import static io.helidon.config.testing.MatcherWithRetry.assertThatWithRetry; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -42,22 +44,26 @@ public class MeteredBeanTest { public void testSimpleCounted() { int exp = 3; IntStream.range(0, exp).forEach(i -> meteredBean.count()); - assertThat("Value from simple counted meter", registry.counter(MeteredBean.COUNTED).count(), is((double) exp)); + assertThatWithRetry("Value from simple counted meter", () -> registry.counter(MeteredBean.COUNTED).count(), + is((double) exp)); } @Test public void testSinglyTimed() { int exp = 4; IntStream.range(0, exp).forEach(i -> meteredBean.timed()); - assertThat("Count from singly-timed meter", registry.timer(MeteredBean.TIMED_1).count(), is((long) exp)); + assertThatWithRetry("Count from singly-timed meter", () -> registry.timer(MeteredBean.TIMED_1).count(), + is((long) exp)); } @Test public void testDoublyTimed() { int exp = 5; IntStream.range(0, exp).forEach(i -> meteredBean.timedA()); - assertThat("Count from doubly-timed meter (A)", registry.timer(MeteredBean.TIMED_A).count(), is((long) exp)); - assertThat("Count from doubly-timed meter (B)", registry.timer(MeteredBean.TIMED_B).count(), is((long) exp)); + assertThatWithRetry("Count from doubly-timed meter (A)", () -> registry.timer(MeteredBean.TIMED_A).count(), + is((long) exp)); + assertThatWithRetry("Count from doubly-timed meter (B)", () -> registry.timer(MeteredBean.TIMED_B).count(), + is((long) exp)); } @Test diff --git a/metrics/metrics/src/test/java/io/helidon/metrics/TestPeriodicExecutor.java b/metrics/metrics/src/test/java/io/helidon/metrics/TestPeriodicExecutor.java index 6f7fa108c5e..c8cc4d857ad 100644 --- a/metrics/metrics/src/test/java/io/helidon/metrics/TestPeriodicExecutor.java +++ b/metrics/metrics/src/test/java/io/helidon/metrics/TestPeriodicExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,8 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Handler; import java.util.logging.Level; @@ -33,20 +35,20 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.fail; class TestPeriodicExecutor { - private static final int SLEEP_TIME_MS = 1500; - private static final int SLEEP_TIME_NO_DATA_MS = 100; + private static final int APPROX_TEST_DURATION_MS = 1500; + private static final int MAX_TEST_WAIT_TIME_MS = APPROX_TEST_DURATION_MS * 15 / 10; // 1.5 * approx test duration private static final int FAST_INTERVAL = 250; private static final int SLOW_INTERVAL = 400; - private static final double SLOWDOWN_FACTOR = 0.80; // for slow pipelines! - - private static final double MIN_FAST_COUNT = 1500 / FAST_INTERVAL * SLOWDOWN_FACTOR; - private static final double MIN_SLOW_COUNT = 1500 / SLOW_INTERVAL * SLOWDOWN_FACTOR; + private static final int MIN_FAST_COUNT = APPROX_TEST_DURATION_MS / FAST_INTERVAL; + private static final int MIN_SLOW_COUNT = APPROX_TEST_DURATION_MS / SLOW_INTERVAL; private static final Logger PERIODIC_EXECUTOR_LOGGER = Logger.getLogger(PeriodicExecutor.class.getName()); @@ -59,13 +61,28 @@ void testWithNoDeferrals() throws InterruptedException { AtomicInteger countA = new AtomicInteger(); AtomicInteger countB = new AtomicInteger(); - exec.enrollRunner(() -> countA.incrementAndGet(), Duration.ofMillis(FAST_INTERVAL)); - exec.enrollRunner(() -> countB.incrementAndGet(), Duration.ofMillis(SLOW_INTERVAL)); + CountDownLatch latchA = new CountDownLatch(MIN_FAST_COUNT); + CountDownLatch latchB = new CountDownLatch(MIN_SLOW_COUNT); + + long startTime = System.nanoTime(); + exec.enrollRunner(() -> { + countA.incrementAndGet(); + latchA.countDown(); + }, Duration.ofMillis(FAST_INTERVAL)); + + exec.enrollRunner(() -> { + countB.incrementAndGet(); + latchB.countDown(); + }, Duration.ofMillis(SLOW_INTERVAL)); - Thread.sleep(SLEEP_TIME_MS); + assertThat("Wait latch for fast interval", latchA.await(MAX_TEST_WAIT_TIME_MS, TimeUnit.MILLISECONDS), is(true)); + assertThat("Wait latch for slow interval", latchB.await(MAX_TEST_WAIT_TIME_MS, TimeUnit.MILLISECONDS), is(true)); - assertThat("CountA", (double) countA.get(), is(greaterThan(MIN_FAST_COUNT))); - assertThat("CountB", (double) countB.get(), is(greaterThan(MIN_SLOW_COUNT))); + Duration elapsedTime = Duration.ofNanos(System.nanoTime() - startTime); + + assertThat("CountA", countA.get(), is(greaterThanOrEqualTo(MIN_FAST_COUNT))); + assertThat("CountB", countB.get(), is(greaterThanOrEqualTo(MIN_SLOW_COUNT))); + assertThat("Wait duration", elapsedTime, greaterThanOrEqualTo(Duration.ofMillis(1500))); } finally { if (exec.executorState() == PeriodicExecutor.State.STARTED) { exec.stopExecutor(); @@ -80,16 +97,23 @@ void testWithDeferredEnrollments() throws InterruptedException { AtomicInteger countA = new AtomicInteger(); AtomicInteger countB = new AtomicInteger(); - exec.enrollRunner(() -> countA.incrementAndGet(), Duration.ofMillis(FAST_INTERVAL)); + CountDownLatch latchA = new CountDownLatch(MIN_FAST_COUNT); + CountDownLatch latchB = new CountDownLatch(MIN_SLOW_COUNT); - exec.startExecutor(); + exec.enrollRunner(() -> { + countA.incrementAndGet(); + latchA.countDown(); + }, Duration.ofMillis(FAST_INTERVAL)); - exec.enrollRunner(() -> countB.incrementAndGet(), Duration.ofMillis(SLOW_INTERVAL)); + exec.startExecutor(); - Thread.sleep(SLEEP_TIME_MS); + exec.enrollRunner(() -> { + countB.incrementAndGet(); + latchB.countDown(); + }, Duration.ofMillis(SLOW_INTERVAL)); - assertThat("CountA", (double) countA.get(), is(greaterThan(MIN_FAST_COUNT))); - assertThat("CountB", (double) countB.get(), is(greaterThan(MIN_SLOW_COUNT))); + assertThat("Wait latch for fast interval", latchA.await(MAX_TEST_WAIT_TIME_MS, TimeUnit.MILLISECONDS), is(true)); + assertThat("Wait latch for slow interval", latchB.await(MAX_TEST_WAIT_TIME_MS, TimeUnit.MILLISECONDS), is(true)); } finally { if (exec.executorState() == PeriodicExecutor.State.STARTED) { exec.stopExecutor(); @@ -104,17 +128,25 @@ void testWithLateEnrollment() throws InterruptedException { AtomicInteger countA = new AtomicInteger(); AtomicInteger countB = new AtomicInteger(); - exec.enrollRunner(() -> countA.incrementAndGet(), Duration.ofMillis(FAST_INTERVAL)); + CountDownLatch latchA = new CountDownLatch(MIN_FAST_COUNT); + + exec.enrollRunner(() -> { + countA.incrementAndGet(); + latchA.countDown(); + }, Duration.ofMillis(FAST_INTERVAL)); exec.startExecutor(); - Thread.sleep(SLEEP_TIME_MS); + assertThat("Wait latch", latchA.await(MAX_TEST_WAIT_TIME_MS, TimeUnit.MILLISECONDS), is(true)); exec.stopExecutor(); exec.enrollRunner(() -> countB.incrementAndGet(), Duration.ofMillis(SLOW_INTERVAL)); - assertThat("CountA", (double) countA.get(), is(greaterThan(MIN_FAST_COUNT))); // should be 8 - assertThat("CountB", (double) countB.get(), is(0.0)); + // The executor is no longer running, so we cannot use a countdown latch to know when to check countB. Use time. + Thread.sleep(MAX_TEST_WAIT_TIME_MS); + + assertThat("CountA", countA.get(), is(greaterThanOrEqualTo(MIN_FAST_COUNT))); // should be 8 + assertThat("CountB", countB.get(), is(0)); } finally { if (exec.executorState() == PeriodicExecutor.State.STARTED) { exec.stopExecutor(); @@ -130,7 +162,8 @@ void testNoWarningOnStopWhenStopped() throws InterruptedException { try { PeriodicExecutor executor = PeriodicExecutor.create(); executor.stopExecutor(); - Thread.sleep(SLEEP_TIME_NO_DATA_MS); + + waitForExecutorState(executor, PeriodicExecutor.State.STOPPED); handler.clear(); executor.stopExecutor(); @@ -152,7 +185,7 @@ void testFineMessageOnStopWhenStopped() throws InterruptedException { PERIODIC_EXECUTOR_LOGGER.setLevel(Level.FINE); PeriodicExecutor executor = PeriodicExecutor.create(); executor.stopExecutor(); - Thread.sleep(SLEEP_TIME_NO_DATA_MS); + waitForExecutorState(executor, PeriodicExecutor.State.STOPPED); executor.stopExecutor(); handler.clear(); @@ -184,7 +217,7 @@ void testFineLoggingOnExpectedStop(PeriodicExecutor.State testState) throws Inte if (testState == PeriodicExecutor.State.STARTED) { executor.startExecutor(); - Thread.sleep(SLEEP_TIME_NO_DATA_MS); + waitForExecutorState(executor, PeriodicExecutor.State.STARTED); } handler.clear(); @@ -205,6 +238,15 @@ void testFineLoggingOnExpectedStop(PeriodicExecutor.State testState) throws Inte } } + private void waitForExecutorState(PeriodicExecutor executor, PeriodicExecutor.State expectedState) { + for (int i = MAX_TEST_WAIT_TIME_MS; i > 0; i -= 500) { + if (executor.executorState() == expectedState) { + return; + } + } + fail("Timed out waiting for executor in state " + executor.executorState() + " to enter state " + expectedState.name()); + } + private static class MyHandler extends Handler { private List logRecords = new ArrayList<>(); diff --git a/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldAsyncResponseTest.java b/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldAsyncResponseTest.java index ef4e5d05dfa..11fb29dc255 100644 --- a/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldAsyncResponseTest.java +++ b/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldAsyncResponseTest.java @@ -133,7 +133,7 @@ public void test() throws Exception { } @Test - public void testAsyncWithArg() throws InterruptedException { + public void testAsyncWithArg() { LongStream.range(0, 3).forEach( i -> webTarget .path("helloworld/slowWithArg/Johan") diff --git a/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldAsyncResponseWithRestRequestTest.java b/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldAsyncResponseWithRestRequestTest.java index 67014de60ff..21691fe8b6e 100644 --- a/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldAsyncResponseWithRestRequestTest.java +++ b/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldAsyncResponseWithRestRequestTest.java @@ -88,28 +88,23 @@ void checkForAsyncMethodRESTRequestMetric() throws NoSuchMethodException { AtomicReference nextRestRequest = new AtomicReference<>(); - try { - // With async endpoints, metrics updates can occur after the server sends the response. - // Retry as needed (including fetching the metrics again) for a little while for the count to change. - assertThatWithRetry("getAsync count value after invocation", - () -> JsonNumber.class.cast(getRESTRequestValueForGetAsyncMethod( - // Set the reference using fresh metrics and get that newly-set JSON object, then - // extract the 'count' field cast as a number. - nextRestRequest.updateAndGet(old -> getRESTRequestJSON()), - "count", - false)) - .longValue(), - is(1L)); - - // Reuse (no need to update the atomic reference again) the freshly-fetched metrics JSON. - getAsyncTime = JsonNumber.class.cast(getRESTRequestValueForGetAsyncMethod(nextRestRequest.get(), - "elapsedTime", - false)); - assertThat("getAsync elapsedTime value after invocation", getAsyncTime.longValue(), is(greaterThan(0L))); - } catch (Exception e) { - throw new RuntimeException("Dump of REST.request metrics due to following assertion failure\n" - + restRequest, e); - } + // With async endpoints, metrics updates can occur after the server sends the response. + // Retry as needed (including fetching the metrics again) for a little while for the count to change. + assertThatWithRetry("getAsync count value after invocation", + () -> JsonNumber.class.cast(getRESTRequestValueForGetAsyncMethod( + // Set the reference using fresh metrics and get that newly-set JSON object, then + // extract the 'count' field cast as a number. + nextRestRequest.updateAndGet(old -> getRESTRequestJSON()), + "count", + false)) + .longValue(), + is(1L)); + + // Reuse (no need to update the atomic reference again) the freshly-fetched metrics JSON. + getAsyncTime = JsonNumber.class.cast(getRESTRequestValueForGetAsyncMethod(nextRestRequest.get(), + "elapsedTime", + false)); + assertThat("getAsync elapsedTime value after invocation", getAsyncTime.longValue(), is(greaterThan(0L))); } private JsonObject getRESTRequestJSON() { diff --git a/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldRestEndpointSimpleTimerDisabledTest.java b/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldRestEndpointSimpleTimerDisabledTest.java index 3a21ab3a3a9..f687e859a2b 100644 --- a/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldRestEndpointSimpleTimerDisabledTest.java +++ b/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldRestEndpointSimpleTimerDisabledTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,13 @@ package io.helidon.microprofile.metrics; +import io.helidon.microprofile.tests.junit5.AddConfig; import io.helidon.microprofile.tests.junit5.HelidonTest; import jakarta.inject.Inject; import org.eclipse.microprofile.metrics.MetricRegistry; import org.eclipse.microprofile.metrics.annotation.RegistryType; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; @@ -31,15 +33,21 @@ * the config disables that feature. */ @HelidonTest +@AddConfig(key = "metrics." + MetricsCdiExtension.REST_ENDPOINTS_METRIC_ENABLED_PROPERTY_NAME, value = "false") public class HelloWorldRestEndpointSimpleTimerDisabledTest { + @BeforeAll + static void init() { + MetricsMpServiceTest.cleanUpSyntheticSimpleTimerRegistry(); + } + @Inject @RegistryType(type = MetricRegistry.Type.BASE) MetricRegistry syntheticSimpleTimerRegistry; boolean isSyntheticSimpleTimerPresent() { return !syntheticSimpleTimerRegistry.getSimpleTimers((metricID, metric) -> - metricID.equals(MetricsCdiExtension.SYNTHETIC_SIMPLE_TIMER_METRIC_NAME)) + metricID.getName().equals(MetricsCdiExtension.SYNTHETIC_SIMPLE_TIMER_METRIC_NAME)) .isEmpty(); } diff --git a/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldTest.java b/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldTest.java index 4b2d29ff1ee..113bd9a381c 100644 --- a/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldTest.java +++ b/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldTest.java @@ -113,9 +113,9 @@ public void testMetrics() throws InterruptedException { assertThat("Value of explicitly-updated counter", registry.counter("helloCounter").getCount(), is((long) iterations)); - assertThat("Diff in value of interceptor-updated class-level counter for constructor", - classLevelCounterForConstructor.getCount() - classLevelCounterStart, - is((long) iterations)); + assertThatWithRetry("Diff in value of interceptor-updated class-level counter for constructor", + () -> classLevelCounterForConstructor.getCount() - classLevelCounterStart, + is((long) iterations)); Counter classLevelCounterForMethod = registry.getCounters().get(new MetricID(HelloWorldResource.class.getName() + ".message")); @@ -125,13 +125,13 @@ public void testMetrics() throws InterruptedException { SimpleTimer simpleTimer = getSyntheticSimpleTimer("message"); assertThat("Synthetic simple timer", simpleTimer, is(notNullValue())); - assertThat("Synthetic simple timer count value", simpleTimer.getCount(), is((long) iterations)); + assertThatWithRetry("Synthetic simple timer count value", simpleTimer::getCount, is((long) iterations)); checkMetricsUrl(iterations); } @Test - public void testSyntheticSimpleTimer() { + public void testSyntheticSimpleTimer() throws InterruptedException { testSyntheticSimpleTimer(1L); } @@ -182,6 +182,10 @@ void testUnmappedException() throws Exception { } void testSyntheticSimpleTimer(long expectedSyntheticSimpleTimerCount) { + SimpleTimer explicitSimpleTimer = registry.getSimpleTimer(new MetricID(MESSAGE_SIMPLE_TIMER)); + assertThat("SimpleTimer from explicit @SimplyTimed", explicitSimpleTimer, is(notNullValue())); + SimpleTimer syntheticSimpleTimer = getSyntheticSimpleTimer("messageWithArg", String.class); + assertThat("SimpleTimer from @SyntheticRestRequest", syntheticSimpleTimer, is(notNullValue())); IntStream.range(0, (int) expectedSyntheticSimpleTimerCount).forEach( i -> webTarget .path("helloworld/withArg/Joe") @@ -189,14 +193,13 @@ void testSyntheticSimpleTimer(long expectedSyntheticSimpleTimerCount) { .get(String.class)); pause(); - SimpleTimer explicitSimpleTimer = registry.simpleTimer(MESSAGE_SIMPLE_TIMER); - assertThat("SimpleTimer from explicit @SimplyTimed", explicitSimpleTimer, is(notNullValue())); - assertThat("SimpleTimer from explicit @SimpleTimed count", explicitSimpleTimer.getCount(), - is(expectedSyntheticSimpleTimerCount)); + assertThatWithRetry("SimpleTimer from explicit @SimpleTimed count", + explicitSimpleTimer::getCount, + is(expectedSyntheticSimpleTimerCount)); - SimpleTimer syntheticSimpleTimer = getSyntheticSimpleTimer("messageWithArg", String.class); - assertThat("SimpleTimer from @SyntheticRestRequest", syntheticSimpleTimer, is(notNullValue())); - assertThat("SimpleTimer from @SyntheticRestRequest count", syntheticSimpleTimer.getCount(), is(expectedSyntheticSimpleTimerCount)); + assertThatWithRetry("SimpleTimer from @SyntheticRestRequest count", + syntheticSimpleTimer::getCount, + is(expectedSyntheticSimpleTimerCount)); } SimpleTimer getSyntheticSimpleTimer(String methodName, Class... paramTypes) { @@ -218,12 +221,14 @@ SimpleTimer getSyntheticSimpleTimer(MetricID metricID) { } void checkMetricsUrl(int iterations) { - JsonObject app = webTarget - .path("metrics") - .request() - .accept(MediaType.APPLICATION_JSON_TYPE) - .get(JsonObject.class) - .getJsonObject("application"); - assertThat(app.getJsonNumber("helloCounter").intValue(), is(iterations)); + assertThatWithRetry("helloCounter count", () -> { + JsonObject app = webTarget + .path("metrics") + .request() + .accept(MediaType.APPLICATION_JSON_TYPE) + .get(JsonObject.class) + .getJsonObject("application"); + return app.getJsonNumber("helloCounter").intValue(); + }, is(iterations)); } } From 3e76f6456ccd1c43162c34a77642b0de0c243959 Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Tue, 13 Dec 2022 17:24:52 +0100 Subject: [PATCH 14/16] Adding feature file for MP native image. (#5652) Signed-off-by: Tomas Langer --- .../native-image.properties | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 integrations/graal/mp-native-image-extension/src/main/resources/META-INF/native-image/io.helidon.integrations.graal/helidon-mp-graal-native-image-extension/native-image.properties diff --git a/integrations/graal/mp-native-image-extension/src/main/resources/META-INF/native-image/io.helidon.integrations.graal/helidon-mp-graal-native-image-extension/native-image.properties b/integrations/graal/mp-native-image-extension/src/main/resources/META-INF/native-image/io.helidon.integrations.graal/helidon-mp-graal-native-image-extension/native-image.properties new file mode 100644 index 00000000000..6fc5d87449a --- /dev/null +++ b/integrations/graal/mp-native-image-extension/src/main/resources/META-INF/native-image/io.helidon.integrations.graal/helidon-mp-graal-native-image-extension/native-image.properties @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022 Oracle and/or its affiliates. +# +# 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 +# +# http://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. +# + +Args=--features=io.helidon.integrations.graal.mp.nativeimage.extension.WeldFeature From e582e0bd4f4c3f82e0c10d9aaac15ce07da59e7a Mon Sep 17 00:00:00 2001 From: Daniel Kec Date: Tue, 13 Dec 2022 18:19:01 +0100 Subject: [PATCH 15/16] Native image Signed-off-by: Daniel Kec --- .../native-image.properties | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 microprofile/server/src/main/resources/META-INF/native-image/io.helidon.microprofile.jaxrs/helidon-microprofile-jaxrs/native-image.properties diff --git a/microprofile/server/src/main/resources/META-INF/native-image/io.helidon.microprofile.jaxrs/helidon-microprofile-jaxrs/native-image.properties b/microprofile/server/src/main/resources/META-INF/native-image/io.helidon.microprofile.jaxrs/helidon-microprofile-jaxrs/native-image.properties new file mode 100644 index 00000000000..ad103d0500e --- /dev/null +++ b/microprofile/server/src/main/resources/META-INF/native-image/io.helidon.microprofile.jaxrs/helidon-microprofile-jaxrs/native-image.properties @@ -0,0 +1,16 @@ +# +# Copyright (c) 2020, 2022 Oracle and/or its affiliates. +# +# 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 +# +# http://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. +# +Args=--initialize-at-build-time=org.crac.Core From a27e5fbc0bfd2306934666c4d03c1622ce583ba1 Mon Sep 17 00:00:00 2001 From: Daniel Kec Date: Fri, 16 Dec 2022 16:40:32 +0100 Subject: [PATCH 16/16] Startup time measurement Signed-off-by: Daniel Kec --- .../java/io/helidon/microprofile/server/ServerCdiExtension.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java b/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java index 377e7fa7d92..aa8e0e8e8c3 100644 --- a/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java +++ b/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java @@ -244,6 +244,8 @@ private void startServer(@Observes @Priority(PLATFORM_AFTER + 100) @Initialized( LOGGER.info(() -> "Server started on " + protocol + "://" + host + ":" + port + note + startupTimeReport); + //TODO: Remove, for startup time measurements only + if (Boolean.parseBoolean(System.getProperty("dieAfterStart", "false"))) System.exit(0); // this is not needed at runtime, collect garbage serverBuilder = null;