Helidon SE 2.x Upgrade Guide

In Helidon 2 we have made some changes to APIs and runtime behavior. This guide will help you migrate a Helidon SE 1.x application to 2.x.

Java 11 Runtime

Java 8 is no longer supported in Helidon 2. Java 11 or newer is required.

Tracing

We have upgraded to OpenTracing version 0.33.0 that is not backward compatible. OpenTracing introduced the following breaking changes:

RemovedReplacement
ScopeManager.active()Tracer.activeSpan()
ScopeManager.activate(Span, boolean)ScopeManager.activate(Span) - second parameter is now always false
SpanBuilder.startActive()Tracer.activateSpan(Span)
TextMapExtractAdapter and TextMapInjectAdapterTextMapAdapter
Module name changed opentracing.apiio.opentracing.api (same for noop and util)

If you use the TracerBuilder abstraction in Helidon and have no custom Spans, there is no change required

Security: OIDC

When the OIDC provider is configured to use cookie (default configuration) to carry authentication information, the cookie Same-Site is now set to Lax (used to be Strict). This is to prevent infinite redirects, as browsers would refuse to set the cookie on redirected requests (due to this setting). Only in the case of the frontend host and identity host match, we leave Strict as the default

Getters

Some methods that act as getters of type T have been modified to return Optional<T>. You will need to change your code to handle the Optional return type. For example ServerRequest.spanContext() in 1.x had a return type of SpanContext. In 2.x it has a return type of Optional<SpanContext>. So if you had code like:

Helidon 1.x Code
Span myNewSpan = GlobalTracer.get()
        .buildSpan("my-operation")
        .asChildOf(serverRequest.spanContext())
        .start();
Copied

you will need to change it to something like:

Helidon 2.x Code
Tracer.SpanBuilder spanBuilder = serverRequest.tracer()
        .buildSpan("my-operation");
serverRequest.spanContext().ifPresent(spanBuilder::asChildOf);
Span myNewSpan = spanBuilder.start();
Copied

Note the use of ifPresent() on the returned Optional<SpanContext>.

Configuration

  1. File watching is now done through a ChangeWatcher - use of PollingStrategies.watch() needs to be refactored to FileSystemWatcher.create() and the method to configure it on config source builder has changed to changeWatcher(ChangeWatcher).
  2. Methods on ConfigSources now return specific builders (they used to return AbstractParsableConfigSource.Builder with a complex type declaration). If you store such a builder in a variable, either change it to the correct type, or use var
  3. Some APIs were cleaned up to be aligned with the development guidelines of Helidon. When using Git config source, or etcd config source, the factory methods moved to the config source itself, and the builder now accepts all configuration options through methods
  4. The API of config source builders has been cleaned, so now only methods that are relevant to a specific config source type can be invoked on such a builder. Previously you could configure a polling strategy on a source that did not support polling
  5. There is a small change in behavior of Helidon Config vs. MicroProfile Config: The MP TCK require that system properties are fully mutable (e.g. as soon as the property is changed, it must be used), so MP Config methods work in this manner (with a certain performance overhead). Helidon Config treats System properties as a mutable config source, with an (optional) time based polling strategy. So the change is reflected as well, though not immediately (this is only relevant if you use change notifications).
  6. CompositeConfigSource has been removed from Config. If you need to configure MerginStrategy, you can do it now on Config Builder

Example of advanced configuration of config:

Config.builder()
        // system properties with a polling strategy of 10 seconds
        .addSource(ConfigSources.systemProperties()
                           .pollingStrategy(PollingStrategies.regular(Duration.ofSeconds(10))))
        // environment variables
        .addSource(ConfigSources.environmentVariables())
        // optional file config source with change watcher
        .addSource(ConfigSources.file(Paths.get("/conf/app.yaml"))
                           .optional()
                           .changeWatcher(FileSystemWatcher.create()))
        // classpath config source
        .addSource(ConfigSources.classpath("application.yaml"))
        // map config source (also supports polling strategy)
        .addSource(ConfigSources.create(Map.of("key", "value")))
        .build();
Copied

Resource Class When Loaded from Config

The configuration approach to Resource class was using prefixes which was not aligned with our approach to configuration. All usages were refactored as follows:

  1. The Resource class expects a config node resource that will be used to read it
  2. The feature set remains unchanged - we support path, classpath, url, content as plain text, and content as base64
  3. Classes using resources are changed as well, such as KeyConfig - see details below

Media Support

In Helidon 1.x support for JSON and other media types was configured when constructing webserver.Routing using the register method. In Helidon 2 Media Support has been refactored so that it can be shared between the Helidon WebServer and WebClient. You now specify media support as part of the WebServer build:

