Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Improving the Developer Experience of Writing Cloud-Native Java Microservices

DZone 's Guide to

Improving the Developer Experience of Writing Cloud-Native Java Microservices

In this post, we take a look at what the Open Liberty team has been up to with regard to developing Java-based microservices with MicroProfile

· Microservices Zone ·
Free Resource

In the third of our new 4-weekly Open Liberty releases, we have a full implementation of MicroProfile 2.2. MicroProfile 2.2 focuses on improving the developer experience of writing cloud-native Java microservices with MicroProfile. It includes updates to the MicroProfile Rest Client, Fault Tolerance, OpenAPI, and Open Tracing features, and is based on Java EE 8 technologies.

If you're using Maven, here are the coordinates:

<dependency>
    <groupId>io.openliberty</groupId>
    <artifactId>openliberty-runtime</artifactId>
    <version>19.0.0.3</version>
    <type>zip</type>
</dependency>
Or for Gradle:
dependencies {
    libertyRuntime group: 'io.openliberty', name: 'openliberty-runtime', version: '[19.0.0.3,)'
}

Or if you're using Docker:

docker pull open-liberty

Or take a look at our Downloads page.

MicroProfile 2.2: Improving the developer experience of writing cloud-native Java microservices

Open Liberty 19.0.0.3 provides a full implementation of MicroProfile 2.2, which focuses on improving the developer experience of writing cloud-native Java microservices with MicroProfile. MicroProfile 2.2 includes updates to the MicroProfile Rest Client, Fault Tolerance, OpenAPI, and Open Tracing features, and is based on Java EE 8 technologies (depending on JAX-RS 2.1, CDI 2.0, and JSON-P 1.1).

Open Liberty provides a MicroProfile 2.2 convenience feature which enables all four of the updated features in your server: mpFaultTolerance-2.0, mpOpenAPI-1.1, mpOpenTracing-1.3, and mpRestClient-1.2.

To enable the MicroProfile 2.2 convenience feature in your server.xml:

<featureManager>
  <feature>microProfile-2.2</feature>
</featureManager>

MicroProfile Rest Client 1.2: Improved Developer Experience and Improved Integration with other features

MicroProfile Rest Client is a type-safe client API for invoking RESTful services. Version 1.2 improves the developer experience and enhances integration with other MicroProfile technologies such as CDI, Fault Tolerance, OpenTracing, etc.

Previous releases of MicroProfile Rest Client lacked integration with CDI, Fault Tolerance, OpenTracing, etc. making it difficult to add things like timeouts, retries, circuit breakers, etc. to client interfaces. MicroProfile Rest Client 1.2 improves integration with other MicroProfile technologies but also improves the developer experience so that you can specify the base URL directly in the @RegisterRestClient annotation in the interface and specify connect and read timeouts in a portable fashion.

To enable the MicroProfile Rest Client feature in your server.xml:

<featureManager>
  <feature>mpRestClient-1.2</feature>
</featureManager>

To try it out, take a look at Andy’s blog post on using MicroProfile Rest Client 1.2.

For more details, take a look at the MicroProfile Javadoc.

To learn to use MicroProfile Rest Client, see the Consuming RESTful services with template interfaces guide.

MicroProfile Fault Tolerance 2.0: Improved Developer Experience and Control Over Interceptor Priorities

MicroProfile Fault Tolerance allows developers to easily apply strategies for mitigating failure to their code. It provides annotations which developers can add to methods to use bulkhead, circuit breaker, retry, timeout, and fallback strategies. In addition, it provides an annotation which causes a method to be run asynchronously.

Version 2.0 adds several new features:

  • Asynchronous methods can now return a CompletionStage, making it easier to compose asynchronous operations and use fault tolerance with existing asynchronous technologies.
  • The user can now configure the priority of the CDI interceptors added by Fault Tolerance, giving them control over how Fault Tolerance interacts with any other interceptors which have been applied.

To enable the MicroProfile Fault Tolerance in your server.xml:

<featureManager>
  <feature>mpFaultTolerance-2.0</feature>
</featureManager>

Add any of the Fault Tolerance annotations to a business method of a CDI bean:

@Retry(5)
  public User lookupUserFromRegistry(String name) {
    return registry.getUser(name);
  }

When that method is called as a a CDI business method, the fault tolerance strategy will be used. In this example, if lookupUserFromRegistry() would throw an exception, it will instead be retried up to five times.

For more details, take a look at the MicroProfile Javadoc.

MicroProfile OpenAPI 1.1: Easier to Generate and Filter Documentation for Your App's APIs

The MicroProfile OpenAPI feature enables generation of OpenAPI 3.0 documentation from Java annotations in your JAX-RS REST applications.

