- 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 11 is no longer supported in Helidon 3. Java 17 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:
| Removed | Replacement |
|---|---|
ScopeManager.active() | Tracer.activeSpan() |
ScopeManager.activate(Span, boolean) | ScopeManager.activate(Span) - second parameter is now always false |
SpanBuilder.startActive() | Tracer.activateSpan(Span) |
TextMapExtractAdapter and TextMapInjectAdapter | TextMapAdapter |
Module name changed opentracing.api | io.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:
Span myNewSpan = GlobalTracer.get()
.buildSpan(“my-operation”)
.asChildOf(serverRequest.spanContext())
.start();you will need to change it to something like:
Tracer.SpanBuilder spanBuilder = serverRequest.tracer()
.buildSpan("my-operation");
serverRequest.spanContext().ifPresent(spanBuilder::asChildOf);
Span myNewSpan = spanBuilder.start();Note the use of ifPresent() on the returned Optional<SpanContext>.
Configuration
- File watching is now done through a
ChangeWatcher- use ofPollingStrategies.watch()needs to be refactored toFileSystemWatcher.create()and the method to configure it on config source builder has changed tochangeWatcher(ChangeWatcher). - Methods on
ConfigSourcesnow return specific builders (they used to returnAbstractParsableConfigSource.Builderwith a complex type declaration). If you store such a builder in a variable, either change it to the correct type, or usevar - 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
- 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
- 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 a (optional) time based polling strategy. So the change is reflected as well, though not immediately (this is only relevant if you use change notifications).
CompositeConfigSourcehas been removed fromConfig. If you need to configureMerginStrategy, you can do it now onConfigBuilder
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();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:
- The
Resourceclass expects a config noderesourcethat will be used to read it - The feature set remains unchanged - we support path, classpath, url, content as plain text, and content as base64
- 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()This replaces Routing.builder().register(JsonSupport.create())…
The new JSON MediaSupport classes are:
io.helidon.media.jsonp.JsonpSupportin moduleio.helidon.media:helidon-media-jsonpio.helidon.media.jsonb.JsonbSupportin moduleio.helidon.media:helidon-media-jsonbio.helidon.media.jackson.JacksonSupportin moduleio.helidon.media:helidon-media-jackson
Reactive
| Removed | Replacement |
|---|---|
io.helidon.common.reactive.ReactiveStreamsAdapter | org.reactivestreams.FlowAdapters |
Security: OidcConfig
Configuration has been updated to use the new Resource approach:
oidc-metadata.resourceis the new key for loadingoidc-metadatafrom local resourcesign-jwk.resourceis the new key for loading signing JWK resource
Security: JwtProvider and JwtAuthProvider
Configuration has been updated to use the new Resource approach:
jwk.resourceis the new key for loading JWK for verifying signaturesjwt.resourceis 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"Example of a private key from keystore:
keystore:
key:
alias: "myPrivateKey"
passphrase: "password"
resource.resource-path: "keystore/keystore.p12"
passphrase: "password"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"GrpcTlsDescriptor
Configuration has been updated to use the new Resource approach:
tls-cert.resourceis the new key for certificatetls-key.resourceis the new key for private keytl-ca-certis the 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")The builder or built instance can be registered with a socket configuration builder including the WebServer.Builder itself:
WebServer.builder(routing())
.tls(webServerTls)
.build();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: falseSocket 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")));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();Other Significant WebServer Deprecations
io.helidon.webserver.WebServer.Builder- all methods that acceptServerConfigurationor its builder are deprecated, please use methods onWebServer.Builderinsteadio.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 itselfio.helidon.webserver.ResponseHeaders.whenSend()- please usewhenSent()io.helidon.webserver.Routing.createServer(ServerConfiguration)- please useWebServer.builder()io.helidon.webserver.Routing.createServer()- please useWebServer.builder()io.helidon.webserver.SocketConfiguration.DEFAULT- use a builder to create a named configurationio.helidon.webserver.SocketConfiguration.Builder.ssl(SSLContext) - use `WebServerTlsinsteadio.helidon.webserver.SocketConfiguration.Builder.enabledSSlProtocols(String…) - use `WebServerTlsinstead