diff --git a/.gitignore b/.gitignore index b842221..5671a9a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ .idea/ helidon/target/ +helidon-se/target/ +helidon-se/.classpath +helidon-se/.factorypath +helidon-se/.project +*.prefs diff --git a/helidon-se/.dockerignore b/helidon-se/.dockerignore new file mode 100644 index 0000000..c8b241f --- /dev/null +++ b/helidon-se/.dockerignore @@ -0,0 +1 @@ +target/* \ No newline at end of file diff --git a/helidon-se/.helidon b/helidon-se/.helidon new file mode 100644 index 0000000..4836e90 --- /dev/null +++ b/helidon-se/.helidon @@ -0,0 +1,6 @@ +#Helidon Project Configuration +#Thu Oct 12 14:27:24 MDT 2023 +schema.version=1.1.0 +helidon.version=3.2.2 +project.flavor=mp +project.archetype=quickstart diff --git a/helidon-se/Dockerfile b/helidon-se/Dockerfile new file mode 100644 index 0000000..4c2115c --- /dev/null +++ b/helidon-se/Dockerfile @@ -0,0 +1,39 @@ + +# 1st stage, build the app +FROM container-registry.oracle.com/java/openjdk:21 as build + +# Install maven +WORKDIR /usr/share +RUN set -x && \ + curl -O https://archive.apache.org/dist/maven/maven-3/3.8.4/binaries/apache-maven-3.8.4-bin.tar.gz && \ + tar -xvf apache-maven-*-bin.tar.gz && \ + rm apache-maven-*-bin.tar.gz && \ + mv apache-maven-* maven && \ + ln -s /usr/share/maven/bin/mvn /bin/ + +WORKDIR /helidon + +# Create a first layer to cache the "Maven World" in the local repository. +# Incremental docker builds will always resume after that, unless you update +# the pom +ADD pom.xml pom.xml +RUN mvn package -Dmaven.test.skip -Declipselink.weave.skip + +# Do the Maven build! +# Incremental docker builds will resume here when you change sources +ADD src src +RUN mvn package -DskipTests + +RUN echo "done!" + +# 2nd stage, build the runtime image +FROM container-registry.oracle.com/java/openjdk:21 +WORKDIR /helidon + +# Copy the binary built in the 1st stage +COPY --from=build /helidon/target/helidon-se.jar ./ +COPY --from=build /helidon/target/libs ./libs + +CMD ["java", "-jar", "helidon-se.jar"] + +EXPOSE 8080 diff --git a/helidon-se/Dockerfile.jlink b/helidon-se/Dockerfile.jlink new file mode 100644 index 0000000..baa608c --- /dev/null +++ b/helidon-se/Dockerfile.jlink @@ -0,0 +1,25 @@ + +# 1st stage, build the app +FROM maven:3.8.4-openjdk-17-slim as build + +WORKDIR /helidon + +# Create a first layer to cache the "Maven World" in the local repository. +# Incremental docker builds will always resume after that, unless you update +# the pom +ADD pom.xml . +RUN mvn package -Dmaven.test.skip -Declipselink.weave.skip + +# Do the Maven build to create the custom Java Runtime Image +# Incremental docker builds will resume here when you change sources +ADD src src +RUN mvn package -Pjlink-image -DskipTests +RUN echo "done!" + +# 2nd stage, build the final image with the JRI built in the 1st stage + +FROM debian:stretch-slim +WORKDIR /helidon +COPY --from=build /helidon/target/helidon-se-jri ./ +ENTRYPOINT ["/bin/bash", "/helidon/bin/start"] +EXPOSE 8080 diff --git a/helidon-se/Dockerfile.native b/helidon-se/Dockerfile.native new file mode 100644 index 0000000..0747609 --- /dev/null +++ b/helidon-se/Dockerfile.native @@ -0,0 +1,39 @@ + +# 1st stage, build the app +FROM ghcr.io/graalvm/graalvm-community:21.0.0-ol9 as build + +WORKDIR /usr/share + +# Install maven +RUN set -x && \ + curl -O https://archive.apache.org/dist/maven/maven-3/3.8.4/binaries/apache-maven-3.8.4-bin.tar.gz && \ + tar -xvf apache-maven-*-bin.tar.gz && \ + rm apache-maven-*-bin.tar.gz && \ + mv apache-maven-* maven && \ + ln -s /usr/share/maven/bin/mvn /bin/ + +WORKDIR /helidon + +# Create a first layer to cache the "Maven World" in the local repository. +# Incremental docker builds will always resume after that, unless you update +# the pom +ADD pom.xml . +RUN mvn package -Pnative-image -Dnative.image.skip -Dmaven.test.skip -Declipselink.weave.skip + +# Do the Maven build! +# Incremental docker builds will resume here when you change sources +ADD src src +RUN mvn package -Pnative-image -Dnative.image.buildStatic -DskipTests + +RUN echo "done!" + +# 2nd stage, build the runtime image +FROM scratch +WORKDIR /helidon + +# Copy the binary built in the 1st stage +COPY --from=build /helidon/target/helidon-se . + +ENTRYPOINT ["./helidon-se"] + +EXPOSE 8080 diff --git a/helidon-se/README.md b/helidon-se/README.md new file mode 100644 index 0000000..d4096b9 --- /dev/null +++ b/helidon-se/README.md @@ -0,0 +1,133 @@ +# helidon + +Minimal Helidon MP project suitable to start from scratch. + +## Build and run + + +With JDK17+ +```bash +mvn package +java -jar target/helidon.jar +``` + +## Exercise the application +``` +curl -X GET http://localhost:8080/simple-greet +{"message":"Hello World!"} +``` + +``` +curl -X GET http://localhost:8080/greet +{"message":"Hello World!"} + +curl -X GET http://localhost:8080/greet/Joe +{"message":"Hello Joe!"} + +curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Hola"}' http://localhost:8080/greet/greeting + +curl -X GET http://localhost:8080/greet/Jose +{"message":"Hola Jose!"} +``` + + + +## Try metrics + +``` +# Prometheus Format +curl -s -X GET http://localhost:8080/metrics +# TYPE base:gc_g1_young_generation_count gauge +. . . + +# JSON Format +curl -H 'Accept: application/json' -X GET http://localhost:8080/metrics +{"base":... +. . . +``` + + + +## Try health + +``` +curl -s -X GET http://localhost:8080/health +{"outcome":"UP",... + +``` + + + +## Building a Native Image + +Make sure you have GraalVM locally installed: + +``` +$GRAALVM_HOME/bin/native-image --version +``` + +Build the native image using the native image profile: + +``` +mvn package -Pnative-image +``` + +This uses the helidon-maven-plugin to perform the native compilation using your installed copy of GraalVM. It might take a while to complete. +Once it completes start the application using the native executable (no JVM!): + +``` +./target/helidon +``` + +Yep, it starts fast. You can exercise the application’s endpoints as before. + + +## Building the Docker Image + +``` +docker build -t helidon . +``` + +## Running the Docker Image + +``` +docker run --rm -p 8080:8080 helidon:latest +``` + +Exercise the application as described above. + + +## Building a Custom Runtime Image + +Build the custom runtime image using the jlink image profile: + +``` +mvn package -Pjlink-image +``` + +This uses the helidon-maven-plugin to perform the custom image generation. +After the build completes it will report some statistics about the build including the reduction in image size. + +The target/helidon-jri directory is a self contained custom image of your application. It contains your application, +its runtime dependencies and the JDK modules it depends on. You can start your application using the provide start script: + +``` +./target/helidon-jri/bin/start +``` + +Class Data Sharing (CDS) Archive +Also included in the custom image is a Class Data Sharing (CDS) archive that improves your application’s startup +performance and in-memory footprint. You can learn more about Class Data Sharing in the JDK documentation. + +The CDS archive increases your image size to get these performance optimizations. It can be of significant size (tens of MB). +The size of the CDS archive is reported at the end of the build output. + +If you’d rather have a smaller image size (with a slightly increased startup time) you can skip the creation of the CDS +archive by executing your build like this: + +``` +mvn package -Pjlink-image -Djlink.image.addClassDataSharingArchive=false +``` + +For more information on available configuration options see the helidon-maven-plugin documentation. + diff --git a/helidon-se/pom.xml b/helidon-se/pom.xml new file mode 100644 index 0000000..2e32cbe --- /dev/null +++ b/helidon-se/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + io.helidon.applications + helidon-se + 4.0.0 + + + com.okta.rest + helidon-se + 1.0-SNAPSHOT + + + com.okta.rest.HelloApplication + + + + + io.helidon.webserver + helidon-webserver + + + io.helidon.webserver + helidon-webserver-security + + + io.helidon.webserver + helidon-webserver-context + + + io.helidon.security.providers + helidon-security-providers-jwt + + + io.helidon.http.media + helidon-http-media-jsonp + + + diff --git a/helidon-se/src/main/java/com/okta/rest/HelloApplication.java b/helidon-se/src/main/java/com/okta/rest/HelloApplication.java new file mode 100644 index 0000000..b798647 --- /dev/null +++ b/helidon-se/src/main/java/com/okta/rest/HelloApplication.java @@ -0,0 +1,53 @@ +package com.okta.rest; + +import java.net.URI; + +import com.okta.rest.controller.HelloResource; + +import io.helidon.common.configurable.Resource; +import io.helidon.config.Config; +import io.helidon.security.Security; +import io.helidon.security.providers.jwt.JwtProvider; +import io.helidon.webserver.WebServer; +import io.helidon.webserver.context.ContextFeature; +import io.helidon.webserver.http.HttpRouting; +import io.helidon.webserver.security.SecurityFeature; + +public class HelloApplication { + + public static void main(String[] args) { + + var config = Config.global(); + var oauth = + JwtProvider.builder() + .issuer(config.get("se.jwt.verify.issuer").asString().get()) + .verifyJwk( + Resource.create( + config + .get("se.jwt.verify.publickey.location") + .asString() + .map(URI::create) + .orElseThrow())) + .build(); + + Security security = Security.builder().addProvider(oauth).build(); + var securityFeature = + SecurityFeature.create( + sfb -> + sfb.security(security) + .addPath(p -> p.path("/hello").handler(h -> h.authenticate(true)))); + + WebServer.builder() + .config(config.get("server")) + .routing(HelloApplication::routing) + .addFeature(ContextFeature.create()) + .addFeature(securityFeature) + .build() + .start(); + } + + /** Updates HTTP Routing. */ + static void routing(HttpRouting.Builder routing) { + routing.addFeature(new HelloResource()); + } +} diff --git a/helidon-se/src/main/java/com/okta/rest/controller/HelloResource.java b/helidon-se/src/main/java/com/okta/rest/controller/HelloResource.java new file mode 100644 index 0000000..b6fae9a --- /dev/null +++ b/helidon-se/src/main/java/com/okta/rest/controller/HelloResource.java @@ -0,0 +1,26 @@ +package com.okta.rest.controller; + +import static io.helidon.http.Status.OK_200; + +import io.helidon.common.media.type.MediaTypes; +import io.helidon.security.SecurityContext; +import io.helidon.webserver.http.HttpFeature; +import io.helidon.webserver.http.HttpRouting; +import io.helidon.webserver.http.ServerRequest; +import io.helidon.webserver.http.ServerResponse; + +public class HelloResource implements HttpFeature { + + @Override + public void setup(HttpRouting.Builder routing) { + routing.get("/hello", this::hello); + } + + public void hello(ServerRequest req, ServerResponse res) { + + SecurityContext context = req.context().get(SecurityContext.class).orElseThrow(); + res.status(OK_200); + res.headers().contentType(MediaTypes.TEXT_PLAIN); + res.send("Hello, " + context.userName() + "!"); + } +} diff --git a/helidon-se/src/main/resources/META-INF/native-image/com.okta.rest/helidon/native-image.properties b/helidon-se/src/main/resources/META-INF/native-image/com.okta.rest/helidon/native-image.properties new file mode 100644 index 0000000..e677c0a --- /dev/null +++ b/helidon-se/src/main/resources/META-INF/native-image/com.okta.rest/helidon/native-image.properties @@ -0,0 +1,3 @@ +Args=--initialize-at-build-time=com.okta.rest \ + --enable-url-protocols=https \ + --report-unsupported-elements-at-runtime diff --git a/helidon-se/src/main/resources/application.properties b/helidon-se/src/main/resources/application.properties new file mode 100644 index 0000000..d561137 --- /dev/null +++ b/helidon-se/src/main/resources/application.properties @@ -0,0 +1,6 @@ +# Microprofile server properties +server.port=8080 +server.host=0.0.0.0 + +se.jwt.verify.issuer=https://dev-ota46tsay8d5jodg.us.auth0.com/ +se.jwt.verify.publickey.location=${se.jwt.verify.issuer}.well-known/jwks.json \ No newline at end of file diff --git a/helidon-se/src/main/resources/logging.properties b/helidon-se/src/main/resources/logging.properties new file mode 100644 index 0000000..5157244 --- /dev/null +++ b/helidon-se/src/main/resources/logging.properties @@ -0,0 +1,22 @@ + +# Example Logging Configuration File +# For more information see $JAVA_HOME/jre/lib/logging.properties + +# Send messages to the console +handlers=io.helidon.common.HelidonConsoleHandler + +# HelidonConsoleHandler uses a SimpleFormatter subclass that replaces "!thread!" with the current thread +java.util.logging.SimpleFormatter.format=%1$tY.%1$tm.%1$td %1$tH:%1$tM:%1$tS %4$s %3$s !thread!: %5$s%6$s%n + +# Global logging level. Can be overridden by specific loggers +.level=INFO + +# Quiet Weld +org.jboss.level=WARNING + +# Component specific log levels +#io.helidon.webserver.level=INFO +#io.helidon.config.level=INFO +#io.helidon.security.level=INFO +#io.helidon.common.level=INFO +#io.netty.level=INFO diff --git a/helidon-se/src/test/resources/META-INF/microprofile-config.properties b/helidon-se/src/test/resources/META-INF/microprofile-config.properties new file mode 100644 index 0000000..e69de29 diff --git a/helidon-se/src/test/resources/application.yaml b/helidon-se/src/test/resources/application.yaml new file mode 100644 index 0000000..94f3451 --- /dev/null +++ b/helidon-se/src/test/resources/application.yaml @@ -0,0 +1,2 @@ +security: + enabled: false \ No newline at end of file