Quarkus with MicroProfile, RAM, Jetty and -Xmx18m

The memory consumption of a "hello, world" quarkus JAX-RS application is comparable to "empty" Tomcat or Jetty (see: Memory (RAM) Consumption of: Tomcat, Jetty and Quarkus).

However: how big is quarkus application relying on MicroProfile dependencies like e.g. JSON-B, JAX-RS, CDI, Metrics, Config, OpenAPI and Health? ...and what is the impact of -Xmx18m on Java's / Quarkus memory consumption?:

In this screencast I created a Quarkus application from scratch and compared the memory footprint with "empty" Jetty:

In-Browser Unit Testing with MochaJS, ChaiJS, BrowserSync and ES Modules

In-browser unit testing JavaScript functions with MochaJS test runner, using chaijs assertions and loading the "function under test" using ES 6 modules. No local NPM / build was used, only Browsersync:

JAX-RS Response#seeOther, vs. 301, 302, 307 and 308 HTTP Redirections

The JAX-RS Response#seeOther method is a convenience wrapper of the following snippet status(303).location(location).

Status 303 is used for:

"Used to redirect after a PUT or a POST, so that refreshing the result page doesn't re-trigger the operation."

To test HTTP redirections, a ping JAX-RS resource:


@Path("ping")
public class PingResource {
    
    @POST
    @Path("redirect/{status}")
    public Response save(String payload, @Context HttpHeaders headers, @Context UriInfo info, @PathParam("status") int status) {
        String header = headers.getHeaderString("description");
        System.out.println("ping: payload: " + payload + " " + header + " status " + status);
        URI uri = info.getBaseUriBuilder().path("/newping").build();
        return Response.status(status).location(uri).build();
    }
    
}

...will redirect requests to newping JAX-RS class:

@Path("newping")
public class NewPingResource {
    
    @POST
    public Response save(String payload, @Context HttpHeaders headers) {
        String header = headers.getHeaderString("description");
        System.out.println("newping: payload: " + payload + " header " + header);
        return Response.ok("newping: payload: " + payload + " header " + header).build();
    }
}

The 302, 303 and 301 statuses: curl -L -XPOST -H'description: dev' -d'hello,duke' -i http://localhost:8080/redirects/resources/ping/redirect/302 and 303 generate the following log statements:


ping: payload: hello,duke dev status [...]
newping: payload:  header dev

The NewPingResource#save method does not receive the body / payload and the client always receives the following response:

