Riptide 5 requires Java 17 or higher. Riptide 5 requires Spring 7 (Spring Boot 4) or higher.
Riptide 5.0 migrates to Spring Framework 7 and Spring Boot 4 including several breaking changes that affect Riptide users.
Spring Boot 4 migrates from Jackson 2.x to Jackson 3.x including a major package switch from com.fasterxml.jackson to tools.jackson.
Key Changes:
- Jackson group id changed from
com.fasterxml.jackson.*totools.jackson.*(for core modules) - Jackson Annotations remains at
com.fasterxml.jackson.core:jackson-annotationsbut switches to version 2.20+ - If you use Jackson directly in your code, you'll need to update imports:
com.fasterxml.jackson.databind.*→tools.jackson.databind.*com.fasterxml.jackson.core.*→tools.jackson.core.*
Migration Steps:
- Update your Jackson dependencies to Jackson 3.x compatible versions
- Replace all
com.fasterxml.jackson.databindimports withtools.jackson.databind - Replace all
com.fasterxml.jackson.coreimports withtools.jackson.core - Leave
com.fasterxml.jackson.core.jackson-annotationsimports unchanged
Spring Boot 4 renames the primary Jackson HTTP message converter:
Before (Riptide 4.x):
Http.builder()
.converter(new MappingJackson2HttpMessageConverter())
.build();After (Riptide 5.x):
Http.builder()
.converter(new JacksonJsonHttpMessageConverter())
.build();The following Jackson modules are no longer automatically included:
jackson-module-parameter-namesjackson-datatype-jdk8jackson-datatype-problem
Riptide 5.0 removes the dependency on Zalando's problem library in favor of Spring's built-in ProblemDetail support (RFC 9457):
Before (Riptide 4.x):
import org.zalando.problem.Problem;
import org.zalando.problem.Exceptional;
try {
http.post("/").dispatch(series(),
on(SUCCESSFUL).call(pass()),
anySeries().call(problemHandling()))
.join();
} catch (CompletionException e) {
Problem problem = (Problem) e.getCause();
// handle problem
}After (Riptide 5.x):
import org.springframework.http.ProblemDetail;
import org.zalando.riptide.problem.ProblemResponseException;
try {
http.post("/").dispatch(series(),
on(SUCCESSFUL).call(pass()),
anySeries().call(problemHandling()))
.join();
} catch (CompletionException e) {
if (e.getCause() instanceof ProblemResponseException) {
ProblemDetail problem = ((ProblemResponseException) e.getCause()).getProblem();
// handle problem
}
}Migration Steps:
- Replace
org.zalando.problem.Problemwithorg.springframework.http.ProblemDetail - Replace
org.zalando.problem.Exceptionalwithorg.zalando.riptide.problem.ProblemResponseException - Update exception handling to unwrap
ProblemDetailfromProblemResponseException - Remove
org.zalando:problemdependency as it is no longer needed - Remove
org.zalando:jackson-datatype-problemdependency
MediaType.SPECIFICITY_COMPARATOR Removed:
Spring 7 removed the deprecated MediaType.SPECIFICITY_COMPARATOR. Riptide now uses a custom comparator internally based on MediaType.isMoreSpecific().
This change is Riptide internal and should not affect user code unless you were directly using this constant.
Spring Boot 4 reorganizes some autoconfiguration classes into more specific packages:
Jackson AutoConfiguration:
- Old:
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration - New:
org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration
Metrics AutoConfiguration:
- Old:
org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration - New:
org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConfiguration
If you're importing these autoconfiguration classes in your tests (e.g., with @ImportAutoConfiguration), you'll need to update the import statements.
Spring Framework 7 removed ListenableFuture and related classes. As a result, Riptide 5.0 removes the CompletableToListenableFutureAdapter from the riptide-compatibility module.
Removed class:
org.zalando.riptide.compatibility.CompletableToListenableFutureAdapter
Migration:
If you were using this adapter, you need to migrate to CompletableFuture directly. Spring Framework 7 recommends using CompletableFuture or reactive types (Reactor, RxJava) instead of ListenableFuture.
Before (Riptide 4.x):
ListenableFuture<ClientHttpResponse> future =
new CompletableToListenableFutureAdapter<>(http.get("/api").call(pass()));After (Riptide 5.x):
CompletableFuture<ClientHttpResponse> future = http.get("/api").call(pass());The following dependencies have been updated:
| Dependency | Riptide 4.x | Riptide 5.x |
|---|---|---|
| Spring Framework | 6.2.x | 7.0.x |
| Spring Boot | 3.1.x | 4.0.x |
| Jackson | 2.18.x | 3.0.x |
| Apache HttpClient | 5.3.x | 5.5.x |
| JUnit Jupiter | 5.12.x | 6.0.x |
| Logbook | 3.11.x | 4.0.0-RC.1 |
If you use Jackson in your tests, update your test setup:
Before:
private static MappingJackson2HttpMessageConverter createJsonConverter() {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(new ObjectMapper().findAndRegisterModules());
return converter;
}After:
import tools.jackson.databind.json.JsonMapper;
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
private static JacksonJsonHttpMessageConverter createJsonConverter() {
var mapper = JsonMapper.builder().build();
return new JacksonJsonHttpMessageConverter(mapper);
}The riptide-stream module has been updated to use Jackson 3.x APIs. If you're using the Streams module, note the following changes:
ObjectMapper replaced with JsonMapper:
Before (Riptide 4.x):
import com.fasterxml.jackson.databind.ObjectMapper;
StreamConverter<T> converter = Streams.streamConverter(new ObjectMapper());After (Riptide 5.x):
import tools.jackson.databind.json.JsonMapper;
StreamConverter<T> converter = Streams.streamConverter(new JsonMapper());Internal API changes:
ObjectMapper.getFactory().createParser()→JsonMapper.createParser()parser.getCodec().readValue()→parser.readValueAs()- Removed deprecated
TypeFactory.constructType()overload with context class
These changes are mostly internal to the Streams module, but if you're extending or customizing the stream converters, you'll need to update to the Jackson 3.x APIs.
- Spring 7 / Spring Boot 4 required - Update all Spring dependencies
- Jackson 3.x migration - Update Jackson imports and dependencies
- HTTP Message Converter renamed - Use
JacksonJsonHttpMessageConverterinstead ofMappingJackson2HttpMessageConverter - Problem library removed - Use Spring's
ProblemDetailinstead of Zalando'sProblem - Removed Jackson modules - Explicitly add if needed:
jackson-module-parameter-names,jackson-datatype-jdk8 - AutoConfiguration package changes - Update imports for
JacksonAutoConfigurationandMetricsAutoConfiguration - Compatibility module -
CompletableToListenableFutureAdapterremoved; useCompletableFuturedirectly - Streams module - Use
JsonMapperinstead ofObjectMapperfor stream converters - Dependency updates - Apache HttpClient 5.5.x, JUnit Jupiter 6.x, Logbook 4.x
- Update to Spring Boot 4.0.0 or later
- Update Jackson imports from
com.fasterxml.jacksontotools.jacksonwhere applicable - Replace
MappingJackson2HttpMessageConverterwithJacksonJsonHttpMessageConverter - Replace Zalando
Problemwith SpringProblemDetail - Update exception handling for
ProblemResponseException - Remove
org.zalando:problemandorg.zalando:jackson-datatype-problemdependencies if no longer needed - Add back any Jackson modules you need explicitly
- Update autoconfiguration imports (
JacksonAutoConfiguration,MetricsAutoConfiguration) - Replace
CompletableToListenableFutureAdapterusage withCompletableFuture(if using compatibility module) - If using Streams module, replace
ObjectMapperwithJsonMapperin stream converters - Update test code to use Jackson 3.x APIs
- Test thoroughly - Jackson 3.x has behavioral changes
Riptide 4 requires Java 17 or up. Riptide 4 requires Spring 6 or up.
Riptide now requires Failsafe 3.3.x
There are many breaking changes between Failsafe version 2.4.3 and version 3.3.0,
see Failsafe CHANGELOG for all details.
Here are some of the breaking changes that can affect riptide-failsafe users:
- The maven group id for Failsafe has changed to
dev.failsafe - All files have been moved to the
dev.failsafepackage Scheduler,DefaultScheduledFutureandPolicyExecutorwere moved to the spi package- All policies now use a builder API instead of constructors
DelayFunctioninterface has been removed,ContextualSuppliershould be used instead since it provides access to the same informationCircuitBreakerBuilderonOpen,onClose, andonHalfOpenmethods now accept anEventListener<CircuitBreakerStateChangedEvent>argument
Since Spring 5, AsyncRestTemplate is deprecated in favor of WebClient.
For that reason, we have removed AsyncHttpOperations from riptide-compatibility layer.
Since Spring 6, AsyncRestTemplate and all corresponding classes like AsyncClientHttpRequestFactory have been removed.
For that reason, we have removed NonBlockingIO from riptide-core to avoid additionally migrating to WebClient.
The same reason applies to the removal of HttpOutputMessageAsyncClientHttpRequestAdapter from riptide-compatibility-layer.
Apache HttpClient 5 removed the definition of whether a specific HTTP method is allowed to have a body or not. Due to
this StreamingApacheClientHttpRequest::setBody will not throw an exception anymore.
The SpanDecorators obtained by ServiceLoaderSpanDecorator
(via the ServiceLoader facility)
are loaded eagerly and only once.
As the project is in maintenance mode and no changes are planned anymore including support for newer Spring versions, it was decided to include an adapted copy into Riptide itself, so the dependency is not needed anymore.
Riptide 3 requires Spring 4.1.7 or up. The Starter and Auto Configuration require Spring Boot 2.2 though.
If you are running Spring Boot 1.x with Spring 4, consider using Riptide 2.x.
Modules have been added/removed/changed and the configuration structure in your application.yml will most like require change. Please read the following sections carefully:
- Authorization
- Caching
- Chaos and Fault Injection
- Async/RestTemplate Compatibility
- Idempotency Detection
- Native Logbook support
- OpenTracing
- SOAP
Riptide 2.x had a
PluginInterceptor which allowed to use a Riptide Plugin in an ordinary RestTemplate or AsyncRestTemplate. The Riptide Spring Boot Auto Configuration also made use of that and registered both kinds of templates for each configured client, allowing to inject them instead of an Http instance:
@Autowired
Client(RestTemplate example) {
// ...
}Both, the PluginInterceptor as well as pre-configured templates have been removed. As a cleaner alternative Riptide now offers a Compatibility module which includes custom implementations of Spring's RestOperations and AsyncRestOperations which use Riptide under the hood. The Auto Configuration registers an instance of both automatically:
@Autowired
Client(RestOperations example) {
// ...
}Riptide now requires Failsafe 2.x
- The
TimeoutPlugin(riptide-timeout) has been removed in favor of Failsafe'sTimeoutpolicy - The
BackupRequestPlugin(riptide-backup) has been removed in favor of a custom FailsafeBackupRequestpolicy - The
FailsafePluginno longer needs a custom scheduler but rather uses Failsafe's default
Riptide now requires Logbook 2.x
Riptide used to rely on Logbook's integration for the Apache HTTP Client. The usages of the LogbookHttpRequestInterceptor and LogbookHttpResponseInterceptor have been replaced by a LogbookPlugin.
Logbook instance by hand, e.g. by doing this:
@ImportAutoConfiguration(LogbookAutoConfiguration.class)
class MyTest {
// ...
}Module, package and plugin was renamed:
riptide-metricsis nowriptide-micrometerorg.zalando.riptide.metricsis noworg.zalando.riptide.micrometerMetricsPluginis nowMicrometerPlugin
STUPS OAuth 2.0 Token support has been dropped in favor of K8s.
The Apache HTTP client specific GzipHttpRequestInterceptor has been replaced with a request factory agnostic RequestCompressionPlugin.
Riptide 2.x was looking for specific beans named meterRegistry, logbook or tracer during the construction of appropriate plugins. This behaviour changed to resolution by type.
enabled flag:
- added
riptide.defaults.backup-request.enabled(default:false) - added
riptide.defaults.certificate-pinning.enabled(default:false) - added
riptide.defaults.circuit-breaker.enabled(default:false) - added
riptide.defaults.logging.enabled(default:false️) - added
riptide.defaults.metrics.enabled(default:false) - added
riptide.defaults.auth.enabled(default:false) - added
riptide.defaults.request-compression.enabled(default:false) - added
riptide.defaults.retry.enabled(default:false) - added
riptide.defaults.retry.backoff.enabled(default:false) - added
riptide.defaults.stack-trace-preservation.enabled(default:true) - added
riptide.defaults.timeouts.enabled(default:false) - added
riptide.defaults.transient-fault-detection.enabled(default:false)
| Before | After |
|---|---|
riptide.oauth.credentials-directory |
riptide.defaults.auth.credentials-directory |
riptide.defaults.keystore.password |
riptide.defaults.certificate-pinning.keystore.password |
riptide.defaults.keystore.path |
riptide.defaults.certificate-pinning.keystore.path |
riptide.defaults.connect-timeout |
riptide.defaults.connections.connect-timeout |
riptide.defaults.max-connections-per-route |
riptide.defaults.connections.max-per-route |
riptide.defaults.max-connections-total |
riptide.defaults.connections.max-total |
riptide.defaults.socket-timeout |
riptide.defaults.connections.socket-timeout |
riptide.defaults.connection-time-to-live |
riptide.defaults.connections.time-to-live |
riptide.defaults.record-metrics |
riptide.defaults.metrics.enabled |
riptide.defaults.compress-request |
riptide.defaults.request-compression.enabled |
riptide.defaults.preserve-stack-trace |
riptide.defaults.stack-trace-preservation.enabled |
riptide.defaults.thread-pool.keep-alive |
riptide.defaults.threads.keep-alive |
riptide.defaults.thread-pool.max-size |
riptide.defaults.threads.max-size |
riptide.defaults.thread-pool.min-size |
riptide.defaults.threads.min-size |
riptide.defaults.thread-pool.queue-size |
riptide.defaults.threads.queue-size |
riptide.defaults.timeout |
riptide.defaults.timeouts.global |
riptide.defaults.detect-transient-faults |
riptide.defaults.transient-fault-detection.enabled |
riptide.clients.<id>.keystore.password |
riptide.clients.<id>.certificate-pinning.keystore.password |
riptide.clients.<id>.keystore.path |
riptide.clients.<id>.certificate-pinning.keystore.path |
riptide.clients.<id>.connect-timeout |
riptide.clients.<id>.connections.connect-timeout |
riptide.clients.<id>.max-connections-per-route |
riptide.clients.<id>.connections.max-per-route |
riptide.clients.<id>.max-connections-total |
riptide.clients.<id>.connections.max-total |
riptide.clients.<id>.socket-timeout |
riptide.clients.<id>.connections.socket-timeout |
riptide.clients.<id>.connection-time-to-live |
riptide.clients.<id>.connections.time-to-live |
riptide.clients.<id>.record-metrics |
riptide.clients.<id>.metrics.enabled |
riptide.clients.<id>.compress-request |
riptide.clients.<id>.request-compression.enabled |
riptide.clients.<id>.preserve-stack-trace |
riptide.clients.<id>.stack-trace-preservation.enabled |
riptide.clients.<id>.thread-pool.keep-alive |
riptide.clients.<id>.threads.keep-alive |
riptide.clients.<id>.thread-pool.max-size |
riptide.clients.<id>.threads.max-size |
riptide.clients.<id>.thread-pool.min-size |
riptide.clients.<id>.threads.min-size |
riptide.clients.<id>.thread-pool.queue-size |
riptide.clients.<id>.threads.queue-size |
riptide.clients.<id>.timeout |
riptide.clients.<id>.timeouts.global |
riptide.clients.<id>.detect-transient-faults |
riptide.clients.<id>.transient-fault-detection.enabled |
- removed
riptide.oauth.access-token-url - removed
riptide.oauth.scheduling-period - removed
riptide.oauth.connect-timeout - removed
riptide.oauth.socket-timeout - removed
riptide.clients.<id>.oauth.access-token-url - removed
riptide.clients.<id>.oauth.scheduling-period - removed
riptide.clients.<id>.oauth.connect-timeout - removed
riptide.clients.<id>.oauth.socket-timeout
Riptide now uses OpenTracing Flow-ID instead of Tracer. OpenTracing is now a prerequisite for X-Flow-ID support.
Riptide used to rely on Tracer's integration for the Apache HTTP Client. The usages of the TracerHttpRequestInterceptor have been replaced partially by the OpenTracingPlugin and the new FlowHttpRequestInterceptor (provided by opentracing-flowid-httpclient).