MicroProfile OpenAPI 1.1 improves the OpenAPI model interfaces by adding convenience setters, add and remove methods, which make it easier to use to generate documentation or to filter the generated document.

To enable the MicroProfile OpenAPI feature in your server.xml:

<featureManager>
  <feature>mpOpenAPI-1.1</feature>
</featureManager>

No other configuration is required to generate documentation for your application's API. The application documentation will be available at /openapi endpoint. The UI to render and try out REST APIs is available at openapi/ui endpoint.

For more details, take a look at the MicroProfile Javadoc.

To learn to use MicroProfile OpenAPI, see the Documenting RESTful APIs guide.

MicroProfile Open Tracing 1.3: Tracing Support in MicroProfile REST Clients

In MicroProfile Open Tracing 1.3, tracing of client/server calls can be done with JAX-RS clients and MicroProfile REST clients. Client/server calls become a single trace with multiple spans. In the previous version of MicroProfile Open Tracing, tracing of client/server calls could be done with JAX-RS clients only. MicroProfile REST client calls were broken down into two separate traces: one trace for the first service (i.e. caller) and another trace for the second service.

To enable the MicroProfile Open Tracing feature for REST clients in your server.xml:

<featureManager>
  <feature>mpOpenTracing-1.3</feature>
  <feature>mpRestClient-1.2</feature>
</featureManager>

No other configuration is required. Tracing for MicroProfile REST clients is enabled globally by default if the features are specified in the server configuration. You can turn tracing off on specific classes or methods by specifying @Traced(false) on the REST client interface class level or method.

For more details, take a look at the MicroProfile Javadoc.

To learn to use MicroProfile Open Tracing, see the Enabling distributed tracing in microservices guide.

Config Variable Improvements

As Docker has become a more and more important deployment platform, the importance of being able to integrate configuration variables from the environment into the Liberty configuration has become more and more important. It was always possible to reference configuration variables by prefixing the variable name with env., but it means you have to design your configuration to receive this configuration from the environment and the value has to be provided in the environment for the server to receive configuration.

To simplify these things 19.0.0.3 has three important new capabilities:

  1. It is now possible to directly reference environment variables without the prefix. The Liberty variable resolution order means that this won't affect existing configurations (a variable from any other source overrides the environment), but it means that when you go from local development environments to Docker containers, you do not need to rewrite your server configuration file just to specify it from the environment.
  2. With the environment being overridden by everything else it isn't possible to specify defaults from the environment. This means that when you go to Docker you have to specify everything in docker -e or in Kubernetes even if you can sensibly define a default most of the time. Now you can just define a default value in server.xml that will only be used if the value is not provided by any other source. This greatly simplifies docker configuration. To implement this simply specify the default value like this:
    <variable name="my.variable" defaultValue="some default" />
  3. When writing configuration in server.xml most people use Java property name conventions: all lowercase with periods as separators. If the server config has been written with Java-style property names, the property can't be specified in an environment variable because the period isn't valid in an environment variable name. To work around this when resolving variable names, Liberty configuration uses the same algorithm as MicroProfile Config by mapping non-alphanumeric characters to _ and, if that doesn't match, upper-casing everything. This means if your server.xml has http.port then this can be read from the environment as http_port or HTTP_PORT.

Non-Root Docker Images and More

Following industry best practices, we have updated all of Open Liberty's Docker images to run as non-root (user:group set as 1001:0). We have taken care of ensuring all of the needed directories have the correct permissions, but if you find that your application's Dockerfile is failing, see the updated documentation for the different options you have.

With this update, the Open Liberty Docker images can natively run on OpenShift and any other Kubernetes platform that requires the image to run as non-root.

We also now have Open Liberty Docker images based on Java 11 + OpenJ9 in our community repository of Docker Hub. This is in addition to the Java 8 + OpenJ9 images we already had. Check it out today at: https://hub.docker.com/r/openliberty/open-liberty

Finally, daily Open Liberty Docker images are now available. These images pickup the daily Open Liberty binaries.

PKCS12 as the Default Keystore

PKCS12 is now used as the default keystore instead of JKS. PKCS12 has a number of advantages:

  • It is more extensible.
  • It supports stronger cryptographic algorithms.
  • It is widely adopted. PKCS12 is frequently the format provided by certificate authorities when issuing certificates.

This change means that any new keystores are created in the PKCS12 format if the configuration uses the default configuration. It will not, however, affect existing applications that rely upon the original JKS keystore type. Backwards compatibility is maintained to allow existing applications to continue operating unmodified.