WebServer.builder()
        .addMediaSupport(JsonpSupport.create()) //registers reader and writer for Json-P
        .build();
Copied

This replaces Routing.builder().register(JsonSupport.create())…​

The new JSON MediaSupport classes are:

  • io.helidon.http.media.jsonp.JsonpSupport in module io.helidon.http.media:helidon-media-jsonp

  • io.helidon.http.media.jsonb.JsonbSupport in module io.helidon.http.media:helidon-media-jsonb

  • io.helidon.http.media.jackson.JacksonSupport in module io.helidon.http.media:helidon-media-jackson

Reactive

RemovedReplacement
io.helidon.common.reactive.ReactiveStreamsAdapterorg.reactivestreams.FlowAdapters

Security: OidcConfig

Configuration has been updated to use the new Resource approach:

  1. oidc-metadata.resource is the new key for loading oidc-metadata from local resource
  2. sign-jwk.resource is the new key for loading signing JWK resource

Security: JwtProvider and JwtAuthProvider

Configuration has been updated to use the new Resource approach:

  1. jwk.resource is the new key for loading JWK for verifying signatures
  2. jwt.resource is also used for outbound as key for loading JWK for signing tokens

PKI Key Configuration

The configuration has been updated to have a nicer tree structure:

Example of a public key from keystore:

keystore:
   cert.alias: "service_cert"
   resource.path: "/conf/keystore.p12"
   type: "PKCS12"
   passphrase: "password"
Copied

Example of a private key from keystore:

keystore:
  key:
    alias: "myPrivateKey"
    passphrase: "password"
  resource.resource-path: "keystore/keystore.p12"
  passphrase: "password"
Copied

Example of a pem resource with private key and certificate chain:

pem:
  key:
    passphrase: "password"
    resource.resource-path: "keystore/id_rsa.p8"
  cert-chain:
    resource.resource-path: "keystore/public_key_cert.pem"
Copied

GrpcTlsDescriptor

Configuration has been updated to use the new Resource approach:

  1. tls-cert.resource is the new key for certificate
  2. tls-key.resource is the new key for private key
  3. tl-ca-cert is the new key for certificate

WebServer Configuration

SSL/TLS

There is a new class io.helidon.webserver.WebServerTls that can be used to configure TLS for a WebServer socket. Class io.helidon.webserver.SSLContextBuilder has been deprecated and will be removed.

The class uses a Builder pattern:

WebServerTls.builder()
        .privateKey(KeyConfig.keystoreBuilder()
                            .keystore(Resource.create("certificate.p12"))
                            .keystorePassphrase("helidon"));
Copied

The builder or built instance can be registered with a socket configuration builder including the WebServer.Builder itself:

WebServer.builder(routing())
        .tls(webServerTls)
        .build();
Copied

Additional Sockets

Additional socket configuration has changed both in config and in API.

The configuration now accepts following structure:

server:
   port: 8000
   sockets:
     - name: "admin"
       port: 8001
     - name: "static"
       port: 8002
       enabled: false
Copied

Socket name is now a value of a property, allowing more freedom in naming. The default socket name is implicit (and set to @default).

We have added the enabled flag to support disabling sockets through configuration.

To add socket using a builder, you can use:

WebServer.builder()
        .addSocket(SocketConfigurationBuilder.builder()
                           .port(8001)
                           .name("admin"));
Copied

There is also a specialized method to add a socket and routing together, to remove mapping through a name.

Deprecation of ServerConfiguration

io.helidon.webserver.ServerConfiguration.Builder is no longer used to configure WebServer.

Most methods from this class have been moved to WebServer.Builder or deprecated.

Example of a simple WebServer setup:

WebServer.builder()
        .port(8001)
        .host("localhost")
        .routing(createRouting())
        .build();
Copied

Other Significant WebServer Deprecations

  • io.helidon.webserver.WebServer.Builder - all methods that accept ServerConfiguration or its builder are deprecated, please use methods on WebServer.Builder instead

  • io.helidon.webserver.WebServer.Builder - all methods for socket configuration that accept a name and socket are deprecated, socket name is now part of socket configuration itself

  • io.helidon.webserver.ResponseHeaders.whenSend() - please use whenSent()

  • io.helidon.webserver.Routing.createServer(ServerConfiguration) - please use WebServer.builder()

  • io.helidon.webserver.Routing.createServer() - please use WebServer.builder()

  • io.helidon.webserver.SocketConfiguration.DEFAULT - use a builder to create a named configuration

  • io.helidon.webserver.SocketConfiguration.Builder.ssl(SSLContext) - use `WebServerTls instead

  • io.helidon.webserver.SocketConfiguration.Builder.enabledSSlProtocols(String…​) - use `WebServerTls instead