Skip to content

Latest commit

 

History

History
350 lines (248 loc) · 9.25 KB

demo.adoc

File metadata and controls

350 lines (248 loc) · 9.25 KB

Native Java REST API Demo Steps

In this demo, I’ll show how to create native images with Micronaut, Quarkus, and Spring Boot. You’ll see how to run a secure, OAuth 2.0-protected, Java REST API that allows JWT authentication.

Prerequisites:

Tip
The brackets at the end of some steps indicate the IntelliJ Live Templates to use. You can find the template definitions at mraible/idea-live-templates.

Install a JDK with GraalVM

Use SDKMAN to install Java 17 with GraalVM

sdk install java 22.3.r17-grl

Generate an OAuth 2.0 Access Token

  1. Install the Okta CLI and run okta register to sign up for a new account. If you already have an account, run okta login.

  2. Run okta apps create spa. Set oidcdebugger as an app name and press Enter.

  3. Use https://oidcdebugger.com/debug for the Redirect URI and set the Logout Redirect URI to https://oidcdebugger.com.

  4. Navigate to the OpenID Connect Debugger website.

    1. Fill in your client ID

    2. Use https://{yourOktaDomain}/oauth2/default/v1/authorize for the Authorize URI

    3. Select code for the response type and Use PKCE

    4. Click Send Request to continue

  5. Set the access token as a TOKEN environment variable in a terminal window.

    TOKEN=eyJraWQiOiJYa2pXdjMzTDRBYU1ZSzNGM...

Make a Micronaut Java API

  1. Use SDKMAN to install Micronaut’s CLI and create an app:

    sdk install micronaut
    mn create-app com.okta.rest.app --build maven -f security-jwt -f micronaut-aot
    mv app micronaut
  2. Create controller/HelloController.java: [mn-hello]

    package com.okta.rest.controller;
    
    import io.micronaut.http.MediaType;
    import io.micronaut.http.annotation.Controller;
    import io.micronaut.http.annotation.Get;
    import io.micronaut.http.annotation.Produces;
    import io.micronaut.security.annotation.Secured;
    import io.micronaut.security.rules.SecurityRule;
    
    import java.security.Principal;
    
    @Controller("/hello")
    public class HelloController {
    
        @Get
        @Secured(SecurityRule.IS_AUTHENTICATED)
        @Produces(MediaType.TEXT_PLAIN)
        public String hello(Principal principal) {
            return "Hello, " + principal.getName() + "!";
        }
    
    }
  3. Enable and configure JWT security in src/main/resources/application.yml: [mn-security-config]

    micronaut:
      ...
      security:
        enabled: true
        token:
          jwt:
            enabled: true
            claims-validators:
              issuer: https://{yourOktaDomain}/oauth2/default
            signatures:
              jwks:
                okta:
                  url: https://{yourOktaDomain}/oauth2/default/v1/keys

Run and Test Your Micronaut API with HTTPie

  1. Start your app:

    ./mvnw mn:run
  2. Use HTTPie to pass the JWT in as a bearer token in the Authorization header:

    http :8080/hello Authorization:"Bearer $TOKEN"

    You should get a 200 response with your email in it.

Build a Native Micronaut App

  1. Compile your Micronaut app into a native binary:

    ./mvnw package -Dpackaging=native-image
  2. Start your Micronaut app:

    ./target/app
  3. Test it with HTTPie and an access token. You may have to generate a new JWT with oidcdebugger.com if yours has expired.

    http :8080/hello Authorization:"Bearer $TOKEN"

Create a Quarkus Java API

  1. Use Maven to generate a new Quarkus app with JWT support:

    mvn io.quarkus:quarkus-maven-plugin:2.13.3.Final:create \
        -DprojectGroupId=com.okta.rest \
        -DprojectArtifactId=quarkus \
        -DclassName="com.okta.rest.quarkus.HelloResource" \
        -Dpath="/hello" \
        -Dextensions="smallrye-jwt,resteasy-reactive"
  2. Edit src/java/com/okta/rest/quarkus/HelloResource.java and add user information to the hello() method: [qk-hello]

    package com.okta.rest.quarkus;
    
    import io.quarkus.security.Authenticated;
    
    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.Context;
    import javax.ws.rs.core.MediaType;
    import javax.ws.rs.core.SecurityContext;
    import java.security.Principal;
    
    @Path("/hello")
    public class HelloResource {
    
        @GET
        @Authenticated
        @Produces(MediaType.TEXT_PLAIN)
        public String hello(@Context SecurityContext context) {
            Principal userPrincipal = context.getUserPrincipal();
            return "Hello, " + userPrincipal.getName() + "!";
        }
    }
  3. Add your Okta endpoints to src/main/resources/application.properties: [qk-properties]

    mp.jwt.verify.publickey.location=https://{yourOktaDomain}/oauth2/default/v1/keys
    mp.jwt.verify.issuer=https://{yourOktaDomain}/oauth2/default
  4. Modify the HelloResourceTest to expect a 401 instead of a 200:

    package com.okta.rest.quarkus;
    
    import io.quarkus.test.junit.QuarkusTest;
    import org.junit.jupiter.api.Test;
    
    import static io.restassured.RestAssured.given;
    
    @QuarkusTest
    public class HelloResourceTest {
    
        @Test
        public void testHelloEndpoint() {
            given()
                .when().get("/hello")
                .then()
                .statusCode(401);
        }
    
    }