Usage scenarios:

  1. With no keystore config at all in server.xml, a PKCS12 keystore is created on server start-up.
  2. With no keystore config at all in server.xml, and with a key.jks keystore that exists in the default /resource/security location, the existing key.jks is used and no PKCS12 keystore is created.
  3. With a minimal SSL configuration, e.g., <keyStore id="defaultKeyStore" password="Liberty">, and a key.jks keystore file that exists in the default /resources/security location, the default key.jks keystore is used and no PKCS12 keystore is created.
  4. With a minimal SSL configuration, e.g., <keyStore id="defaultKeyStore" password="Liberty">, and a key.p12 keystore file that exists in the default /resources/security location, the default key.p12 keystore is used.
  5. A new JKS keystore is created if there is a minimal SSL configuration where a type is specified and the type is JKS e.g., <keyStore id="defaultKeyStore" type="JKS" password="liberty" />
  6. A new PKCS12 keystore is created if there is a minimal SSL configuration where a type is specified and the type is PKCS12 e.g., <keyStore id="defaultKeyStore" type="PKCS12" password="liberty" />
  7. When a keystore is specified in server.xml and it is not the default keystore, and the type is not specified, but the location of the JKS keystore is specified, that JKS keystore is used. e.g., <keyStore id="myKeyStore" location="c:/temp/myKeyStore.jks" password="liberty">
  8. When a keystore is specified in server.xml and it is not the default keystore, and the type is not specified, but the location of the PKCS12 keystore is specified, that PKCS12 keystore is used. e.g., <keyStore id="myKeyStore" location="c:/temp/myKeyStore.p12" password="liberty">

When using the ORACLE v7 JDK, ensure that upgrade 101 or higher is used, which provides the support to add trusted certificates to a PKCS12 keystore.

When using securityUtility createSSLCertificateTask a PKCS12 file is created.

Get the Maven or Gradle coordinates (and other download options) from the top of this post.

Previews of Early Implementations Available in the Latest Development Builds

You can now also try out early implementations of some new capabilities in the latest Open Liberty development builds:

This early implementation is not available in 19.0.0.3 but you can try it out by downloading the latest Open Liberty development build. Let us know what you think!

MicroProfile Concurrency 1.0

MicroProfile Concurrency allows you to create completion stages that run with predictable thread context regardless of which thread the completion stage action ends up running on.

MicroProfile Concurrency provides completion stages that run with predictable thread context that also benefit from being backed by the automatically-tuned Liberty global thread pool. Configuration of concurrency constraints and context propagation is possible programmatically with builders as well as by CDI annotations which can be overriden via MicroProfile Config.

It should be noted that this implementation does not yet include thread context capture and propagation for CDI context, but other context types should be working.

To enable the MicroProfile Concurrency 1.0 feature in your server.xml:

<featureManager>
    <feature>mpConcurrency-1.0</feature>
    <feature>cdi-2.0</feature> <!-- If CDI injection is desired -->
    <feature>jndi-1.0</feature> <!-- used in example -->
    ... other features
  </featureManager>

Example usage of programmatic builders:

ManagedExecutor executor = ManagedExecutor.builder()
    .maxAsync(5)
    .propagated(ThreadContext.APPLICATION, ThreadContext.SECURITY)
    .build();

CompletableFuture<Integer> stage1 = executor.newIncompleteFuture();
stage1.thenApply(function1).thenAccept(value -> {
    try {
        // access resource reference in application's java:comp namespace,
        DataSource ds = InitialContext.doLookup("java:comp/env/jdbc/ds1");
        ...
    } catch (Exception x) {
        throw new CompletionException(x);
    }
};
...
stage1.complete(result);

Example usage in a CDI bean:

// CDI qualifier which is used to identify the executor instance
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
public @interface AppContext {}

// Example producer field, defined in a CDI bean,
@Produces @ApplicationScoped @AppContext
ManagedExecutor appContextExecutor = ManagedExecutor.builder()
    .propagated(ThreadContext.APPLICATION)
    .build();

// Example disposer method, also defined in the CDI bean,
void disposeExecutor(@Disposes @AppContext exec) {
    exec.shutdownNow();
}

// Example injection point, defined in a CDI bean,
@Inject @AppContext
ManagedExecutor executor;

...

CompletableFuture<Integer> stage = executor
    .supplyAsync(supplier1)
    .thenApply(function1)
    .thenApplyAsync(value -> {
        try {
            // access resource reference in application's java:comp namespace,
            DataSource ds = InitialContext.doLookup("java:comp/env/jdbc/ds1");
            ...
            return result;
        } catch (Exception x) {
            throw new CompletionException(x);
        }
    });
Topics:
microservices ,microservices java ,openliberty ,microprofile

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}