HTTP/1.1 303 See Other
Server: Payara Server  5.194 #badassfish
X-Powered-By: Servlet/4.0 JSP/2.3 (Payara Server  5.194 #badassfish Java/Oracle Corporation/1.8)
Location: http://localhost:8080/redirects/resources/newping
Content-Length: 0
X-Frame-Options: SAMEORIGIN

HTTP/1.1 200 OK
Server: Payara Server  5.194 #badassfish
X-Powered-By: Servlet/4.0 JSP/2.3 (Payara Server  5.194 #badassfish Java/Oracle Corporation/1.8)
Content-Type: text/plain
Content-Length: 29
X-Frame-Options: SAMEORIGIN

newping: payload:  header dev

A 307 and 308 statuses: curl -L -XPOST -H'description: dev' -d'hello,duke' -i http://localhost:8080/redirects/resources/ping/redirect/307 and 308 are passing the entire body to the second resource. The following log statements are created:


ping: payload: hello,duke dev status [...]
newping: payload: hello,duke header dev

The client receives the response including the body sent by the user agent:


HTTP/1.1 307 Temporary Redirect
Server: Payara Server  5.194 #badassfish
X-Powered-By: Servlet/4.0 JSP/2.3 (Payara Server  5.194 #badassfish Java/Oracle Corporation/1.8)
Location: http://localhost:8080/redirects/resources/newping
Content-Length: 0
X-Frame-Options: SAMEORIGIN

HTTP/1.1 200 OK
Server: Payara Server  5.194 #badassfish
X-Powered-By: Servlet/4.0 JSP/2.3 (Payara Server  5.194 #badassfish Java/Oracle Corporation/1.8)
Content-Type: text/plain
Content-Length: 39
X-Frame-Options: SAMEORIGIN

newping: payload: hello,duke header dev    

conclusion

The user agent does not resend the body in the case of 301, 302, 303 HTTP redirections.

307 and 308 were introduced to provide a uniform user agent's (browser, curl, wget, JAX-RS client) behavior and "remove the ambiguity of the behavior when using non-GET methods".

The 5.6kB ThinWAR project was created with Jakarta EE Essentials Archetype and deployed with: wad.sh in 2s

Quarkus Developer Experience--airhacks.fm podcast

Subscribe to airhacks.fm podcast via: spotify| iTunes| RSS

The #76 airhacks.fm episode with Alex Soto (@alexsotob) about:
quarkus developer experience, acceptance and optimizations
is available for download.

Migration from @Stateless (BCE) to Quarkus

A typical BCE Jakarta EE application comprises a JAX-RS resource:


import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
    
@Path("ping")
public class MessageResource {

    @Inject
    MessageFetcher messageFetcher;

    @GET
    public String ping() {
        return "hello, " + this.messageFetcher.getMessage();
    }

}
and a corresponding implementation in the boundary package:

package com.airhacks.ping.boundary;
import com.airhacks.ping.control.MessageConfigurator;
import javax.ejb.Stateless;
import javax.inject.Inject;

@Stateless
public class MessageFetcher {

    @Inject
    MessageConfigurator configurator;

    public String getMessage() {
        return this.configurator.message();
    }

}

The boundary acts as a facade and usually coordinates multiple controls:


package com.airhacks.ping.control;

import javax.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;

public class MessageConfigurator {

    @Inject
    @ConfigProperty(name = "message")
    String message;

    public String message() {
        return this.message + " generated at: " + System.currentTimeMillis();
    }
}    

To run the code on quarkus you will have to replace @Stateless with @Transactional and @RequestScoped, or a stereotype which combines both:


@Stereotype
@Transactional
@RequestScoped
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Boundary {}    

Now the MessageFetcher looks like:


@Boundary
public class MessageFetcher {    }    

All controls have to annotated to be injectable on quarkus. A @Dependent (default) scope could be used for that purpose, or the following stereotype:


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Stereotype;

@Stereotype
@Dependent
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Control {}

The MessageConfigurator control has to be annotated with the @Control stereotype and looks like:


@Control    
public class MessageConfigurator { }

Both stereotype are used in the Web Push gateway: gatelink.

February / March 2020 Java and Web Events

  1. Cloud Native Patterns und Approaches mit Jakarta EE + MicroProfile #lowslides #cloudful
    JUG Oberpfalz OTH Weiden 2020-02-19
  2. Microprofile Productivity with Quarkus
    JUG BMW MUC 2020-02-20
  3. Kickass Web Components with a bit of Quarkus
    Free Meetup Mayflower MUC 2020-03-10
  4. Keynote: What Should Happen in 2020 with Java and Web
    Voxxed Days Bucharest Sheraton Hotel Bucharest 2020-03-13
  5. Session: 2020 Predictions Interactive On-Stage Hacking #slideless #nomigrations
    Voxxed Days Bucharest Sheraton Hotel Bucharest 2020-03-13
  6. From Java Developer to Web Guru in 1 hour - No slides
    JUG Session Google MUC Munich 2020-03-20
  7. MicroProfile with Quarkus
    Airhacks Workshops Airport MUC 2020-03-24
  8. Micro Frontends with Web Components
    Airhacks Workshops Airport MUC 2020-03-25
  9. Jakarta EE + MicroProfile + Kubernetes = The Productivity Dream #slideless
    Kubecon Cloud Native for Java (CN4J) RAI Amsterdam 2020-03-30

Paths "Subtraction" with Path#relativize

The method relativize computes the difference between an absolute path (src/main/java/com/airhacks) and it's root (src/main/java). This is useful for subtraction of a common path prefix:


import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.Test;

public class PathSubstractionTest {

    @Test
    public void splitWithDot() {
        Path fromProjectRoot = Paths.get("src/main/java/com/airhacks");
        Path projectRoot = Paths.get("src/main/java");
        Path substracted = projectRoot.relativize(fromProjectRoot);
        String actual = substracted.toString();
        String expected = "com/airhacks";
        assertThat(actual, is(expected));
    }
}

String#split with a "dot"

String#split with a dot "." does return an empty array:


    String packages[] = "com.airhacks".split(".");
    assertThat(packages.length, is(0));

The split method parameter is a regular expression, and "dot" is Any character (may or may not match line terminators)

Escaping the dot solves the "problem":


    String packages[] = "com.airhacks".split("\\.");
    assertThat(packages.length, is(2));

Online Workshops
realworldpatterns.com
...the last 150 posts
...the last 10 comments
License