Run and Test Your Quarkus API with HTTPie

  1. Run your Quarkus app:

    ./mvnw quarkus:dev
  2. Test it from another terminal:

    http :8080/hello
  3. Test with access token:

    http :8080/hello Authorization:"Bearer $TOKEN"

Build a Native Quarkus App

  1. Compile your Quarkus app into a native binary:

    ./mvnw package -Pnative
  2. Start your Quarkus app:

    ./target/quarkus-1.0.0-SNAPSHOT-runner
  3. Test it with HTTPie and an access token:

    http :8080/hello Authorization:"Bearer $TOKEN"

Start a Spring Boot Java API

  1. Create a Spring Boot app with OAuth 2.0 support:

    https start.spring.io/starter.zip \
      bootVersion==3.0.0-RC1 \
      dependencies==web,oauth2-resource-server,native \
      packageName==com.okta.rest \
      name==spring-boot \
      type==maven-project \
      baseDir==spring-boot | tar -xzvf -
  2. Add a HelloController class that returns the user’s information: [sb-hello]

    package com.okta.rest.controller;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.security.Principal;
    
    @RestController
    public class HelloController {
    
        @GetMapping("/hello")
        public String hello(Principal principal) {
            return "Hello, " + principal.getName() + "!";
        }
    
    }
  3. Configure the app to be an OAuth 2.0 resource server by adding the issuer to application.properties.

    spring.security.oauth2.resourceserver.jwt.issuer-uri=https://{yourOktaDomain}/oauth2/default

Run and Test Your Spring Boot API with HTTPie

  1. Start your app from your IDE or using a terminal:

    ./mvnw spring-boot:run
  2. Test your API with an access token.

    http :8080/hello Authorization:"Bearer $TOKEN"

Build a Native Spring Boot App

  1. Compile your Spring Boot app into a native executable using the native profile:

    ./mvnw native:compile -Pnative
    Tip
    To build a native app and a Docker container, use the Spring Boot Maven plugin and ./mvnw spring-boot:build-image.
  2. Start your Spring Boot app:

    ./target/demo
  3. Test your API with an access token.

    http :8080/hello Authorization:"Bearer $TOKEN"

Startup Time Comparison

  1. Run each image three times before recording the numbers, then each command five times

  2. Write each time down, add them up, and divide by five for the average. For example:

    Micronaut: (17 + 19 + 19 + 20 + 15) / 5 = 18
    Quarkus: (25 + 18 + 20 + 19 + 21) / 5 = 20.6
    Spring Boot: (39 + 40 + 38 + 37 + 41) / 5 = 39
Table 1. Native Java startup times in milliseconds
Framework Command executed Milliseconds to start

Micronaut

./micronaut/target/app

18

Quarkus

./quarkus/target/quarkus-1.0.0-SNAPSHOT-runner

20.6

Spring Boot

./spring-boot/target/demo

39

Memory Usage Comparison

Test the memory usage in MB of each app using the command below. Make sure to send an HTTP request to each one before measuring.

ps -o pid,rss,command | grep --color <executable> | awk '{$2=int($2/1024)"M";}{ print;}'

Substitute <executable> as follows:

Table 2. Native Java memory used in megabytes
Framework Executable Megabytes before request Megabytes after request Megabytes after 5 requests

Micronaut

app

43

58

69

Quarkus

quarkus

37

48

50

Spring Boot

demo

74

98

99

Important
If you disagree with these numbers and think X framework should be faster, I encourage you to clone the repo and run these tests yourself. If you get faster startup times for Spring Boot, do you get faster startup times for Micronaut and Quarkus too?

Native Java REST APIs FTW!