Dependencies
- Java 17
- Spring 6
Configuration
Integration of your typical Spring Boot Application with Riptide, Logbook and Tracer can be greatly simplified by using the Riptide: Spring Boot Starter. Go check it out!
Http.builder()
.executor(Executors.newCachedThreadPool())
.requestFactory(new HttpComponentsClientHttpRequestFactory())
.baseUrl("https://api.github.com")
.converter(new MappingJackson2HttpMessageConverter())
.converter(new Jaxb2RootElementHttpMessageConverter())
.plugin(new OriginalStackTracePlugin())
.build();
The following code is the bare minimum, since a request factory is required:
Http.builder()
.executor(Executors.newCachedThreadPool())
.requestFactory(new HttpComponentsClientHttpRequestFactory())
.build();
This defaults to:
- no base URL
- same list of converters as
new RestTemplate()
OriginalStackTracePlugin
Examples
Without queue, elastic size
ThreadPoolExecutors.builder() .withoutQueue() .elasticSize(5, 20) .keepAlive(1, MINUTES) .build()
Bounded queue, fixed size
ThreadPoolExecutors.builder() .boundedQueue(20) .fixedSize(20) .keepAlive(1, MINUTES) .build()
Scale-first, unbounded queue, elastic size
ThreadPoolExecutors.builder() .scaleFirst() .unboundedQueue() .elasticSize(20) .keepAlive(1, MINUTES) .build()
You can read more about scale-first here:
- Java Scale First ExecutorService — A myth or a reality
- How to get the ThreadPoolExecutor to increase threads to max before queueing?
In order to configure the thread pool correctly, please refer to How to set an ideal thread pool size.
URI Template
- parameter expansion, e.g
/{id}
(seeUriTemplate.expand
) - encoding
URI
- none, used as is
- expected to be already encoded
Responses
Riptide is special in the way it handles responses. Rather than having a single return value, you need to register callbacks. Traditionally, you would attach different callbacks for different response status codes. Alternatively, there are built-in routing capabilities on status code families (called series in Spring) as well as on content types.
http.post("/sales-order")
// ...
.dispatch(series(),
on(SUCCESSFUL).dispatch(contentType(),
on(SALES_ORDER).call(SalesOrder.class, this::persist),
on(CLIENT_ERROR).dispatch(status(),
on(CONFLICT).call(this::retry),
on(PRECONDITION_FAILED).call(this::readAgainAndRetry),
anyStatus().call(problemHandling())),
on(SERVER_ERROR).dispatch(status(),
on(SERVICE_UNAVAILABLE).call(this::scheduleRetryLater))));
The callbacks can have the following signatures:
persist(SalesOrder)
retry(ClientHttpResponse)
scheduleRetryLater()
Futures
Riptide will return a CompletableFuture<ClientHttpResponse>
. That means you can choose to chain transformations/callbacks or block
on it.
If you need proper return values take a look at Riptide: Capture.
Exceptions
The only special custom exception you may receive is UnexpectedResponseException
, if and only if there was no matching condition and
no wildcard condition.
Testing
Riptide is built on the same foundation as Spring’s RestTemplate
. That allows us, with a small
trick, to use the same testing facilities, the MockRestServiceServer
:
RestTemplate template = new RestTemplate();
MockRestServiceServer server = MockRestServiceServer.createServer(template);
ClientHttpRequestFactory requestFactory = template.getRequestFactory();
Http.builder()
.requestFactory(requestFactory)
// continue configuration
We basically use an intermediate RestTemplate
as a holder of the special ClientHttpRequestFactory
that the
MockRestServiceServer
manages.
If you are using Spring Boot Starter, the test setup is provided by a convenient annotation @RiptideClientTest
.
See here.
Getting help
If you have questions, concerns, bug reports, etc., please file an issue in this repository’s Issue Tracker.